<?php

namespace App\Service;

use App\Entity\ScanSession;
use App\Ocr\VinMakeResolver;
use OpenSpout\Common\Entity\Cell;
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 de la comparaison de deux inventaires (ajouts / suppressions / inchangés).
 */
final class ComparisonXlsxBuilder
{
    private const HEADERS = ['État', 'Plaque', 'VIN', 'Marque', 'Inventaire A', 'Inventaire B'];
    private const COLUMN_WIDTHS = [14, 16, 20, 16, 28, 28];

    public function __construct(
        private readonly InventoryComparator $comparator,
        private readonly VinMakeResolver $vinMake,
    ) {
    }

    /**
     * @param array{added: array<int, array<string, mixed>>, removed: array<int, array<string, mixed>>, unchanged: array<int, array<string, mixed>>} $diff
     */
    public function build(ScanSession $a, ScanSession $b, array $diff): string
    {
        $labelA = $this->label($a);
        $labelB = $this->label($b);

        $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_cmp_');
        if (false === $path) {
            throw new \RuntimeException('Impossible de créer un fichier temporaire pour la comparaison.');
        }

        $green = new Style(fontColor: Color::rgb(22, 101, 52));
        $red = new Style(fontColor: Color::rgb(153, 27, 27));

        try {
            $writer->openToFile($path);
            $writer->addRow(Row::fromValues(['Comparaison d\'inventaires']));
            $writer->addRow(Row::fromValues(['Inventaire A', $labelA]));
            $writer->addRow(Row::fromValues(['Inventaire B', $labelB]));
            $writer->addRow(Row::fromValues([
                sprintf('%d ajout(s)', \count($diff['added'])),
                sprintf('%d suppression(s)', \count($diff['removed'])),
                sprintf('%d inchangé(s)', \count($diff['unchanged'])),
            ]));
            $writer->addRow(Row::fromValues([]));
            $writer->addRow(Row::fromValuesWithStyle(self::HEADERS, new Style(fontBold: true)));

            foreach ($diff['added'] as $r) {
                $writer->addRow($this->row('Ajouté', $r, '', '✓', $green));
            }
            foreach ($diff['removed'] as $r) {
                $writer->addRow($this->row('Supprimé', $r, '✓', '', $red));
            }
            foreach ($diff['unchanged'] as $r) {
                $writer->addRow($this->row('Inchangé', $r, '✓', '✓', null));
            }

            $writer->close();

            $content = file_get_contents($path);
            if (false === $content) {
                throw new \RuntimeException('Impossible de lire le fichier de comparaison.');
            }

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

    /**
     * @param array<string, mixed> $r
     */
    private function row(string $etat, array $r, string $inA, string $inB, ?Style $style): Row
    {
        $vin = $this->comparator->vin($r);
        $values = [
            $etat,
            self::safeText($this->comparator->plate($r)),
            self::safeText($vin),
            $this->vinMake->getVehicleMake('-' !== $vin ? $vin : null),
            $inA,
            $inB,
        ];

        return null !== $style ? Row::fromValuesWithStyle($values, $style) : Row::fromValues($values);
    }

    /** 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;
    }

    private function label(ScanSession $s): string
    {
        return sprintf('%s — %s', $s->getLabel(), $s->getCreatedAt()->format('d/m/Y H:i'));
    }
}
