<?php

declare(strict_types=1);

namespace App\Tests\Module\Catalog\Api;

/**
 * Tests RG-1.07 : unicite case-insensitive du nom GLOBALEMENT (LOWER(name))
 * parmi les categories non soft-deleted. Depuis le passage en ManyToMany,
 * l'unicite n'est plus liee au type. L'index Postgres partiel
 * `uq_category_name_active` est traduit en 409 Conflict par le CategoryProcessor.
 *
 * Cas couverts :
 *  - doublon strict (meme name) → 409 ;
 *  - doublon case-insensitive (Vis / VIS) → 409 ;
 *  - meme name avec des types differents → 409 (unicite GLOBALE) ;
 *  - recreation apres soft delete → 201 (l'index partiel libere le nom).
 *
 * @internal
 */
final class CategoryUniqueTest extends AbstractCatalogApiTestCase
{
    public function testDuplicateNameReturns409(): void
    {
        $type   = $this->createCategoryType();
        $client = $this->createAdminClient();

        // 1er POST : doit reussir.
        $client->request('POST', '/api/categories', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'          => self::TEST_CATEGORY_PREFIX.'unique',
                'categoryTypes' => ['/api/category_types/'.$type->getId()],
            ],
        ]);
        self::assertResponseStatusCodeSame(201);

        // 2eme POST : meme name → doublon (unicite globale).
        $response = $client->request('POST', '/api/categories', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'          => self::TEST_CATEGORY_PREFIX.'unique',
                'categoryTypes' => ['/api/category_types/'.$type->getId()],
            ],
        ]);
        self::assertSame(409, $response->getStatusCode());

        // Message attendu par la spec RG-1.07 (reformulee, sans "pour ce type").
        $payload     = $response->toArray(false);
        $description = $payload['description'] ?? $payload['detail'] ?? $payload['hydra:description'] ?? '';
        self::assertStringContainsString(
            'existe déjà',
            $description,
            'Le message d\'erreur 409 doit citer la spec ("existe deja").',
        );
    }

    public function testDuplicateNameCaseInsensitiveReturns409(): void
    {
        // RG-1.07 : la collision est case-insensitive (index sur LOWER(name)).
        $type   = $this->createCategoryType();
        $client = $this->createAdminClient();

        $client->request('POST', '/api/categories', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'          => self::TEST_CATEGORY_PREFIX.'Vis',
                'categoryTypes' => ['/api/category_types/'.$type->getId()],
            ],
        ]);
        self::assertResponseStatusCodeSame(201);

        $response = $client->request('POST', '/api/categories', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                // Meme prefix mais variation de casse → meme LOWER → collision.
                'name'          => self::TEST_CATEGORY_PREFIX.'VIS',
                'categoryTypes' => ['/api/category_types/'.$type->getId()],
            ],
        ]);
        self::assertSame(409, $response->getStatusCode());
    }

    public function testSameNameDifferentTypeReturns409(): void
    {
        // RG-1.07 (reformulee) : l'unicite du nom est desormais GLOBALE — le
        // meme nom sur deux types differents est un doublon.
        $type1  = $this->createCategoryType();
        $type2  = $this->createCategoryType();
        $client = $this->createAdminClient();

        $client->request('POST', '/api/categories', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'          => self::TEST_CATEGORY_PREFIX.'shared',
                'categoryTypes' => ['/api/category_types/'.$type1->getId()],
            ],
        ]);
        self::assertResponseStatusCodeSame(201);

        $response = $client->request('POST', '/api/categories', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'          => self::TEST_CATEGORY_PREFIX.'shared',
                'categoryTypes' => ['/api/category_types/'.$type2->getId()],
            ],
        ]);
        self::assertSame(409, $response->getStatusCode());
    }

    public function testRecreateAfterSoftDeleteAllowed(): void
    {
        // RG-1.07 : l'index Postgres est partiel (WHERE deleted_at IS NULL).
        // Apres un soft delete, le nom est libere et un nouveau POST identique
        // doit reussir.
        $type   = $this->createCategoryType();
        $client = $this->createAdminClient();

        // 1) creation
        $response = $client->request('POST', '/api/categories', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'          => self::TEST_CATEGORY_PREFIX.'recreate',
                'categoryTypes' => ['/api/category_types/'.$type->getId()],
            ],
        ]);
        self::assertSame(201, $response->getStatusCode());
        $created = $response->toArray();

        // 2) soft delete
        $client->request('DELETE', '/api/categories/'.$created['id']);
        self::assertResponseStatusCodeSame(204);

        // 3) recreation : meme name → autorise (nom libere par l'archivage).
        $client->request('POST', '/api/categories', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'          => self::TEST_CATEGORY_PREFIX.'recreate',
                'categoryTypes' => ['/api/category_types/'.$type->getId()],
            ],
        ]);
        self::assertResponseStatusCodeSame(201);
    }
}
