<?php

namespace App\Service;

use App\Entity\ScanSession;
use App\Ocr\VinMakeResolver;
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;

/**
 * Classeur Excel de la confrontation inventaire scanné vs photo du stock
 * (manquants / en trop / concordants).
 */
final class StockComparisonXlsxBuilder
{
    private const HEADERS = ['État', 'Plaque', 'VIN', 'Marque', 'Type', 'Localisation'];
    private const COLUMN_WIDTHS = [22, 16, 20, 16, 14, 26];

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

    /**
     * @param array{missing: array<int, array<string, mixed>>, extra: array<int, array<string, mixed>>, matched: array<int, array<string, mixed>>} $diff
     */
    public function build(ScanSession $scan, array $diff, ?string $stockDate = null, ?string $vnVo = null): string
    {
        $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_stk_');
        if (false === $path) {
            throw new \RuntimeException('Impossible de créer un fichier temporaire pour la comparaison.');
        }

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

        try {
            $writer->openToFile($path);
            $writer->addRow(Row::fromValues(['Comparaison inventaire / stock']));
            $writer->addRow(Row::fromValues(['Inventaire', sprintf('%s — %s', $scan->getLabel(), $scan->getCreatedAt()->format('d/m/Y H:i'))]));
            $writer->addRow(Row::fromValues(['Concession', self::safeText(implode(', ', $this->concessions->names($scan->getConcessions())) ?: '-')]));
            $writer->addRow(Row::fromValues(['VN/VO', self::safeText($vnVo ?: 'Tous')]));
            $writer->addRow(Row::fromValues(['Date du stock', self::safeText($stockDate ?: '-')]));
            $writer->addRow(Row::fromValues([
                sprintf('%d manquant(s) au scan', \count($diff['missing'])),
                sprintf('%d en trop (non attendu)', \count($diff['extra'])),
                sprintf('%d concordant(s)', \count($diff['matched'])),
            ]));
            $writer->addRow(Row::fromValues([]));
            $writer->addRow(Row::fromValuesWithStyle(self::HEADERS, new Style(fontBold: true)));

            foreach ($diff['missing'] as $r) {
                $writer->addRow($this->row('Manquant au scan', $r, $red));
            }
            foreach ($diff['extra'] as $r) {
                $writer->addRow($this->row('En trop (non attendu)', $r, $amber));
            }
            foreach ($diff['matched'] as $r) {
                $writer->addRow($this->row('Concordant', $r, $green));
            }

            $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, Style $style): Row
    {
        $vin = $this->comparator->vin($r);

        return Row::fromValuesWithStyle([
            $etat,
            self::safeText($this->comparator->plate($r)),
            self::safeText($vin),
            $this->vinMake->getVehicleMake('-' !== $vin ? $vin : null),
            self::safeText(\is_string($r['type'] ?? null) ? $r['type'] : ''),
            self::safeText(\is_string($r['localisation'] ?? null) ? $r['localisation'] : ''),
        ], $style);
    }

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