<?php

declare(strict_types=1);

namespace App\Tests\Module\Catalog\Api;

/**
 * Tests RG-1.07 : unicite case-insensitive de (LOWER(name), category_type_id)
 * parmi les categories non soft-deleted. L'index Postgres partiel
 * `uq_category_name_type_active` est traduit en 409 Conflict par le
 * CategoryProcessor.
 *
 * Cas couverts :
 *  - doublon strict (meme name + meme type) → 409 ;
 *  - doublon case-insensitive (Vis / vis sur meme type) → 409 ;
 *  - meme name sur 2 types differents → les deux passent (pas de doublon) ;
 *  - recreation apres soft delete → 201 (l'index partiel libere le couple).
 *
 * @internal
 */
final class CategoryUniqueTest extends AbstractCatalogApiTestCase
{
    public function testDuplicateNameSameTypeReturns409(): 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',
                'categoryType' => '/api/category_types/'.$type->getId(),
            ],
        ]);
        self::assertResponseStatusCodeSame(201);

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

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

    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',
                'categoryType' => '/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',
                'categoryType' => '/api/category_types/'.$type->getId(),
            ],
        ]);
        self::assertSame(409, $response->getStatusCode());
    }

    public function testSameNameDifferentTypeAllowed(): void
    {
        // RG-1.07 : la contrainte est SUR (name, type), pas sur name seul.
        // Le meme nom doit etre acceptable sur deux types differents.
        $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',
                'categoryType' => '/api/category_types/'.$type1->getId(),
            ],
        ]);
        self::assertResponseStatusCodeSame(201);

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

    public function testRecreateAfterSoftDeleteAllowed(): void
    {
        // RG-1.07 : l'index Postgres est partiel (WHERE deleted_at IS NULL).
        // Apres un soft delete, le couple (name, type) 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',
                'categoryType' => '/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 + meme type → autorise (couple libere).
        $client->request('POST', '/api/categories', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'         => self::TEST_CATEGORY_PREFIX.'recreate',
                'categoryType' => '/api/category_types/'.$type->getId(),
            ],
        ]);
        self::assertResponseStatusCodeSame(201);
    }
}
