<?php

declare(strict_types=1);

namespace App\Tests\Module\Commercial\Api;

/**
 * Tests de structure / migration M1 (ERP-60).
 *
 * Verifie la decision Q4 (29/05/2026) au niveau du schema Postgres :
 *  - l'unique index partiel fonctionnel uq_client_company_name_active existe
 *    (un seul, sur LOWER(company_name), partiel sur les actifs non archives /
 *    non supprimes) — seule unicite metier conservee (RG-1.16) ;
 *  - les anciens index uq_client_siren_active (RG-1.15) et uq_client_email_active
 *    (RG-1.17) ont ete supprimes / ne sont jamais crees.
 *
 * Verifie aussi la refonte contact (Version20260603120000) : les 5 colonnes de
 * contact principal inline ont ete supprimees de la table `client`.
 *
 * @internal
 */
final class ClientMigrationTest extends AbstractCommercialApiTestCase
{
    /**
     * Refonte contact : first_name / last_name / phone_primary / phone_secondary
     * / email ne doivent plus exister sur la table `client` (deplaces vers
     * client_contact). NB : le backfill de la migration ne s'exerce que sur une
     * base portant des donnees pre-refonte ; sur le schema de test (table client
     * vierge au moment de la migration) il est un no-op, donc non assertable ici
     * au runtime — seul l'etat de schema final est verifie.
     */
    public function testInlineContactColumnsAreDropped(): void
    {
        self::bootKernel();

        /** @var list<array{column_name: string}> $columns */
        $columns = $this->getEm()->getConnection()->fetchAllAssociative(
            "SELECT column_name FROM information_schema.columns "
            ."WHERE table_schema = 'public' AND table_name = 'client'",
        );
        $names = array_map(static fn (array $r): string => $r['column_name'], $columns);

        foreach (['first_name', 'last_name', 'phone_primary', 'phone_secondary', 'email'] as $dropped) {
            self::assertNotContains(
                $dropped,
                $names,
                sprintf('La colonne client.%s aurait du etre supprimee (refonte contact).', $dropped),
            );
        }
    }

    public function testCompanyNameActivePartialIndexExistsExactlyOnce(): void
    {
        $rows = $this->clientIndexes();

        $companyNameIndexes = array_filter(
            $rows,
            static fn (array $r): bool => 'uq_client_company_name_active' === $r['indexname'],
        );

        self::assertCount(
            1,
            $companyNameIndexes,
            'Il doit exister exactement UN index uq_client_company_name_active.',
        );

        // Confirme la nature fonctionnelle (LOWER) + partielle (WHERE) de l'index.
        // Postgres serialise l'expression sous la forme `lower((company_name)::text)`,
        // d'ou des verifications de sous-chaines distinctes.
        $def = strtolower((string) array_values($companyNameIndexes)[0]['indexdef']);
        self::assertStringContainsString('unique', $def);
        self::assertStringContainsString('lower', $def);
        self::assertStringContainsString('company_name', $def);
        self::assertStringContainsString('where', $def, 'L\'index doit etre partiel (clause WHERE sur les actifs).');
    }

    public function testNoSirenOrEmailUniqueIndex(): void
    {
        $names = array_map(static fn (array $r): string => $r['indexname'], $this->clientIndexes());

        // RG-1.15 / RG-1.17 supprimees (Q4) : aucun index unique siren / email.
        self::assertNotContains('uq_client_siren_active', $names);
        self::assertNotContains('uq_client_email_active', $names);
    }

    /**
     * @return list<array{indexname: string, indexdef: string}>
     */
    private function clientIndexes(): array
    {
        self::bootKernel();

        /** @var list<array{indexname: string, indexdef: string}> $rows */
        return $this->getEm()->getConnection()->fetchAllAssociative(
            "SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' AND tablename = 'client'",
        );
    }
}
