<?php

namespace App\Controller;

use App\Entity\Account;
use App\Entity\AccountResetPasswordRequest;
use App\Entity\Contrats;
use App\Entity\ContratsPayments;
use App\Entity\Customer;
use App\Entity\CustomerAddress;
use App\Form\RequestPasswordConfirmType;
use App\Form\RequestPasswordRequestType;
use App\Logger\AppLogger;
use App\Repository\ContratsRepository;
use App\Repository\CustomerAddressRepository;
use App\Repository\CustomerRepository;
use App\Service\Mailer\Mailer;
use App\Service\Pdf\ContratPdfService;
use App\Service\Pdf\PlPdf;
use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent;
use App\Service\ResetPassword\Event\ResetPasswordEvent;
use App\Service\Signature\Client;
use Doctrine\ORM\EntityManagerInterface;
use Google\Service\Directory\UserAddress;
use Symfony\Bundle\SecurityBundle\Security;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
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\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;


class ContratController extends AbstractController
{

    #[Route('/contrat/payment/cancel/{id}', name: 'gestion_contrat_cancel')]
    public function gestionContratCancel(
        Contrats $contrat,
        Request $request
    ): Response {
        // On peut éventuellement ajouter un message flash pour informer l'utilisateur
        $this->addFlash('info', 'Le paiement a été annulé. Votre réservation n\'est pas encore validée.');

        return $this->render('reservation/contrat/cancel.twig', [
            'contrat' => $contrat
        ]);
    }
    #[Route('/contrat/payment/success/{id}', name: 'gestion_contrat_success')]
    public function gestionContratSuccess(
        Contrats $contrat,
        Request $request,
        EntityManagerInterface $entityManager,
    ): Response {
        $type = $request->query->get('type', 'accompte');
        if ($type === "accompte") {

            $pl = $entityManager->getRepository(ContratsPayments::class)->findOneBy([
                'type' => 'accompte',
                'contrat' => $contrat
            ]);

            // Si le paiement est déjà marqué comme complété par le Webhook
            if ($pl && $pl->getState() === "complete") {
                return $this->render('reservation/contrat/success.twig', [
                    'contrat' => $contrat,
                    'type' => $type
                ]);
            }
        }
        if ($type === "solde_partiel") {

            $pl = $entityManager->getRepository(ContratsPayments::class)->findOneBy([
                'type' => 'accompte',
                'state' => 'complete',
                'contrat' => $contrat
            ]);

            // Si le paiement est déjà marqué comme complété par le Webhook
            if ($pl) {
                return $this->render('reservation/contrat/success.twig', [
                    'contrat' => $contrat,
                    'type' => 'Paiement partiel'
                ]);
            }
        }
        if ($type === "solde") {

            $pl = $entityManager->getRepository(ContratsPayments::class)->findOneBy([
                'type' => 'accompte',
                'state' => 'complete',
                'contrat' => $contrat
            ]);

            // Si le paiement est déjà marqué comme complété par le Webhook
            if ($pl) {
                return $this->render('reservation/contrat/success.twig', [
                    'contrat' => $contrat,
                    'type' => 'Solde'
                ]);
            }
        }

        return $this->render('reservation/contrat/check.twig', [
            'contrat' => $contrat,
            'type' => $type
        ]);
    }

    #[Route('/reservation/gestion-contrat/{num}', name: 'gestion_contrat_view')]
    public function gestionContratView(
        string $num,
        UploaderHelper $uploaderHelper,
        KernelInterface $kernel,
        \App\Service\Stripe\Client $stripeClient,
        Client $client,
        Mailer $mailer,
        EntityManagerInterface $entityManager,
        Request $request,
        ContratsRepository $contratsRepository
    ): Response {
        $contrat = $contratsRepository->findOneBy(['numReservation' => $num]);

        if (null === $contrat) {
            return $this->render('reservation/contrat/nofound.twig');
        }

        $customer = $contrat->getCustomer();

        if (!$customer->isAccountConfigured()) {
            $code = str_pad((string)random_int(0, 999999), 6, '0', STR_PAD_LEFT);
            $customer->setVerificationCode($code);
            $customer->setVerificationCodeExpiresAt(new \DateTimeImmutable('+15 minutes'));
            $entityManager->flush();

            $mailer->send(
                $customer->getEmail(),
                $customer->getSurname().' '.$customer->getName(),
                "[Ludikevent] Votre code de vérification",
                "mails/customer/verification_code.twig",
                ['code' => $code, 'customer' => $customer]
            );

            $request->getSession()->set('config_customer_id', $customer->getId());
            $request->getSession()->set('num_reservation', $num);

            return $this->redirectToRoute('gestion_contrat_finish');
        } else {
            if ($this->getUser() == null) {
                return $this->redirectToRoute('reservation_login');
            }
        }

        if (!$this->isGranted('ROLE_ROOT') && $this->getUser()->getId() !== $contrat->getCustomer()->getId()) {
            return $this->redirectToRoute('reservation');
        }

        $tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
        $totals = $this->calculateContractTotals($contrat, $tvaEnabled);

        $totalDays = $totals['days'];
        $totalHT = $totals['totalHT'];
        $totalTTC = $totals['totalTTC'];
        $totalHT_BeforeDiscount = $totals['totalHT_BeforeDiscount']; // Add this line
        $totalCaution = $totals['totalCaution'];
        $arrhes = $totals['arrhes'];
        $solde = $totals['solde']; // This is total contract value

        // --- Payment Logic Update ---
        $paymentList = [];
        $paymentCaution = [];
        $paymentCtaList = [];
        $soldeRestant = $solde; // We decrement this based on payments

        foreach ($contrat->getContratsPayments() as $contratsPayment) {
            $type = $contratsPayment->getType();
            $state = $contratsPayment->getState();
            $amount = $contratsPayment->getAmount();

            if ($type !== "caution" && $type !== "solde" && $type !== "solde_partiel") {
                // Should match 'accompte' or other types
                if ($state == "complete") {
                    $soldeRestant -= $amount;
                }
                $paymentList[] = $contratsPayment;
            } else {
                if ($state == "complete") {
                    if ($type == "caution") {
                        $paymentCaution[] = $contratsPayment;
                    } elseif ($type == "solde_partiel" || $type == "solde") {
                        $paymentCtaList[] = $contratsPayment;
                        $soldeRestant -= $amount;
                    }
                }
            }
        }

        if ($request->query->has('act') && $request->query->get('act') === 'accomptePay') {
            $response = $this->handlePayment(
                $contrat,
                'accompte',
                $arrhes,
                $entityManager,
                $stripeClient,
                false
            );
            if ($response) return $response;
        }

        if ($request->query->has('act') && $request->query->get('act') === 'soldePay') {
            $amountRequested = (float) $request->query->get('amountToPay', $soldeRestant);
            $finalAmount = min($amountRequested, (float)$soldeRestant);
            $isSoldeTotal = (abs($finalAmount - (float)$soldeRestant) < 0.01);
            $type = $isSoldeTotal ? 'solde' : 'solde_partiel';

            $response = $this->handlePayment(
                $contrat,
                $type,
                $finalAmount,
                $entityManager,
                $stripeClient,
                $isSoldeTotal
            );
            if ($response) return $response;

            // Fallback
            $this->addFlash('error', 'Impossible de générer le lien de paiement.');
            return new RedirectResponse($request->headers->get('referer'));
        }

        if($request->query->get('idPaymentPdf')) {
            $pl = $entityManager->getRepository(ContratsPayments::class)->find($request->query->get('idPaymentPdf'));
            if($pl instanceof ContratsPayments && $pl->getState() == "complete") {
                if ($pl->getPaymentSignedFileName() == null) {
                    $pdf = new PlPdf($kernel, $pl, $contrat);
                    $pdf->generate();
                    $content = $pdf->Output('S');
                    $tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf';
                    file_put_contents($tmpSigned, $content);

                    // On utilise UploadedFile pour simuler un upload propre pour VichUploader
                    $pl->setPaymentFile(new UploadedFile($tmpSigned, "confirmed-" . $pl->getId() . ".pdf", "application/pdf", null, true));
                    $pl->setUpdateAt(new \DateTimeImmutable('now'));
                    $entityManager->persist($pl);
                    $entityManager->flush();
                    $data = $client->autoSignConfirmedPayment($pl);
                    // 1. Gestion du PDF SIGNÉ
                    $tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf';
                    $signedContent = file_get_contents($data);
                    file_put_contents($tmpSigned, $signedContent);

                    // On utilise UploadedFile pour simuler un upload propre pour VichUploader
                    $pl->setPaymentSignedFile(new UploadedFile($tmpSigned, "confirmed-certificate-" . $pl->getId() . ".pdf", "application/pdf", null, true));
                    $pl->setUpdateAt(new \DateTimeImmutable('now'));
                    $entityManager->persist($pl);
                    $entityManager->flush();
                }
                $path= $kernel->getProjectDir() . "/public".$uploaderHelper->asset($pl,'paymentSignedFile');
                return $this->file($path, 'confirmation-paiement.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
            }
        }
        return $this->render('reservation/contrat/view.twig', [
            'contrat' => $contrat,
            'days' => $totalDays,
            'totalHT' => $totalHT, // Discounted HT
            'totalHT_BeforeDiscount' => $totalHT_BeforeDiscount, // Raw HT
            'totalCaution' => $totalCaution,
            'arrhes' => $arrhes,
            'totalTTC' => $totalTTC,
            'paymentList' => $paymentList,
            'paymentCtaList' => $paymentCtaList,
            'paymentCaution' => $paymentCaution,
            'tvaEnabled' => $tvaEnabled,
            'solde' => $soldeRestant, // Use calculated remaining balance
            'signUrl' => (!$contrat->isSigned())?$client->getLinkSign($contrat->getSignID()):null,
            'signEvents' => ($contrat->getSignID() !="")?$client->eventSign($contrat):[],
            'signedNumber' => ($contrat->isSigned())?$client->signedData($contrat): null,
        ]);
    }

    #[Route('/reservation/gestion-contrat/configuration', name: 'gestion_contrat_finish', priority: 5)]
    public function gestionContratConfig(
        Request $request,
        EntityManagerInterface $em,
        UserPasswordHasherInterface $hasher,
        CustomerRepository $customerRepository,
        Security $security, // Injection du service Security
        \App\Service\Stripe\Client $stripeClient
    ): Response {
        $session = $request->getSession();
        $customer = $session->get('config_customer_id') ? $customerRepository->find($session->get('config_customer_id')) : null;

        if (!$customer || $customer->isAccountConfigured()) return $this->redirectToRoute('reservation');

        if ($request->isMethod('POST')) {
            $inputCode = $request->request->get('verification_code');
            $password = $request->request->get('password');

            // 1. Vérification du code
            if ($inputCode !== $customer->getVerificationCode() || new \DateTimeImmutable() > $customer->getVerificationCodeExpiresAt()) {
                $this->addFlash('danger', 'Code invalide ou expiré.');
                return $this->render('reservation/contrat/finish_error.twig', ['customer' => $customer, 'error' => 'Code incorrect']);
            }

            // 2. Configuration du compte
            $customer->setPassword($hasher->hashPassword($customer, $password));
            $customer->setIsAccountConfigured(true);
            $customer->setRoles(['ROLE_USER', 'ROLE_CUSTOMER']);
            $customer->setVerificationCode(null);
            $customer->setVerificationCodeExpiresAt(null);

            // Création Stripe automatique
            try {
                $stripeClient->createCustomer($customer);
            } catch (\Exception $e) {
                // Log l'erreur mais ne bloque pas le process, on pourra le refaire plus tard
                // $logger->error('Erreur création Stripe auto : ' . $e->getMessage());
            }

            $em->flush();

            $security->login($customer, \App\Security\CustomerAuthenticator::class, 'main');

            return $this->render('reservation/contrat/finish_activate.twig', [
                'customer' => $customer,
                'link' => $this->generateUrl('gestion_contrat_view', ['num' => $session->get('num_reservation')])
            ]);
        }

        return $this->render('reservation/contrat/finish_config.twig', ['customer' => $customer]);
    }

    #[Route('/reservation/espace-contrat', name: 'gestion_contrat', priority: 5)]
    public function gestionContrat(
        Request $request,
        EntityManagerInterface $em,
        CustomerAddressRepository $addressRepo
    ): Response {
        /** @var Customer|null $customer */
        $customer = $this->getUser();

        if (!$customer instanceof Customer) {
            return $this->redirectToRoute('reservation_login');
        }

        $active = $request->query->get('active', '');

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

            // --- TRAITEMENT DU PROFIL ---
            if ($active === 'profils') {
                $customer->setCiv($payload->getString('civ'));
                $customer->setName($payload->getString('name'));
                $customer->setSurname($payload->getString('surname'));
                $customer->setPhone($payload->getString('phone'));

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

                $em->flush();
                $this->addFlash('success', 'Profil mis à jour.');
                return $this->redirectToRoute('gestion_contrat', ['active' => 'profils']);
            }

            // --- TRAITEMENT DES ADRESSES ---
            if ($active === 'adresses') {
                $action = $payload->getString('action');

                // AJOUTER
                if ($action === 'add') {
                    $address = new CustomerAddress();
                    $address->setAddress($payload->getString('address'));
                    $address->setAddress2($payload->getString('address2'));
                    $address->setAddress3($payload->getString('address3'));
                    $address->setZipcode($payload->getString('zipcode'));
                    $address->setCity($payload->getString('city'));
                    $address->setCountry($payload->getString('country'));
                    $address->setCustomer($customer);

                    $em->persist($address);
                    $this->addFlash('success', 'Nouvelle adresse ajoutée.');
                }

                // SUPPRIMER
                if ($action === 'delete') {
                    $addressId = $payload->getInt('address_id');
                    // Sécurité : on vérifie que l'adresse appartient bien au client connecté
                    $address = $addressRepo->findOneBy([
                        'id' => $addressId,
                        'customer' => $customer
                    ]);

                    if ($address) {
                        $em->remove($address);
                        $this->addFlash('success', 'Adresse supprimée.');
                    }
                }

                $em->flush();
                return $this->redirectToRoute('gestion_contrat', ['active' => 'adresses']);
            }
        }

        $processedContrats = [];
        foreach ($customer->getContrats() as $contrat) {
            $tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
            $totals = $this->calculateContractTotals($contrat, $tvaEnabled);

            $totalDays = $totals['days'];
            $totalHT = $totals['totalHT'];
            $totalTTC = $totals['totalTTC'];
            $totalCaution = $totals['totalCaution'];
            $arrhes = $totals['arrhes'];
            $soldeCalculated = $totals['solde']; // This is total contract value before payments

            $totalPaid = 0;
            foreach ($contrat->getContratsPayments() as $payment) {
                if ($payment->getState() === "complete") {
                    $totalPaid += $payment->getAmount();
                }
            }

            // Augment the contract object with the calculated totals
            $contrat->totalDays = $totalDays;
            $contrat->totalHT = $totalHT;
            $contrat->totalTTC = $totalTTC;
            $contrat->totalCaution = $totalCaution;
            $contrat->arrhes = $arrhes;
            $contrat->totalPaid = $totalPaid;
            $contrat->soldeCalculated = $soldeCalculated; // Full value before payments

            $processedContrats[] = $contrat;
        }

        return $this->render('reservation/contrat/gestion_contrat.twig', [
            'active' => $active,
            'customer' => $customer,
            'tvaEnabled' => isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true",
            'processedContrats' => $processedContrats,
        ]);
    }

    // --- Private Helper Methods ---

    private function calculateContractTotals(Contrats $contrat, bool $tvaEnabled): array
    {
        $dateStart = $contrat->getDateAt();
        $dateEnd = $contrat->getEndAt();
        $totalDays = ($dateStart && $dateEnd) ? ($dateStart->diff($dateEnd)->days + 1) : 1;

        $totalHT = 0;
        $totalTTC = 0;
        $totalCaution = 0;

        $devis = $contrat->getDevis();
        $formule = $devis?->getFormule();

        if ($formule) {
            // --- LOGIQUE FORMULE ---
            $fPrice = $formule->getPrice1j() ?? 0;
            if ($totalDays >= 2 && $formule->getPrice2j()) $fPrice = $formule->getPrice2j();
            if ($totalDays >= 5 && $formule->getPrice5j()) $fPrice = $formule->getPrice5j();

            $totalHT += $fPrice;
            $totalCaution += $formule->getCaution() ?? 0;

            // En formule, les lignes produits sont incluses (prix 0 pour le calcul)
            // Les options sont incluses SAUF livraison/déplacement
            foreach ($contrat->getContratsOptions() as $option) {
                $optName = mb_strtolower($option->getName());
                if (str_contains($optName, 'livraison') || str_contains($optName, 'déplacement')) {
                    $totalHT += $option->getPrice();
                }
            }

        } else {
            // --- LOGIQUE CLASSIQUE ---
            foreach ($contrat->getContratsLines() as $line) {
                $linePriceHT = $line->getPrice1DayHt();

                if ($totalDays > 1) {
                    $linePriceHT += (($line->getPriceSupDayHt()) * ($totalDays - 1));
                }

                $totalHT += $linePriceHT;
                $totalCaution += $line->getCaution();
            }

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

        $totalHT_BeforeDiscount = $totalHT; // Store total HT before discount

        // --- GESTION PROMOTION ---
        $promotion = $devis?->getOrderSession()?->getPromotion();
        if ($promotion && isset($promotion['percentage'])) {
            $discount = $totalHT * ($promotion['percentage'] / 100);
            $totalHT -= $discount;
        }

        // --- CALCUL TVA ---
        if ($tvaEnabled) {
            $totalTTC = $totalHT * 1.20;
        } else {
            $totalTTC = $totalHT;
        }

        // Fix totals based on original logic (Arrhes calc uses full total)
        $basisTotal = $tvaEnabled ? $totalTTC : $totalHT;
        $arrhes = $basisTotal * 0.25;
        $solde = $basisTotal; // This is the full amount before payments

        return [
            'days' => $totalDays,
            'totalHT' => $totalHT, // This is the discounted HT
            'totalHT_BeforeDiscount' => $totalHT_BeforeDiscount, // Raw HT before discount
            'totalTTC' => $totalTTC,
            'totalCaution' => $totalCaution,
            'arrhes' => $arrhes,
            'solde' => $solde
        ];
    }

    private function handlePayment(
        Contrats $contrat,
        string $type,
        float $amount,
        EntityManagerInterface $em,
        \App\Service\Stripe\Client $stripeClient,
        bool $isSoldeTotal = false
    ): ?RedirectResponse {
        $criteria = [
            'contrat' => $contrat,
            'state' => ['complete', 'created'],
            'type' => $type
        ];

        // For solde/solde_partiel, we only check for 'created' to allow new attempts if previous failed/expired
        // but typically 'solde' payments are unique or sequential.
        if ($type === 'solde' || $type === 'solde_partiel') {
             $criteria = [
                'contrat' => $contrat,
                'state' => 'created',
                'type' => ['solde', 'solde_partiel']
            ];
        }

        $existingPayment = $em->getRepository(ContratsPayments::class)->findOneBy($criteria);

        if (!$existingPayment) {
            // Check if customer exists in Stripe
            $customer = $contrat->getCustomer();
            if (!$customer->getCustomerId()) {
                try {
                    $stripeClient->createCustomer($customer);
                    $em->flush();
                } catch (\Exception $e) {
                    // Log error or handle it, maybe return null to show error
                    // For now we continue, assuming createPayment might fail gracefully or we just try
                }
            }

            // Create new payment intent
            if ($type === 'accompte') {
                $result = $stripeClient->createPaymentAccompte($amount, $contrat);
            } else {
                $result = $stripeClient->createPaymentSolde($amount, $contrat, $isSoldeTotal);
            }

            if ($result['state']) {
                $pl = new ContratsPayments();
                $pl->setContrat($contrat);
                $pl->setType($type);
                $pl->setAmount($amount);
                $pl->setPaymentAt(new \DateTimeImmutable('now'));
                $pl->setState("created");
                $pl->setPaymentId($result['id']);

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

                return new RedirectResponse($result['url']);
            }
        } elseif ($existingPayment->getState() === 'created') {
            // Link existing
            if ($type === 'accompte') {
                $result = $stripeClient->linkPaymentAccompte($amount, $contrat, $existingPayment);
            } else {
                $result = $stripeClient->linkPaymentSolde($amount, $contrat, $existingPayment, $isSoldeTotal);
            }

            if ($result['state']) {
                return new RedirectResponse($result['url']);
            }
        }

        return null;
    }
}
