<?php

declare(strict_types=1);

namespace App\Tests\Module\Catalog\Api;

use DateTimeImmutable;

/**
 * Tests du contrat de pagination sur GET /api/categories (ERP-72).
 *
 * Invariants testes :
 *  - la collection expose les metadonnees Hydra (totalItems, view, member) ;
 *  - itemsPerPage est plafonne au maximum global (50) ;
 *  - une page hors limites retourne une collection vide, pas une 500 ;
 *  - ?pagination=false retourne tous les items sans troncature (select-box) ;
 *  - la pagination est compatible avec le flag ?includeDeleted=true.
 *
 * @internal
 */
final class CategoryPaginationTest extends AbstractCatalogApiTestCase
{
    /**
     * La collection expose les metadonnees de pagination JSON-LD sans prefixe :
     * `totalItems`, `view`, `member` (convention API Platform 4, pas hydra:*).
     *
     * On cree 12 categories pour depasser la limite par page (10) : la cle
     * `view` n'est presente que lorsqu'il y a plus d'items que la taille de page.
     */
    public function testCollectionExposesHydraPaginationMetadata(): void
    {
        $type = $this->createCategoryType();
        for ($i = 1; $i <= 12; ++$i) {
            $this->createCategory(self::TEST_CATEGORY_PREFIX.'meta_'.$i, $type);
        }

        $client   = $this->createAdminClient();
        $response = $client->request('GET', '/api/categories');

        self::assertSame(200, $response->getStatusCode());

        $data = $response->toArray();
        self::assertArrayHasKey('totalItems', $data, 'La collection doit exposer totalItems.');
        self::assertArrayHasKey('view', $data, 'La collection doit exposer view (pagination) quand totalItems > itemsPerPage.');
        self::assertIsArray($data['member'], 'member doit etre un tableau.');
    }

    /**
     * Un itemsPerPage arbitrairement grand (99999) doit etre plafonne au
     * maximum global configure (50). On cree 12 categories pour etre certain
     * de disposer de donnees ; le cap doit s'appliquer quelle que soit la taille
     * reelle de la collection.
     */
    public function testItemsPerPageIsCappedAtMaximum(): void
    {
        $type = $this->createCategoryType();
        for ($i = 1; $i <= 12; ++$i) {
            $this->createCategory(self::TEST_CATEGORY_PREFIX.'cap_'.$i, $type);
        }

        $client   = $this->createAdminClient();
        $response = $client->request('GET', '/api/categories?itemsPerPage=99999');

        self::assertSame(200, $response->getStatusCode());
        // Le cap global est 50 : jamais plus d'items par page que le maximum.
        self::assertLessThanOrEqual(
            50,
            count($response->toArray()['member']),
            'itemsPerPage doit etre plafonne au maximum global (50).',
        );
    }

    /**
     * Une page tres elevee (99999) sur une petite collection ne doit pas
     * produire une 500 PG (OFFSET negatif ou depassement de capacite) mais
     * retourner 200 avec un tableau member vide.
     */
    public function testOutOfBoundPageReturnsEmptyCollectionNot500(): void
    {
        $type = $this->createCategoryType();
        $this->createCategory(self::TEST_CATEGORY_PREFIX.'oob', $type);

        $client   = $this->createAdminClient();
        $response = $client->request('GET', '/api/categories?page=99999');

        self::assertSame(200, $response->getStatusCode());
        // La page 99999 est forcement vide (on a bien moins que 99999*10 items).
        self::assertSame(
            [],
            $response->toArray()['member'],
            'Une page hors limites doit retourner un member vide, jamais une 500.',
        );
    }

    /**
     * ?pagination=false permet au frontend de desactiver la pagination pour
     * alimenter un select-box. On cree exactement 12 categories dont les noms
     * commencent par `test_cat_select_` : le filtre sur ce prefixe isole nos
     * entrees des donnees concurrentes et prouve que les 12 items sont tous
     * retournes (et pas seulement les 10 premiers de la page 1).
     */
    public function testClientCanDisablePaginationToFeedASelect(): void
    {
        $type = $this->createCategoryType();
        for ($i = 1; $i <= 12; ++$i) {
            $this->createCategory(self::TEST_CATEGORY_PREFIX.'select_'.$i, $type);
        }

        $client   = $this->createAdminClient();
        $response = $client->request('GET', '/api/categories?pagination=false');

        self::assertSame(200, $response->getStatusCode());

        $members = $response->toArray()['member'];

        // Filtre sur le sous-prefixe pour ne pas comptabiliser les categories
        // d'autres tests qui partagent la meme base de donnees.
        $selectItems = array_values(array_filter(
            $members,
            fn (array $m): bool => str_starts_with($m['name'], self::TEST_CATEGORY_PREFIX.'select_'),
        ));

        self::assertCount(
            12,
            $selectItems,
            '?pagination=false doit retourner toutes les categories (pas seulement la page 1).',
        );
    }

    /**
     * La pagination doit fonctionner conjointement avec le flag ?includeDeleted=true.
     * On seed 3 categories actives + 2 soft-deleted, on demande itemsPerPage=5 :
     * la page 1 doit contenir exactement 5 items et totalItems doit valoir >= 5.
     */
    public function testPaginationCombinedWithIncludeDeletedFlag(): void
    {
        $type = $this->createCategoryType();

        // 3 categories actives.
        for ($i = 1; $i <= 3; ++$i) {
            $this->createCategory(self::TEST_CATEGORY_PREFIX.'pag_active_'.$i, $type);
        }

        // 2 categories soft-deleted.
        for ($i = 1; $i <= 2; ++$i) {
            $this->createCategory(
                self::TEST_CATEGORY_PREFIX.'pag_deleted_'.$i,
                $type,
                new DateTimeImmutable(),
            );
        }

        $client   = $this->createAdminClient();
        $response = $client->request('GET', '/api/categories?includeDeleted=true&itemsPerPage=5');

        self::assertSame(200, $response->getStatusCode());

        $data = $response->toArray();
        // La page retournee ne doit pas exceder itemsPerPage=5.
        self::assertCount(
            5,
            $data['member'],
            'La page 1 doit contenir exactement 5 items (itemsPerPage=5 avec >= 5 categories disponibles).',
        );
        self::assertGreaterThanOrEqual(
            5,
            $data['totalItems'],
            'totalItems doit refleter au moins les 5 categories seedees (actives + soft-deleted).',
        );
    }
}
