<?php

namespace App\Service;

use App\Ocr\VinMakeResolver;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Cell\FormulaCell;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Color;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\XLSX\Options;
use OpenSpout\Writer\XLSX\Writer;

/**
 * Génère le classeur Excel (.xlsx) du rapport de scan.
 * Port de la génération xlsx de app/api/send-report/route.ts (via OpenSpout).
 */
final class XlsxReportBuilder
{
    private const HEADERS = [
        'Concession(s)', 'Lieu', 'Scanné par', 'VIN', 'Plaque', 'Marque', 'Modèle', '% modèle',
        'Fichier VIN', 'Date VIN', 'Position VIN',
        'Fichier Plaque', 'Date Plaque', 'Position Plaque', 'Commentaire',
    ];

    private const COLUMN_WIDTHS = [30, 22, 28, 20, 14, 16, 20, 10, 28, 18, 18, 28, 18, 18, 40];

    public function __construct(
        private readonly VinMakeResolver $vinMake,
        private readonly VehicleErrorChecker $errors,
    ) {
    }

    /**
     * @param array<int, array<string, mixed>> $results
     *
     * @return string Contenu binaire du fichier .xlsx
     */
    public function build(array $results, string $concessionStr, ?string $locationNote = null, ?string $scannedBy = null): string
    {
        $locationStr = (null !== $locationNote && '' !== $locationNote) ? $locationNote : '-';
        $scannedByStr = (null !== $scannedBy && '' !== $scannedBy) ? $scannedBy : '-';

        $options = new Options();
        foreach (self::COLUMN_WIDTHS as $i => $width) {
            $options->setColumnWidth((float) $width, $i + 1);
        }

        $writer = new Writer($options);
        $path = tempnam(sys_get_temp_dir(), 'pv_xlsx_');
        if (false === $path) {
            throw new \RuntimeException('Impossible de créer un fichier temporaire pour le rapport.');
        }

        // Surbrillance jaune des cellules plaque/VIN en erreur (à corriger).
        $yellow = new Style(backgroundColor: Color::YELLOW);

        try {
            $writer->openToFile($path);
            $writer->addRow(Row::fromValuesWithStyle(self::HEADERS, new Style(fontBold: true)));

            foreach ($results as $r) {
                $vin = $r['vin'] ?? $r['rawVin'] ?? null;
                // Marque : priorité à l'identification IA (VIN + photos), repli sur le décodage du WMI du VIN.
                $aiMake = (\is_string($r['make'] ?? null) && '' !== $r['make']) ? $r['make'] : null;
                $make = $aiMake ?? $this->vinMake->getVehicleMake(\is_string($vin) ? $vin : null);
                $model = (\is_string($r['model'] ?? null) && '' !== $r['model']) ? $r['model'] : '-';
                $conf = (isset($r['modelConfidence']) && is_numeric($r['modelConfidence'])) ? ((int) $r['modelConfidence']) . '%' : '';
                $writer->addRow(new Row([
                    Cell::fromValue(self::safeText($concessionStr)),
                    Cell::fromValue(self::safeText($locationStr)),
                    Cell::fromValue(self::safeText($scannedByStr)),
                    Cell::fromValue(self::safeText((string) ($vin ?? '-')), $this->errors->vinInError($r) ? $yellow : null),
                    Cell::fromValue(self::safeText((string) ($r['plate'] ?? $r['rawPlate'] ?? '-')), $this->errors->plateInError($r) ? $yellow : null),
                    Cell::fromValue(self::safeText((string) $make)),
                    Cell::fromValue(self::safeText((string) $model)),
                    Cell::fromValue(self::safeText((string) $conf)),
                    Cell::fromValue(self::safeText((string) ($r['vinFilename'] ?? '-'))),
                    Cell::fromValue(self::safeText((string) ($r['vinDate'] ?? ''))),
                    $this->mapsCell($r['vinLatitude'] ?? null, $r['vinLongitude'] ?? null),
                    Cell::fromValue(self::safeText((string) ($r['plateFilename'] ?? '-'))),
                    Cell::fromValue(self::safeText((string) ($r['plateDate'] ?? ''))),
                    $this->mapsCell($r['plateLatitude'] ?? null, $r['plateLongitude'] ?? null),
                    Cell::fromValue(self::safeText((string) ($r['comment'] ?? ''))),
                ]));
            }

            $writer->close();

            $content = file_get_contents($path);
            if (false === $content) {
                throw new \RuntimeException('Impossible de lire le rapport généré.');
            }

            return $content;
        } finally {
            if (is_file($path)) {
                @unlink($path);
            }
        }
    }

    /** Anti-injection de formule : préfixe une apostrophe si la valeur commence par = + - @ (ou tab/retour). */
    private static function safeText(string $v): string
    {
        return '' !== $v && \in_array($v[0], ['=', '+', '-', '@', "\t", "\r", "\n"], true) ? "'" . $v : $v;
    }

    /**
     * Cellule cliquable "Voir sur Maps" via formule =HYPERLINK (lien actif sans édition).
     */
    private function mapsCell(mixed $lat, mixed $lon): Cell
    {
        if (!\is_numeric($lat) || !\is_numeric($lon)) {
            return Cell::fromValue('');
        }
        $latStr = number_format((float) $lat, 6, '.', '');
        $lonStr = number_format((float) $lon, 6, '.', '');
        $url = sprintf('https://www.google.com/maps?q=%s,%s', $latStr, $lonStr);
        $label = 'Voir sur Maps';
        $linkStyle = new Style(fontUnderline: true, fontColor: Color::BLUE);

        return new FormulaCell(sprintf('=HYPERLINK("%s","%s")', $url, $label), $label, $linkStyle);
    }
}
