<?php

namespace App\Controller;

use App\Entity\Echeancier;
use App\Service\DocuSealService;
use App\Service\MailerService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Twig\Environment;

class EcheancierProcessController extends AbstractController
{
    public function __construct(
        private EntityManagerInterface $em,
        private MailerService $mailer,
        private Environment $twig,
    ) {
    }

    /**
     * Verification par code email avant d'acceder a l'echeancier.
     */
    #[Route('/echeancier/verify/{id}', name: 'app_echeancier_verify', requirements: ['id' => '\d+'])]
    public function verify(int $id, Request $request): Response
    {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            throw $this->createNotFoundException('Echeancier introuvable.');
        }

        $customer = $echeancier->getCustomer();
        $session = $request->getSession();
        $sessionKey = 'echeancier_verified_'.$echeancier->getId();

        if ($session->get($sessionKey, false)) {
            return $this->redirectToRoute('app_echeancier_process', ['id' => $id]);
        }

        if (null === $customer->getEmail()) {
            $session->set($sessionKey, true);

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

        $codeKey = 'echeancier_code_'.$id;
        $codeExpiresKey = 'echeancier_code_expires_'.$id;
        $error = null;

        if ('POST' === $request->getMethod()) {
            $code = trim($request->request->getString('code'));
            $storedCode = $session->get($codeKey);
            $expiresAt = $session->get($codeExpiresKey, 0);

            if (time() > $expiresAt) {
                $error = 'Code expire. Cliquez sur "Renvoyer" pour en recevoir un nouveau.';
                $session->remove($codeKey);
                $session->remove($codeExpiresKey);
            } elseif ($code === $storedCode) {
                $session->set($sessionKey, true);
                $session->remove($codeKey);
                $session->remove($codeExpiresKey);

                return $this->redirectToRoute('app_echeancier_process', ['id' => $id]);
            } else {
                $error = 'Code incorrect. Veuillez reessayer.';
            }
        }

        // Envoyer le code si pas encore envoye ou expire
        if (null === $session->get($codeKey) || time() > $session->get($codeExpiresKey, 0)) {
            $code = str_pad((string) random_int(0, 999999), 6, '0', \STR_PAD_LEFT);
            $session->set($codeKey, $code);
            $session->set($codeExpiresKey, time() + 900);

            $this->mailer->sendEmail(
                $customer->getEmail(),
                'Code de verification - Echeancier',
                $this->twig->render('emails/echeancier_verify_code.html.twig', [
                    'customer' => $customer,
                    'code' => $code,
                ]),
                null,
                null,
                false,
            );
        }

        return $this->render('echeancier/verify.html.twig', [
            'echeancier' => $echeancier,
            'customer' => $customer,
            'error' => $error,
        ]);
    }

    /**
     * Renvoie le code de verification.
     */
    #[Route('/echeancier/verify/{id}/resend', name: 'app_echeancier_resend_code', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function resendCode(int $id, Request $request): Response
    {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            throw $this->createNotFoundException('Echeancier introuvable.');
        }

        $customer = $echeancier->getCustomer();
        $session = $request->getSession();

        if (null !== $customer->getEmail()) {
            $code = str_pad((string) random_int(0, 999999), 6, '0', \STR_PAD_LEFT);
            $session->set('echeancier_code_'.$id, $code);
            $session->set('echeancier_code_expires_'.$id, time() + 900);

            $this->mailer->sendEmail(
                $customer->getEmail(),
                'Nouveau code de verification - Echeancier',
                $this->twig->render('emails/echeancier_verify_code.html.twig', [
                    'customer' => $customer,
                    'code' => $code,
                ]),
                null,
                null,
                false,
            );
        }

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

    /**
     * Page de detail echeancier avant signature (protegee par code).
     */
    #[Route('/echeancier/process/{id}', name: 'app_echeancier_process', requirements: ['id' => '\d+'])]
    public function process(int $id, Request $request): Response
    {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            throw $this->createNotFoundException('Echeancier introuvable.');
        }

        // Verifier si authentifie
        $session = $request->getSession();
        if (!$session->get('echeancier_verified_'.$echeancier->getId(), false)) {
            return $this->redirectToRoute('app_echeancier_verify', ['id' => $id]);
        }

        if (Echeancier::STATE_PENDING_SETUP === $echeancier->getState()) {
            return $this->redirectToRoute('app_echeancier_setup_payment', ['id' => $id]);
        }

        if (\in_array($echeancier->getState(), [Echeancier::STATE_SIGNED, Echeancier::STATE_ACTIVE, Echeancier::STATE_COMPLETED], true)) {
            return $this->render('echeancier/signed.html.twig', [
                'echeancier' => $echeancier,
                'customer' => $echeancier->getCustomer(),
            ]);
        }

        if (Echeancier::STATE_CANCELLED === $echeancier->getState()) {
            return $this->render('echeancier/refused.html.twig', [
                'echeancier' => $echeancier,
                'customer' => $echeancier->getCustomer(),
            ]);
        }

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

    /**
     * Redirige vers DocuSeal pour signer.
     */
    #[Route('/echeancier/sign/{id}', name: 'app_echeancier_sign', requirements: ['id' => '\d+'])]
    public function sign(
        int $id,
        DocuSealService $docuSeal,
        #[Autowire(env: 'DOCUSEAL_URL')] string $docuSealUrl = '',
    ): Response {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            throw $this->createNotFoundException('Echeancier introuvable.');
        }

        $submitterId = (int) ($echeancier->getSubmissionId() ?? '0');
        if ($submitterId <= 0) {
            throw $this->createNotFoundException('Echeancier non envoye pour signature.');
        }

        $slug = $docuSeal->getSubmitterSlug($submitterId);
        if (null === $slug) {
            throw $this->createNotFoundException('Lien de signature introuvable.');
        }

        return $this->redirect(rtrim($docuSealUrl, '/').'/s/'.$slug);
    }

    /**
     * Refuse l'echeancier.
     */
    #[Route('/echeancier/refuse/{id}', name: 'app_echeancier_refuse', requirements: ['id' => '\d+'])]
    public function refuse(int $id): Response
    {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            throw $this->createNotFoundException('Echeancier introuvable.');
        }

        $echeancier->setState(Echeancier::STATE_CANCELLED);
        $this->em->flush();

        $customer = $echeancier->getCustomer();
        $ref = $echeancier->getReference();

        // Notification email au client
        if (null !== $customer->getEmail()) {
            try {
                $this->mailer->sendEmail(
                    $customer->getEmail(),
                    'Echeancier '.$ref.' refuse',
                    $this->twig->render('emails/echeancier_refused_client.html.twig', [
                        'customer' => $customer,
                        'echeancier' => $echeancier,
                        'reason' => null,
                    ]),
                    null,
                    null,
                    false,
                );
            } catch (\Throwable) {
                // silencieux
            }
        }

        // Notification email a l'admin
        try {
            $this->mailer->sendEmail(
                'notification@e-cosplay.fr',
                'Echeancier '.$ref.' refuse par '.$customer->getFullName(),
                $this->twig->render('emails/echeancier_refused_admin.html.twig', [
                    'customer' => $customer,
                    'echeancier' => $echeancier,
                    'reason' => null,
                ]),
                null,
                null,
                false,
            );
        } catch (\Throwable) {
            // silencieux
        }

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

    /**
     * Page de configuration du prelevement SEPA (saisie IBAN).
     */
    #[Route('/echeancier/setup-payment/{id}', name: 'app_echeancier_setup_payment', requirements: ['id' => '\d+'])]
    public function setupPayment(
        int $id,
        Request $request,
        #[Autowire(env: 'STRIPE_SK')] string $stripeSk = '',
        #[Autowire(env: 'STRIPE_PK')] string $stripePk = '',
    ): Response {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            throw $this->createNotFoundException('Echeancier introuvable.');
        }

        // Verifier si authentifie
        $session = $request->getSession();
        if (!$session->get('echeancier_verified_'.$echeancier->getId(), false)) {
            return $this->redirectToRoute('app_echeancier_verify', ['id' => $id]);
        }

        // Si le SEPA est deja configure, afficher la confirmation
        if (null !== $echeancier->getStripePaymentMethodId()) {
            return $this->render('echeancier/setup_payment_done.html.twig', [
                'echeancier' => $echeancier,
                'customer' => $echeancier->getCustomer(),
            ]);
        }

        if (!\in_array($echeancier->getState(), [Echeancier::STATE_SIGNED, Echeancier::STATE_PENDING_SETUP], true)) {
            return $this->redirectToRoute('app_echeancier_process', ['id' => $id]);
        }

        $customer = $echeancier->getCustomer();

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

        // Creer le customer Stripe si besoin
        $stripeCustomerId = $echeancier->getStripeCustomerId() ?? $customer->getStripeCustomerId();
        if (null === $stripeCustomerId) {
            $stripeCustomer = \Stripe\Customer::create([
                'email' => $customer->getEmail(),
                'name' => $customer->getFullName(),
            ]);
            $stripeCustomerId = $stripeCustomer->id;
            $customer->setStripeCustomerId($stripeCustomerId);
            $echeancier->setStripeCustomerId($stripeCustomerId);
            $this->em->flush();
        }

        $setupIntent = \Stripe\SetupIntent::create([
            'customer' => $stripeCustomerId,
            'payment_method_types' => ['sepa_debit'],
            'metadata' => [
                'echeancier_id' => (string) $echeancier->getId(),
            ],
        ]);
        // @codeCoverageIgnoreEnd

        return $this->render('echeancier/setup_payment.html.twig', [
            'echeancier' => $echeancier,
            'customer' => $customer,
            'clientSecret' => $setupIntent->client_secret,
            'stripePk' => $stripePk,
        ]);
    }

    /**
     * Confirmation SEPA : recoit le payment_method ID apres confirmation Stripe.js.
     */
    #[Route('/echeancier/setup-payment/{id}/confirm', name: 'app_echeancier_setup_payment_confirm', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function setupPaymentConfirm(
        int $id,
        Request $request,
        #[Autowire(env: 'STRIPE_SK')] string $stripeSk = '',
    ): Response {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            return new JsonResponse(['error' => 'Echeancier introuvable'], Response::HTTP_NOT_FOUND);
        }

        $session = $request->getSession();
        if (!$session->get('echeancier_verified_'.$echeancier->getId(), false)) {
            return new JsonResponse(['error' => 'Non autorise'], Response::HTTP_FORBIDDEN);
        }

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

        if (null === $paymentMethodId) {
            return new JsonResponse(['error' => 'payment_method manquant'], Response::HTTP_BAD_REQUEST);
        }

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

            $stripeCustomerId = $echeancier->getStripeCustomerId();

            // Attacher le moyen de paiement au client
            $pm = \Stripe\PaymentMethod::retrieve($paymentMethodId);
            $pm->attach(['customer' => $stripeCustomerId]);
            \Stripe\Customer::update($stripeCustomerId, [
                'invoice_settings' => ['default_payment_method' => $paymentMethodId],
            ]);

            // Sauvegarder les infos SEPA
            $sepa = $pm->sepa_debit ?? null;
            if (null !== $sepa) {
                $echeancier->setStripeSepaLast4($sepa->last4 ?? null);
                $echeancier->setStripeSepaBankName($sepa->bank_code ?? null);
                $echeancier->setStripeSepaCountry($sepa->country ?? null);
            }
        } catch (\Throwable $e) {
            return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
        // @codeCoverageIgnoreEnd

        $echeancier->setStripePaymentMethodId($paymentMethodId);
        $echeancier->setState(Echeancier::STATE_ACTIVE);
        $this->em->flush();

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

    /**
     * Regularisation d'une echeance echouee par CB via Stripe Checkout.
     */
    #[Route('/echeancier/regularize/{id}/{lineId}', name: 'app_echeancier_regularize', requirements: ['id' => '\d+', 'lineId' => '\d+'])]
    public function regularize(
        int $id,
        int $lineId,
        Request $request,
        #[Autowire(env: 'STRIPE_SK')] string $stripeSk = '',
    ): Response {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            throw $this->createNotFoundException('Echeancier introuvable.');
        }

        $session = $request->getSession();
        if (!$session->get('echeancier_verified_'.$echeancier->getId(), false)) {
            return $this->redirectToRoute('app_echeancier_verify', ['id' => $id]);
        }

        $line = $this->em->getRepository(EcheancierLine::class)->find($lineId);
        if (null === $line || $line->getEcheancier()->getId() !== $echeancier->getId()) {
            throw $this->createNotFoundException('Echeance introuvable.');
        }

        if (EcheancierLine::STATE_OK === $line->getState()) {
            return $this->redirectToRoute('app_echeancier_process', ['id' => $id]);
        }

        $customer = $echeancier->getCustomer();

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

        $successUrl = $this->generateUrl('app_echeancier_regularize_success', [
            'id' => $id,
            'lineId' => $lineId,
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        $cancelUrl = $this->generateUrl('app_echeancier_process', [
            'id' => $id,
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        $checkoutSession = \Stripe\Checkout\Session::create([
            'mode' => 'payment',
            'payment_method_types' => ['card'],
            'customer_email' => $customer->getEmail(),
            'line_items' => [[
                'price_data' => [
                    'currency' => 'eur',
                    'unit_amount' => (int) round((float) $line->getAmount() * 100),
                    'product_data' => [
                        'name' => $line->getLabel().' - '.$echeancier->getReference(),
                    ],
                ],
                'quantity' => 1,
            ]],
            'payment_intent_data' => [
                'metadata' => [
                    'echeancier_id' => (string) $echeancier->getId(),
                    'echeancier_line_id' => (string) $line->getId(),
                    'position' => (string) $line->getPosition(),
                    'reference' => $echeancier->getReference(),
                    'regularization' => '1',
                ],
            ],
            'success_url' => $successUrl,
            'cancel_url' => $cancelUrl,
        ]);

        return $this->redirect($checkoutSession->url);
        // @codeCoverageIgnoreEnd
    }

    /**
     * Page de succes apres regularisation CB.
     */
    #[Route('/echeancier/regularize/{id}/{lineId}/success', name: 'app_echeancier_regularize_success', requirements: ['id' => '\d+', 'lineId' => '\d+'])]
    public function regularizeSuccess(int $id, int $lineId, Request $request): Response
    {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            throw $this->createNotFoundException('Echeancier introuvable.');
        }

        $session = $request->getSession();
        if (!$session->get('echeancier_verified_'.$echeancier->getId(), false)) {
            return $this->redirectToRoute('app_echeancier_verify', ['id' => $id]);
        }

        $line = $this->em->getRepository(EcheancierLine::class)->find($lineId);
        if (null === $line || $line->getEcheancier()->getId() !== $echeancier->getId()) {
            throw $this->createNotFoundException('Echeance introuvable.');
        }

        return $this->render('echeancier/regularize_success.html.twig', [
            'echeancier' => $echeancier,
            'customer' => $echeancier->getCustomer(),
            'line' => $line,
        ]);
    }

    /**
     * Callback DocuSeal apres signature du client.
     */
    #[Route('/echeancier/signed/{id}', name: 'app_echeancier_signed', requirements: ['id' => '\d+'])]
    public function signed(int $id, Request $request): Response
    {
        $echeancier = $this->em->getRepository(Echeancier::class)->find($id);
        if (null === $echeancier) {
            throw $this->createNotFoundException('Echeancier introuvable.');
        }

        // Verifier si authentifie (protection anti brute-force)
        $session = $request->getSession();
        if (!$session->get('echeancier_verified_'.$echeancier->getId(), false)) {
            return $this->redirectToRoute('app_echeancier_verify', ['id' => $id]);
        }

        if (Echeancier::STATE_SIGNED !== $echeancier->getState()) {
            $echeancier->setState(Echeancier::STATE_SIGNED);
            $this->em->flush();
        }

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