<?php

namespace App\Twig;

use App\Entity\Contrats;
use App\Entity\ContratsPayments;
use App\Entity\Devis;
use App\Entity\DevisOptions;
use App\Entity\Options;
use App\Entity\Product;
use App\Repository\OrderSessionRepository;
use App\Repository\ProductRepository;
use App\Service\Stripe\Client;
use Doctrine\ORM\EntityManagerInterface;
use Jaybizzle\CrawlerDetect\CrawlerDetect;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;

class StripeExtension extends AbstractExtension
{
    public function __construct(
        private readonly UploaderHelper                $uploaderHelper,
        private readonly \App\Service\Signature\Client $clientSignature,
        private readonly Client                        $client,
        private readonly EntityManagerInterface        $em,
        private readonly OrderSessionRepository        $orderSessionRepository,

    ) {
    }

    public function getFilters(): array
    {
        return [
            new TwigFilter('totalQuoto',[$this,'totalQuoto']),
            new TwigFilter('discountAmount',[$this,'discountAmount']),
            new TwigFilter('totalQuotoBeforeDiscount',[$this,'totalQuotoBeforeDiscount']),
            new TwigFilter('totalQuotoHT',[$this,'totalQuotoHT']),
            new TwigFilter('totalCaution',[$this,'totalCaution']),
            new TwigFilter('totalQuotoAccompte', [$this, 'totalQuotoAccompte']),
            new TwigFilter('totalContrat',[$this,'totalContrat']),
            new TwigFilter('totalPayContrat',[$this,'totalPayContrat']),
            new TwigFilter('totalRestpayContrat',[$this,'totalRestpayContrat']),
            new TwigFilter('totalContratAccompte',[$this,'totalContratAccompte']),
            new TwigFilter('devisSignUrl',[$this,'devisSignUrl']),
        ];
    }

    public function loadProductByName(string $name)
    {
        $p = $this->em->getRepository(Product::class)->findOneBy(['name' => $name]);

        return [
            'id' => $p->getId(),
            'name' => $p->getName(),
            'image' => $this->uploaderHelper->asset($p,'imageFile'),
        ];

    }

    private function calculateDevis(Devis $devis): array
    {
        $totalHT = 0;
        $totalCautions = 0;
        $tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
        $tvaRate = 0.20;

        $formule = $devis->getFormule();
        
        $nbDays = 1;
        if ($devis->getStartAt() && $devis->getEndAt()) {
             $diff = $devis->getEndAt()->diff($devis->getStartAt());
             $nbDays = $diff->days + 1;
        }

        // 1. Products & Cautions
        foreach ($devis->getDevisLines() as $line) {
            if (!$formule) {
                $price1Day = (float) ($line->getPriceHt() ?? 0);
                $priceSupHT = (float) ($line->getPriceHtSup() ?? 0);
                $lDays = (int) ($line->getDay() ?? 1);
                $totalHT += $price1Day + (max(0, $lDays - 1) * $priceSupHT);
            }
            
            if (!$formule) {
                $product = $this->em->getRepository(Product::class)->findOneBy(['name' => $line->getProduct()]);
                if ($product) {
                    $totalCautions += (float) ($product->getCaution() ?? 0);
                }
            }
        }

        // 2. Formule
        if ($formule) {
            $fPrice = $formule->getPrice1j() ?? 0;
            if ($nbDays >= 2 && $formule->getPrice2j()) $fPrice = $formule->getPrice2j();
            if ($nbDays >= 5 && $formule->getPrice5j()) $fPrice = $formule->getPrice5j();
            
            $totalHT += $fPrice;
            $totalCautions += (float) ($formule->getCaution() ?? 0);
        }

        // 3. Options
        foreach ($devis->getDevisOptions() as $option) {
            $price = (float) ($option->getPriceHt() ?? 0);
            $name = $option->getOption();
            if ($formule) {
                $isDelivery = stripos($name, 'livraison') !== false || stripos($name, 'déplacement') !== false;
                if (!$isDelivery) $price = 0;
            }
            $totalHT += $price;
        }

        $totalBeforeDiscount = $totalHT;
        $discount = 0;

        // 4. Promotion
        $promotion = $devis->getOrderSession()?->getPromotion();
        if ($promotion) {
            $percentage = $promotion['percentage'] ?? 0;
            $discount = $totalHT * ($percentage / 100);
            $totalHT -= $discount;
        }

        // 5. TVA
        $montantPrestation = $totalHT;
        if ($tvaEnabled) {
            $montantPrestation = $totalHT * (1 + $tvaRate);
        }

        return [
            'discount' => $discount,
            'final' => $montantPrestation + $totalCautions,
            'prestation' => $montantPrestation,
            'totalBeforeDiscount' => $totalBeforeDiscount,
            'caution' => $totalCautions
        ];
    }

    public function totalQuoto(Devis $devis): float
    {
        return round($this->calculateDevis($devis)['final'], 2);
    }

    public function totalQuotoHT(Devis $devis): float
    {
        return round($this->calculateDevis($devis)['prestation'], 2);
    }

    public function discountAmount(Devis $devis): float
    {
        return round($this->calculateDevis($devis)['discount'], 2);
    }

    public function totalQuotoBeforeDiscount(Devis $devis): float
    {
        return round($this->calculateDevis($devis)['totalBeforeDiscount'], 2);
    }

    public function totalCaution(Devis $devis): float
    {
        return round($this->calculateDevis($devis)['caution'], 2);
    }
    public function totalContratAccompte(Contrats $devis): float
    {
        $totalHT = 0;

        // 1. Calcul des lignes de produits (Location)
        foreach ($devis->getContratsLines() as $line) {
            $totalHT = $totalHT + $line->getCaution();
        }

        return (float) $totalHT;
    }
    public function totalQuotoAccompte(Devis $devis): float
    {
        $totalCautions = 0;

        foreach ($devis->getDevisLines() as $line) {
            // On récupère le produit en base via le nom stocké dans la ligne
            $product = $this->em->getRepository(Product::class)->findOneBy([
                'name' => $line->getProduct()
            ]);

            if ($product) {
                // Additionne la caution du produit (valeur par défaut 0 si nulle)
                $totalCautions += (float) ($product->getCaution() ?? 0);
            }
        }

        return round($totalCautions, 2);
    }

    public function totalPayContrat(Contrats $contrats)
    {
        $total = 0;
        foreach ($contrats->getContratsPayments() as $contratsPayment) {
            if($contratsPayment->getState() == "complete")
                $total += $contratsPayment->getAmount();
        }
        return $total;
    }

    public function totalRestpayContrat(Contrats $contrats)
    {
        $total = 0;
        foreach ($contrats->getContratsPayments() as $contratsPayment) {
            if($contratsPayment->getState() == "complete" && $contratsPayment->getType() != "caution"  && $contratsPayment->getType() != "accompte")
                $total += $contratsPayment->getAmount();
        }

        $totalA = $this->totalContrat($contrats);
        return $totalA - $total ;
    }

    public function totalContrat(Contrats $devis): float
    {
        $totalHT = 0;

        // Calcul de la durée (différence entre dateDebut et dateFin)
        $dateDebut = $devis->getDateAt();
        $dateFin = $devis->getEndAt();

        // Calcul du nombre de jours (minimum 1 jour)
        $nbDays = 1;
        if ($dateDebut instanceof \DateTimeInterface && $dateFin instanceof \DateTimeInterface) {
            $diff = $dateDebut->diff($dateFin);
            $nbDays = $diff->days + 1; // +1 pour inclure le jour de départ
        }

        // 1. Calcul des lignes de produits (Location)
        foreach ($devis->getContratsLines() as $line) {
            $price1Day = (float) ($line->getPrice1DayHt() ?? 0);
            $priceSupHT = (float) ($line->getPriceSupDayHt() ?? 0);

            // Calcul : J1 + (Jours restants * Prix Sup)
            $lineTotalHT = $price1Day + (max(0, $nbDays - 1) * $priceSupHT);
            $totalHT += $lineTotalHT;
        }

        // 2. Calcul des options additionnelles
        foreach ($devis->getContratsOptions() as $devisOption) {
            $totalHT += (float) ($devisOption->getPrice() ?? 0);
        }

        // 3. Gestion de la TVA (20%)
        // Vérification de l'activation via la variable d'environnement
        $tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";

        if ($tvaEnabled) {
            $tauxTVA = 1.20; // Taux de 20%
            return (float) ($totalHT * $tauxTVA);
        }

        return (float) $totalHT;
    }

    public function isBot(array $headers)
    {
       return $this->detectLighthouse();
    }

    function detectLighthouse(): bool
    {
        // Define known patterns associated with Lighthouse or similar tools
        $LIGHTHOUSE_PATTERNS = [
            'chrome-lighthouse',
            'google/lighthouse',
            'lighthouse',
            'pagespeed',
            'pagespeedinsights',
            'googleads-lighthouse',
            'headless',
            'webdriver'
        ];

        // Retrieve the User-Agent string from the request
        $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null;

        // If User-Agent is not present, return false early
        if (!$userAgent) {
            return false;
        }

        // Check if any pattern exists in the User-Agent string
        foreach ($LIGHTHOUSE_PATTERNS as $pattern) {
            if (stripos($userAgent, $pattern) !== false) {
                return true; // Match found
            }
        }

        return false; // No matches found
    }

    public function getFunctions(): array
    {
        return [
          new TwigFunction('isBot', [$this, 'isBot']),
          new TwigFunction('syncStripe', [$this, 'syncStripe']),
          new TwigFunction('contratPaymentPay', [$this, 'contratPaymentPay']),
          new TwigFunction('loadProductByName',[$this,'loadProductByName']),
          new TwigFunction('loadProductById',[$this,'loadProductById']),
          new TwigFunction('loadOptionById',[$this,'loadOptionById']),
          new TwigFunction('totalSession',[$this,'totalSession']),
          new TwigFunction('getPendingOrderSessionCount', [$this, 'getPendingOrderSessionCount']),
        ];
    }

    public function loadProductById($id)
    {
        $p = $this->em->getRepository(Product::class)->find($id);

        if (!$p) return null;

        return [
            'name' => $p->getName(),
            'image' => $this->uploaderHelper->asset($p,'imageFile'),
            'price1day' => $p->getPriceDay(),
            'priceSup' => $p->getPriceSup(),
        ];
    }

    public function loadOptionById($id)
    {
        $o = $this->em->getRepository(Options::class)->find($id);

        if (!$o) return null;

        return [
            'name' => $o->getName(),
            'price' => $o->getPriceHt(),
        ];
    }

    public function totalSession(\App\Entity\OrderSession $session): array
    {
        $sessionData = $session->getProducts();
        $ids = $sessionData['ids'] ?? [];
        $selectedOptionsMap = $sessionData['options'] ?? [];

        $startStr = $sessionData['start'] ?? null;
        $endStr = $sessionData['end'] ?? null;

        $duration = 1;
        if ($startStr && $endStr) {
            try {
                $start = new \DateTimeImmutable($startStr);
                $end = new \DateTimeImmutable($endStr);
                if ($end >= $start) {
                    $duration = $start->diff($end)->days + 1;
                }
            } catch (\Exception $e) {
                $duration = 1;
            }
        }

        $totalHT = 0;
        $formulaExtras = 0;
        $productRepo = $this->em->getRepository(Product::class);
        $optionsRepo = $this->em->getRepository(Options::class);

        $formule = $session->getFormule();
        $config = [];
        if ($formule && ($restrict = $formule->getFormulesRestriction())) {
            $config = $restrict->getRestrictionConfig() ?? [];
        }

        // Products
        foreach ($ids as $id) {
            $product = $productRepo->find($id);
            if ($product) {
                $price = $product->getPriceDay() + ($product->getPriceSup() * max(0, $duration - 1));

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

                if ($formule) {
                    if (!$isInFormule) {
                        $formulaExtras += $price;
                    }
                } else {
                    $totalHT += $price;
                }
            }
        }

        // Options
        foreach ($selectedOptionsMap as $prodId => $optIds) {
            foreach ($optIds as $optId) {
                $option = $optionsRepo->find($optId);
                if ($option) {
                    if ($formule) {
                        $formulaExtras += $option->getPriceHt();
                    } else {
                        $totalHT += $option->getPriceHt();
                    }
                }
            }
        }

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

            $totalHT = $formulaBasePrice + $formulaExtras;
        }

        $originalHT = $totalHT;
        $discountAmount = 0;

        if ($promo = $session->getPromotion()) {
            $discountAmount = $totalHT * ($promo['percentage'] / 100);
            $totalHT -= $discountAmount;
        }

        $tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";

        return [
            'ht' => $totalHT,
            'originalHT' => $originalHT,
            'discount' => $discountAmount,
            'duration' => $duration,
            'tvaEnabled' => $tvaEnabled
        ];
    }

    public function getPendingOrderSessionCount(): int
    {
        return $this->orderSessionRepository->count(['state' => 'send']);
    }

    public function devisSignUrl(Devis $devis): string
    {
        return $this->clientSignature->getLinkSign($devis->getSignatureId());
    }

    public function contratPaymentPay(Contrats $contrat,string $type): bool
    {
        if($type == "caution_free"){
            $pl = $this->em->getRepository(ContratsPayments::class)->findOneBy(['type'=>'caution','contrat'=>$contrat]);
            if($pl instanceof ContratsPayments) {
                return $pl->getState() == "release";
            }
        }
        if($type == "caution_recup"){
            $pl = $this->em->getRepository(ContratsPayments::class)->findOneBy(['type'=>'caution','contrat'=>$contrat]);
            if($pl instanceof ContratsPayments) {
                return $pl->getState() == "recup";
            }
        }
        $pl = $this->em->getRepository(ContratsPayments::class)->findOneBy(['type'=>$type,'contrat'=>$contrat]);
        if($pl instanceof ContratsPayments) {
            return $pl->getState() == "complete";
        }

        return false;

    }
    public function syncStripe(): array
    {
        return $this->client->check();
    }
}
