<?php

namespace App\Service;

use App\Entity\ScanSession;
use App\Ocr\VinMakeResolver;
use Dompdf\Dompdf;
use Dompdf\Options;
use Twig\Environment;

/**
 * Génère le PDF du rapport d'un inventaire (mêmes données que l'export Excel).
 * Rend un template Twig en HTML puis le convertit en PDF via Dompdf.
 */
final class PdfReportBuilder
{
    public function __construct(
        private readonly Environment $twig,
        private readonly VinMakeResolver $vinMake,
        private readonly ConcessionsProvider $concessions,
        private readonly VehicleErrorChecker $errors,
        private readonly ImageStorage $images,
    ) {
    }

    /**
     * Rapport « avec images » d'un inventaire : une ligne par véhicule avec la photo plaque
     * ET la photo VIN (embarquées en base64, réduites), l'immatriculation, le VIN et l'heure.
     */
    public function buildPhotoReport(ScanSession $scan, string $concessionStr): string
    {
        $creator = $scan->getUserEmail();
        $sid = $scan->getId();
        $rows = [];
        foreach ($scan->getResults() as $r) {
            if (!\is_array($r)) {
                continue;
            }
            $vin = $r['vin'] ?? $r['rawVin'] ?? null;
            $plate = $r['plate'] ?? $r['rawPlate'] ?? null;
            $time = '';
            foreach (['plateDate', 'vinDate', 'time'] as $k) {
                $v = $r[$k] ?? null;
                if (\is_string($v) && '' !== trim($v)) {
                    $time = trim($v);
                    break;
                }
            }
            $rows[] = [
                'plate' => \is_string($plate) && '' !== trim($plate) ? $plate : '-',
                'vin' => \is_string($vin) && '' !== trim($vin) ? $vin : '-',
                'time' => '' !== $time ? $time : '-',
                'comment' => (string) ($r['comment'] ?? ''),
                // VIN gravé fin → moins compressé (largeur originale / 4, meilleure qualité) pour rester lisible.
                'vinImg' => $this->embed($creator, $sid, $r['vinImageId'] ?? null, 130, 72, 4),
                'plateImg' => $this->embed($creator, $sid, $r['plateImageId'] ?? null, 150, 60),
            ];
        }

        $html = $this->twig->render('pdf/inventory_photos.html.twig', [
            'label' => $scan->getLabel(),
            'concessionStr' => $concessionStr,
            'scannedBy' => $scan->getUserEmail(),
            'createdAt' => $scan->getCreatedAt(),
            'rows' => $rows,
            'total' => \count($rows),
            'signature' => $scan->getSignature(),
            'signatureDate' => $scan->getSignatureDate(),
        ]);

        return $this->htmlToPdf($html, 'landscape');
    }

    /**
     * @param array<int, array<string, mixed>> $results
     *
     * @return string Contenu binaire du fichier .pdf
     */
    public function build(
        array $results,
        string $concessionStr,
        ?string $locationNote = null,
        ?string $scannedBy = null,
        string $label = 'Inventaire',
        ?\DateTimeImmutable $createdAt = null,
    ): string {
        $rows = [];
        foreach ($results as $r) {
            if (!\is_array($r)) {
                continue;
            }
            $vin = $r['vin'] ?? $r['rawVin'] ?? null;
            $plate = $r['plate'] ?? $r['rawPlate'] ?? null;
            $aiMake = (\is_string($r['make'] ?? null) && '' !== $r['make']) ? $r['make'] : null;
            $model = (\is_string($r['model'] ?? null) && '' !== $r['model']) ? $r['model'] : '';
            $conf = (isset($r['modelConfidence']) && is_numeric($r['modelConfidence'])) ? ((int) $r['modelConfidence']) . '%' : '';
            $rows[] = [
                'plate' => \is_string($plate) && '' !== trim($plate) ? $plate : '-',
                'vin' => \is_string($vin) && '' !== trim($vin) ? $vin : '-',
                'make' => $aiMake ?? $this->vinMake->getVehicleMake(\is_string($vin) ? $vin : null),
                'model' => '' !== $model ? $model : '-',
                'modelConfidence' => $conf,
                'plateDate' => (string) ($r['plateDate'] ?? ''),
                'vinDate' => (string) ($r['vinDate'] ?? ''),
                'comment' => (string) ($r['comment'] ?? ''),
                'plateError' => $this->errors->plateInError($r),
                'vinError' => $this->errors->vinInError($r),
            ];
        }

        $html = $this->twig->render('pdf/inventory.html.twig', [
            'label' => $label,
            'concessionStr' => $concessionStr,
            'locationStr' => (null !== $locationNote && '' !== $locationNote) ? $locationNote : '-',
            'scannedBy' => (null !== $scannedBy && '' !== $scannedBy) ? $scannedBy : '-',
            'createdAt' => $createdAt,
            'rows' => $rows,
            'total' => \count($rows),
        ]);

        return $this->htmlToPdf($html);
    }

    /**
     * PDF de la confrontation inventaire scanné vs export de stock.
     *
     * @param array{missing: array<int, array<string, mixed>>, extra: array<int, array<string, mixed>>, matched: array<int, array<string, mixed>>} $diff
     */
    public function buildStockComparison(ScanSession $scan, array $diff, ?string $stockDate = null, ?string $vnVo = null): string
    {
        $section = function (array $items): array {
            $out = [];
            foreach ($items as $r) {
                $vin = $r['vin'] ?? $r['rawVin'] ?? null;
                $plate = $r['plate'] ?? $r['rawPlate'] ?? null;
                $out[] = [
                    'plate' => \is_string($plate) && '' !== trim($plate) ? $plate : '-',
                    'vin' => \is_string($vin) && '' !== trim($vin) ? $vin : '-',
                    'make' => $this->vinMake->getVehicleMake(\is_string($vin) ? $vin : null),
                    'type' => \is_string($r['type'] ?? null) ? $r['type'] : '',
                    'localisation' => \is_string($r['localisation'] ?? null) ? $r['localisation'] : '',
                ];
            }

            return $out;
        };

        $html = $this->twig->render('pdf/stock_comparison.html.twig', [
            'label' => $scan->getLabel(),
            'concessions' => implode(', ', $this->concessions->names($scan->getConcessions())) ?: '-',
            'vnVo' => $vnVo ?: 'Tous',
            'stockDate' => $stockDate,
            'createdAt' => $scan->getCreatedAt(),
            'missing' => $section($diff['missing']),
            'extra' => $section($diff['extra']),
            'matched' => $section($diff['matched']),
        ]);

        return $this->htmlToPdf($html);
    }

    /**
     * Lit une image de la session, la réduit, et renvoie une data-URI base64 (ou null si absente).
     * Si $divisor est fourni, la largeur cible = largeurOriginale / $divisor (le VIN, gravé fin,
     * a besoin de plus de résolution pour rester lisible) ; sinon largeur fixe $maxW.
     */
    private function embed(string $email, string $sessionId, ?string $imageId, int $maxW = 130, int $quality = 55, ?int $divisor = null): ?string
    {
        if (!\is_string($imageId) || '' === $imageId) {
            return null;
        }
        $path = $this->images->pathFor($email, $sessionId, $imageId);
        if (null === $path) {
            return null;
        }

        $targetW = $maxW;
        if (null !== $divisor && $divisor > 0) {
            $size = @getimagesize($path);
            $origW = \is_array($size) ? (int) $size[0] : 0;
            $targetW = $origW > 0 ? max(120, (int) round($origW / $divisor)) : $maxW;
        }

        $jpeg = $this->downscaleJpeg($path, $targetW, $quality);

        return null === $jpeg ? null : 'data:image/jpeg;base64,' . base64_encode($jpeg);
    }

    /** Réduit une image JPEG à $maxW px de large (Imagick puis GD), pour alléger le PDF. */
    private function downscaleJpeg(string $path, int $maxW, int $quality = 55): ?string
    {
        $blob = @file_get_contents($path);
        if (false === $blob || '' === $blob) {
            return null;
        }

        if (class_exists(\Imagick::class)) {
            try {
                $im = new \Imagick();
                $im->readImageBlob($blob);
                $im->setIteratorIndex(0);
                if ($im->getImageWidth() > $maxW) {
                    $im->thumbnailImage($maxW, 0);
                }
                $im->setImageFormat('jpeg');
                $im->setImageCompressionQuality($quality);
                $out = $im->getImageBlob();
                $im->clear();

                return '' !== $out ? $out : null;
            } catch (\Throwable) {
            }
        }

        $img = @imagecreatefromstring($blob);
        if (false === $img) {
            return null;
        }
        $w = imagesx($img);
        if ($w > $maxW) {
            $resized = imagescale($img, $maxW);
            if (false !== $resized) {
                imagedestroy($img);
                $img = $resized;
            }
        }
        ob_start();
        imagejpeg($img, null, $quality);
        $out = (string) ob_get_clean();
        imagedestroy($img);

        return '' !== $out ? $out : null;
    }

    private function htmlToPdf(string $html, string $orientation = 'landscape'): string
    {
        $options = new Options();
        $options->set('isRemoteEnabled', false);
        $options->set('isHtml5ParserEnabled', true);
        $options->set('defaultFont', 'DejaVu Sans'); // police embarquée gérant les accents/UTF-8

        $dompdf = new Dompdf($options);
        $dompdf->loadHtml($html, 'UTF-8');
        $dompdf->setPaper('A4', $orientation);
        $dompdf->render();

        $output = $dompdf->output();

        return false === $output ? '' : $output;
    }
}
