<?php

namespace App\Controller\Dashboard;

use App\Entity\Contrats;
use App\Entity\ContratsLine;
use App\Entity\ContratsOption;
use App\Entity\ContratsPayments;
use App\Entity\Devis;
use App\Entity\Facture;
use App\Event\Signature\ContratEvent;
use App\Form\Type\ContratsType;
use App\Logger\AppLogger;
use App\Repository\ContratsRepository;
use App\Repository\DevisRepository;
use App\Service\Mailer\Mailer;
use App\Service\Pdf\ContratPdfService;
use App\Service\Pdf\FacturePdfService;
use App\Service\Pdf\PlPdf;
use App\Service\Signature\Client;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Vich\UploaderBundle\Storage\StorageInterface;

#[Route('/crm/contrats')]
class ContratsController extends AbstractController
{
    public function __construct(
        private readonly EntityManagerInterface $em,
        private readonly AppLogger $appLogger,
        private readonly EventDispatcherInterface $eventDispatcher,
        private readonly Client $signatureClient,
        private readonly Mailer $mailer,
        private readonly StorageInterface $storage,
        private readonly KernelInterface $kernel,
    ) {
    }

    #[Route('', name: 'app_crm_contrats', options: ['sitemap' => false], methods: ['GET'])]
    public function index(
        ContratsRepository $contratsRepository,
        PaginatorInterface $paginator,
        Request $request
    ): Response {
        // Gestion du renvoi de contrat (Action rapide)
        if ($idSend = $request->query->get('idSend')) {
            return $this->handleResendContract($idSend, $contratsRepository);
        }

        $this->appLogger->record('VIEW', 'Consultation de la liste des contrats');

        $pagination = $paginator->paginate(
            $contratsRepository->findBy([], ['createAt' => 'DESC']),
            $request->query->getInt('page', 1),
            10
        );

        return $this->render('dashboard/contrats/list.twig', [
            'contrats' => $pagination,
        ]);
    }

    #[Route('/view/{id}', name: 'app_crm_contrats_view', options: ['sitemap' => false], methods: ['GET', 'POST'])]
    public function view(Contrats $contrat,Mailer $mailer, Request $request): Response
    {
        // 1. Actions sur la Caution (Libérer / Encaisser)
        if ($action = $request->query->get('action')) {
            return $this->handleCautionAction($contrat, $action);
        }

        // 2. Paiement Manuel (Accompte / Solde / Caution)
        if ($type = $request->query->get('type')) {
            $method = $request->query->get('method', 'Autre');
            return $this->handleManualPayment($contrat, $type, $method);
        }

        if ($request->query->has('act')) {
            $act = $request->query->get('act');

            if ($act === 'generateInvoice') {
                $facturePdfService = new FacturePdfService($this->kernel,$contrat);
                // Utilisation du service pour générer le binaire du PDF
                $pdfContent = $facturePdfService->generate();
                $filename = 'Facture_Ludikevent_' . $contrat->getNumReservation() . '.pdf';
                $attachment = new \Symfony\Component\Mime\Part\DataPart($pdfContent, $filename, 'application/pdf');
                $fc = $contrat->getFacture();
                if(!$fc instanceof Facture){
                    $fc = new Facture();
                    $fc->setContrat($contrat);
                    $fc->setCreateAt(new \DateTimeImmutable('now'));
                    $fc->setUpdateAt(new \DateTimeImmutable());
                    $fc->setNum('F' . $contrat->getNumReservation());
                    $this->em->persist($fc);
                }
                $tmpPath = sys_get_temp_dir() . '/facture_' . uniqid() . '.pdf';
                file_put_contents($tmpPath,$pdfContent); // generate() retourne le contenu ou Output('S')

                $fc->setFactureFile(new UploadedFile($tmpPath,"facture.pdf","application/pdf",0,true));
                $fc->setUpdateAt(new \DateTimeImmutable());
                $this->em->persist($fc);
                $this->em->flush();

                $mailer->send(
                    $contrat->getCustomer()->getEmail(),
                    $contrat->getCustomer()->getSurname() . ' ' . $contrat->getCustomer()->getName(),
                    "Votre facture Ludikevent - #" . $contrat->getNumReservation(),
                    "mails/customer/invoice.twig", // Créez ce template simple
                    ['contrat' => $contrat],
                    [$attachment] // Passage de la pièce jointe
                );
                return new Response($pdfContent, 200, [
                    'Content-Type' => 'application/pdf',
                    'Content-Disposition' => HeaderUtils::makeDisposition(
                        HeaderUtils::DISPOSITION_INLINE,
                        'Facture_Ludikevent_' . $contrat->getNumReservation() . '.pdf'
                    )
                ]);
            }
            if ($act == "downloadFilePv" && $contrat->getEtatLieux()) {
                $etatLieux = $contrat->getEtatLieux();

                $zipName = 'Photos_EDL_' . $contrat->getNumReservation() . '.zip';
                $tempFile = tempnam(sys_get_temp_dir(), 'zip_edl');

                $zip = new \ZipArchive();
                if ($zip->open($tempFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === TRUE) {

                    // 1. AJOUT DU FICHIER ok.txt
                    $zip->addFromString('ok.txt', 'ok');

                    // 2. Photos Installation
                    foreach ($etatLieux->getFiles() as $file) {
                        $filePath = $this->kernel->getProjectDir() . "/public" . $this->storage->resolveUri($file, 'file');
                        if (file_exists($filePath)) {
                            $zip->addFile($filePath,  basename($filePath));
                        }
                    }

                    $zip->close();
                }

                if (!file_exists($tempFile)) {
                    $this->addFlash('error', "Échec de la génération du ZIP.");
                    return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]);
                }

                // Préparation de la réponse
                $response = new Response(file_get_contents($tempFile));

                $disposition = HeaderUtils::makeDisposition(
                    HeaderUtils::DISPOSITION_ATTACHMENT,
                    $zipName
                );

                $response->headers->set('Content-Type', 'application/zip');
                $response->headers->set('Content-Disposition', $disposition);

                // Nettoyage
                unlink($tempFile);

                return $response;
            }
            if ($act == "downloadFileReturnPv" && $contrat->getEtatLieux()) {
                $etatLieux = $contrat->getEtatLieux();

                $zipName = 'Photos_EDL_' . $contrat->getNumReservation() . '.zip';
                $tempFile = tempnam(sys_get_temp_dir(), 'zip_edl');

                $zip = new \ZipArchive();
                if ($zip->open($tempFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === TRUE) {

                    // 1. AJOUT DU FICHIER ok.txt
                    $zip->addFromString('ok.txt', 'ok');

                    // 2. Photos Installation
                    foreach ($etatLieux->getFileReturn() as $file) {
                        $filePath = $this->kernel->getProjectDir() . "/public" . $this->storage->resolveUri($file, 'file');
                        if (file_exists($filePath)) {
                            $zip->addFile($filePath, basename($filePath));
                        }
                    }
                    $zip->close();
                }

                if (!file_exists($tempFile)) {
                    $this->addFlash('error', "Échec de la génération du ZIP.");
                    return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]);
                }

                // Préparation de la réponse
                $response = new Response(file_get_contents($tempFile));

                $disposition = HeaderUtils::makeDisposition(
                    HeaderUtils::DISPOSITION_ATTACHMENT,
                    $zipName
                );

                $response->headers->set('Content-Type', 'application/zip');
                $response->headers->set('Content-Disposition', $disposition);

                // Nettoyage
                unlink($tempFile);

                return $response;
            }
        }
        // 3. Calculs financiers pour l'affichage
        $totalHt = 0;
        $totalCaution = 0;
        $days = $contrat->getDateAt()->diff($contrat->getEndAt())->days + 1;

        foreach ($contrat->getContratsLines() as $line) {
            $totalHt += $line->getPrice1DayHt() + ($line->getPriceSupDayHt() * max(0, $days - 1));
            $totalCaution += $line->getCaution();
        }

        foreach ($contrat->getContratsOptions() as $option) {
            $totalHt += $option->getPrice();
        }

        $dejaPaye = 0;
        foreach ($contrat->getContratsPayments() as $p) {
            if ($p->getState() === 'complete' && $p->getType() !== 'caution') {
                $dejaPaye += $p->getAmount();
            }
        }

        return $this->render('dashboard/contrats/view.twig', [
            'contrat' => $contrat,
            'days' => $days,
            'solde' => $totalHt - $dejaPaye,
            'totalHT' => $totalHt,
            'totalCaution' => $totalCaution,
            'arrhes' => $totalHt * 0.25,
        ]);
    }

    #[Route('/add', name: 'app_crm_contrats_create', options: ['sitemap' => false], methods: ['GET', 'POST'])]
    public function add(Request $request, DevisRepository $devisRepository): Response
    {
        $devis = $devisRepository->find($request->query->get('idDevis', 0));
        $contrat = new Contrats();

        // Pré-remplissage depuis le devis
        if ($devis instanceof Devis) {
            $this->hydrateFromDevis($contrat, $devis);
        }

        $form = $this->createForm(ContratsType::class, $contrat);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            return $this->handleCreateContract($contrat, $request);
        }

        // Prepare lines and options for the view
        $lines = [];
        if ($devis instanceof Devis) {
            foreach ($devis->getDevisLines() as $line) {
                $lines[] = [
                    'id' => '', // New ID for contract line
                    'name' => $line->getProduct(),
                    'priceHt1Day' => $line->getPriceHt(),
                    'priceHtSupDay' => $line->getPriceHtSup(),
                    'caution' => 0 // Default caution
                ];
            }
        }
        if (empty($lines)) {
            $lines[] = ['id' => '', 'name' => '', 'priceHt1Day' => '', 'priceHtSupDay' => '', 'caution' => ''];
        }

        $options = [];
        if ($devis instanceof Devis) {
            foreach ($devis->getDevisOptions() as $opt) {
                $options[] = [
                    'id' => '',
                    'name' => $opt->getOption(),
                    'details' => $opt->getDetails(),
                    'priceHt' => $opt->getPriceHt()
                ];
            }
        }
        if (empty($options)) {
            $options[] = ['id' => '', 'name' => '', 'details' => '', 'priceHt' => ''];
        }

        return $this->render('dashboard/contrats/add.twig', [
            'devis' => $devis,
            'form' => $form->createView(),
            'lines' => $lines,
            'options' => $options
        ]);
    }

    // --- PRIVATE HELPERS ---

    private function handleResendContract(int $id, ContratsRepository $repo): RedirectResponse
    {
        $contrat = $repo->find($id);
        if (!$contrat) {
            $this->addFlash("danger", "Contrat introuvable.");
            return $this->redirectToRoute('app_crm_contrats');
        }

        $this->eventDispatcher->dispatch(new ContratEvent($contrat));

        $this->appLogger->record('RESEND', "Renvoi du contrat N°" . $contrat->getNumReservation());
        $this->addFlash("success", "Le contrat a bien été envoyé à " . $contrat->getCustomer()->getEmail());

        return $this->redirectToRoute('app_crm_contrats');
    }

    private function handleCautionAction(Contrats $contrat, string $action): RedirectResponse
    {
        $paymentCaution = $this->em->getRepository(ContratsPayments::class)->findOneBy([
            'contrat' => $contrat,
            'type' => 'caution',
        ]);

        if (!$paymentCaution) {
            return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]);
        }

        $isRelease = ($action === 'liberer');
        $newState = $isRelease ? "release" : "recup";
        $subject = $isRelease
            ? "[Ludikevent] Votre caution a été libérée - #" . $contrat->getNumReservation()
            : "[Ludikevent] Votre caution a été encaissée - #" . $contrat->getNumReservation();
        $template = $isRelease
            ? "mails/customer/caution_release.twig"
            : "mails/customer/caution_encaissement.twig";
        $logAction = $isRelease ? "Libération caution" : "Encaissement caution";

        $paymentCaution->setState($newState);
        $this->em->flush();

        $customer = $contrat->getCustomer();
        $this->mailer->send(
            $customer->getEmail(),
            $customer->getSurname() . ' ' . $customer->getName(),
            $subject,
            $template,
            [
                'datas' => [
                    'contrat' => $contrat,
                    'payment' => $paymentCaution,
                    'customer' => $customer,
                    'reservationLink' => "https://reservation.ludikevent.fr" . $this->generateUrl('gestion_contrat_view', ['num' => $contrat->getNumReservation()])
                ]
            ]
        );

        $this->appLogger->record('PAYMENT', "$logAction pour #" . $contrat->getNumReservation());
        $this->addFlash("success", "Action effectuée avec succès.");

        return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]);
    }

    private function handleManualPayment(Contrats $contrat, string $type, string $paymentMethod = 'Autre'): RedirectResponse
    {
        // Calculs basiques pour le montant (à affiner selon vos besoins réels si différent de la vue)
        $totalHt = 0;
        $totalCaution = 0;
        $days = $contrat->getDateAt()->diff($contrat->getEndAt())->days + 1;

        foreach ($contrat->getContratsLines() as $line) {
            $totalHt += $line->getPrice1DayHt() + ($line->getPriceSupDayHt() * ($days - 1));
            $totalCaution += $line->getCaution();
        }
        foreach ($contrat->getContratsOptions() as $opt) {
            $totalHt += $opt->getPrice();
        }

        // Calcul solde restant
        $dejaPaye = 0;
        foreach ($contrat->getContratsPayments() as $p) {
            if ($p->getState() === 'complete' && $p->getType() !== 'caution') {
                $dejaPaye += $p->getAmount();
            }
        }

        $amount = match($type) {
            'accompte' => $totalHt * 0.25,
            'caution' => $totalCaution,
            'solde' => $totalHt - $dejaPaye,
            default => 0
        };

        $payment = $this->em->getRepository(ContratsPayments::class)->findOneBy([
            'contrat' => $contrat,
            'type' => $type,
        ]) ?? new ContratsPayments();

        $payment->setContrat($contrat)
            ->setType($type)
            ->setAmount($amount)
            ->setState("complete")
            ->setPaymentAt(new \DateTimeImmutable())
            ->setValidateAt(new \DateTimeImmutable())
            ->setUpdateAt(new \DateTimeImmutable())
            ->setCard(['type' => 'manuel'])
            ->setTypePayment($paymentMethod)
            ->setPaymentId("MANUAL-" . uniqid());

        // Génération PDF Reçu
        $pdf = new PlPdf($this->kernel, $payment, $contrat);
        $tmpPath = sys_get_temp_dir() . '/pay_' . uniqid() . '.pdf';
        file_put_contents($tmpPath, $pdf->generate()); // generate() retourne le contenu ou Output('S')

        $payment->setPaymentFile(new UploadedFile($tmpPath, "recu-" . $type . ".pdf", "application/pdf", null, true));

        $this->em->persist($payment);
        $this->em->flush();

        // Signature automatique (si nécessaire pour manuel ?)
        $signedUrl = $this->signatureClient->autoSignConfirmedPayment($payment);
        if ($signedUrl) {
            $tmpSigned = sys_get_temp_dir() . '/signed_' . uniqid() . '.pdf';
            file_put_contents($tmpSigned, file_get_contents($signedUrl));
            $payment->setPaymentSignedFile(new UploadedFile($tmpSigned, "sign-" . $type . ".pdf", "application/pdf", null, true));
            $this->em->flush();
        }

        // Notification Client
        $customer = $contrat->getCustomer();
        $this->mailer->send(
            $customer->getEmail(),
            $customer->getSurname() . ' ' . $customer->getName(),
            "[Ludikevent] Confirmation de paiement - " . $type,
            "mails/customer/accompte_confirmation.twig",
            [
                'datas' => [
                    'contrat' => $contrat,
                    'payment' => $payment,
                    'customer' => $customer,
                    'reservationLink' => "https://reservation.ludikevent.fr" . $this->generateUrl('gestion_contrat_view', ['num' => $contrat->getNumReservation()])
                ]
            ]
        );

        $this->appLogger->record('PAYMENT', "Validation manuelle $type pour #" . $contrat->getNumReservation());
        $this->addFlash("success", "Paiement $type enregistré.");

        return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]);
    }

    private function handleCreateContract(Contrats $contrat, Request $request): RedirectResponse
    {
        $postData = $request->request->all();

        // Traitement manuel des lignes et options car non mappés directement dans le formulaire Symfony
        foreach ($postData['lines'] ?? [] as $line) {
            $vc = (new ContratsLine())
                ->setContrat($contrat)
                ->setName($line['name'])
                ->setPrice1DayHt($line['priceHt1Day'])
                ->setPriceSupDayHt($line['priceHtSupDay'])
                ->setCaution($line['caution']);
            $this->em->persist($vc);
            $contrat->addContratsLine($vc);
        }

        foreach ($postData['options'] ?? [] as $opt) {
            $vo = (new ContratsOption())
                ->setName($opt['name'])
                ->setDetails($opt['details'])
                ->setPrice($opt['priceHt']);
            $this->em->persist($vo);
            $contrat->addContratsOption($vo);
        }

        $contrat->setNumReservation($this->generateReservationNumber());
        $contrat->setCreateAt(new \DateTimeImmutable());

        // Génération des PDFs (Interne & DocuSeal)
        foreach ([true, false] as $isDocuseal) {
            $service = new ContratPdfService($this->kernel, $contrat, $isDocuseal);
            $tmp = sys_get_temp_dir() . '/' . uniqid() . '.pdf';
            file_put_contents($tmp, $service->generate());

            $file = new UploadedFile($tmp, 'doc.pdf', 'application/pdf', null, true);
            $isDocuseal ? $contrat->setDevisDocuSealFile($file) : $contrat->setDevisFile($file);
        }

        $this->em->persist($contrat);
        $this->em->flush();

        // Création de la soumission de signature
        $this->signatureClient->createSubmissionContrat($contrat);

        $this->appLogger->record('CREATE', "Contrat généré : " . $contrat->getNumReservation());
        return $this->redirectToRoute('app_crm_contrats');
    }

    private function hydrateFromDevis(Contrats $contrat, Devis $devis): void
    {
        $contrat->setDateAt($devis->getStartAt())
            ->setEndAt($devis->getEndAt())
            ->setCustomer($devis->getCustomer())
            ->setDevis($devis);

        if ($devis->getOrderSession() && $devis->getOrderSession()->getPrestataire()) {
            $contrat->setPrestataire($devis->getOrderSession()->getPrestataire());
        }

        if ($address = $devis->getAddressShip()) {
            $contrat->setAddressEvent($address->getAddress())
                ->setZipCodeEvent($address->getZipcode())
                ->setTownEvent($address->getCity());
        }
    }

    private function generateReservationNumber(): string
    {
        return 'RESERV-' . date('Ymd') . '-' . substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 10);
    }
}
