<?php

declare(strict_types=1);

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

use Doctrine\DBAL\Connection;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;

/**
 * Test fonctionnel de `app:idtf:sync` via --file : genere un vrai .xlsx, le
 * passe a la commande et verifie le parsing, l'upsert, le journal et le
 * soft-delete (chemin complet IOFactory -> parser -> DBAL).
 *
 * @internal
 */
final class SyncIdtfCommandTest 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 testSyncParsesXlsxUpsertsAndLogs(): void
    {
        $path = $this->makeXlsx([
            ['Export date: 12-6-2026'],
            ['Avertissement preambule'],
            [],
            ['Numéro IDTF', 'Product Group', 'Nom de la marchandise', 'Régime de nettoyage', 'Exigences importantes', 'Date d’application obligatoire', 'Produits apparentés', 'Formule', 'Code EURAL', 'Numéro CAS', 'Annotations'],
            ['30748', 'Inorganiques', 'Argiles régime C', 'C', 'Exig X', '02-04-2026', 'Poudre', 'Al2O3', '01 01 01', '7631-86-9 ; 1344-28-1', 'Note'],
            ['', '', '', '', '', '', '', '', '', '', ''],
            ['30744', '', 'Additifs', 'A', '', '', '', '', '', '', ''],
        ]);

        $tester = $this->runSync($path);
        $tester->assertCommandIsSuccessful();

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

        $row = $this->connection->fetchAssociative("SELECT * FROM idtf_product WHERE idtf_number = 30748 AND schema = 'road'");
        self::assertNotFalse($row);
        self::assertSame('Argiles régime C', $row['name']);
        self::assertSame('C', $row['cleaning_regime']);
        self::assertSame('2026-04-02', $row['mandatory_date']);
        self::assertSame('2026-06-12', $row['source_export_date']);
        self::assertSame(['7631-86-9', '1344-28-1'], json_decode((string) $row['cas_numbers'], true));

        $log = $this->connection->fetchAssociative('SELECT * FROM idtf_sync_log ORDER BY id DESC LIMIT 1');
        self::assertNotFalse($log);
        self::assertSame('road', $log['schema']);
        self::assertSame('2026-06-12', $log['export_date']);
        self::assertSame(2, (int) $log['rows_total']);
        self::assertSame(2, (int) $log['rows_upserted']);
        self::assertSame(0, (int) $log['rows_deactivated']);
    }

    public function testSecondSyncSoftDeletesMissing(): void
    {
        $header = ['Numéro IDTF', 'Nom de la marchandise', 'Régime de nettoyage'];

        $this->runSync($this->makeXlsx([
            ['Export date: 1-6-2026'],
            $header,
            ['100', 'Produit 100', 'A'],
            ['200', 'Produit 200', 'B'],
        ]))->assertCommandIsSuccessful();
        self::assertSame(2, $this->countRows('SELECT COUNT(*) FROM idtf_product WHERE is_active = TRUE'));

        // 2e export sans 200 -> soft-delete de 200, mise a jour de 100.
        $tester = $this->runSync($this->makeXlsx([
            ['Export date: 2-6-2026'],
            $header,
            ['100', 'Produit 100 maj', 'C'],
        ]));
        $tester->assertCommandIsSuccessful();

        self::assertSame(2, $this->countRows('SELECT COUNT(*) FROM idtf_product'));
        self::assertSame(1, $this->countRows('SELECT COUNT(*) FROM idtf_product WHERE is_active = TRUE'));
        self::assertSame(1, $this->countRows('SELECT COUNT(*) FROM idtf_product WHERE idtf_number = 200 AND is_active = FALSE'));

        $row100 = $this->connection->fetchAssociative('SELECT * FROM idtf_product WHERE idtf_number = 100');
        self::assertNotFalse($row100);
        self::assertSame('Produit 100 maj', $row100['name']);
        self::assertSame('C', $row100['cleaning_regime']);

        $log = $this->connection->fetchAssociative('SELECT * FROM idtf_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']);
    }

    public function testInvalidSchemaIsRejected(): void
    {
        $path = $this->makeXlsx([
            ['Numéro IDTF', 'Nom de la marchandise', 'Régime de nettoyage'],
            ['1', 'X', 'A'],
        ]);

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

        @unlink($path);

        self::assertSame(2, $exitCode); // Command::INVALID
        self::assertSame(0, $this->countRows('SELECT COUNT(*) FROM idtf_product'));
    }

    /**
     * @param array<int, array<int, mixed>> $matrix
     */
    private function makeXlsx(array $matrix): string
    {
        $spreadsheet = new Spreadsheet();
        $spreadsheet->getActiveSheet()->fromArray($matrix, null, 'A1', true);

        $path = tempnam(sys_get_temp_dir(), 'idtf_').'.xlsx';
        new Xlsx($spreadsheet)->save($path);
        $spreadsheet->disconnectWorksheets();

        return $path;
    }

    private function runSync(string $path): CommandTester
    {
        $application = new Application(self::$kernel);
        $tester      = new CommandTester($application->find('app:idtf:sync'));
        $tester->execute(['--file' => $path, '--schema' => 'road']);

        @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 idtf_product');
        $this->connection->executeStatement('DELETE FROM idtf_sync_log');
    }
}
