<?php

declare(strict_types=1);

namespace App\Tests\Module\Transport\Infrastructure\Console;

use Doctrine\DBAL\Connection;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;

use const JSON_THROW_ON_ERROR;

/**
 * Test fonctionnel de `app:qualimat:sync` via l'option --file (pas d'appel
 * reseau) : verifie l'upsert normalise, le journal et le soft-delete.
 *
 * @internal
 */
final class SyncQualimatCommandTest extends KernelTestCase
{
    private Connection $connection;

    protected function setUp(): void
    {
        self::bootKernel();

        /** @var Connection $connection */
        $connection       = self::getContainer()->get('doctrine.dbal.default_connection');
        $this->connection = $connection;
        $this->purge();
    }

    protected function tearDown(): void
    {
        $this->purge();
        parent::tearDown();
    }

    public function testFirstSyncInsertsNormalizesAndLogs(): void
    {
        $tester = $this->runSync([
            [
                'Nom'         => '2C TRANS',
                'Societe'     => '2C TRANS',
                'Adresse'     => '66 Impasse Mendi',
                'CodePostal'  => '65500',
                'Ville'       => 'VIC EN BIGORRE',
                'Telephone_1' => '+33|0608890316',
                'Siret'       => '444 156 285 000 25',
                'Validite'    => '14/05/2027',
                'Statut'      => 'Audité',
                'Departement' => '65 - Hautes-Pyrénées',
            ],
            // Item sans SIRET : doit etre ignore (compte dans rows_skipped).
            ['Nom' => 'SANS SIRET', 'Siret' => null, 'Validite' => '01/01/2030', 'Statut' => 'Valide'],
        ]);

        $tester->assertCommandIsSuccessful();

        self::assertSame(1, $this->countRows('SELECT COUNT(*) FROM qualimat_carrier'));

        $row = $this->connection->fetchAssociative('SELECT * FROM qualimat_carrier');
        self::assertNotFalse($row);
        self::assertSame('44415628500025', $row['siret']);
        self::assertSame('2C TRANS', $row['name']);
        self::assertSame('2027-05-14', $row['validity_date']);
        self::assertSame('+33|0608890316', $row['phone']);
        self::assertSame(1, $this->countRows('SELECT COUNT(*) FROM qualimat_carrier WHERE is_active = TRUE'));

        $log = $this->connection->fetchAssociative('SELECT * FROM qualimat_sync_log ORDER BY id DESC LIMIT 1');
        self::assertNotFalse($log);
        self::assertSame(2, (int) $log['rows_total']);
        self::assertSame(1, (int) $log['rows_upserted']);
        self::assertSame(1, (int) $log['rows_skipped']);
        self::assertSame(0, (int) $log['rows_deactivated']);
    }

    public function testSecondSyncUpdatesAndSoftDeletesMissing(): void
    {
        $a = ['Nom' => 'A', 'Siret' => '111 111 111 00011', 'Validite' => '01/01/2030', 'Statut' => 'Audité'];
        $b = ['Nom' => 'B', 'Siret' => '222 222 222 00022', 'Validite' => '01/01/2030', 'Statut' => 'Audité'];

        $this->runSync([$a, $b])->assertCommandIsSuccessful();
        self::assertSame(2, $this->countRows('SELECT COUNT(*) FROM qualimat_carrier WHERE is_active = TRUE'));

        // 2e run sans B et avec A renomme : A est mis a jour, B est soft-delete.
        $aRenamed = ['Nom' => 'A BIS', 'Siret' => '111 111 111 00011', 'Validite' => '02/02/2031', 'Statut' => 'Valide'];
        $tester   = $this->runSync([$aRenamed]);
        $tester->assertCommandIsSuccessful();

        // Toujours 2 lignes en base, mais une seule active.
        self::assertSame(2, $this->countRows('SELECT COUNT(*) FROM qualimat_carrier'));
        self::assertSame(1, $this->countRows('SELECT COUNT(*) FROM qualimat_carrier WHERE is_active = TRUE'));
        self::assertSame(1, $this->countRows("SELECT COUNT(*) FROM qualimat_carrier WHERE siret = '22222222200022' AND is_active = FALSE"));

        // A a bien ete mis a jour (nom + statut + date).
        $a = $this->connection->fetchAssociative("SELECT * FROM qualimat_carrier WHERE siret = '11111111100011'");
        self::assertNotFalse($a);
        self::assertSame('A BIS', $a['name']);
        self::assertSame('Valide', $a['status']);
        self::assertSame('2031-02-02', $a['validity_date']);

        $log = $this->connection->fetchAssociative('SELECT * FROM qualimat_sync_log ORDER BY id DESC LIMIT 1');
        self::assertNotFalse($log);
        self::assertSame(1, (int) $log['rows_upserted']);
        self::assertSame(1, (int) $log['rows_deactivated']);
        self::assertSame(0, (int) $log['rows_skipped']);
    }

    public function testEmptySourceAbortsWithoutMassDeactivation(): void
    {
        // Premier run : 2 transporteurs actifs.
        $a = ['Nom' => 'A', 'Siret' => '111 111 111 00011', 'Validite' => '01/01/2030', 'Statut' => 'Audité'];
        $b = ['Nom' => 'B', 'Siret' => '222 222 222 00022', 'Validite' => '01/01/2030', 'Statut' => 'Audité'];
        $this->runSync([$a, $b])->assertCommandIsSuccessful();
        self::assertSame(2, $this->countRows('SELECT COUNT(*) FROM qualimat_carrier WHERE is_active = TRUE'));

        // Source ne contenant que des items inexploitables (zero ligne mappee) :
        // la commande doit ECHOUER sans toucher le referentiel (pas de soft-delete
        // de masse) et sans journaliser de run.
        $logsBefore = $this->countRows('SELECT COUNT(*) FROM qualimat_sync_log');
        $tester     = $this->runSync([
            ['Nom' => 'SANS SIRET 1', 'Siret' => null],
            ['Nom' => 'SANS SIRET 2', 'Siret' => '   '],
        ]);

        self::assertSame(Command::FAILURE, $tester->getStatusCode());
        // Les 2 transporteurs restent ACTIFS (aucune desactivation de masse).
        self::assertSame(2, $this->countRows('SELECT COUNT(*) FROM qualimat_carrier WHERE is_active = TRUE'));
        // Aucun journal supplementaire (abandon avant la transaction).
        self::assertSame($logsBefore, $this->countRows('SELECT COUNT(*) FROM qualimat_sync_log'));
    }

    /**
     * @param array<int, array<string, mixed>> $items
     */
    private function runSync(array $items): CommandTester
    {
        $path = tempnam(sys_get_temp_dir(), 'qualimat_').'.json';
        file_put_contents($path, json_encode($items, JSON_THROW_ON_ERROR));

        $application = new Application(self::$kernel);
        $tester      = new CommandTester($application->find('app:qualimat:sync'));
        $tester->execute(['--file' => $path]);

        @unlink($path);

        return $tester;
    }

    private function countRows(string $sql): int
    {
        return (int) $this->connection->fetchOne($sql);
    }

    private function purge(): void
    {
        $this->connection->executeStatement('DELETE FROM qualimat_carrier');
        $this->connection->executeStatement('DELETE FROM qualimat_sync_log');
    }
}
