<?php

declare(strict_types=1);

namespace App\Tests\Module\Catalog\Api;

/**
 * Tests des filtres de la liste admin sur GET /api/categories :
 *  - `?name=` : recherche partielle case-insensitive sur le nom ;
 *  - `?typeId[]=` : categories portant AU MOINS UN des types coches (OR), sans
 *    doublon meme pour une categorie multi-types ;
 *  - combinaison `?name=` + `?typeId[]=` (ET entre filtres).
 *
 * @internal
 */
final class CategoryFilterTest extends AbstractCatalogApiTestCase
{
    /**
     * @param array<int, array<string, mixed>> $members
     *
     * @return list<string>
     */
    private function testNames(array $members): array
    {
        $names = array_map(static fn (array $m): string => $m['name'], $members);
        $names = array_values(array_filter(
            $names,
            fn (string $n): bool => str_starts_with($n, self::TEST_CATEGORY_PREFIX),
        ));
        sort($names);

        return $names;
    }

    public function testNameFilterIsPartialAndCaseInsensitive(): void
    {
        $type = $this->createCategoryType();
        $this->createCategory(self::TEST_CATEGORY_PREFIX.'Acier inox', $type);
        $this->createCategory(self::TEST_CATEGORY_PREFIX.'Aluminium', $type);

        $client   = $this->createAdminClient();
        $response = $client->request('GET', '/api/categories?name=ACIER&pagination=false');
        self::assertSame(200, $response->getStatusCode());

        self::assertSame(
            [self::TEST_CATEGORY_PREFIX.'Acier inox'],
            $this->testNames($response->toArray()['member']),
            'Le filtre ?name= doit etre partiel et insensible a la casse.',
        );
    }

    public function testTypeIdFilterReturnsCategoriesWithAtLeastOneType(): void
    {
        $typeA = $this->createCategoryType();
        $typeB = $this->createCategoryType();
        $typeC = $this->createCategoryType();

        $this->createCategory(self::TEST_CATEGORY_PREFIX.'only_a', $typeA);
        $this->createCategory(self::TEST_CATEGORY_PREFIX.'only_b', $typeB);
        $this->createCategory(self::TEST_CATEGORY_PREFIX.'only_c', $typeC);

        $client   = $this->createAdminClient();
        $response = $client->request(
            'GET',
            sprintf('/api/categories?typeId[]=%d&typeId[]=%d&pagination=false', $typeA->getId(), $typeB->getId()),
        );
        self::assertSame(200, $response->getStatusCode());

        self::assertSame(
            [
                self::TEST_CATEGORY_PREFIX.'only_a',
                self::TEST_CATEGORY_PREFIX.'only_b',
            ],
            $this->testNames($response->toArray()['member']),
            'Le filtre ?typeId[]= doit remonter les categories portant AU MOINS UN des types (OR).',
        );
    }

    public function testMultiTypeCategoryAppearsOnceWhenFilteredByOneType(): void
    {
        // Une categorie portant deux types ne doit pas etre dupliquee quand on
        // filtre sur l'un de ses types (la sous-requete EXISTS evite les doublons).
        $typeA = $this->createCategoryType();
        $typeB = $this->createCategoryType();

        $this->createCategory(
            self::TEST_CATEGORY_PREFIX.'multi',
            $typeA,
            null,
            [$typeB],
        );

        $client   = $this->createAdminClient();
        $response = $client->request(
            'GET',
            sprintf('/api/categories?typeId[]=%d&pagination=false', $typeA->getId()),
        );
        self::assertSame(200, $response->getStatusCode());

        $members = $response->toArray()['member'];
        self::assertSame(
            [self::TEST_CATEGORY_PREFIX.'multi'],
            $this->testNames($members),
            'La categorie multi-types ne doit apparaitre qu une seule fois.',
        );

        // Les deux types restent embarques (la collection n'est pas tronquee).
        $multi = array_values(array_filter(
            $members,
            fn (array $m): bool => $m['name'] === self::TEST_CATEGORY_PREFIX.'multi',
        ))[0];
        self::assertCount(2, $multi['categoryTypes'], 'Les 2 types doivent rester embarques malgre le filtre.');
    }

    public function testNameAndTypeIdFiltersCombine(): void
    {
        $typeA = $this->createCategoryType();
        $typeB = $this->createCategoryType();

        $this->createCategory(self::TEST_CATEGORY_PREFIX.'steel_a', $typeA);
        $this->createCategory(self::TEST_CATEGORY_PREFIX.'steel_b', $typeB);
        $this->createCategory(self::TEST_CATEGORY_PREFIX.'wood_a', $typeA);

        $client   = $this->createAdminClient();
        $response = $client->request(
            'GET',
            sprintf('/api/categories?name=steel&typeId[]=%d&pagination=false', $typeA->getId()),
        );
        self::assertSame(200, $response->getStatusCode());

        self::assertSame(
            [self::TEST_CATEGORY_PREFIX.'steel_a'],
            $this->testNames($response->toArray()['member']),
            'Les filtres ?name= et ?typeId[]= doivent se combiner (ET).',
        );
    }
}
