<?php

namespace App\Controller\Dashboard;

use App\Entity\CustomerAddress;
use App\Entity\Devis;
use App\Entity\DevisLine;
use App\Entity\DevisOptions;
use App\Event\Signature\DevisSend;
use App\Logger\AppLogger;
use App\Repository\DevisRepository;
use App\Repository\OptionsRepository;
use App\Repository\OrderSessionRepository;
use App\Repository\PrestaireRepository;
use App\Repository\ProductRepository;
use App\Service\Pdf\DevisPdfService;
use App\Service\Signature\Client;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\HttpClient\HttpClientInterface;

#[Route('/crm/flow')]
class FlowController extends AbstractController
{
    public function __construct(
        private readonly AppLogger                $appLogger,
        private readonly OrderSessionRepository   $orderSessionRepository,
        private readonly HttpClientInterface      $client,
        private readonly DevisRepository          $devisRepository,
        private readonly ProductRepository        $productRepository,
        private readonly OptionsRepository        $optionsRepository,
        private readonly PrestaireRepository      $prestaireRepository,
        private readonly KernelInterface          $kernel,
        private readonly Client                   $signatureClient,
        private readonly EventDispatcherInterface $eventDispatcher, private readonly RequestStack $requestStack
    ) {
    }

    #[Route('', name: 'app_crm_flow', methods: ['GET'])]
    public function index(Request $request, PaginatorInterface $paginator): Response
    {
        $this->appLogger->record('VIEW', 'Consultation des réservations en ligne');

        $query = $this->orderSessionRepository->findBy(['state' => 'send'], ['id' => 'DESC']);

        $pagination = $paginator->paginate(
            $query,
            $request->query->getInt('page', 1),
            20
        );

        return $this->render('dashboard/flow/index.twig', [
            'sessions' => $pagination,
        ]);
    }
    #[Route('/{id}', name: 'app_crm_flow_view', methods: ['GET'])]
    public function view(\App\Entity\OrderSession $session, \Doctrine\ORM\EntityManagerInterface $em): Response
    {
        $this->appLogger->record('VIEW', 'Consultation détails réservation en ligne #' . $session->getId());

        // Auto-calculation of delivery if missing or geometry missing
        if (($session->getDeliveryDistance() === null || $session->getDeliveryPrice() === null || $session->getDeliveryGeometry() === null) && $session->getAdressEvent()) {
            $this->calculateDelivery($session);
            $em->flush();
        }

        return $this->render('dashboard/flow/view.twig', [
            'session' => $session,
            'prestataires' => $this->prestaireRepository->findAll(),
        ]);
    }

    #[Route('/update/{id}', name: 'app_crm_flow_update', methods: ['POST'])]
    public function update(\App\Entity\OrderSession $session, Request $request, \Doctrine\ORM\EntityManagerInterface $em): Response
    {
        if ($request->request->has('deliveryDistance')) {
            $session->setDeliveryDistance((float)$request->request->get('deliveryDistance'));
        }
        if ($request->request->has('deliveryPrice')) {
            $session->setDeliveryPrice((float)$request->request->get('deliveryPrice'));
        }
        if ($request->request->has('typePaiement')) {
            $session->setTypePaiement($request->request->get('typePaiement'));
        }
        if ($request->request->has('prestataire')) {
            $prestataireId = $request->request->get('prestataire');
            if ($prestataireId) {
                $prestataire = $this->prestaireRepository->find($prestataireId);
                $session->setPrestataire($prestataire);
            } else {
                $session->setPrestataire(null);
            }
        }

        $em->flush();

        $this->addFlash('success', 'Informations mises à jour.');

        return $this->redirectToRoute('app_crm_flow_view', ['id' => $session->getId()]);
    }
    #[Route('/allow/rv', name: 'app_crm_flow_allow_rv', methods: ['GET'])]
    public function allowRv(\Doctrine\ORM\EntityManagerInterface $em): Response
    {
        $ids = $this->requestStack->getMainRequest()->query->get('idS');
        /** @var Devis $devisItem */
        $devisItem = $this->devisRepository->find($ids);
        sleep(5);

        $session = $devisItem->getOrderSession();

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

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

        $session->setState('allow');
        $em->flush();

        $this->addFlash('success', 'La réservation a été validée et le devis ' . $devisItem->getNum() . ' a été créé.');
        return $this->redirectToRoute('app_crm_flow');

    }
    #[Route('/allow/{id}', name: 'app_crm_flow_allow', methods: ['GET'])]
    public function allow(\App\Entity\OrderSession $session, \Doctrine\ORM\EntityManagerInterface $em): Response
    {

        // 1. Create Devis
        $devis = new Devis();
        $devisNumber = "DEVIS-" . sprintf('%05d', $this->devisRepository->count() + 1);
        $devis->setNum($devisNumber)
            ->setState("created_waitsign")
            ->setCreateA(new \DateTimeImmutable())
            ->setUpdateAt(new \DateTimeImmutable());

        // 2. Customer
        $devis->setCustomer($session->getCustomer());

        // 2.1 Set additional Devis fields from OrderSession
        $devis->setDistance($session->getDeliveryDistance());
        $devis->setPriceShip($session->getDeliveryPrice());
        $devis->setPaymentMethod($session->getTypePaiement());
        $devis->setOrderSession($session);

        if (str_contains($session->getTypePaiement() ?? '', 'Chorus')) {
            $devis->setIsNotAddCaution(true);
        }

        $billAddr = $this->findOrCreateAddress(
            $session->getCustomer(),
            $session->getBillingAddress() ?: $session->getAdressEvent(),
            $session->getBillingZipCode() ?: $session->getZipCodeEvent(),
            $session->getBillingTown() ?: $session->getTownEvent(),
            $em
        );
        $devis->setBillAddress($billAddr);

        // Shipping Address
        $shipAddr = $this->findOrCreateAddress(
            $session->getCustomer(),
            $session->getAdressEvent(),
            $session->getZipCodeEvent(),
            $session->getTownEvent(),
            $em,
            $session->getAdress2Event()
        );
        $devis->setAddressShip($shipAddr);

        // 4. Dates
        $productsData = $session->getProducts();
        $start = null;
        $end = null;
        try {
            if (!empty($productsData['start'])) {
                $start = new \DateTimeImmutable($productsData['start']);
                $devis->setStartAt($start);
            }
            if (!empty($productsData['end'])) {
                $end = new \DateTimeImmutable($productsData['end']);
                $devis->setEndAt($end);
            }
        } catch (\Exception $e) {
            // Ignore date parsing errors
        }

        // Calculate Duration
        $days = 1;
        if ($start && $end) {
            $days = max(1, $start->diff($end)->days + 1);
        }

        // 5. Products & Linked Options
        $pos = 0;
        if (isset($productsData['ids']) && is_array($productsData['ids'])) {
            foreach ($productsData['ids'] as $prodId) {
                $product = $this->productRepository->find($prodId);
                if ($product) {
                    $line = new DevisLine();
                    $line->setPos($pos++);
                    $line->setProduct($product->getName());
                    $line->setDay($days);
                    $line->setPriceHt($product->getPriceDay());
                    $line->setPriceHtSup($product->getPriceSup());
                    $em->persist($line);
                    $devis->addDevisLine($line);

                    // Linked Options
                    if (isset($productsData['options'][$prodId])) {
                        foreach ($productsData['options'][$prodId] as $optId) {
                            $option = $this->optionsRepository->find($optId);
                            if ($option) {
                                $devisOpt = new DevisOptions();
                                $devisOpt->setOption($option->getName());
                                $devisOpt->setPriceHt($option->getPriceHt());
                                $em->persist($devisOpt);
                                $devis->addDevisOption($devisOpt);
                            }
                        }
                    }
                }
            }
        }


        // 6. Orphan Options
        $sessionOptions = $session->getOptions();
        $productIds = $productsData['ids'] ?? [];

        if ($sessionOptions && is_array($sessionOptions)) {
            foreach ($sessionOptions as $prodId => $opts) {
                if (!in_array($prodId, $productIds) && is_array($opts)) {
                    foreach ($opts as $optId) {
                        $option = $this->optionsRepository->find($optId);
                        if ($option) {
                            $devisOpt = new DevisOptions();
                            $devisOpt->setDevis($devis);
                            $devisOpt->setOption($option->getName());
                            $devisOpt->setPriceHt($option->getPriceHt());
                            $em->persist($devisOpt);
                        }
                    }
                }
            }
        }

        // 7. Delivery Fee
        if ($session->getDeliveryPrice() > 0) {
            $devisOpt = new DevisOptions();
            $devisOpt->setOption("Frais de livraison");
            $dist = number_format($session->getDeliveryDistance(), 1, ',', ' ');
            $town = $session->getBillingTown() ?: 'Ville inconnue';
            $devisOpt->setDetails("Livraison ($dist km) - $town");
            $devisOpt->setPriceHt($session->getDeliveryPrice());
            $em->persist($devisOpt);
            $devis->addDevisOption($devisOpt);
        }

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

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

        // 8. Persist & Flush to generate IDs
        $em->persist($devis);
        $em->flush();

        return $this->redirectToRoute('app_crm_flow_allow_rv',['id' => $session->getId(),'idS'=>$devis->getId()]);
    }

    #[Route('/delete/{id}', name: 'app_crm_flow_delete', methods: ['GET'])]
    public function delete(\App\Entity\OrderSession $session, \Doctrine\ORM\EntityManagerInterface $em): Response
    {
        $em->remove($session);
        $em->flush();

        $this->addFlash('success', 'La réservation a été supprimée (refusée).');

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

    private function calculateDelivery(\App\Entity\OrderSession $session): void
    {
        if (!$session->getAdressEvent() || !$session->getZipCodeEvent() || !$session->getTownEvent()) {
            return;
        }

        $query = sprintf('%s %s %s', $session->getAdressEvent(), $session->getZipCodeEvent(), $session->getTownEvent());

        try {
            $response = $this->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 = $this->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'];
                $geometry = $itineraire['geometry'] ?? null;

                $rate = 0.50;
                $trips = 4;
                $price = 0.0;

                if ($distance > 10) {
                    $chargedDistance = $distance - 10;
                    $price = ($chargedDistance * $trips) * $rate;
                }

                $session->setDeliveryDistance($distance);
                $session->setDeliveryPrice($price);
                $session->setDeliveryGeometry($geometry);
            }
        } catch (\Exception $e) {
            // Log error or silent fail
        }
    }

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

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

    private function findOrCreateAddress(\App\Entity\Customer $customer, ?string $address, ?string $zip, ?string $city, \Doctrine\ORM\EntityManagerInterface $em, ?string $address2 = null): CustomerAddress
    {
        $address = $address ?: 'Adresse inconnue';
        $zip = $zip ?: '00000';
        $city = $city ?: 'Ville inconnue';

        foreach ($customer->getCustomerAddresses() as $existingAddr) {
            if ($existingAddr->getAddress() === $address &&
                $existingAddr->getZipcode() === $zip &&
                $existingAddr->getCity() === $city &&
                $existingAddr->getAddress2() === $address2) {
                return $existingAddr;
            }
        }

        $newAddr = new CustomerAddress();
        $newAddr->setCustomer($customer)
            ->setAddress($address)
            ->setZipcode($zip)
            ->setCity($city)
            ->setAddress2($address2)
            ->setCountry('France');

        $em->persist($newAddr);
        return $newAddr;
    }
}
