<?php

declare(strict_types=1);

namespace App\Tests\Shared\Infrastructure\Export;

use App\Shared\Infrastructure\Export\PhpSpreadsheetExporter;
use Generator;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;

/**
 * Test unitaire du service Shared d'export XLSX. Verifie que le binaire produit
 * est un vrai fichier XLSX relisible, que l'en-tete et les lignes sont ecrits
 * dans le bon ordre, qu'un iterable paresseux (generator) est accepte et que le
 * titre d'onglet est assaini.
 *
 * @internal
 */
final class PhpSpreadsheetExporterTest extends TestCase
{
    public function testExportProducesReadableXlsxWithHeadersAndRows(): void
    {
        $binary = new PhpSpreadsheetExporter()->export(
            'Feuille test',
            ['Nom', 'Email'],
            [
                ['Alpha', 'alpha@test.fr'],
                ['Beta', null],
            ],
        );

        self::assertNotSame('', $binary);
        // Un fichier XLSX (OOXML) est une archive ZIP : signature "PK\x03\x04".
        self::assertStringStartsWith("PK\x03\x04", $binary);

        $grid = $this->grid($binary);
        self::assertSame(['Nom', 'Email'], $grid[0]);
        self::assertSame('Alpha', $grid[1][0]);
        self::assertSame('alpha@test.fr', $grid[1][1]);
        self::assertSame('Beta', $grid[2][0]);
        // Cellule null a l'ecriture -> vide a la relecture.
        self::assertNull($grid[2][1]);
    }

    public function testExportAcceptsGeneratorRows(): void
    {
        $rows = (static function (): Generator {
            yield ['L1'];

            yield ['L2'];
        })();

        $grid = $this->grid(new PhpSpreadsheetExporter()->export('Gen', ['H'], $rows));

        self::assertSame('H', $grid[0][0]);
        self::assertSame('L1', $grid[1][0]);
        self::assertSame('L2', $grid[2][0]);
    }

    public function testLongOrInvalidSheetTitleIsSanitized(): void
    {
        // Titre > 31 caracteres + caracteres interdits par Excel ([ ] : * etc.).
        $binary = new PhpSpreadsheetExporter()->export(
            str_repeat('A', 50).'[]:*?/\\',
            ['H'],
            [['x']],
        );

        $title = $this->load($binary)->getActiveSheet()->getTitle();
        self::assertLessThanOrEqual(31, mb_strlen($title));
        self::assertStringNotContainsString('[', $title);
        self::assertStringNotContainsString(':', $title);
    }

    /**
     * Relit le binaire XLSX et renvoie la grille de cellules (ligne 0 = entete).
     *
     * @return array<int, array<int, mixed>>
     */
    private function grid(string $binary): array
    {
        return $this->load($binary)->getActiveSheet()->toArray();
    }

    private function load(string $binary): Spreadsheet
    {
        $tmp = tempnam(sys_get_temp_dir(), 'xlsx_test_');
        self::assertIsString($tmp);
        file_put_contents($tmp, $binary);

        try {
            return IOFactory::load($tmp);
        } finally {
            @unlink($tmp);
        }
    }
}
