<?php

namespace App\Controller\Admin;

use App\Entity\Customer;
use App\Entity\EFlex;
use App\Entity\EFlexLine;
use App\Service\DocuSealService;
use App\Service\MailerService;
use App\Service\Pdf\EFlexPdf;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Twig\Environment;

#[Route('/admin/eflex', name: 'app_admin_eflex_')]
#[IsGranted('ROLE_EMPLOYE')]
class EFlexController extends AbstractController
{
    private const MSG_NOT_FOUND = 'E-Flex introuvable';

    public function __construct(
        private EntityManagerInterface $em,
    ) {
    }

    #[Route('/create/{customerId}', name: 'create', requirements: ['customerId' => '\d+'], methods: ['POST'])]
    public function create(int $customerId, Request $request, KernelInterface $kernel): Response
    {
        $customer = $this->em->getRepository(Customer::class)->find($customerId);
        if (null === $customer) {
            throw $this->createNotFoundException('Client introuvable');
        }

        // Bloquer si un E-Flex est en cours ou annule (defaut)
        $existingEflex = $this->em->getRepository(EFlex::class)->findBy(['customer' => $customer]);
        foreach ($existingEflex as $existing) {
            if (\in_array($existing->getState(), [EFlex::STATE_ACTIVE, EFlex::STATE_PENDING_SETUP, EFlex::STATE_DRAFT], true)) {
                $this->addFlash('error', 'Un E-Flex est deja en cours ('.$existing->getReference().'). Impossible d\'en creer un nouveau.');

                return $this->redirectToRoute('app_admin_clients_show', ['id' => $customerId, 'tab' => 'esyflex']);
            }
            if (EFlex::STATE_CANCELLED === $existing->getState() && $existing->getNbFailed() > 0) {
                $this->addFlash('error', 'Un E-Flex est en defaut ('.$existing->getReference().'). Le client doit regulariser avant de creer un nouveau E-Flex.');

                return $this->redirectToRoute('app_admin_clients_show', ['id' => $customerId, 'tab' => 'esyflex']);
            }
        }

        $description = trim($request->request->getString('description'));
        $totalAmount = $request->request->getString('totalAmount');
        $nbEcheances = $request->request->getInt('nbEcheances');

        if ('' === $description || $nbEcheances < 2 || $nbEcheances > 36) {
            $this->addFlash('error', 'Donnees invalides. Minimum 2 echeances, maximum 36.');

            return $this->redirectToRoute('app_admin_clients_show', ['id' => $customerId, 'tab' => 'esyflex']);
        }

        $totalFloat = (float) str_replace(',', '.', $totalAmount);
        $monthlyAmount = round($totalFloat / $nbEcheances, 2);

        $eflex = new EFlex($customer, $description, number_format($totalFloat, 2, '.', ''));

        // 1ere echeance = maintenant +2 jours, les suivantes tous les 2 mois
        $firstDate = (new \DateTimeImmutable())->modify('+2 days');

        for ($i = 1; $i <= $nbEcheances; ++$i) {
            $scheduledAt = 1 === $i ? $firstDate : $firstDate->modify('+'.($i - 1) * 2 .' months');
            $amount = $i === $nbEcheances
                ? number_format($totalFloat - ($monthlyAmount * ($nbEcheances - 1)), 2, '.', '')
                : number_format($monthlyAmount, 2, '.', '');

            $line = new EFlexLine($eflex, $i, $amount, $scheduledAt);
            $eflex->addLine($line);
            $this->em->persist($line);
        }

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

        // Generer le PDF
        $this->generateEFlexPdf($eflex, $kernel);

        $this->addFlash('success', 'E-Flex '.$eflex->getReference().' cree avec '.$nbEcheances.' echeances de '.$monthlyAmount.' EUR/mois.');

        return $this->redirectToRoute('app_admin_eflex_show', ['id' => $eflex->getId()]);
    }

    #[Route('/{id}', name: 'show', requirements: ['id' => '\d+'])]
    public function show(int $id): Response
    {
        $eflex = $this->em->getRepository(EFlex::class)->find($id);
        if (null === $eflex) {
            throw $this->createNotFoundException(self::MSG_NOT_FOUND);
        }

        return $this->render('admin/eflex/show.html.twig', [
            'eflex' => $eflex,
            'customer' => $eflex->getCustomer(),
        ]);
    }

    #[Route('/{id}/generate-pdf', name: 'generate_pdf', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function generatePdf(int $id, KernelInterface $kernel): Response
    {
        $eflex = $this->em->getRepository(EFlex::class)->find($id);
        if (null === $eflex) {
            throw $this->createNotFoundException(self::MSG_NOT_FOUND);
        }

        $this->generateEFlexPdf($eflex, $kernel);
        $this->addFlash('success', 'PDF E-Flex regenere.');

        return $this->redirectToRoute('app_admin_eflex_show', ['id' => $id]);
    }

    /**
     * Envoie le PDF pour signature via DocuSeal (2 parties : Company auto-signe + Client signe).
     */
    #[Route('/{id}/send-signature', name: 'send_signature', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function sendSignature(
        int $id,
        DocuSealService $docuSeal,
        MailerService $mailer,
        Environment $twig,
        UrlGeneratorInterface $urlGenerator,
        #[Autowire(env: 'DOCUSEAL_URL')] string $docuSealUrl = '',
        #[Autowire('%kernel.project_dir%')] string $projectDir = '',
    ): Response {
        $eflex = $this->em->getRepository(EFlex::class)->find($id);
        if (null === $eflex) {
            throw $this->createNotFoundException(self::MSG_NOT_FOUND);
        }

        $customer = $eflex->getCustomer();
        if (null === $eflex->getPdfUnsigned() || null === $customer->getEmail()) {
            $this->addFlash('error', null === $eflex->getPdfUnsigned() ? 'Le PDF doit etre genere.' : 'Email client introuvable.');

            return $this->redirectToRoute('app_admin_eflex_show', ['id' => $id]);
        }

        $pdfPath = $projectDir.'/public/uploads/eflex/'.$eflex->getPdfUnsigned();
        if (!file_exists($pdfPath)) {
            $this->addFlash('error', 'Fichier PDF introuvable.');

            return $this->redirectToRoute('app_admin_eflex_show', ['id' => $id]);
        }

        $signedRedirectUrl = $urlGenerator->generate('app_eflex_signed', [
            'id' => $eflex->getId(),
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        try {
            $pdfBase64 = base64_encode(file_get_contents($pdfPath));

            $result = $docuSeal->getApi()->createSubmissionFromPdf([
                'name' => 'E-Flex '.$eflex->getReference().' - '.$customer->getFullName(),
                'send_email' => false,
                'flatten' => true,
                'documents' => [[
                    'name' => 'eflex-'.$eflex->getReference().'.pdf',
                    'file' => 'data:application/pdf;base64,'.$pdfBase64,
                ]],
                'submitters' => [
                    [
                        'email' => 'client@e-cosplay.fr',
                        'name' => 'Association E-Cosplay',
                        'role' => 'Company',
                        'completed' => true,
                        'send_email' => false,
                        'values' => ['Sign' => $docuSeal->getLogoBase64()],
                        'metadata' => ['doc_type' => 'eflex', 'eflex_id' => $eflex->getId()],
                    ],
                    [
                        'email' => $customer->getEmail(),
                        'name' => $customer->getFullName(),
                        'role' => 'First Party',
                        'send_email' => false,
                        'completed_redirect_url' => $signedRedirectUrl,
                        'metadata' => ['doc_type' => 'eflex', 'eflex_id' => $eflex->getId()],
                    ],
                ],
            ]);

            $submitterId = $result['submitters'][1]['id'] ?? ($result[1]['id'] ?? null);
            if (null !== $submitterId) {
                $eflex->setSubmissionId((string) $submitterId);
                $this->em->flush();

                // Envoyer email au client avec lien vers la page de detail
                $processUrl = $urlGenerator->generate('app_eflex_process', [
                    'id' => $eflex->getId(),
                ], UrlGeneratorInterface::ABSOLUTE_URL);

                $mailer->sendEmail(
                    $customer->getEmail(),
                    'Contrat E-Flex '.$eflex->getReference().' a signer',
                    $twig->render('emails/eflex_signature.html.twig', [
                        'customer' => $customer,
                        'eflex' => $eflex,
                        'processUrl' => $processUrl,
                    ]),
                    null,
                    null,
                    false,
                );

                $this->addFlash('success', 'E-Flex envoye pour signature a '.$customer->getEmail().'.');
            } else {
                $this->addFlash('error', 'Erreur DocuSeal : aucun submitter retourne.');
            }
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur DocuSeal : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_eflex_show', ['id' => $id]);
    }

    #[Route('/{id}/cancel', name: 'cancel', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function cancel(int $id): Response
    {
        $eflex = $this->em->getRepository(EFlex::class)->find($id);
        if (null === $eflex) {
            throw $this->createNotFoundException(self::MSG_NOT_FOUND);
        }

        $eflex->setState(EFlex::STATE_CANCELLED);
        $this->em->flush();

        $this->addFlash('success', 'E-Flex annule.');

        return $this->redirectToRoute('app_admin_clients_show', [
            'id' => $eflex->getCustomer()->getId(),
            'tab' => 'esyflex',
        ]);
    }

    /**
     * Force le prelevement d'une echeance.
     */
    #[Route('/{id}/force-payment/{lineId}', name: 'force_payment', requirements: ['id' => '\d+', 'lineId' => '\d+'], methods: ['POST'])]
    public function forcePayment(
        int $id,
        int $lineId,
        #[Autowire(env: 'STRIPE_SK')] string $stripeSk = '',
    ): Response {
        $eflex = $this->em->getRepository(EFlex::class)->find($id);
        if (null === $eflex) {
            throw $this->createNotFoundException(self::MSG_NOT_FOUND);
        }

        $line = $this->em->getRepository(EFlexLine::class)->find($lineId);
        if (null === $line || $line->getEflex()->getId() !== $eflex->getId()) {
            throw $this->createNotFoundException('Echeance introuvable');
        }

        if (null === $eflex->getStripePaymentMethodId() || null === $eflex->getStripeCustomerId()) {
            $this->addFlash('error', 'SEPA non configure.');

            return $this->redirectToRoute('app_admin_eflex_show', ['id' => $id]);
        }

        // @codeCoverageIgnoreStart
        try {
            \Stripe\Stripe::setApiKey($stripeSk);

            $pi = \Stripe\PaymentIntent::create([
                'amount' => (int) round((float) $line->getAmount() * 100),
                'currency' => 'eur',
                'customer' => $eflex->getStripeCustomerId(),
                'payment_method' => $eflex->getStripePaymentMethodId(),
                'off_session' => true,
                'confirm' => true,
                'payment_method_types' => ['sepa_debit'],
                'metadata' => [
                    'eflex_id' => (string) $eflex->getId(),
                    'eflex_line_id' => (string) $line->getId(),
                    'position' => (string) $line->getPosition(),
                    'reference' => $eflex->getReference(),
                ],
                'description' => $line->getLabel().' - '.$eflex->getReference(),
            ]);

            if (EFlexLine::STATE_KO === $line->getState()) {
                $line->setState(EFlexLine::STATE_PREPARED);
                $line->setFailureReason(null);
            }

            $line->setStripePaymentIntentId($pi->id);
            $this->em->flush();

            $this->addFlash('success', 'Prelevement lance pour l\'echeance '.$line->getPosition().'.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur Stripe : '.$e->getMessage());
        }
        // @codeCoverageIgnoreEnd

        return $this->redirectToRoute('app_admin_eflex_show', ['id' => $id]);
    }

    /**
     * Enregistre un paiement manuel (virement, CB externe, etc.).
     */
    #[Route('/{id}/manual-payment/{lineId}', name: 'manual_payment', requirements: ['id' => '\d+', 'lineId' => '\d+'], methods: ['POST'])]
    public function manualPayment(int $id, int $lineId, Request $request, MailerService $mailer, Environment $twig): Response
    {
        $eflex = $this->em->getRepository(EFlex::class)->find($id);
        if (null === $eflex) {
            throw $this->createNotFoundException(self::MSG_NOT_FOUND);
        }

        $line = $this->em->getRepository(EFlexLine::class)->find($lineId);
        if (null === $line || $line->getEflex()->getId() !== $eflex->getId()) {
            throw $this->createNotFoundException('Echeance introuvable');
        }

        $method = $request->request->getString('method', 'virement');
        $paidAtStr = $request->request->getString('paidAt');
        $paidAt = '' !== $paidAtStr ? new \DateTimeImmutable($paidAtStr) : new \DateTimeImmutable();

        $line->setState(EFlexLine::STATE_OK);
        $line->setPaidAt($paidAt);
        $line->setPaidMethod($method);
        $this->em->flush();

        // Verifier si toutes les echeances sont payees
        if ($eflex->getNbPaid() >= $eflex->getNbLines()) {
            $eflex->setState(EFlex::STATE_COMPLETED);
            $this->em->flush();
        }

        // Email de confirmation au client
        $customer = $eflex->getCustomer();
        if (null !== $customer->getEmail()) {
            try {
                $methodLabels = [
                    'virement' => 'Virement bancaire',
                    'cb_externe' => 'Carte bancaire (externe)',
                    'cheque' => 'Cheque',
                    'especes' => 'Especes',
                ];

                $mailer->sendEmail(
                    $customer->getEmail(),
                    'Paiement recu - Echeance '.$line->getPosition().'/'.$eflex->getNbLines().' - '.$eflex->getReference(),
                    $twig->render('emails/eflex_paiement_recu.html.twig', [
                        'customer' => $customer,
                        'eflex' => $eflex,
                        'line' => $line,
                        'methodLabel' => $methodLabels[$method] ?? $method,
                    ]),
                    null,
                    null,
                    false,
                );
            } catch (\Throwable) {
                // silencieux
            }
        }

        $this->addFlash('success', 'Echeance '.$line->getPosition().' marquee comme payee ('.$method.'). Email de confirmation envoye.');

        return $this->redirectToRoute('app_admin_eflex_show', ['id' => $id]);
    }

    private function generateEFlexPdf(EFlex $eflex, KernelInterface $kernel): void
    {
        $pdf = new EFlexPdf($kernel, $eflex);
        $pdf->generate();

        $tmpPath = tempnam(sys_get_temp_dir(), 'eflex_').'.pdf';
        $pdf->Output('F', $tmpPath);

        $eflex->setPdfUnsignedFile(new UploadedFile(
            $tmpPath,
            'eflex-'.$eflex->getReference().'.pdf',
            'application/pdf',
            null,
            true,
        ));
        $eflex->setUpdatedAt(new \DateTimeImmutable());
        $this->em->flush();

        @unlink($tmpPath);
    }
}
