<?php

namespace App\Controller\Dashboard;

use App\Entity\Devis;
use App\Entity\DevisLine;
use App\Entity\DevisOptions;
use App\Event\Signature\DevisSend;
use App\Form\NewDevisType;
use App\Logger\AppLogger;
use App\Repository\CustomerAddressRepository;
use App\Repository\CustomerRepository;
use App\Repository\DevisLineRepository;
use App\Repository\DevisOptionsRepository;
use App\Repository\DevisRepository;
use App\Repository\OptionsRepository;
use App\Repository\PrestaireRepository;
use App\Repository\ProductRepository;
use App\Service\Pdf\DevisPdfService;
use App\Service\Signature\Client;
use DateTimeImmutable;
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\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/crm/devis')]
class DevisController extends AbstractController
{
    public function __construct(
        private readonly EntityManagerInterface $em,
        private readonly AppLogger $appLogger,
        private readonly EventDispatcherInterface $eventDispatcher,
        private readonly Client $signatureClient,
        private readonly KernelInterface $kernel,
        private readonly DevisRepository $devisRepository,
        private readonly ProductRepository $productRepository,
        private readonly CustomerAddressRepository $customerAddressRepository,
        private readonly CustomerRepository $customerRepository,
        private readonly PrestaireRepository $prestaireRepository,
    ) {
    }

    #[Route('', name: 'app_crm_devis', options: ['sitemap' => false], methods: ['GET', 'POST'])]
    public function index(PaginatorInterface $paginator, Request $request): Response
    {
        // Gestion de la relance (Action Rapide)
        if ($resendId = $request->query->get('resend')) {
            return $this->handleResend($resendId);
        }



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

        $pagination = $paginator->paginate(
            $this->devisRepository->findBy([], ['createA' => 'DESC']),
            $request->query->getInt('page', 1),
            20
        );

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

    #[Route('/add', name: 'app_crm_devis_add', options: ['sitemap' => false], methods: ['GET', 'POST'])]
    public function add(Request $request, OptionsRepository $optionsRepository): Response
    {
        $devisNumber = "DEVIS-" . sprintf('%05d', $this->devisRepository->count() + 1);
        $this->appLogger->record('VIEW', 'Consultation de la création d\'un devis');

        $devis = new Devis();
        $devis->setNum($devisNumber)
            ->setState("draft")
            ->setCreateA(new \DateTimeImmutable())
            ->setUpdateAt(new \DateTimeImmutable());

        $form = $this->createForm(NewDevisType::class, $devis);
        $form->handleRequest($request);

        if($request->query->has('idDevis')) {
            $devisItem = $this->em->getRepository(Devis::class)->find($request->query->get('idDevis'));
            $this->generateAndSavePdfs($devisItem);
            $devisItem->setUpdateAt(new \DateTimeImmutable());
            $this->em->persist($devisItem);
            $this->em->flush();
            sleep(5);
            $this->signatureClient->createSubmissionDevis($devisItem);
            $devisItem->setState("created_waitsign");
            $this->em->persist($devisItem);
            $this->em->flush();
            $this->eventDispatcher->dispatch(new DevisSend($devisItem));

            return $this->redirectToRoute('app_crm_devis');
        }
        if ($request->isMethod('POST')) {
            $this->processDevisForm($devis, $request->request->all());

            $this->generateAndSavePdfs($devis);

            $devis->setState("wait-send")
                ->setUpdateAt(new \DateTimeImmutable());

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


            $this->addFlash('success', sprintf('Le devis %s a été créé.', $devis->getNum()));
            return $this->redirectToRoute('app_crm_devis_add',['idDevis'=>$devis->getId()]);
        }

        return $this->render('dashboard/devis/add.twig', [
            'form' => $form->createView(),
            'lines' => [['product' => '', 'days' => '', 'price_ht' => '', 'price_sup_ht' => '']],
            'options' => [['product' => '', 'details' => '', 'price_ht' => '']],
            // Variables nécessaires pour la vue d'ajout si elle utilise les mêmes blocks que edit
            'products' => $this->productRepository->findAll(),
            'optionsList' => $optionsRepository->findAll(),
            'shipAddress' => [],
            'billAddress' => [],
            'devis' => $devis,
            'prestataires' => $this->prestaireRepository->findAll(),
        ]);
    }

    #[Route('/{id}', name: 'app_crm_devis_edit', options: ['sitemap' => false], methods: ['GET', 'POST'])]
    public function edit(
        Devis $devis,
        Request $request,
        DevisLineRepository $devisLineRepository,
        DevisOptionsRepository $devisOptionsRepository,
        OptionsRepository $optionsRepository
    ): Response
    {
        $this->appLogger->record('VIEW', 'Consultation pour modifier le devis ' . $devis->getNum());

        $form = $this->createForm(NewDevisType::class, $devis);
        $form->handleRequest($request);

        if ($request->isMethod('POST')) {
            $this->processDevisForm($devis, $request->request->all(), $devisLineRepository, $devisOptionsRepository);

            $this->generateAndSavePdfs($devis);

            $devis->setUpdateAt(new \DateTimeImmutable())
                ->setState("wait-send");

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

            $this->signatureClient->createSubmissionDevis($devis);

            $this->addFlash('success', sprintf('Le devis %s a été modifié.', $devis->getNum()));
            return $this->redirectToRoute('app_crm_devis');
        }

        // Préparation des données pour la vue
        $lines = [];
        foreach ($devis->getDevisLines() as $line) {
            $lines[] = [
                'id' => $line->getId(),
                'product' => $line->getProduct(),
                'days' => $line->getDay(),
                'price_ht' => $line->getPriceHt(),
                'price_sup_ht' => $line->getPriceHtSup()
            ];
        }
        if (empty($lines)) $lines[] = ['id' => '', 'product' => '', 'days' => '', 'price_ht' => '', 'price_sup_ht' => ''];

        $options = [];
        foreach ($devis->getDevisOptions() as $line) {
            $options[] = [
                'id' => $line->getId(),
                'details' => $line->getDetails(),
                'product' => $line->getOption(),
                'price_ht' => $line->getPriceHt(),
            ];
        }
        if (empty($options)) $options[] = ['id' => '', 'product' => '', 'details' => '', 'price_ht' => ''];

        $shipAddress = [];
        $billAddress = [];
        if ($customer = $devis->getCustomer()) {
            foreach ($customer->getCustomerAddresses() as $addr) {
                $label = $addr->getAddress() . " " . $addr->getZipcode() . " " . $addr->getCity();
                $shipAddress[$addr->getId()] = $label;
                $billAddress[$addr->getId()] = $label;
            }
        }

        return $this->render('dashboard/devis/add.twig', [
            'form' => $form->createView(),
            'devis' => $devis,
            'lines' => $lines,
            'options' => $options,
            'products' => $this->productRepository->findAll(),
            'optionsList' => $optionsRepository->findAll(),
            'shipAddress' => $shipAddress,
            'billAddress' => $billAddress,
            'prestataires' => $this->prestaireRepository->findAll(),
        ]);
    }

    #[Route('/delete/{id}', name: 'app_crm_devis_delete', options: ['sitemap' => false], methods: ['GET', 'POST'])]
    public function delete(Devis $devis, Request $request): RedirectResponse
    {
        if ($this->isCsrfTokenValid('delete' . $devis->getId(), $request->query->get('_token'))) {
            $this->appLogger->record('DELETE', sprintf('Suppression définitive du devis : %s', $devis->getNum()));

            if ($devis->getSignatureId()) {
                $this->signatureClient->cancelSign($devis->getSignatureId());
            }

            // Suppression manuelle des lignes et options pour éviter les orphelins si cascade non configurée
            foreach ($devis->getDevisLines() as $line) $this->em->remove($line);
            foreach ($devis->getDevisOptions() as $opt) $this->em->remove($opt);

            $this->em->remove($devis);
            $this->em->flush();

            $this->addFlash('success', sprintf('Le devis %s a été supprimé définitivement.', $devis->getNum()));
        } else {
            $this->addFlash('error', 'Jeton de sécurité invalide. La suppression a été annulée.');
        }

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

    // --- PRIVATE HELPERS ---

    private function handleResend(string $id): RedirectResponse
    {
        $quote = $this->devisRepository->find($id);

        if (!$quote) {
            $this->addFlash("error", "Devis introuvable.");
            return $this->redirectToRoute('app_crm_devis');
        }

        $quote->setState("created_waitsign");
        $this->em->flush();

        $this->eventDispatcher->dispatch(new DevisSend($quote));

        $this->appLogger->record('RESEND', 'Relance signature pour le devis ' . $quote->getNum());
        $this->addFlash("success", "Le lien de signature pour le devis " . $quote->getNum() . " a été renvoyé au client.");

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

    /**
     * Traite les données brutes POST pour hydrater le Devis (Lignes, Options, Dates, Adresses)
     */
    private function processDevisForm(
        Devis $devis,
        array $postData,
        ?DevisLineRepository $lineRepo = null,
        ?DevisOptionsRepository $optRepo = null
    ): void
    {
        $formData = $postData['new_devis'] ?? [];
        $devisData = $postData['devis'] ?? [];

        // 1. Champs de base
        if (!empty($formData['startAt'])) $devis->setStartAt(new DateTimeImmutable($formData['startAt']));
        if (!empty($formData['endAt'])) $devis->setEndAt(new DateTimeImmutable($formData['endAt']));

        if (!empty($devisData['bill_address'])) $devis->setBillAddress($this->customerAddressRepository->find($devisData['bill_address']));
        if (!empty($devisData['ship_address'])) $devis->setAddressShip($this->customerAddressRepository->find($devisData['ship_address']));
        if (!empty($formData['customer'])) $devis->setCustomer($this->customerRepository->find($formData['customer']));

        if (!empty($devisData['paymentMethod'])) $devis->setPaymentMethod($devisData['paymentMethod']);
        if (!empty($devisData['prestataire'])) $devis->setPrestataire($this->prestaireRepository->find($devisData['prestataire']));

        // Calcul durée
        $day = 1;
        if ($devis->getStartAt() && $devis->getEndAt()) {
            $interval = $devis->getStartAt()->diff($devis->getEndAt());
            $day = $interval->days ?: 1;
        }

        // 2. Lignes Produits
        if (!empty($postData['lines']) && is_array($postData['lines'])) {
            foreach ($postData['lines'] as $cd => $lineData) {
                if (empty($lineData['product'])) continue;

                $line = null;
                if ($lineRepo && !empty($lineData['id'])) {
                    $line = $lineRepo->find($lineData['id']);
                }

                if (!$line) {
                    $line = new DevisLine();
                    $line->setDevi($devis);
                    $line->setPos($cd);
                }

                $line->setDay($day)
                    ->setProduct($lineData['product'])
                    ->setPriceHt(floatval($lineData['price_ht'] ?? 0))
                    ->setPriceHtSup(floatval($lineData['price_sup_ht'] ?? 0));

                $this->em->persist($line);
            }
        }

        // 3. Lignes Options
        if (!empty($postData['options']) && is_array($postData['options'])) {
            foreach ($postData['options'] as $optData) {
                if (empty($optData['product'])) continue;

                $opt = null;
                if ($optRepo && !empty($optData['id'])) {
                    $opt = $optRepo->find($optData['id']);
                }

                if (!$opt) {
                    $opt = new DevisOptions();
                    $opt->setDevis($devis);
                }

                $opt->setOption($optData['product'])
                    ->setDetails($optData['details'] ?? '')
                    ->setPriceHt(floatval($optData['price_ht'] ?? 0));

                $this->em->persist($opt);
            }
        }

        $this->em->persist($devis);
    }

    private function generateAndSavePdfs(Devis $devis): void
    {
        // 1. DocuSeal (Version pour signature)
        $docusealService = new DevisPdfService($this->kernel, $devis, $this->productRepository, true);
        $this->savePdfFile($devis, $docusealService->generate(), 'dc_', 'setDevisDocuSealFile');

        // 2. Interne (Version simple)
        $devisService = new DevisPdfService($this->kernel, $devis, $this->productRepository, false);
        $this->savePdfFile($devis, $devisService->generate(), 'devis_', 'setDevisFile');
    }

    private function savePdfFile(Devis $devis, string $content, string $prefix, string $setterMethod): void
    {
        $tmpPath = sys_get_temp_dir() . '/' . $prefix . uniqid() . '.pdf';
        file_put_contents($tmpPath, $content);

        $file = new UploadedFile($tmpPath, $prefix . $devis->getNum() . '.pdf', 'application/pdf', null, true);
        $devis->$setterMethod($file);
    }
}
