<?php

namespace App\Controller;

use App\Entity\Customer;
use App\Entity\CustomerTracking;
use App\Entity\Formules;
use App\Entity\Product;
use App\Entity\ProductReserve;
use App\Entity\Promotion;
use App\Entity\SitePerformance;
use App\Repository\CategoryRepository;
use App\Repository\CustomerRepository;
use App\Repository\CustomerTrackingRepository;
use App\Repository\FormulesRepository;
use App\Repository\OrderSessionRepository;
use App\Repository\OptionsRepository;
use App\Repository\ProductRepository;
use App\Repository\ProductReserveRepository;
use App\Repository\PromotionRepository;
use App\Service\Mailer\Mailer;
use App\Service\Search\Client;
use Doctrine\ORM\EntityManagerInterface;
use Fkrzski\RobotsTxt\RobotsTxt;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
use App\Service\Pdf\DevisPdfService;
use App\Entity\Devis;
use App\Entity\DevisLine;
use App\Entity\CustomerAddress;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class ReserverController extends AbstractController
{
    private KernelInterface $kernel;
    private ?array $simplifiedCommunes = null;

    public function __construct(KernelInterface $kernel)
    {
        $this->kernel = $kernel;
    }

    #[Route('/flow/{sessionId}/devis', name: 'reservation_generate_devis', methods: ['GET'])]
    public function generateDevis(
        string $sessionId,
        OrderSessionRepository $repository,
        ProductRepository $productRepository,
        OptionsRepository $optionsRepository
    ): Response {
        $session = $repository->findOneBy(['uuid' => $sessionId]);
        if (!$session) {
            return $this->redirectToRoute('reservation');
        }

        if ($session->getState() === 'send') {
            return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
        }

        $sessionData = $session->getProducts();
        $ids = $sessionData['ids'] ?? [];
        $startStr = $sessionData['start'] ?? null;
        $endStr = $sessionData['end'] ?? null;

        $duration = $this->calculateDuration($startStr, $endStr);
        $dates = $this->getDatesFromStrings($startStr, $endStr);

        // Création des objets temporaires pour le PDF
        $customer = new Customer();
        $customer->setName($session->getCustomer() ? $session->getCustomer()->getName() : 'Client');
        $customer->setSurname($session->getCustomer() ? $session->getCustomer()->getSurname() : 'Temporaire');
        $customer->setEmail($session->getCustomer() ? $session->getCustomer()->getEmail() : '');
        $customer->setPhone($session->getCustomer() ? $session->getCustomer()->getPhone() : '');

        // Adresse de facturation
        $billAddress = new CustomerAddress();
        $billAddress->setAddress($session->getBillingAddress() ?? '');
        $billAddress->setZipcode($session->getBillingZipCode() ?? '');
        $billAddress->setCity($session->getBillingTown() ?? '');

        // Adresse de prestation
        $shipAddress = new CustomerAddress();
        $shipAddress->setAddress($session->getAdressEvent() ?? '');
        $shipAddress->setAddress2($session->getAdress2Event() ?? '');
        $shipAddress->setZipcode($session->getZipCodeEvent() ?? '');
        $shipAddress->setCity($session->getTownEvent() ?? '');

        $devis = new Devis();
        $devis->setCustomer($customer);
        $devis->setBillAddress($billAddress);
        $devis->setAddressShip($shipAddress);
        $devis->setNum('PROVISOIRE');
        $devis->setStartAt($dates['start']);
        $devis->setEndAt($dates['end']);

        $selectedOptionsMap = $sessionData['options'] ?? [];
        $runningTotalHT = 0;

        $formule = $session->getFormule();
        $formuleConfig = [];
        if ($formule) {
            $restriction = $formule->getFormulesRestriction();
            if ($restriction) {
                $formuleConfig = $restriction->getRestrictionConfig() ?? [];
            }

            $formulaBasePrice = 0;
            if ($duration <= 1) $formulaBasePrice = $formule->getPrice1j();
            elseif ($duration <= 2) $formulaBasePrice = $formule->getPrice2j();
            else $formulaBasePrice = $formule->getPrice5j();

            $line = new DevisLine();
            $line->setProduct("Formule : " . $formule->getName());
            $line->setPriceHt($formulaBasePrice);
            $line->setPriceHtSup(0);
            $line->setDay(1);
            $devis->addDevisLine($line);

            $runningTotalHT += $formulaBasePrice;
        }

        if (!empty($ids)) {
            $products = $productRepository->findBy(['id' => $ids]);
            $processedProductIds = [];

            foreach ($products as $product) {
                $processedProductIds[] = $product->getId();

                $isInFormule = false;
                if ($formule) {
                    foreach ($formuleConfig as $c) {
                        if (($c['product'] ?? '') === $product->getName()) {
                            $isInFormule = true;
                            break;
                        }
                    }
                }

                $line = new DevisLine();
                $line->setProduct($product->getName());

                if ($formule && $isInFormule) {
                    $line->setPriceHt(0);
                    $line->setPriceHtSup(0);
                } else {
                    $line->setPriceHt($product->getPriceDay());
                    $line->setPriceHtSup($product->getPriceSup());
                    $runningTotalHT += $product->getPriceDay() + ($product->getPriceSup() * max(0, $duration - 1));
                }

                $line->setDay($duration);
                $devis->addDevisLine($line);

                if (isset($selectedOptionsMap[$product->getId()])) {
                    $optionIds = $selectedOptionsMap[$product->getId()];
                    if (!empty($optionIds)) {
                        $options = $optionsRepository->findBy(['id' => $optionIds]);
                        foreach ($options as $option) {
                            $lineOpt = new DevisLine();
                            $lineOpt->setProduct("Option : " . $option->getName());
                            $lineOpt->setPriceHt($option->getPriceHt());
                            $lineOpt->setPriceHtSup(0);
                            $lineOpt->setDay($duration);
                            $devis->addDevisLine($lineOpt);

                            $runningTotalHT += $option->getPriceHt();
                        }
                    }
                }
            }

            foreach ($selectedOptionsMap as $prodId => $optIds) {
                if (!in_array($prodId, $processedProductIds) && !empty($optIds)) {
                    $options = $optionsRepository->findBy(['id' => $optIds]);
                    foreach ($options as $option) {
                        $lineOpt = new DevisLine();
                        $lineOpt->setProduct("Option : " . $option->getName());
                        $lineOpt->setPriceHt($option->getPriceHt());
                        $lineOpt->setPriceHtSup(0);
                        $lineOpt->setDay($duration);
                        $devis->addDevisLine($lineOpt);

                        $runningTotalHT += $option->getPriceHt();
                    }
                }
            }
        }

        $promoData = $session->getPromotion();
        if ($promoData && $runningTotalHT > 0) {
            $discount = $runningTotalHT * ($promoData['percentage'] / 100);
            $promoLine = new DevisLine();
            $promoLine->setProduct("Promotion : " . $promoData['name'] . " (-" . $promoData['percentage'] . "%)");
            $promoLine->setPriceHt(-$discount);
            $promoLine->setPriceHtSup(0);
            $promoLine->setDay(1);
            $devis->addDevisLine($promoLine);
        }

        $pdfService = new DevisPdfService($this->kernel, $devis, $productRepository);
        $content = $pdfService->generate();

        return new Response($content, 200, [
            'Content-Type' => 'application/pdf',
            'Content-Disposition' => 'attachment; filename="devis_provisoire.pdf"'
        ]);
    }

    private function loadSimplifiedCommunes(): array
    {
        if ($this->simplifiedCommunes !== null) {
            return $this->simplifiedCommunes;
        }

        $filePath = $this->kernel->getProjectDir() . '/public/simplified_communes_by_zip.json';
        if (!file_exists($filePath)) {
            return [];
        }

        $content = file_get_contents($filePath);
        if ($content === false) {
            return [];
        }

        $this->simplifiedCommunes = json_decode($content, true);
        if ($this->simplifiedCommunes === null && json_last_error() !== JSON_ERROR_NONE) {
            return [];
        }

        return $this->simplifiedCommunes;
    }

    #[Route('/robots.txt', name: 'robots_txt', defaults: ['_format' => 'txt'])]
    public function index(Request $request): Response
    {
        $robots = new RobotsTxt();

        if (!file_exists($this->kernel->getProjectDir() . '/var/.online')) {
            $robots->disallow('/');
        } else {
            $robots->disallow('/signature');
            $robots->disallow('/payment');
            $robots->disallow('/paiment');
            $robots->disallow('/intranet');
            $robots->disallow('/crm');
            $robots->disallow('/etl');
            $robots->crawlDelay(60);
            $robots->allow('/reservation');
            $robots->sitemap($request->getSchemeAndHttpHost().'/seo/sitemap.xml');
        }

        return new Response($robots->toString(), Response::HTTP_OK, [
            'Content-Type' => 'text/plain'
        ]);
    }

    #[Route('/', name: 'reservation')]
    public function revervation(FormulesRepository $formulesRepository, ProductRepository $productRepository): Response
    {
        $products = $productRepository->findBy(['category' => '3-15 ans', 'isPublish' => true], ['updatedAt' => 'DESC'], 3);
        $formules = $formulesRepository->findBy(['isPublish' => true], ['pos' => 'ASC'], 3);

        return $this->render('revervation/home.twig', [
            'products' => $products,
            'formules' => $formules,
        ]);
    }

    #[Route('/produit/check', name: 'produit_check', methods: ['GET', 'POST'])]
    public function productCheck(Request $request, ProductReserveRepository $productReserveRepository, ProductRepository $productRepository): Response
    {
        $productId = $request->query->get('id');
        $startStr = $request->query->get('start');
        $endStr = $request->query->get('end');

        if (!$productId && $request->isMethod('POST')) {
            $payload = $request->getPayload();
            $productId = $payload->get('id');
            $startStr = $payload->get('start');
            $endStr = $payload->get('end');
        }

        if (!$productId || !$startStr || !$endStr) {
            return new JsonResponse(['error' => 'Missing parameters'], Response::HTTP_BAD_REQUEST);
        }

        $product = $productRepository->find($productId);
        if (!$product) {
            return new JsonResponse(['error' => 'Product not found'], Response::HTTP_NOT_FOUND);
        }

        try {
            $start = new \DateTimeImmutable($startStr);
            $end = new \DateTimeImmutable($endStr);
        } catch (\Exception $e) {
            return new JsonResponse(['error' => 'Invalid date format'], Response::HTTP_BAD_REQUEST);
        }

        $reserve = new ProductReserve();
        $reserve->setProduct($product);
        $reserve->setStartAt($start);
        $reserve->setEndAt($end);

        $isAvailable = $productReserveRepository->checkAvailability($reserve);

        return new JsonResponse(['dispo' => $isAvailable]);
    }

    #[Route('/produit/check/basket', name: 'produit_check_basket', methods: ['POST'])]
    public function productCheckBasket(Request $request, ProductReserveRepository $productReserveRepository, ProductRepository $productRepository): JsonResponse
    {
        $data = json_decode($request->getContent(), true);
        $ids = $data['ids'] ?? [];
        $startStr = $data['start'] ?? null;
        $endStr = $data['end'] ?? null;

        if (!is_array($ids) || empty($ids) || !$startStr || !$endStr) {
            return new JsonResponse(['available' => false, 'message' => 'Missing or invalid parameters'], Response::HTTP_BAD_REQUEST);
        }

        $availability = $this->_checkProductsAvailability($ids, $startStr, $endStr, $productRepository, $productReserveRepository);

        if (!$availability['allProductsAvailable']) {
            return new JsonResponse([
                'available' => false,
                'message' => 'Certains produits de votre panier ne sont pas disponibles aux dates sélectionnées.',
                'unavailable_products_ids' => $availability['unavailableProductIds']
            ]);
        }

        return new JsonResponse(['available' => true, 'message' => 'Tous les produits sont disponibles.']);
    }

    private function _checkProductsAvailability(
        array $ids,
        ?string $startStr,
        ?string $endStr,
        ProductRepository $productRepository,
        ProductReserveRepository $productReserveRepository
    ): array {
        $allProductsAvailable = true;
        $unavailableProductIds = [];

        if (empty($ids) || !$startStr || !$endStr) {
            return ['allProductsAvailable' => false, 'unavailableProductIds' => $ids];
        }

        try {
            $start = new \DateTimeImmutable($startStr);
            $end = new \DateTimeImmutable($endStr);
        } catch (\Exception $e) {
            return ['allProductsAvailable' => false, 'unavailableProductIds' => $ids];
        }

        foreach ($ids as $productId) {
            $product = $productRepository->find($productId);
            if (!$product) {
                $allProductsAvailable = false;
                $unavailableProductIds[] = $productId;
                continue;
            }

            $reserve = new ProductReserve();
            $reserve->setProduct($product);
            $reserve->setStartAt($start);
            $reserve->setEndAt($end);

            $isAvailable = $productReserveRepository->checkAvailability($reserve);

            if (!$isAvailable) {
                $allProductsAvailable = false;
                $unavailableProductIds[] = $productId;
            }
        }

        return ['allProductsAvailable' => $allProductsAvailable, 'unavailableProductIds' => $unavailableProductIds];
    }

    #[Route('/web-vitals', name: 'reservation_web-vitals', methods: ['POST'])]
    public function webVitals(Request $request, EntityManagerInterface $em): Response
    {
        $data = json_decode($request->getContent(), true);

        if (!$data || !isset($data['name'], $data['value'])) {
            return new Response('Invalid data', Response::HTTP_BAD_REQUEST);
        }

        $existing = $em->getRepository(SitePerformance::class)->findOneBy(['metricId' => $data['id']]);
        $perf = $existing ?? new SitePerformance();

        $perf->setName($data['name']);
        $perf->setValue((float)$data['value']);
        $perf->setPath($data['path'] ?? '/');
        $perf->setMetricId($data['id'] ?? null);
        $perf->setCreatedAt(new \DateTimeImmutable());

        if (!$existing) {
            $em->persist($perf);
        }

        $em->flush();

        return new Response('', Response::HTTP_NO_CONTENT);
    }

    #[Route('/basket/json', name: 'reservation_basket_json', methods: ['POST'])]
    public function basketJson(
        Request $request,
        ProductRepository $productRepository,
        OptionsRepository $optionsRepository,
        UploaderHelper $uploaderHelper,
        PromotionRepository $promotionRepository,
        FormulesRepository $formulesRepository
    ): Response {
        $data = json_decode($request->getContent(), true);
        $ids = $data['ids'] ?? [];
        $selectedOptionsMap = $data['options'] ?? [];
        $startStr = $data['start'] ?? null;
        $endStr = $data['end'] ?? null;

        $duration = $this->calculateDuration($startStr, $endStr);

        $products = [];
        if (!empty($ids)) {
            $products = $productRepository->findBy(['id' => $ids]);
        }

        $foundIds = array_map(fn($p) => $p->getId(), $products);
        $removedIds = array_values(array_diff($ids, $foundIds));

        $promotions = $promotionRepository->findActivePromotions(new \DateTime());
        $promotion = $promotions[0] ?? null;

        $formuleId = $data['formule'] ?? null;
        $formule = null;
        if ($formuleId) {
            $formule = $formulesRepository->find($formuleId);
        }

        $cartData = $this->buildCartData($products, $selectedOptionsMap, $duration, $optionsRepository, $uploaderHelper, $promotion, $formule);

        return new JsonResponse([
            'start_date' => $startStr,
            'end_date' => $endStr,
            'products' => $cartData['items'],
            'options' => $cartData['rootOptions'],
            'unavailable_products_ids' => $removedIds,
            'total' => $cartData['total'],
            'promotion' => $promotion ? ['name' => $promotion->getName(), 'percentage' => $promotion->getPercentage()] : null,
            'formule' => $formule ? ['name' => $formule->getName()] : null
        ]);
    }

    #[Route('/session', name: 'reservation_session_create', methods: ['POST'])]
    public function createSession(Request $request, EntityManagerInterface $em, OrderSessionRepository $sessionRepository, PromotionRepository $promotionRepository, FormulesRepository $formulesRepository, ProductRepository $productRepository): Response
    {
        $data = json_decode($request->getContent(), true);
        $existingUuid = $request->getSession()->get('order_session_uuid');
        $session = null;

        if ($existingUuid) {
            $session = $sessionRepository->findOneBy(['uuid' => $existingUuid]);
        }

        if (!$session) {
            $session = new \App\Entity\OrderSession();
            $session->setUuid(\Symfony\Component\Uid\Uuid::v4()->toRfc4122());
            $session->setState('created');
        }

        // Save promos along with other data
        $sessionData = $data ?? [];
        // Ensure promos key exists if sent (it should be in $data if frontend sends it)
        $session->setProducts($sessionData);

        $promotions = $promotionRepository->findActivePromotions(new \DateTime());
        $promotion = $promotions[0] ?? null;

        if ($promotion) {
            $session->setPromotion(['name' => $promotion->getName(), 'percentage' => $promotion->getPercentage()]);
        } else {
            $session->setPromotion(null);
        }

        $formuleId = $data['formule'] ?? null;
        if ($formuleId) {
            $formule = $formulesRepository->find($formuleId);
            if ($formule) {
                // --- DURATION CHECK ---
                $startStr = $data['start'] ?? null;
                $endStr = $data['end'] ?? null;
                $duration = $this->calculateDuration($startStr, $endStr);

                if ($duration > 5) {
                    return new JsonResponse(['error' => "Impossible d'ajouter cette formule : la durée de réservation excède 5 jours."], Response::HTTP_BAD_REQUEST);
                }

                // --- SECURITY CHECK ---
                $restriction = $formule->getFormulesRestriction();
                if ($restriction) {
                    $ids = $data['ids'] ?? [];
                    if (!empty($ids)) {
                        $products = $productRepository->findBy(['id' => $ids]);
                        $config = $restriction->getRestrictionConfig() ?? [];

                        $counts = ['structure' => 0, 'alimentaire' => 0, 'barhnums' => 0];

                        foreach ($products as $product) {
                            $pName = $product->getName();
                            $type = null;
                            foreach ($config as $c) {
                                if (($c['product'] ?? '') === $pName) {
                                    $type = $c['type'] ?? null;
                                    break;
                                }
                            }

                            if ($type === 'structure') {
                                $counts['structure']++;
                            } elseif ($type === 'alimentaire') {
                                $counts['alimentaire']++;
                            } elseif ($type) {
                                // Only count if type is defined in config (meaning it's part of the formula)
                                $counts['barhnums']++;
                            }
                            // Products not in the formula config are considered "extras" and allowed.
                        }

                        if ($counts['structure'] > $restriction->getNbStructureMax()) {
                            return new JsonResponse(['error' => "Le nombre maximum de structures est dépassé ({$restriction->getNbStructureMax()})."], Response::HTTP_BAD_REQUEST);
                        }
                        if ($counts['alimentaire'] > $restriction->getNbAlimentaireMax()) {
                            return new JsonResponse(['error' => "Le nombre maximum d'éléments alimentaires est dépassé ({$restriction->getNbAlimentaireMax()})."], Response::HTTP_BAD_REQUEST);
                        }
                        if ($counts['barhnums'] > $restriction->getNbBarhumsMax()) {
                            return new JsonResponse(['error' => "Le nombre maximum de barnums/mobilier est dépassé ({$restriction->getNbBarhumsMax()})."], Response::HTTP_BAD_REQUEST);
                        }
                    }
                }
                $session->setFormule($formule);
            }
        }

        $user = $this->getUser();
        if ($user instanceof Customer) {
            $session->setCustomer($user);
        }

        $em->persist($session);
        $em->flush();

        $request->getSession()->set('order_session_uuid', $session->getUuid());

        return new JsonResponse([
            'flowUrl' => $this->generateUrl('reservation_flow', ['sessionId' => $session->getUuid()])
        ]);
    }

    #[Route('/flow/{sessionId}/confirmed', name: 'reservation_flow_confirmed', methods: ['GET', 'POST'])]
    public function flowConfirmed(
        string $sessionId,
        OrderSessionRepository $repository,
        ProductRepository $productRepository,
        UploaderHelper $uploaderHelper,
        ProductReserveRepository $productReserveRepository,
        EntityManagerInterface $em,
        OptionsRepository $optionsRepository,
        HttpClientInterface $client,
        Request $request,
        Mailer $mailer
    ): Response {
        $session = $repository->findOneBy(['uuid' => $sessionId]);
        if (!$session) {
            return $this->render('revervation/session_lost.twig');
        }

        if ($session->getState() === 'send') {
            return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
        }

        if ($request->isMethod('POST')) {
            $mailer->send(
                'contact@ludikevent.fr',
                "Ludikevent",
                "[Ludikevent] - Nouvelle demande de réservation",
                "mails/reserve/confirmation.twig",
                ['session' => $session]
            );

            $session->setState('send');
            $em->flush();
            $request->getSession()->remove('order_session_uuid');
            return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
        }

        $sessionData = $session->getProducts();
        $ids = $sessionData['ids'] ?? [];
        $selectedOptionsMap = $sessionData['options'] ?? [];
        $startStr = $sessionData['start'] ?? null;
        $endStr = $sessionData['end'] ?? null;

        // Check product availability
        $availability = $this->_checkProductsAvailability($ids, $startStr, $endStr, $productRepository, $productReserveRepository);

        if (!$availability['allProductsAvailable']) {
            $this->addFlash('danger', 'Certains produits de votre panier ne sont plus disponibles. Veuillez vérifier votre réservation.');
            return $this->redirectToRoute('reservation_flow', ['sessionId' => $sessionId]);
        }

        $duration = $this->calculateDuration($startStr, $endStr);

        $products = [];
        if (!empty($ids)) {
            $products = $productRepository->findBy(['id' => $ids]);
        }

        // Cleanup missing products from session
        $foundIds = array_map(fn($p) => $p->getId(), $products);
        if (count($foundIds) !== count($ids)) {
            $sessionData['ids'] = $foundIds;
            $session->setProducts($sessionData);
            $em->flush();
        }

        $promoData = $session->getPromotion();
        $promotion = null;
        if ($promoData) {
            $promotion = new Promotion();
            $promotion->setName($promoData['name']);
            $promotion->setPercentage($promoData['percentage']);
        }

        $formule = $session->getFormule();

        $cartData = $this->buildCartData($products, $selectedOptionsMap, $duration, $optionsRepository, $uploaderHelper, $promotion, $formule);

        // --- Calcul Frais de Livraison ---
        $deliveryData = $this->calculateDelivery(
            $session->getAdressEvent(),
            $session->getZipCodeEvent(),
            $session->getTownEvent(),
            $client
        );

        return $this->render('revervation/flow_confirmed.twig', [
            'session' => $session,
            'cart' => [
                'items' => $cartData['items'],
                'options' => $cartData['rootOptions'],
                'startDate' => $startStr ? new \DateTimeImmutable($startStr) : null,
                'endDate' => $endStr ? new \DateTimeImmutable($endStr) : null,
                'duration' => $duration,
                'totalHT' => $cartData['total']['totalHT'],
                'totalTva' => $cartData['total']['totalTva'],
                'totalTTC' => $cartData['total']['totalTTC'],
                'discount' => $cartData['total']['discount'],
                'promotion' => $cartData['total']['promotion'],
                'formule' => $cartData['total']['formule'],
                'tvaEnabled' => $cartData['tvaEnabled'],
            ],
            'delivery' => $deliveryData
        ]);
    }

    #[Route('/flow/{sessionId}/success', name: 'reservation_flow_success', methods: ['GET'])]
    public function flowSuccess(string $sessionId, OrderSessionRepository $repository): Response
    {
        $session = $repository->findOneBy(['uuid' => $sessionId]);

        if (!$session) {
            return $this->redirectToRoute('reservation');
        }

        if ($session->getState() !== 'send') {
            return $this->redirectToRoute('reservation_flow', ['sessionId' => $sessionId]);
        }

        return $this->render('revervation/success.twig');
    }

    #[Route('/flow/{sessionId}', name: 'reservation_flow', methods: ['GET', 'POST'])]
    public function flowLogin(
        string $sessionId,
        AuthenticationUtils $authenticationUtils,
        OrderSessionRepository $repository,
        ProductRepository $productRepository,
        UploaderHelper $uploaderHelper,
        ProductReserveRepository $productReserveRepository,
        OptionsRepository $optionsRepository
    ): Response {
        $session = $repository->findOneBy(['uuid' => $sessionId]);
        if (!$session) {
            return $this->render('revervation/session_lost.twig');
        }

        if ($session->getState() === 'send') {
            return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
        }

        $sessionData = $session->getProducts();
        $ids = $sessionData['ids'] ?? [];
        $selectedOptionsMap = $sessionData['options'] ?? [];
        $startStr = $sessionData['start'] ?? null;
        $endStr = $sessionData['end'] ?? null;

        // Check product availability
        $availability = $this->_checkProductsAvailability($ids, $startStr, $endStr, $productRepository, $productReserveRepository);

        if (!$availability['allProductsAvailable']) {
            $this->addFlash('danger', 'Certains produits de votre panier ne sont plus disponibles. Veuillez vérifier votre sélection.');
            return $this->redirectToRoute('reservation');
        }

        $duration = $this->calculateDuration($startStr, $endStr);

        $products = [];
        if (!empty($ids)) {
            $products = $productRepository->findBy(['id' => $ids]);
        }

        // Cleanup missing products from session
        $foundIds = array_map(fn($p) => $p->getId(), $products);
        if (count($foundIds) !== count($ids)) {
            $sessionData['ids'] = $foundIds;
            $session->setProducts($sessionData);
            // We should probably flush, but no EntityManager injected here in original code for GET/POST mix.
            // Added simple cleanup.
        }

        $promoData = $session->getPromotion();
        $promotion = null;
        if ($promoData) {
            $promotion = new Promotion();
            $promotion->setName($promoData['name']);
            $promotion->setPercentage($promoData['percentage']);
        }

        $formule = $session->getFormule();

        $cartData = $this->buildCartData($products, $selectedOptionsMap, $duration, $optionsRepository, $uploaderHelper, $promotion, $formule);

        return $this->render('revervation/flow.twig', [
            'session' => $session,
            'last_username' => $authenticationUtils->getLastUsername(),
            'error' => $authenticationUtils->getLastAuthenticationError(),
            'cart' => [
                'items' => $cartData['items'],
                'options' => $cartData['rootOptions'],
                'startDate' => $startStr ? new \DateTimeImmutable($startStr) : null,
                'endDate' => $endStr ? new \DateTimeImmutable($endStr) : null,
                'duration' => $duration,
                'totalHT' => $cartData['total']['totalHT'],
                'totalTva' => $cartData['total']['totalTva'],
                'totalTTC' => $cartData['total']['totalTTC'],
                'discount' => $cartData['total']['discount'],
                'promotion' => $cartData['total']['promotion'],
                'formule' => $cartData['total']['formule'],
                'tvaEnabled' => $cartData['tvaEnabled'],
            ]
        ]);
    }

    #[Route('/flow/{sessionId}/update', name: 'reservation_flow_update', methods: ['POST'])]
    public function flowUpdate(
        string $sessionId,
        Request $request,
        OrderSessionRepository $repository,
        EntityManagerInterface $em
    ): Response {
        $session = $repository->findOneBy(['uuid' => $sessionId]);
        if (!$session) {
            return $this->redirectToRoute('reservation');
        }

        if ($session->getState() === 'send') {
            return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
        }

        $session->setBillingAddress($request->request->get('billingAddress'));
        $session->setBillingZipCode($request->request->get('billingZipCode'));
        $session->setBillingTown($request->request->get('billingTown'));

        $session->setAdressEvent($request->request->get('adressEvent'));
        $session->setAdress2Event($request->request->get('adress2Event'));
        $session->setZipCodeEvent($request->request->get('zipCodeEvent'));
        $session->setTownEvent($request->request->get('townEvent'));

        $session->setType($request->request->get('type'));
        $session->setDetails($request->request->get('details'));
        $session->setTypeSol($request->request->get('typeSol'));
        $session->setPente($request->request->get('pente'));
        $session->setAccess($request->request->get('access'));
        $distance = $request->request->get('distancePower');
        if ($distance !== null && $distance !== '') {
            $session->setDistancePower((float)$distance);
        }

        $em->flush();

        return $this->redirectToRoute('reservation_flow_confirmed', ['sessionId' => $sessionId]);
    }

    #[Route('/umami', name: 'reservation_umami', methods: ['POST'])]
    public function umami(
        Request $request,
        CustomerTrackingRepository $customerTrackingRepository,
        EntityManagerInterface $em
    ): Response {
        /** @var Customer $user */
        $user = $this->getUser();
        if (!$user) {
            return new JsonResponse(['error' => 'User not found'], Response::HTTP_UNAUTHORIZED);
        }

        $data = json_decode($request->getContent(), true);
        $umamiSessionId = $data['umami_session'] ?? null;

        if (!$umamiSessionId) {
            return new JsonResponse(['error' => 'No session provided'], Response::HTTP_BAD_REQUEST);
        }

        $track = $customerTrackingRepository->findOneBy(['trackId' => $umamiSessionId]);

        if (!$track) {
            $track = new CustomerTracking();
            $track->setTrackId($umamiSessionId);
            $track->setCreateAT(new \DateTime());
            $track->setCustomer($user);

            $em->persist($track);
        } else {
            if ($track->getCustomer() !== $user) {
                $track->setCustomer($user);
            }
        }

        $em->flush();

        return new JsonResponse(['status' => 'success']);
    }

    #[Route('/catalogue', name: 'reservation_catalogue')]
    public function revervationCatalogue(CategoryRepository $categoryRepository,ProductRepository $productRepository): Response
    {
        return $this->render('revervation/catalogue.twig', [
            'products' => $productRepository->findBy(['isPublish' => true]),
            'categories' => $categoryRepository->findBy([],['id' => 'ASC']),
            'tvaEnabled' => $this->isTvaEnabled(),
        ]);
    }

    #[Route('/formules', name: 'reservation_formules')]
    public function revervationFormules(FormulesRepository $formulesRepository): Response
    {
        return $this->render('revervation/formules.twig', [
            'formules' => $formulesRepository->findBy(['isPublish' => true], ['pos' => 'ASC']),
            'tvaEnabled' => $this->isTvaEnabled(),
        ]);
    }

    #[Route('/formules/{slug}', name: 'reservation_formule_show')]
    public function revervationView(string $slug, FormulesRepository $formulesRepository): Response
    {
        $parts = explode('-', $slug);
        $realId = $parts[0];

        $formule = $formulesRepository->find($realId);

        if (!$formule) {
            throw $this->createNotFoundException('Formules introuvable');
        }

        return $this->render('revervation/formule/show.twig', [
            'formule' => $formule,
            'tvaEnabled' => $this->isTvaEnabled(),
        ]);
    }


    #[Route('/options/{id}', name: 'reservation_options_show')]
    public function revervationShowOpitons(string $id, ProductRepository $productRepository): Response
    {
        // TODO: Implement logic
        return new Response('Not implemented');
    }

    #[Route('/produit/{id}', name: 'reservation_product_show')]
    public function revervationShowProduct(string $id, ProductRepository $productRepository, PromotionRepository $promotionRepository): Response
    {
        $parts = explode('-', $id);
        $realId = $parts[0];

        $product = $productRepository->find($realId);

        if (!$product) {
            throw $this->createNotFoundException('Produit introuvable');
        }

        $allInCat = $productRepository->findBy(['category' => $product->getCategory()], [], 5);

        $otherProducts = array_filter($allInCat, function ($p) use ($product) {
            return $p->getId() !== $product->getId();
        });

        $promotions = $promotionRepository->findActivePromotions(new \DateTime());
        $promotion = $promotions[0] ?? null;

        return $this->render('revervation/produit.twig', [
            'product' => $product,
            'tvaEnabled' => $this->isTvaEnabled(),
            'otherProducts' => array_slice($otherProducts, 0, 4),
            'promotion' => $promotion
        ]);
    }

    #[Route('/connexion', name: 'reservation_login')]
    public function revervationLogin(AuthenticationUtils $authenticationUtils): Response
    {
        return $this->render('revervation/login.twig', [
            'last_username' => $authenticationUtils->getLastUsername(),
            'error' => $authenticationUtils->getLastAuthenticationError()
        ]);
    }

    #[Route('/logout', name: 'reservation_logout')]
    public function revervationLogout(): Response
    {
        return $this->redirectToRoute('reservation');
    }

    #[Route('/creation-compte', name: 'reservation_register')]
    public function revervationRegister(
        Request $request,
        Mailer $mailer,
        EntityManagerInterface $em,
        UserPasswordHasherInterface $hasher
    ): Response {
        if ($request->isMethod('POST')) {
            $payload = $request->getPayload();

            $customer = new Customer();
            $customer->setEmail($payload->getString('email'));
            $customer->setName($payload->getString('name'));
            $customer->setSurname($payload->getString('surname'));
            $customer->setPhone($payload->getString('phone'));
            $customer->setCiv($payload->getString('civ'));
            $customer->setType($payload->getString('type'));

            if ($customer->getType() === 'buisness') {
                $customer->setSiret($payload->getString('siret'));
                $customer->setRaisonSocial($payload->getString('raisonSocial'));
                $customer->setTypCompany($payload->getString('typCompany'));
            }

            $hashedPassword = $hasher->hashPassword($customer, $payload->getString('password'));
            $customer->setPassword($hashedPassword);
            $customer->setRoles(['ROLE_USER']);

            $mailer->send(
                $customer->getEmail(),
                $customer->getName() . " " . $customer->getSurname(),
                "[Ludikevent] - Bienvenue",
                "mails/welcome.twig",
                ['customer' => $customer]
            );

            $em->persist($customer);
            $em->flush();

            $this->addFlash('success', 'Votre compte a été créé avec succès ! Connectez-vous.');
            return $this->redirectToRoute('reservation_login');
        }

        return $this->render('revervation/register.twig');
    }

    #[Route('/mot-de-passe', name: 'reservation_password')]
    public function forgotPassword(
        Request $request,
        CustomerRepository $repository,
        EntityManagerInterface $em,
        Mailer $mailer,
        UserPasswordHasherInterface $hasher
    ): Response {
        $session = $request->getSession();
        $step = $request->query->get('step', 'request');

        if ($request->isMethod('POST')) {
            $payload = $request->getPayload();

            // ÉTAPE 1 : Générer le code et l'envoyer
            if ($payload->has('email_request')) {
                $email = $payload->getString('email_request');
                $customer = $repository->findOneBy(['email' => $email]);

                if ($customer) {
                    $code = str_pad((string)random_int(0, 999999), 6, '0', STR_PAD_LEFT);

                    $session->set('reset_password', [
                        'email' => $email,
                        'code' => $code,
                        'expires' => time() + 900 // Valable 15 minutes
                    ]);

                    $mailer->send(
                        $customer->getEmail(),
                        $customer->getName() . " " . $customer->getSurname(),
                        "[Ludikevent] - Code de récupération",
                        "mails/code_password.twig",
                        ['code' => $code]
                    );

                    return $this->redirectToRoute('reservation_password', ['step' => 'verify']);
                }
                $this->addFlash('danger', 'Email inconnu.');
            }

            // ÉTAPE 2 : Vérifier le code en session
            if ($payload->has('code_verify')) {
                $data = $session->get('reset_password');
                $inputCode = $payload->getString('code_verify');

                if ($data && $data['code'] === $inputCode && time() < $data['expires']) {
                    return $this->redirectToRoute('reservation_password', ['step' => 'reset']);
                }
                $this->addFlash('danger', 'Code invalide ou expiré.');
            }

            // ÉTAPE 3 : Changer le mot de passe
            if ($payload->has('new_password')) {
                $data = $session->get('reset_password');

                if ($data) {
                    $customer = $repository->findOneBy(['email' => $data['email']]);
                    if ($customer) {
                        $newEncoded = $hasher->hashPassword($customer, $payload->getString('new_password'));
                        $customer->setPassword($newEncoded);
                        $em->flush();

                        $session->remove('reset_password');
                        $this->addFlash('success', 'Mot de passe mis à jour !');
                        return $this->redirectToRoute('reservation_login');
                    }
                }
            }
        }

        return $this->render('reservation/password.twig', [
            'step' => $step,
            'email' => $session->get('reset_password')['email'] ?? null
        ]);
    }

    #[Route('/contact', name: 'reservation_contact')]
    public function revervationContact(Request $request, Mailer $mailer): Response
    {
        $form = $this->createFormBuilder()
            ->add('name', TextType::class, ['label' => 'Nom', 'required' => true])
            ->add('surname', TextType::class, ['label' => 'Prenom', 'required' => true])
            ->add('email', EmailType::class, ['label' => 'Email', 'required' => true])
            ->add('phone', TextType::class, ['label' => 'Telephone', 'required' => true])
            ->add('message', TextareaType::class, ['label' => 'Message', 'required' => true]);

        $formObject = $form->getForm();
        $formObject->handleRequest($request);

        if ($formObject->isSubmitted() && $formObject->isValid()) {
            $data = $formObject->getData();

            $mailer->send(
                'lilian@ludikevent.fr',
                "Ludikevent",
                "[Ludikevent] - Demande de contact via la plateforme de reservation",
                "mails/reserve/contact.twig",
                $data
            );

            $this->addFlash('success', 'Votre message a bien été envoyé ! Notre équipe vous répondra dans les plus brefs délais.');

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

        return $this->render('revervation/contact.twig', [
            'form' => $formObject->createView()
        ]);
    }

    #[Route('/recherche', name: 'reservation_search')]
    public function recherche(UploaderHelper $uploaderHelper, Client $client, Request $request, ProductRepository $productRepository): Response
    {
        $results = $client->search('product', $request->query->get('q', ''));
        $items = [];

        foreach ($results['hits'] as $result) {
            $p = $productRepository->find($result['id']);
            if ($p instanceof Product && $p->isPublish()) {
                $items[] = [
                    'image' => $uploaderHelper->asset($p, 'imageFile') ?: "/provider/images/favicon.png",
                    "name" => $p->getName(),
                    "price" => $p->getPriceDay(),
                    "price1day" => $p->getPriceDay(),
                    "caution" => $p->getCaution(),
                    "priceSup" => $p->getPriceSup(),
                    'link' => $this->generateUrl('reservation_product_show', ['id' => $p->slug()]),
                ];
            }
        }

        return $this->render('revervation/search.twig', [
            'products' => $items
        ]);
    }

    #[Route('/mentions-legales', name: 'reservation_mentions-legal')]
    public function revervationLegal(): Response
    {
        return $this->render('revervation/legal.twig');
    }

    #[Route('/cities/lookup', name: 'api_cities_lookup', methods: ['POST'])]
    public function getCityByZipCode(Request $request): JsonResponse
    {
        $data = json_decode($request->getContent(), true);
        $zipCode = $data['zipCode'] ?? null;

        if (!$zipCode) {
            return new JsonResponse(['error' => 'Missing zipCode parameter'], Response::HTTP_BAD_REQUEST);
        }

        $simplifiedCommunes = $this->loadSimplifiedCommunes();
        $cities = $simplifiedCommunes[$zipCode] ?? [];

        if (!empty($cities)) {
            return new JsonResponse(['cities' => $cities]);
        }

        return new JsonResponse(['cities' => [], 'message' => 'City not found for this zip code'], Response::HTTP_NOT_FOUND);
    }

    #[Route('/rgpd', name: 'reservation_rgpd')]
    public function revervationRgpd(): Response
    {
        return $this->render('revervation/rgpd.twig');
    }

    #[Route('/cookies', name: 'reservation_cookies')]
    public function revervationCookies(): Response
    {
        return $this->render('revervation/cookies.twig');
    }

    #[Route('/cgv', name: 'reservation_cgv')]
    public function revervationCgv(): Response
    {
        return $this->render('revervation/cgv.twig');
    }

    #[Route('/hosting', name: 'reservation_hosting')]
    public function revervationHosting(): Response
    {
        return $this->render('revervation/hosting.twig');
    }

    #[Route('/estimer-la-livraison', name: 'reservation_estimate_delivery')]
    public function estimateDelivery(Request $request, HttpClientInterface $client): Response
    {
        $form = $this->createFormBuilder()
            ->add('address', TextType::class, ['required' => true])
            ->add('zipCode', TextType::class, ['required' => true])
            ->add('city', TextType::class, ['required' => true])
            ->getForm();

        $form->handleRequest($request);
        $estimation = null;
        $details = null;
        $geometry = null;

        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();
            $deliveryData = $this->calculateDelivery($data['address'], $data['zipCode'], $data['city'], $client);

            if ($deliveryData['details'] !== null) {
                $estimation = $deliveryData['estimation'];
                $details = $deliveryData['details'];
                $geometry = $deliveryData['geometry'];
            } else {
                $this->addFlash('warning', 'Adresse introuvable ou erreur lors du calcul.');
            }
        }

        return $this->render('revervation/estimate_delivery.twig', [
            'form' => $form->createView(),
            'estimation' => $estimation,
            'details' => $details,
            'geometry' => $geometry
        ]);
    }

    // --- Private Helper Methods ---

    private function calculateDuration(?string $startStr, ?string $endStr): int
    {
        if (!$startStr || !$endStr) {
            return 1;
        }
        try {
            $start = new \DateTimeImmutable($startStr);
            $end = new \DateTimeImmutable($endStr);
            if ($end >= $start) {
                return $start->diff($end)->days + 1;
            }
        } catch (\Exception $e) {
            // Log error if necessary
        }
        return 1;
    }

    private function getDatesFromStrings(?string $startStr, ?string $endStr): array
    {
        $start = null;
        $end = null;
        if ($startStr && $endStr) {
            try {
                $start = new \DateTimeImmutable($startStr);
                $end = new \DateTimeImmutable($endStr);
            } catch (\Exception $e) {
                // Ignore invalid dates
            }
        }
        return ['start' => $start, 'end' => $end];
    }

    private function isTvaEnabled(): bool
    {
        return isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
    }

    private function calculateDelivery(?string $address, ?string $zipCode, ?string $town, HttpClientInterface $client): array
    {
        $result = [
            'estimation' => null,
            'details' => null,
            'geometry' => null
        ];

        if (!$address || !$zipCode || !$town) {
            return $result;
        }

        $query = sprintf('%s %s %s', $address, $zipCode, $town);
        try {
            $response = $client->request('GET', 'https://api-adresse.data.gouv.fr/search/', [
                'query' => [
                    'q' => $query,
                    'limit' => 1
                ]
            ]);

            $content = $response->toArray();

            if (!empty($content['features'])) {
                $coords = $content['features'][0]['geometry']['coordinates'];
                $lon = $coords[0];
                $lat = $coords[1];

                // Point de départ (LudikEvent)
                $startLat = 49.849;
                $startLon = 3.286;

                // Calcul itinéraire via API Geoplateforme
                $itineraireResponse = $client->request('GET', 'https://data.geopf.fr/navigation/itineraire', [
                    'query' => [
                        'resource' => 'bdtopo-osrm',
                        'start' => $startLon . ',' . $startLat,
                        'end' => $lon . ',' . $lat,
                        'profile' => 'car',
                        'optimization' => 'fastest',
                        'distanceUnit' => 'kilometer',
                        'geometryFormat' => 'geojson'
                    ]
                ]);

                $itineraire = $itineraireResponse->toArray();
                $distance = $itineraire['distance'];
                $result['geometry'] = $itineraire['geometry'] ?? null;

                $rate = 0.50;
                $trips = 4;

                if ($distance <= 10) {
                    $result['estimation'] = 0.0;
                    $chargedDistance = 0.0;
                } else {
                    $chargedDistance = $distance - 10;
                    $result['estimation'] = ($chargedDistance * $trips) * $rate;
                }

                $result['details'] = [
                    'distance' => $distance,
                    'chargedDistance' => $chargedDistance,
                    'trips' => $trips,
                    'rate' => $rate,
                    'isFree' => ($distance <= 10)
                ];
            }
        } catch (\Exception $e) {
            // Return default nulls on error
        }

        return $result;
    }

    private function buildCartData(array $products, array $selectedOptionsMap, int $duration, OptionsRepository $optionsRepository, UploaderHelper $uploaderHelper, ?Promotion $promotion = null, ?Formules $formule = null): array
    {
        $items = [];
        $rootOptions = [];
        $totalHT = 0;
        $formulaExtras = 0;
        $tvaEnabled = $this->isTvaEnabled();
        $tvaRate = $tvaEnabled ? 0.20 : 0;
        $processedProductIds = [];

        $formuleConfig = [];
        if ($formule) {
            $restriction = $formule->getFormulesRestriction();
            if ($restriction) {
                $formuleConfig = $restriction->getRestrictionConfig() ?? [];
            }
        }

        foreach ($products as $product) {
            $processedProductIds[] = $product->getId();
            $price1Day = $product->getPriceDay();
            $priceSup = $product->getPriceSup() ?? 0.0;

            // Calcul du coût total pour ce produit selon la durée
            $productTotalHT = $price1Day + ($priceSup * max(0, $duration - 1));

            // Traitement des options
            $productOptions = [];
            $optionsTotalHT = 0;

            if (isset($selectedOptionsMap[$product->getId()])) {
                $optionIds = $selectedOptionsMap[$product->getId()];
                if (!empty($optionIds)) {
                    $optionsEntities = $optionsRepository->findBy(['id' => $optionIds]);
                    foreach ($optionsEntities as $option) {
                        $optPrice = $option->getPriceHt();
                        $optData = [
                            'id' => $option->getId(),
                            'name' => $option->getName(),
                            'price' => $optPrice
                        ];

                        if ($product->getOptions()->contains($option)) {
                            $productOptions[] = $optData;
                            $optionsTotalHT += $optPrice;
                        } else {
                            $rootOptions[] = $optData;
                            if ($formule) {
                                $formulaExtras += $optPrice;
                            } else {
                                $totalHT += $optPrice;
                            }
                        }
                    }
                }
            }

            $productTotalHT += $optionsTotalHT;
            $productTotalTTC = $productTotalHT * (1 + $tvaRate);

            $isInFormule = false;
            if ($formule) {
                foreach ($formuleConfig as $c) {
                    if (($c['product'] ?? '') === $product->getName()) {
                        $isInFormule = true;
                        break;
                    }
                }
            }

            if ($formule) {
                if ($isInFormule) {
                    $formulaExtras += $optionsTotalHT;
                } else {
                    $formulaExtras += $productTotalHT;
                }
            } else {
                $totalHT += $productTotalHT;
            }

            $items[] = [
                'id' => $product->getId(),
                'name' => $product->getName(),
                'product' => $product,
                'image' => $uploaderHelper->asset($product, 'imageFile'),
                'priceHt1Day' => $price1Day,
                'priceHTSupDay' => $priceSup,
                'priceTTC1Day' => $price1Day * (1 + $tvaRate),
                'price1Day' => $price1Day,
                'priceSup' => $priceSup,
                'totalPriceHT' => $productTotalHT,
                'totalPriceTTC' => $productTotalTTC,
                'options' => $productOptions,
                'in_formule' => $isInFormule
            ];
        }

        // Traiter les options orphelines
        foreach ($selectedOptionsMap as $prodId => $optIds) {
            if (!in_array($prodId, $processedProductIds) && !empty($optIds)) {
                $optionsEntities = $optionsRepository->findBy(['id' => $optIds]);
                foreach ($optionsEntities as $option) {
                    $optPrice = $option->getPriceHt();
                    $rootOptions[] = [
                        'id' => $option->getId(),
                        'name' => $option->getName(),
                        'price' => $optPrice,
                        'orphan_product_id' => $prodId
                    ];
                    if ($formule) {
                        $formulaExtras += $optPrice;
                    } else {
                        $totalHT += $optPrice;
                    }
                }
            }
        }

        if ($formule) {
            $formulaBasePrice = 0;
            if ($duration <= 1) {
                $formulaBasePrice = $formule->getPrice1j();
            } elseif ($duration <= 2) {
                $formulaBasePrice = $formule->getPrice2j();
            } else {
                $formulaBasePrice = $formule->getPrice5j();
            }

            $totalHT = $formulaBasePrice + $formulaExtras;
        }

        $discountAmount = 0;
        if ($promotion) {
            $discountAmount = $totalHT * ($promotion->getPercentage() / 100);
            $totalHT -= $discountAmount;
        }

        $totalTva = $totalHT * $tvaRate;
        $totalTTC = $totalHT + $totalTva;

        return [
            'items' => $items,
            'rootOptions' => $rootOptions,
            'total' => [
                'totalHT' => $totalHT,
                'totalTva' => $totalTva,
                'totalTTC' => $totalTTC,
                'discount' => $discountAmount,
                'promotion' => $promotion ? $promotion->getName() : null,
                'formule' => $formule ? $formule->getName() : null
            ],
            'tvaEnabled' => $tvaEnabled
        ];
    }
}
