<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Controller\Site;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Admin\GiftCheckType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Payment\AxeptaPayment;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Payment\CawlPayment;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Payment\PayPlugPayment;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Payment\Sherlock2Payment;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Payment\SogeCommercePayment;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Payment\SystemPayPayment;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Utils\Mailer;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Contact\Repository\ContactRepository;
use CpCreation\VitiCore\Payment\Model\Payment;
use CpCreation\VitiCore\Payment\Repository\PaymentRepository;
use CpCreation\VitiCore\Product\Model\ProductGift;
use CpCreation\VitiCore\Shop\Repository\ShopConfigRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Attribute\Route;

final class CartController extends AbstractController
{
    public function __construct(
        private CartRepository $cartRepository,
        private PaymentRepository $paymentRepository,
        private ContactRepository $contactRepository,
        private Mailer $mailer,
        private SystemPayPayment $systempayPayment,
        private SogeCommercePayment $sogeCommercePayment,
        private AxeptaPayment $axeptaPayment,
        private Sherlock2Payment $sherlock2Payment,
        private PayPlugPayment $payplugPayment,
        private EntityManagerInterface $em,
        private RequestStack $requestStack,
        private ShopConfigRepository $shopConfigRepository,
        private CawlPayment $cawlPayment,
    ) {
    }

    private function getSession(): ?SessionInterface
    {
        return $this->requestStack->getSession();
    }

    #[Route('/request/etransactions/responseAt', name: 'etransactions_responseAt', methods: ['GET', 'POST'])]
    public function etransactionsResponseAt(Request $request): Response
    {
        $code = $request->request->get('Erreur') ?? $request->query->get('Erreur');
        $ref = $request->request->get('Ref') ?? $request->query->get('Ref');

        $response = $request->request->all() ?: $request->query->all();

        if (!$ref || !$code) {
            throw new BadRequestHttpException();
        }

        $this->em->getFilters()->disable('soft_deletable');

        /** @var Cart|null $cart */
        $cart = $this->cartRepository->findOneBy(['token' => $ref]);

        if ($cart) {
            $checkout = $cart->getCartCheckout();
            $checkout->setResponse($response);

            if ('00000' === $code) {
                $checkout->setPayedAt(new \DateTime('now'));
                $this->em->persist($checkout);
                $this->em->flush();

                $contact = $this->contactRepository->findOne();
                $this->mailer->sendPaidOrder($contact, $cart);
            } else {
                $this->em->persist($checkout);
                $this->em->flush();
            }

            return new Response('', 200);
        }

        /** @var Payment|null $payment */
        $payment = $this->paymentRepository->findOneBy(['slug' => $ref]);

        if ($payment) {
            $payment->setResponse($response);

            if ('00000' === $code) {
                $payment->setPayedAt(new \DateTime('now'));
                $this->mailer->sendPayment($payment);
            }

            $this->em->persist($payment);
            $this->em->flush();
        }

        return new Response('', 200);
    }

    #[Route('/request/monetico/responseAt', name: 'monetico_responseAt', methods: ['GET', 'POST'])]
    public function moneticoResponseAt(Request $request): Response
    {
        $code = $request->request->get('code-retour') ?? $request->query->get('code-retour');
        $ref = $request->request->get('reference') ?? $request->query->get('reference');

        $response = $request->request->all() ?: $request->query->all();

        if (!$ref || !$code) {
            throw new BadRequestHttpException();
        }

        $this->em->getFilters()->disable('soft_deletable');

        /** @var Cart|null $cart */
        $cart = $this->cartRepository->findOneBy(['token' => $ref]);

        if ($cart) {
            $checkout = $cart->getCartCheckout();
            $checkout->setResponse($response);

            if ('payetest' === $code || 'paiement' === $code) {
                $checkout->setPayedAt(new \DateTime('now'));
                $this->em->persist($checkout);
                $this->em->flush();

                $contact = $this->contactRepository->findOne();
                $this->mailer->sendPaidOrder($contact, $cart);
            } else {
                $this->em->persist($checkout);
                $this->em->flush();
            }

            return new Response("version=2\ncdr=0", 200);
        }

        /** @var Payment|null $payment */
        $payment = $this->paymentRepository->findOneBy(['slug' => $ref]);
        if ($payment) {
            if ('payetest' === $code || 'paiement' === $code) {
                $payment->setPayedAt(new \DateTime('now'));
                $payment->setResponse($response);
                $this->mailer->sendPayment($payment);
            } else {
                $payment->setResponse($response);
            }

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

            return new Response("version=2\ncdr=0", 200);
        }

        return new Response("version=2\ncdr=1\n", 200);
    }

    #[Route('/request/systempay/responseAt', name: 'systempay_responseAt', methods: ['POST'])]
    public function systemPayResponseAt(Request $request): Response
    {
        $response = $request->request->all();
        if (!$response) {
            return new Response('POST is empty');
        }

        $token = $response['vads_trans_id'] ?? null;
        if (!$token) {
            return new Response('Data received but no cart associated');
        }

        $cart = $this->cartRepository->findOneBy(['token' => $token]);

        if ($this->systempayPayment->valid($response)) {
            if ($cart) {
                $checkout = $cart->getCartCheckout();
                $checkout->setResponse($response);
                $checkout->setPayedAt(new \DateTime('now'));
                $this->em->persist($checkout);
                $this->em->flush();

                $contact = $this->contactRepository->findOne();
                $this->mailer->sendPaidOrder($contact, $cart);

                return new Response('Order successfully updated');
            }

            $payment = $this->paymentRepository->findOneBy(['slug' => $token]);
            if ($payment) {
                $payment->setResponse($response);
                $payment->setPayedAt(new \DateTime('now'));
                $this->em->persist($payment);
                $this->em->flush();

                $this->mailer->sendPayment($payment);

                return new Response('Order successfully updated');
            }

            return new Response('Data received but no cart associated');
        }

        if ($cart) {
            $checkout = $cart->getCartCheckout();
            $checkout->setResponse($response);
            $this->em->persist($checkout);
            $this->em->flush();
        } else {
            $payment = $this->paymentRepository->findOneBy(['slug' => $token]);
            if ($payment) {
                $payment->setResponse($response);
                $this->em->persist($payment);
                $this->em->flush();
            }
        }

        return new Response('Payment refused');
    }

    #[Route('/request/sogecommerce/responseAt', name: 'sogecommerce_responseAt', methods: ['POST'])]
    public function sogeCommerceResponseAt(Request $request): Response
    {
        $response = $request->request->all();
        if (!$response) {
            return new Response('POST is empty');
        }

        $token = $response['vads_trans_id'] ?? null;
        if (!$token) {
            return new Response('Data received but no cart associated');
        }

        $cart = $this->cartRepository->findOneBy(['token' => $token]);

        if ($this->sogeCommercePayment->valid($response)) {
            if ($cart) {
                $checkout = $cart->getCartCheckout();
                $checkout->setResponse($response);
                $checkout->setPayedAt(new \DateTime('now'));
                $this->em->persist($checkout);
                $this->em->flush();

                $contact = $this->contactRepository->findOne();
                $this->mailer->sendPaidOrder($contact, $cart);

                return new Response('Order successfully updated');
            }

            $payment = $this->paymentRepository->findOneBy(['slug' => $token]);
            if ($payment) {
                $payment->setResponse($response);
                $payment->setPayedAt(new \DateTime('now'));
                $this->em->persist($payment);
                $this->em->flush();

                $this->mailer->sendPayment($payment);

                return new Response('Order successfully updated');
            }

            return new Response('Data received but no cart associated');
        }

        if ($cart) {
            $checkout = $cart->getCartCheckout();
            $checkout->setResponse($response);
            $this->em->persist($checkout);
            $this->em->flush();
        } else {
            $payment = $this->paymentRepository->findOneBy(['slug' => $token]);
            if ($payment) {
                $payment->setResponse($response);
                $this->em->persist($payment);
                $this->em->flush();
            }
        }

        return new Response('Payment refused');
    }

    #[Route('/request/axepta/responseAt', name: 'axepta_responseAt', methods: ['POST'])]
    public function axeptaResponseAt(Request $request): Response
    {
        $response = $request->request->all();
        if (!$response) {
            return new Response('No response');
        }

        $data = $this->axeptaPayment->valid($response);
        if (empty($data)) {
            return new Response('No response');
        }

        $token = $data['TransID'] ?? null;
        if (!$token) {
            return new Response('No response');
        }

        $cart = $this->cartRepository->findOneBy(['token' => $token]);

        if (($data['Valid'] ?? false) === true) {
            if ($cart) {
                $checkout = $cart->getCartCheckout();
                $checkout->setResponse(array_merge($data, $response));
                $checkout->setPayedAt(new \DateTime('now'));
                $this->em->persist($checkout);
                $this->em->flush();

                $contact = $this->contactRepository->findOne();
                $this->mailer->sendPaidOrder($contact, $cart);

                return new Response('Order successfully updated');
            }

            $payment = $this->paymentRepository->findOneBy(['slug' => $token]);
            if ($payment) {
                $payment->setResponse(array_merge($data, $response));
                $payment->setPayedAt(new \DateTime('now'));
                $this->em->persist($payment);
                $this->em->flush();

                $this->mailer->sendPayment($payment);

                return new Response('Order successfully updated');
            }

            return new Response('Data received but no cart associated');
        }

        // invalid
        if ($cart) {
            $checkout = $cart->getCartCheckout();
            $checkout->setResponse(array_merge($data, $response));
            $this->em->persist($checkout);
            $this->em->flush();
        } else {
            $payment = $this->paymentRepository->findOneBy(['slug' => $token]);
            if ($payment) {
                $payment->setResponse(array_merge($data, $response));
                $this->em->persist($payment);
                $this->em->flush();
            }
        }

        return new Response('Payment refused');
    }

    #[Route('/request/sherlock2/responseAt', name: 'sherlock2_responseAt', methods: ['POST'])]
    public function sherlock2ResponseAt(Request $request): Response
    {
        $response = $request->request->all();
        if (!$response) {
            return new Response('No response');
        }

        $data = $this->sherlock2Payment->valid($response);
        if (empty($data)) {
            return new Response('No response');
        }

        $token = $data['orderId'] ?? null;
        if (!$token) {
            return new Response('No response');
        }

        $cart = $this->cartRepository->findOneBy(['token' => $token]);

        if (($data['Valid'] ?? false) === true) {
            if ($cart) {
                $checkout = $cart->getCartCheckout();
                $checkout->setResponse($response);
                $checkout->setPayedAt(new \DateTime('now'));
                $this->em->persist($checkout);
                $this->em->flush();

                $contact = $this->contactRepository->findOne();
                $this->mailer->sendPaidOrder($contact, $cart);

                return new Response('Order successfully updated');
            }

            $payment = $this->paymentRepository->findOneBy(['slug' => $token]);
            if ($payment) {
                $payment->setResponse($response);
                $payment->setPayedAt(new \DateTime('now'));
                $this->em->persist($payment);
                $this->em->flush();

                $this->mailer->sendPayment($payment);

                return new Response('Order successfully updated');
            }

            return new Response('Order successfully updated');
        }

        // invalid
        if ($cart) {
            $checkout = $cart->getCartCheckout();
            $checkout->setResponse($response);
            $this->em->persist($checkout);
            $this->em->flush();

            return new Response('Data invalid');
        }

        $payment = $this->paymentRepository->findOneBy(['slug' => $token]);
        if ($payment) {
            $payment->setResponse($response);
            $this->em->persist($payment);
            $this->em->flush();

            return new Response('Data invalid');
        }

        return new Response('No response');
    }

    #[Route('/request/gift/{id}/check', name: 'gift_check', methods: ['GET', 'POST'])]
    public function scanGift(Request $request, ?ProductGift $gift = null): Response
    {
        $message = null;

        $form = $this->createForm(GiftCheckType::class);
        $form->handleRequest($request);

        if (empty($gift) || null !== $gift->getUsedAt() || $gift->getValidUntilDate() < new \DateTime()) {
            switch (true) {
                case null === $gift:
                    $message = "Aucune carte cadeau n'as été trouvée";
                    break;
                case null !== $gift->getUsedAt():
                    $message = 'Cette carte cadeau a déjà été utilisée le '.$gift->getUsedAt()->format('d/m/Y').'!';
                    break;
                case $gift->getValidUntilDate() < new \DateTime():
                    $message = 'Cette carte cadeau a expiré le '.$gift->getValidUntilDate()->format('d/m/Y').' !';
                    break;
            }

            return $this->render('gift/check.html.twig', [
                'message' => $message,
            ]);
        }

        if ($form->isSubmitted() && $form->isValid()) {
            $token = $form->getData()['token'];

            if ($this->shopConfigRepository->findBy(['giftCardCheckToken' => $token])) {
                $gift->setUsedAt(new \DateTime('now'));
                $this->em->persist($gift);
                $this->em->flush();

                return $this->render('gift/check.html.twig', [
                    'message' => 'Carte cadeau validée avec succès',
                ]);
            }

            $message = 'Ce code de validation est invalide';
        }

        return $this->render('gift/check.html.twig', [
            'form' => $form->createView(),
            'gift' => $gift,
            'message' => $message,
        ]);
    }

    #[Route('/request/payplug/responseAt', name: 'payplug_responseAt', methods: ['POST'])]
    public function payPlugResponseAt(Request $request): Response
    {
        $data = $request->toArray();

        if (!isset($data['id'])) {
            return new Response('No response');
        }

        $paymentArray = $this->payplugPayment->valid($data);
        $reference = $paymentArray['metadata']['transaction_id'] ?? null;

        if (!$reference) {
            return new Response('No response');
        }

        $cart = $this->cartRepository->findOneBy(['token' => $reference]);

        if (!empty($paymentArray['is_paid'])) {
            if ($cart) {
                $checkout = $cart->getCartCheckout();
                $checkout->setResponse($paymentArray);
                $checkout->setPayedAt(new \DateTime('now'));
                $this->em->persist($checkout);
                $this->em->flush();

                $contact = $this->contactRepository->findOne();
                $this->mailer->sendPaidOrder($contact, $cart);

                return new Response('Order successfully updated');
            }

            $payment = $this->paymentRepository->findOneBy(['slug' => $reference]);
            if ($payment) {
                $payment->setResponse($paymentArray);
                $payment->setPayedAt(new \DateTime('now'));
                $this->em->persist($payment);
                $this->em->flush();

                $this->mailer->sendPayment($payment);

                return new Response('Order successfully updated');
            }
        } else {
            if ($cart) {
                $checkout = $cart->getCartCheckout();
                $checkout->setResponse($paymentArray);
                $this->em->persist($checkout);
                $this->em->flush();

                return new Response('Data invalid');
            }

            $payment = $this->paymentRepository->findOneBy(['slug' => $reference]);
            if ($payment) {
                $payment->setResponse($paymentArray);
                $this->em->persist($payment);
                $this->em->flush();

                return new Response('Data invalid');
            }
        }

        return new Response('No response');
    }

    #[Route('/request/cawl/responseAt', name: 'cawl_responseAt', methods: ['POST'])]
    public function cawlResponseAt(Request $request): Response
    {
        $signature = $request->headers->get('X-GCS-Signature');
        $data = json_decode($request->getContent(), true);

        $jsonMinifie = json_encode($data, \JSON_UNESCAPED_SLASHES);
        $result = json_decode((string) $jsonMinifie, true);

        $valid = $this->cawlPayment->valid((string) $jsonMinifie, (string) $signature);
        $reference = $result['payment']['paymentOutput']['references']['merchantReference'] ?? null;

        if (!$reference) {
            return new Response('No response', 200);
        }

        $cart = $this->cartRepository->findOneBy(['token' => $reference]);

        if (true === $valid) {
            if ($cart) {
                $checkout = $cart->getCartCheckout();
                $checkout->setResponse($result);
                $checkout->setPayedAt(new \DateTime('now'));
                $this->em->persist($checkout);
                $this->em->flush();

                $contact = $this->contactRepository->findOne();
                $this->mailer->sendPaidOrder($contact, $cart);

                return new Response('Order successfully updated', 200);
            }

            $payment = $this->paymentRepository->findOneBy(['slug' => $reference]);
            if ($payment) {
                $payment->setResponse($result);
                $payment->setPayedAt(new \DateTime('now'));
                $this->em->persist($payment);
                $this->em->flush();

                $this->mailer->sendPayment($payment);

                return new Response('Order successfully updated', 200);
            }
        } else {
            if ($cart) {
                $checkout = $cart->getCartCheckout();
                $checkout->setResponse($result);
                $this->em->persist($checkout);
                $this->em->flush();

                return new Response('Received', 200);
            }

            $payment = $this->paymentRepository->findOneBy(['slug' => $reference]);
            if ($payment) {
                $payment->setResponse($result);
                $this->em->persist($payment);
                $this->em->flush();

                $this->mailer->sendPayment($payment);

                return new Response('Received', 200);
            }
        }

        return new Response('No response', 200);
    }
}
