<?php

declare(strict_types=1);

namespace App\Tests\Module\Catalog\Api;

use App\Module\Catalog\Domain\Entity\Category;
use DateTimeImmutable;

/**
 * Tests RG-1.12 / RG-1.13 : suppression et soft-delete de Category.
 *
 *  - RG-1.12 : DELETE pose `deletedAt` au lieu d'un hard delete (la ligne
 *    reste en BDD avec `deleted_at IS NOT NULL`) et renvoie 204.
 *  - RG-1.13 : PATCH ne peut pas ecrire `deletedAt` (groupe `category:write`
 *    l'exclut), donc une tentative d'override est silencieusement ignoree.
 *  - Provider sur PATCH/DELETE : 404 si la categorie cible est deja
 *    soft-deleted (cf. CategoryProvider, ticket 0.3).
 *
 * @internal
 */
final class CategoryDeleteTest extends AbstractCatalogApiTestCase
{
    public function testDeleteReturns204AndPersistsSoftDelete(): void
    {
        $category   = $this->createCategory();
        $categoryId = $category->getId();

        $client = $this->createAdminClient();
        $client->request('DELETE', '/api/categories/'.$categoryId);

        self::assertResponseStatusCodeSame(204);

        // RG-1.12 : la ligne doit toujours exister en BDD avec deletedAt non null.
        $em = $this->getEm();
        $em->clear();

        /** @var null|Category $reloaded */
        $reloaded = $em->getRepository(Category::class)->find($categoryId);
        self::assertNotNull($reloaded, 'La ligne ne doit PAS etre supprimee physiquement (soft delete).');
        self::assertNotNull($reloaded->getDeletedAt(), 'deletedAt doit etre rempli apres DELETE.');
    }

    public function testPatchCannotSetDeletedAt(): void
    {
        // RG-1.13 : le groupe `category:write` ne contient pas `deletedAt`,
        // donc une tentative d'override doit etre silencieusement ignoree.
        $category   = $this->createCategory();
        $categoryId = $category->getId();
        self::assertNull($category->getDeletedAt());

        $client = $this->createAdminClient();
        $client->request('PATCH', '/api/categories/'.$categoryId, [
            'headers' => ['Content-Type' => 'application/merge-patch+json'],
            'json'    => [
                'deletedAt' => new DateTimeImmutable()->format(DateTimeImmutable::ATOM),
            ],
        ]);

        // Le code precis depend d'API Platform : 200 (champ ignore) ou 400.
        // Quoi qu'il arrive, deletedAt en BDD doit rester null.
        $em = $this->getEm();
        $em->clear();

        /** @var Category $reloaded */
        $reloaded = $em->getRepository(Category::class)->find($categoryId);
        self::assertNull(
            $reloaded->getDeletedAt(),
            'PATCH ne doit JAMAIS pouvoir ecrire deletedAt (RG-1.13).',
        );
    }

    public function testPatchOnSoftDeletedReturns404(): void
    {
        // Le Provider est cable sur PATCH (cf. Category::class § Patch). Une
        // categorie deja soft-deletee n'est pas visible en lecture, donc le
        // PATCH doit recevoir 404 (route resolved by API Platform retournee
        // par le provider) — comme un Get unitaire (RG-1.11 etendue).
        $category = $this->createCategory(
            null,
            null,
            new DateTimeImmutable(),
        );
        $client = $this->createAdminClient();
        $client->request('PATCH', '/api/categories/'.$category->getId(), [
            'headers' => ['Content-Type' => 'application/merge-patch+json'],
            'json'    => ['name' => self::TEST_CATEGORY_PREFIX.'try_patch'],
        ]);

        self::assertResponseStatusCodeSame(404);
    }

    public function testDeleteOnSoftDeletedReturns404(): void
    {
        // Idem PATCH : un DELETE sur une categorie deja soft-deletee est un
        // 404 (le Provider la masque), pas une operation idempotente silencieuse.
        $category = $this->createCategory(
            null,
            null,
            new DateTimeImmutable(),
        );
        $client = $this->createAdminClient();
        $client->request('DELETE', '/api/categories/'.$category->getId());

        self::assertResponseStatusCodeSame(404);
    }
}
