<?php

namespace App\Service\Pdf;

use App\Entity\Contrats;
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\Label\LabelAlignment;
use Endroid\QrCode\RoundBlockSizeMode;
use Endroid\QrCode\Writer\PngWriter;
use Fpdf\Fpdf;
use Symfony\Component\HttpKernel\KernelInterface;

class ContratPdfService extends Fpdf
{
    private Contrats $contrats;
    private string $logo;
    private bool $isExtraPage = false;
    private bool $isIntegrateDocusealFields;

    public function __construct(KernelInterface $kernel, Contrats $contrats,bool $isIntegrateDocusealFields = false, $orientation = 'P', $unit = 'mm', $size = 'A4')
    {
        parent::__construct($orientation, $unit, $size);
        $this->contrats = $contrats;
        $this->isIntegrateDocusealFields = $isIntegrateDocusealFields; // Stockage
        $this->logo = $kernel->getProjectDir()."/public/provider/images/favicon.png";

        $this->AliasNbPages();
        $this->SetAutoPageBreak(true, 35);

    }

    /**
     * Génère un code-barres Code 39
     */
    protected function insertQRCode(float $x, float $y, string $data, int $size = 22): void
    {
        $builder = new Builder(
            writer: new PngWriter(),
            writerOptions: [],
            validateResult: false,
            data: $data,
            encoding: new Encoding('UTF-8'),
            errorCorrectionLevel: ErrorCorrectionLevel::High,
            size: 300,
            margin: 0,
            roundBlockSizeMode: RoundBlockSizeMode::Margin,
        );

        $result = $builder->build();

        // Utilisation d'un fichier temporaire pour FPDF
        $tmpFile = tempnam(sys_get_temp_dir(), 'qr_');
        $result->saveToFile($tmpFile);

        $this->Image($tmpFile, $x, $y, $size, $size, 'PNG');

        if (file_exists($tmpFile)) {
            unlink($tmpFile);
        }
    }

    /**
     * Convertit l'UTF-8 en Windows-1252 pour FPDF et gère l'Euro
     */
    protected function clean(?string $text): string
    {
        if (!$text) return '';
        $text = iconv('UTF-8', 'windows-1252//TRANSLIT//IGNORE', $text);
        return str_replace('€', chr(128), $text);
    }

    /**
     * Helper pour afficher le symbole Euro proprement
     */
    protected function euro(): string
    {
        return ' ' . chr(128);
    }

    public function Header()
    {
        // On n'affiche le header standard que sur les pages de devis (pas CGV/Signature)
        if ($this->page > 0 && !$this->isExtraPage) {
            $this->SetY(10);
            if (file_exists($this->logo)) {
                $this->Image($this->logo, 10, 10, 12);
                $this->SetX(25);
            }

            $this->SetFont('Arial', 'B', 14);
            $this->SetTextColor(0, 0, 0);
            $this->Cell(0, 7, $this->clean('Lilian SEGARD - Ludikevent'), 0, 1, 'L');

            $this->SetX(25);
            $this->SetFont('Arial', '', 8);
            $this->SetTextColor(80, 80, 80);
            $this->Cell(0, 4, $this->clean('SIRET : 930 488 408 00012 | RCS : 930 488 408'), 0, 1, 'L');
            $this->SetX(25);
            $this->Cell(0, 4, $this->clean('6 Rue du Château – 02800 Danizy – France'), 0, 1, 'L');
            $this->SetX(25);
            $this->Cell(0, 4, $this->clean('Tél. : 06 14 17 24 47'), 0, 1, 'L');
            $this->SetX(25);
            $this->Cell(0, 4, $this->clean('contact@ludikevent.fr | www.ludikevent.fr'), 0, 1, 'L');

            $this->SetY(40);
            $this->SetFont('Arial', 'B', 16);
            $this->SetTextColor(37, 99, 235);
            $this->Cell(0, 10, $this->clean('Contrat de location N° ' . $this->contrats->getNumReservation()), 0, 1, 'L');


            // Paramètres : X=160 (à droite), Y=42, Code=Numéro, Largeur barre=0.3, Hauteur=8
            $this->insertQRCode(175, 10, $this->contrats->getNumReservation(), 22);

            $this->Ln(10); // Retour à la ligne après le titre et le barcode

            $this->SetDrawColor(37, 99, 235);
            $this->SetLineWidth(0.5);
            $this->Line(10, $this->GetY(), 200, $this->GetY());
        }
    }

    public function generate(): string
    {
        $this->AddPage();
        $this->renderMainContent(); // Ajout du contenu principal (Page 1)
        $this->addCGV();            // Page 2
        
        // Ajout Etat des Lieux si existant
        if ($this->contrats->getEtatLieux()) {
            $this->addEtatLieuxSortant();
        }

        $this->addSignaturePage();  // Page 3 (ou 4)
        return $this->Output('S');
    }

    private function addEtatLieuxSortant(): void
    {
        $this->isExtraPage = true;
        $this->AddPage();
        $this->SetY(20);

        // Titre
        $this->SetFont('Arial', 'B', 14);
        $this->SetTextColor(37, 99, 235);
        $this->Cell(0, 10, $this->clean("ÉTAT DES LIEUX DE RESTITUTION"), 0, 1, 'C');
        
        $this->SetFont('Arial', 'B', 10);
        $this->SetTextColor(0, 0, 0);
        $this->Cell(0, 7, $this->clean("Contrat N° " . $this->contrats->getNumReservation()), 0, 1, 'C');
        $this->Ln(10);

        $etatLieux = $this->contrats->getEtatLieux();

        // Commentaires
        $this->SetFont('Arial', 'B', 11);
        $this->SetFillColor(240, 240, 240);
        $this->Cell(0, 8, $this->clean(" OBSERVATIONS ET COMMENTAIRES"), 0, 1, 'L', true);
        $this->Ln(2);

        $this->SetFont('Arial', '', 10);
        $comments = $etatLieux->getComments();
        
        if ($comments->count() > 0) {
            foreach ($comments as $comment) {
                $date = $comment->getCreatedAt()->format('d/m/Y H:i');
                $content = $comment->getContent();
                $this->MultiCell(0, 6, $this->clean("- [$date] $content"), 0, 'L');
            }
        } else {
            $this->Cell(0, 6, $this->clean("Aucune observation particulière."), 0, 1, 'L');
        }
        
        $this->Ln(10);

        // Files Summary (Optional, just listing count)
        $filesCount = $etatLieux->getFiles()->count();
        if ($filesCount > 0) {
            $this->SetFont('Arial', 'I', 9);
            $this->Cell(0, 6, $this->clean("Nombre de photos/vidéos jointes au dossier : " . $filesCount), 0, 1, 'L');
        }

        $this->Ln(20);
        
        // Signatures EDL (Specific placements if using DocuSeal for EDL specific signatures, 
        // or just rely on the main signature page which now covers everything including this page)
        // The prompt says "send docuseal for signedn for prestaire and customer".
        // Usually one signature document covers all pages.
        // I'll add signature placeholders specific to EDL if needed, or assume the main signature page covers it.
        // But the main signature page is added *after* this page now.
        // So the user signs the whole document (Contract + CGV + EDL + Signature Page).
        // If specific EDL signatures are needed *on this page*, I'd add them.
        // I'll add "Bon pour accord" blocks here too just in case, but usually global signature suffices.
        // I'll leave standard layout.
    }

    private function addCGV(): void
    {
        $this->isExtraPage = true;
        $this->AddPage();
        $this->SetMargins(15, 15, 15);
        $this->SetAutoPageBreak(true, 20);
        $this->SetY(15);

        // --- ENTÊTE ---
        $this->SetFont('Arial', 'B', 12);
        $this->SetTextColor(37, 99, 235); // Bleu Ludikevent
        $this->Cell(0, 10, $this->clean('CONDITIONS GÉNÉRALES DE VENTE'), 0, 1, 'C');

        $this->SetFont('Arial', 'B', 10);
        $this->SetTextColor(0, 0, 0);
        $this->Cell(0, 5, $this->clean('Lilian SEGARD - Ludikevent'), 0, 1, 'C');

        $this->SetFont('Arial', '', 8);
        $this->SetTextColor(80, 80, 80);
        $this->Cell(0, 4, $this->clean('SIRET : 930 488 408 00012 | 6 Rue du Château – 02800 Danizy – France'), 0, 1, 'C');
        $this->Cell(0, 4, $this->clean('Email : contact@ludikevent.fr | Téléphone : 06 14 17 24 47'), 0, 1, 'C');
        $this->Cell(0, 4, $this->clean('Assurance RC Pro : 17006220 0001'), 0, 1, 'C');
        $this->Ln(5);

        $this->SetFont('Arial', 'I', 9);
        $this->SetTextColor(0, 0, 0);
        $this->MultiCell(0, 5, $this->clean("Toute réservation implique l'acceptation sans réserve des présentes Conditions Générales de Vente."), 0, 'C');
        $this->Ln(5);

        // --- TEXTE INTÉGRAL ---
        $fullText = [
            "ARTICLE 1 – OBJET ET CHAMP D'APPLICATION" => "Les présentes Conditions Générales de Vente (ci-après \"CGV\") régissent les relations contractuelles entre Lilian SEGARD - Ludikevent (ci-après \"le Prestataire\") et toute personne physique ou morale (ci-après \"le Client\" ou \"le Locataire\") dans le cadre de :\n• La location de structures gonflables professionnelles appartenant à Lilian SEGARD - Ludikevent\n• La mise en relation avec des propriétaires privés pour du matériel récréatif destiné à des événements privés (anniversaires, baptêmes, etc.)\nDans le cas d'une mise en relation entre particuliers : Lilian SEGARD - Ludikevent intervient uniquement en qualité d'intermédiaire. Le contrat de location est conclu directement entre le propriétaire du matériel et le locataire. Lilian SEGARD - Ludikevent n'assume aucune responsabilité liée à l'état, la conformité ou l'utilisation dudit matériel.",

            "ARTICLE 2 – RÉSERVATION" => "La réservation devient ferme et définitive après réunion des trois conditions cumulatives suivantes :\n✓ Confirmation écrite par Lilian SEGARD - Ludikevent (email ou courrier)\n✓ Paiement effectif des arrhes de 25% du montant total\n✓ Acceptation expresse et signature des présentes CGV\nLes prestations sont proposées sous réserve de disponibilité du matériel aux dates demandées. Lilian SEGARD - Ludikevent se réserve le droit de refuser toute réservation si les conditions d'installation ou de sécurité ne sont pas réunies, sans que sa responsabilité ne puisse être engagée.",

            "ARTICLE 3 – TARIFS ET CONDITIONS DE PAIEMENT" => "Les tarifs sont exprimés en euros (€) toutes taxes comprises (TTC) et sont ceux en vigueur au jour de la réservation, conformément au devis ou contrat signé.\nModalités de paiement :\n• Arrhes de 25% à la réservation (non remboursables sauf cas de force majeure dûment justifié)\n• Solde dû au plus tard le jour de l'installation, AVANT le montage des structures\nEn cas de non-paiement du solde à la date prévue, Lilian SEGARD - Ludikevent se réserve le droit de ne pas procéder à la livraison ni à l'installation, sans que le Client puisse prétendre à un quelconque remboursement des arrhes versées. Des frais de livraison, d'installation et de déplacement peuvent s'appliquer selon la distance et les conditions d'accès au lieu de l'événement. Modes de paiement acceptés : virement bancaire, chèque (à établir à l'ordre de Lilian SEGARD - Ludikevent).",

            "ARTICLE 4 – DROIT DE RÉTRACTATION" => "Conformément à l'article L221-28 du Code de la consommation, le droit de rétractation de 14 jours ne s'applique pas aux prestations de services pleinement exécutées avant la fin du délai de rétractation et dont l'exécution a commencé après accord préalable exprès du consommateur et renoncement exprès à son droit de rétractation. En outre, aucun droit de rétractation n'est applicable pour une prestation de services dont la date d'exécution est fixée à une date déterminée ou à période déterminée (événement daté).",

            "ARTICLE 5 – CONDITIONS D'ANNULATION" => "5.1 – Annulation par le Client :\n• Annulation après versement des arrhes : les arrhes restent acquises au Prestataire à titre d'indemnité forfaitaire\n• Annulation moins de 15 jours calendaires avant la date de l'événement : arrhes non remboursables + facturation de 50% du solde restant dû\n• Annulation moins de 7 jours calendaires avant la date de l'événement : intégralité du montant reste due\n• Cas de force majeure dûment justifié (certificat médical, décès, catastrophe naturelle) : étude au cas par cas, possibilité de report de la prestation\n\n5.2 – Annulation par Lilian SEGARD - Ludikevent :\nEn cas d'impossibilité totale d'assurer la prestation pour cause de force majeure (conditions météorologiques extrêmes, défaillance matérielle majeure, cas fortuit), Lilian SEGARD - Ludikevent s'engage à : informer le Client dans les meilleurs délais, proposer une solution de report ou de remplacement dans la mesure du possible, procéder au remboursement intégral des sommes versées si aucune solution alternative n'est acceptable. La responsabilité de Lilian SEGARD - Ludikevent ne saurait être engagée au-delà du remboursement des sommes effectivement perçues.",

            "ARTICLE 6 – CAUTION DE GARANTIE" => "Une caution de garantie peut être exigée selon le type et la valeur du matériel loué :\n• Structures professionnelles Lilian SEGARD - Ludikevent | Selon devis | Restitution immédiate après état des lieux de fin de location et contrôle de conformité\n• Matériel mis en relation (propriétaires privés) | Selon convention propriétaire | Restitution selon accord entre parties\nLa caution peut être conservée totalement ou partiellement en cas de : dégradation du matériel, salissure importante nécessitant un nettoyage approfondi, perte d'éléments ou d'accessoires, non-respect des conditions d'utilisation ayant entraîné des dommages.",

            "ARTICLE 7 – LIVRAISON, INSTALLATION ET RÉCUPÉRATION" => "7.1 – Structures professionnelles Lilian SEGARD - Ludikevent :\nInstallation : L'installation est réalisée exclusivement par le personnel qualifié de Lilian SEGARD - Ludikevent. Le Client doit fournir un terrain plat, propre, dégagé, avec une alimentation électrique 220V à moins de 50 mètres. L'accès doit être praticable pour les véhicules.\nRécupération : La récupération du matériel est effectuée par Lilian SEGARD - Ludikevent aux horaires convenus.\n7.2 – Matériel en mise en relation :\nLilian SEGARD - Ludikevent peut assurer l'installation et la récupération pour le compte du propriétaire privé. Le Client locataire reste tenu d'assurer une surveillance permanente et constante. Des frais de déplacement supplémentaires peuvent s'appliquer au-delà d'un rayon de 30 km depuis Danizy.",

            "ARTICLE 8 – ÉTATS DES LIEUX ET TRANSFERT DE RESPONSABILITÉ" => "8.1 – État des lieux d'installation : Un état des lieux contradictoire est OBLIGATOIREMENT réalisé. La signature par le Client vaut reconnaissance de conformité et TRANSFERT COMPLET DE LA RESPONSABILITÉ DU MATÉRIEL AU CLIENT. Le Client assume l'ENTIÈRE et EXCLUSIVE responsabilité du matériel, de son utilisation, de sa surveillance et des dommages qui pourraient en résulter.\n8.2 – État des lieux de fin de location : Un état des lieux contradictoire est réalisé lors de la récupération. Sa signature vaut RETOUR DE LA RESPONSABILITÉ À Lilian SEGARD - Ludikevent.\n8.3 – Absence de signature : En cas d'absence ou de refus de signature, l'état des lieux établi par Lilian SEGARD - Ludikevent fera foi. Le matériel sera réputé avoir été livré en parfait état et la responsabilité du Client reste pleine et entière durant toute la période de location.",

            "ARTICLE 9 – OBLIGATIONS ET RESPONSABILITÉS DU CLIENT" => "Le Client s'engage IMPÉRATIVEMENT à :\n9.1 – Surveillance obligatoire : Assurer une surveillance PERMANENTE, CONSTANTE et ACTIVE par un adulte majeur responsable.\n9.2 – Respect des consignes : Ébriété interdite, retrait des chaussures, bijoux, objets pointus, pas de nourriture ou boissons.\n9.3 – Conditions météorologiques : ARRÊTER IMMÉDIATEMENT l'utilisation en cas de vent > 40 km/h, pluie ou orage.\n9.4 – Intégrité du matériel : Ne JAMAIS déplacer, démonter ou modifier les structures, ni débrancher la soufflerie. Signaler toute anomalie immédiatement.",

            "ARTICLE 10 – EXCLUSION ET LIMITATION DE RESPONSABILITÉ" => "10.1 – Lilian SEGARD - Ludikevent garantit exclusivement la livraison d'un matériel conforme et une installation aux normes.\n10.2 – Durant la période de location, Lilian SEGARD - Ludikevent décline FORMELLEMENT et INTÉGRALEMENT toute responsabilité concernant : les dommages corporels (blessures, accidents) subis par les utilisateurs, les dommages matériels causés aux tiers, les conséquences d'une surveillance insuffisante ou du non-respect des consignes.\n10.3 – Le Client reconnaît être le SEUL responsable de la sécurité, assumer tous les risques et renoncer IRRÉVOCABLEMENT à tout recours contre Lilian SEGARD - Ludikevent.",

            "ARTICLE 11 – ASSURANCE OBLIGATOIRE DU CLIENT" => "Le Client doit OBLIGATOIREMENT disposer d'une assurance RC en cours de validité couvrant l'utilisation de structures gonflables. En l'absence d'assurance valide, Lilian SEGARD - Ludikevent se réserve le droit d'annuler la prestation sans remboursement possible.",

            "ARTICLE 12 – CONDITIONS MÉTÉOROLOGIQUES" => "La sécurité est prioritaire. Lilian SEGARD - Ludikevent peut refuser l'installation ou interrompre la prestation en cas de danger. Aucun remboursement ne pourra être réclamé si le matériel a déjà été installé conformément au contrat.",

            "ARTICLE 13 – DÉGRADATIONS, PERTES ET VOLS" => "Toute dégradation ou vol survenu durant la location sera INTÉGRALEMENT facturé au Client (réparation ou valeur de remplacement). Toute dégradation non signalée lors de l'état des lieux d'installation est irréfragablement imputable au Client.",

            "ARTICLE 14 – PROTECTION DES DONNÉES PERSONNELLES" => "Données collectées EXCLUSIVEMENT pour la gestion du contrat (RGPD). Droit d'accès et de rectification via contact@ludikevent.fr.",

            "ARTICLE 15 – RÉCLAMATIONS" => "Toute réclamation doit être adressée par écrit sous 8 jours à : Lilian SEGARD - Ludikevent, 6 Rue du Château, 02800 Danizy. Passé ce délai, aucune réclamation n'est recevable.",

            "ARTICLE 16 – MÉDIATION" => "Le Client consommateur peut recourir gratuitement à un médiateur de la consommation en vue de la résolution amiable du litige.",

            "ARTICLE 17 – DROIT APPLICABLE ET JURIDICTION COMPÉTENTE" => "Loi française. Compétence EXCLUSIVE aux tribunaux du ressort du siège de Lilian SEGARD - Ludikevent (Tribunal de Saint-Quentin).",

            "ARTICLE 18 – ACCEPTATION DES CONDITIONS GÉNÉRALES DE VENTE" => "Le fait de procéder à une réservation vaut ACCEPTATION PLEINE, ENTIÈRE et SANS RÉSERVE des présentes CGV. Le Client reconnaît en avoir pris connaissance et les avoir comprises."
        ];

        foreach ($fullText as $titre => $corps) {
            $this->SetFont('Arial', 'B', 9);
            $this->SetTextColor(37, 99, 235);
            $this->MultiCell(0, 5, $this->clean($titre), 0, 'L');

            $this->SetFont('Arial', '', 8.5);
            $this->SetTextColor(0, 0, 0);
            $this->MultiCell(0, 4, $this->clean($corps), 0, 'L');
            $this->Ln(3);
        }
    }

    private function addSignaturePage(): void
    {
        $this->isExtraPage = true;
        $this->AddPage();
        $this->SetY(30);

        // Titre
        $this->SetFont('Arial', 'B', 14);
        $this->SetTextColor(37, 99, 235);
        $this->Cell(0, 10, $this->clean("BON POUR ACCORD ET SIGNATURE"), 0, 1, 'C');

        $this->Ln(10);
        $this->SetFont('Arial', '', 10);
        $this->SetTextColor(0, 0, 0);
        $this->Cell(0, 7, $this->clean("Fait à Danizy, le : " . date('d/m/Y')), 0, 1, 'L');
        $this->Ln(10);

        // --- SECTION DES CASES À COCHER ---
        $this->SetFont('Arial', '', 10);

        $checkPoints = [
            "J'accepte sans réserve les Conditions Générales de Vente ci-jointes." => "cgv",
            "Je reconnais avoir pris connaissance de l'obligation de disposer d'une assurance RC." => "assurance",
            "Je reconnais avoir pris connaissance des mesures de sécurité et de surveillance." => "securite",
            "Je reconnais avoir pris connaissance du paiement des arrhes de 25% pour validation (non remboursables)." => "arrhes"
        ];

        foreach ($checkPoints as $label => $role) {
            $currentY = $this->GetY();
            $this->Rect(15, $currentY + 1, 5, 5); // Le carré reste visible pour tout le monde
            $this->SetX(25);
            $this->MultiCell(0, 7, $this->clean($label), 0, 'L');

            // AJOUT CONDITIONNEL DOCUSEAL
            if ($this->isIntegrateDocusealFields) {
                $this->SetXY(15, $currentY + 1);
                $this->SetTextColor(0, 0, 0); // Blanc (invisible)
                $this->SetFont('Arial', '', 10);
                $this->Cell(5, 5, '{{type=checkbox;required=true;role=Client;name='.$role.';}}', 0, 0, 'C');

                $this->SetTextColor(0, 0, 0);
                $this->SetFont('Arial', '', 10);
            }

            $this->Ln(10);
        }

        $this->Ln(15);
        $ySign = $this->GetY();

        // --- BLOC PRESTATAIRE ---
        $this->SetXY(15, $ySign);
        $this->SetFont('Arial', 'B', 10);
        $this->Cell(85, 8, $this->clean("Le Prestataire"), 0, 1, 'C');
        $this->SetX(15);
        $this->Cell(85, 45, "", 1, 0);

        $this->SetXY(17, $ySign + 12);

        // AJOUT CONDITIONNEL SIGNATURE PRESTATAIRE
        if ($this->isIntegrateDocusealFields) {
            $this->SetXY(22, $ySign + 9);
            $this->SetTextColor(0, 0, 0);
            $this->SetFont('Arial', '', 10);
            $this->Cell(85, 5, '{{Sign;type=signature;role=Ludikevent;height=120;width=236}}', 0, 0, 'C');
        }

        // --- BLOC CLIENT ---
        $this->SetTextColor(0, 0, 0);
        $this->SetXY(110, $ySign);
        $this->SetFont('Arial', 'B', 10);
        $this->Cell(85, 8, $this->clean("Le Client (Lu et approuvé)"), 0, 1, 'C');
        $this->SetX(110);
        $this->Cell(85, 45, "", 1, 0);

        // AJOUT CONDITIONNEL SIGNATURE CLIENT
        if ($this->isIntegrateDocusealFields) {
            $this->SetXY(113, $ySign+9);
            $this->SetTextColor(0, 0, 0);
            $this->SetFont('Arial', '', 10);
            $this->Cell(85, 5, '{{Sign;type=signature;role=Client;height=120;width=236}}', 0, 0, 'C');
        }
    }

    private function renderAddressBlock(string $label, $address, string $align, int $x, int $y)
    {
        if (!$address) return;
        $this->SetXY($x, $y);
        $this->SetFont('Arial', 'B', 8); $this->SetTextColor(100, 100, 100);
        $this->Cell(90, 5, $this->clean($label), 0, 1, $align);
        $this->SetFont('Arial', '', 10); $this->SetTextColor(50, 50, 50);
        $lines = [$address->getAddress(), $address->getAddress2(), $address->getZipcode() . ' ' . $address->getCity()];
        foreach ($lines as $line) { if ($line) { $this->SetX($x); $this->Cell(90, 5, $this->clean($line), 0, 1, $align); } }
    }

    public function Footer(): void
    {
        $this->SetY(-15);
        $this->SetFont('Arial', 'I', 7);
        $this->SetTextColor(150, 150, 150);
        $this->Cell(0, 10, $this->clean('Contrat N° '.$this->contrats->getNumReservation().' - Ludikevent - Page ' . $this->PageNo() . '/{nb}'), 0, 0, 'C');
    }

    private function renderMainContent(): void
    {
        $tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
        // Configuration TVA
        $tvaRate = $tvaEnabled ? 0.20 : 0;
        $tvaLabel = $tvaEnabled ? '20%' : '0%';

        // --- 1. BLOCS IDENTITÉ (LOCATAIRE & LIEU) ---
        $this->SetY(55);
        $this->SetFont('Arial', 'B', 10);
        $this->SetFillColor(245, 245, 245);

        $this->Cell(92, 7, $this->clean(" LE LOCATAIRE"), 0, 0, 'L', true);
        $this->Cell(6, 7, "", 0, 0);
        $this->Cell(92, 7, $this->clean(" LIEU DE L'ÉVÉNEMENT"), 0, 1, 'L', true);

        $this->Ln(2);
        $startY = $this->GetY();
        $this->SetFont('Arial', '', 10);
        $this->SetTextColor(50, 50, 50);

        // Gauche : Client
        $this->SetX(10);
        $nomComplet = $this->contrats->getCustomer()->getSurname() . ' ' . $this->contrats->getCustomer()->getName();
        $this->MultiCell(92, 5, $this->clean($nomComplet . "\n" . "Tél : " . $this->contrats->getCustomer()->getPhone() . "\n" . "Email : " . $this->contrats->getCustomer()->getEmail()), 0, 'L');

        // Droite : Adresse
        $this->SetXY(108, $startY);
        $adresseEvent = $this->contrats->getAddressEvent() . "\n" . ($this->contrats->getAddress2Event() ? $this->contrats->getAddress2Event() . "\n" : "") . ($this->contrats->getAddress3Event() ? $this->contrats->getAddress3Event() . "\n" : "") . $this->contrats->getZipCodeEvent() . " " . $this->contrats->getTownEvent();
        $this->MultiCell(92, 5, $this->clean($adresseEvent), 0, 'L');

        $this->Ln(8);

        // --- 2. LOGISTIQUE & TECHNIQUE ---
        $this->SetFont('Arial', 'B', 10);
        $this->SetFillColor(250, 250, 250);
        $this->Cell(0, 7, $this->clean(" LOGISTIQUE ET INSTALLATION"), 0, 1, 'L', true);
        $this->Ln(2);

        $this->SetFont('Arial', 'B', 9); $this->Cell(45, 5, $this->clean("Nature du sol :"), 0, 0);
        $this->SetFont('Arial', '', 9); $this->Cell(50, 5, $this->clean($this->contrats->getTypeSol()), 0, 0);
        $this->SetFont('Arial', 'B', 9); $this->Cell(45, 5, $this->clean("Distance électricité :"), 0, 0);
        $this->SetFont('Arial', '', 9); $this->Cell(0, 5, $this->clean($this->contrats->getDistancePower()), 0, 1);

        $this->SetFont('Arial', 'B', 9); $this->Cell(45, 5, $this->clean("Pente / Dénivelé :"), 0, 0);
        $this->SetFont('Arial', '', 9); $this->Cell(50, 5, $this->clean($this->contrats->getPente()), 0, 0);
        $this->SetFont('Arial', 'B', 9); $this->Cell(45, 5, $this->clean("Accès véhicule :"), 0, 0);
        $this->SetFont('Arial', '', 9); $this->Cell(0, 5, $this->clean($this->contrats->getAccess()), 0, 1);

        $this->Ln(6);

        // --- 3. TABLEAU FINANCIER ---
        $devis = $this->contrats->getDevis();
        $formule = $devis?->getFormule();
        $promotion = $devis?->getOrderSession()?->getPromotion();

        $interval = $this->contrats->getDateAt()->diff($this->contrats->getEndAt());
        $nbJoursTotal = $interval->days + 1;
        $nbJoursSup = max(0, $nbJoursTotal - 1);

        $this->SetFont('Arial', 'B', 9);
        $this->SetFillColor(37, 99, 235);
        $this->SetTextColor(255, 255, 255);
        $this->Cell(85, 8, $this->clean("Désignation"), 1, 0, 'L', true);
        $this->Cell(25, 8, $this->clean("Tarif HT"), 1, 0, 'C', true);
        $this->Cell(15, 8, $this->clean("Durée"), 1, 0, 'C', true);
        $this->Cell(25, 8, $this->clean("TVA"), 1, 0, 'C', true);
        $this->Cell(40, 8, $this->clean("Total HT"), 1, 1, 'C', true);

        $this->SetTextColor(0, 0, 0);
        $this->SetFont('Arial', '', 9);
        $totalHt = 0; $totalCaution = 0;
        $isFormuleLineAdded = false;

        foreach ($this->contrats->getContratsLines() as $line) {
            $price1Day = $line->getPrice1DayHt();
            $priceSup = $line->getPriceSupDayHt();
            
            // Si formule, on force les prix à 0
            if ($formule) {
                $price1Day = 0;
                $priceSup = 0;
            }

            $sousTotal = $price1Day + ($priceSup * $nbJoursSup);
            
            // Gestion Formule Line
            if ($formule && !$isFormuleLineAdded && $price1Day == 0 && $priceSup == 0) {
                $fPrice = $formule->getPrice1j() ?? 0;
                if ($nbJoursTotal >= 2 && $formule->getPrice2j()) $fPrice = $formule->getPrice2j();
                if ($nbJoursTotal >= 5 && $formule->getPrice5j()) $fPrice = $formule->getPrice5j();
                
                $totalHt += $fPrice;
                $totalCaution += $formule->getCaution() ?? 0;

                $this->Cell(85, 7, $this->clean("Formule : " . $formule->getName()), 1, 0, 'L');
                $this->Cell(25, 7, number_format($fPrice, 2, ',', ' ') . $this->euro(), 1, 0, 'R');
                $this->Cell(15, 7, $nbJoursTotal . " j.", 1, 0, 'C');
                $this->Cell(25, 7, $tvaLabel, 1, 0, 'C');
                $this->Cell(40, 7, number_format($fPrice, 2, ',', ' ') . $this->euro(), 1, 1, 'R');
                
                $isFormuleLineAdded = true;
            }

            // Caution
            if (!$formule) {
                $totalCaution += $line->getCaution();
            }

            $totalHt += $sousTotal;

            $this->Cell(85, 7, $this->clean($line->getName()), 1, 0, 'L');
            $this->Cell(25, 7, ($price1Day == 0 ? "Inclus" : number_format($price1Day, 2, ',', ' ') . $this->euro()), 1, 0, 'R');
            $this->Cell(15, 7, $nbJoursTotal . " j.", 1, 0, 'C');
            $this->Cell(25, 7, $tvaLabel, 1, 0, 'C');
            $this->Cell(40, 7, ($sousTotal == 0 && $price1Day == 0 ? "Inclus" : number_format($sousTotal, 2, ',', ' ') . $this->euro()), 1, 1, 'R');
        }

        foreach ($this->contrats->getContratsOptions() as $opt) {
            $priceOpt = $opt->getPrice();
            $optionName = $opt->getName();

            // Si formule, options gratuites sauf livraison
            if ($formule) {
                $isDelivery = stripos($optionName, 'livraison') !== false || stripos($optionName, 'déplacement') !== false;
                if (!$isDelivery) {
                    $priceOpt = 0;
                }
            }

            $totalHt += $priceOpt;
            $this->SetFillColor(245, 245, 245);
            $this->Cell(85, 7, $this->clean("[Option] " . $optionName), 1, 0, 'L', true);
            $this->Cell(25, 7, ($priceOpt == 0 ? "Inclus" : number_format($priceOpt, 2, ',', ' ') . $this->euro()), 1, 0, 'R', true);
            $this->Cell(15, 7, "Forfait", 1, 0, 'C', true);
            $this->Cell(25, 7, $tvaLabel, 1, 0, 'C', true);
            $this->Cell(40, 7, ($priceOpt == 0 ? "Inclus" : number_format($priceOpt, 2, ',', ' ') . $this->euro()), 1, 1, 'R', true);
        }

        // --- 4. RÉCAPITULATIF FINANCIER ---
        
        // Gestion Promotion
        if ($promotion) {
             $percentage = $promotion['percentage'] ?? 0;
             $name = $promotion['name'] ?? 'Remise';
             $discountAmount = $totalHt * ($percentage / 100);
             
             // Ligne Total HT Avant Remise
             $this->Ln(5);
             $this->SetX(110);
             $this->SetFont('Arial', '', 9);
             $this->SetTextColor(100, 100, 100);
             $this->Cell(50, 7, $this->clean("Total HT Avant Remise"), 'B', 0, 'L');
             $this->Cell(40, 7, number_format($totalHt, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');
             
             // Ligne Promotion
             $this->Ln();
             $this->SetX(110);
             $this->SetTextColor(37, 99, 235);
             $this->Cell(50, 7, $this->clean("Promotion : $name (-$percentage%)"), 'B', 0, 'L');
             $this->Cell(40, 7, "- " . number_format($discountAmount, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');
             $this->SetTextColor(0, 0, 0);

             $totalHt -= $discountAmount;
        }

        $amountTva = $totalHt * $tvaRate;
        $totalTtc = $totalHt + $amountTva;
        $arrhes = $totalTtc * 0.25; // Arrhes calculées sur le TTC

        $this->Ln(5);
        $this->SetX(110);
        $this->SetFont('Arial', 'B', 9);
        
        $lblTotal = $promotion ? "TOTAL HT REMISÉ" : "TOTAL HT";
        
        $this->Cell(50, 7, $this->clean($lblTotal), 'B', 0, 'L');
        $this->Cell(40, 7, number_format($totalHt, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');

        if($tvaEnabled) {
            $this->SetX(110);
            $this->SetFont('Arial', '', 9);
            $this->Cell(50, 7, $this->clean("TVA (" . $tvaLabel . ")"), 'B', 0, 'L');
            $this->Cell(40, 7, number_format($amountTva, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');
        }

        $this->SetX(110);
        $this->SetFont('Arial', 'B', 11);
        $this->SetFillColor(37, 99, 235); $this->SetTextColor(255, 255, 255);
        $this->Cell(50, 9, $this->clean(" TOTAL TTC"), 0, 0, 'L', true);
        $this->Cell(40, 9, number_format($totalTtc, 2, ',', ' ') . $this->euro() . " ", 0, 1, 'R', true);

        $this->Ln(2);
        $this->SetX(110);
        $this->SetTextColor(37, 99, 235); $this->SetFont('Arial', 'B', 10);
        $this->Cell(50, 9, $this->clean("ARRHES À VERSER (25%)"), 'B', 0, 'L');
        $this->Cell(40, 9, number_format($arrhes, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');

        $this->SetX(110);
        $this->SetFont('Arial', 'I', 7); $this->SetTextColor(100, 100, 100);
        $this->MultiCell(90, 4, $this->clean("* Arrhes non remboursables sauf cas de force majeure dûment justifié (voir CGV ART 3)."), 0, 'R');

        $this->Ln(2);
        $this->SetX(110);
        $this->SetFont('Arial', 'B', 10); $this->SetTextColor(220, 38, 38);
        $this->Cell(50, 9, $this->clean("CAUTION DE GARANTIE"), 'B', 0, 'L');
        $this->Cell(40, 9, number_format($totalCaution, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');

        // Mentions légales
        if (!$tvaEnabled) {
            $this->Ln(5);
            $this->SetFont('Arial', 'I', 8); $this->SetTextColor(0, 0, 0);
            $this->Cell(0, 5, $this->clean('TVA non applicable, art. 293 B du CGI'), 0, 1, 'R');
        }

        $this->SetTextColor(0, 0, 0);
    }


}
