<?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\Filesystem\Filesystem;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Payment\StripePayment;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Payment\StripePaymentStateSynchronizer;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Tracking\MatomoEcommerceTracker;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Tracking\MetaConversionsApiTracker;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Utils\Pdf;
use CpCreation\VitiCore\Cart\CartService;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\PaymentMethod;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Contact\Repository\ContactRepository;
use CpCreation\VitiCore\Product\Model\Product;
use CpCreation\VitiCore\Product\Model\ProductCategory;
use CpCreation\VitiCore\Shop\Model\ShopConfig;
use CpCreation\VitiCore\Shop\ShopCatalogResolver;
use CpCreation\VitiCore\Shop\Repository\ShopConfigRepository;
use CpCreation\VitiCore\User\Model\User;
use CpCreation\VitiCore\User\Repository\UserRepository;
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\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Attribute\ValueResolver;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\LocaleAwareInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class ShopController extends AbstractController
{
    private const string SESSION_REGISTER_EMAIL = 'security.register.email';
    private const string SESSION_REGISTER_MARKETING = 'security.register.marketing_opt_in';

    public function __construct(
        private readonly CartService $cartService,
        private readonly CartRepository $cartRepository,
        private readonly ShopConfigRepository $shopConfigRepository,
        private readonly ShopCatalogResolver $shopCatalogResolver,
        private readonly ContactRepository $contactRepository,
        private readonly Filesystem $filesystem,
        private readonly Pdf $pdf,
        private readonly TranslatorInterface $translator,
        private readonly RequestStack $requestStack,
        private readonly UserRepository $userRepository,
        private readonly RouterInterface $router,
        private readonly StripePaymentStateSynchronizer $stripePaymentStateSynchronizer,
        private readonly MatomoEcommerceTracker $matomoEcommerceTracker,
        private readonly MetaConversionsApiTracker $metaConversionsApiTracker,
    ) {
    }

    public function product(
        Request $request,
        string $categorySlug,
        #[ValueResolver('translation_resolver')]
        Product $product,
    ): Response {
        $config = $this->shopConfigRepository->findOne();
        if (!$config instanceof ShopConfig) {
            throw new \RuntimeException('Shop config not found');
        }

        if (!$config->isEnableProductPageFront()) {
            return $this->redirectToProductCategory($product);
        }

        if (!$this->shopCatalogResolver->isProductVisibleInShop($product)) {
            throw $this->createNotFoundException();
        }

        $category = $product->getCategory();
        if (!$category instanceof ProductCategory) {
            throw $this->createNotFoundException();
        }

        if ($category->getSlug() !== $categorySlug) {
            return $this->redirectToRoute('shop_product', [
                'categorySlug' => $category->getSlug(),
                'slug' => $product->getSlug(),
            ], Response::HTTP_MOVED_PERMANENTLY);
        }

        $activePriceGroupId = $this->shopCatalogResolver->resolveActivePriceGroupId();
        $this->metaConversionsApiTracker->trackViewContent($product, $activePriceGroupId, $request);

        return $this->render('shop/product.html.twig', [
            'config' => $config,
            'category' => $category,
            'product' => $product,
            'activePriceGroupId' => $activePriceGroupId,
        ]);
    }

    public function checkout(Request $request, ?string $cartId = null): Response
    {
        $paymentPending = $request->query->getBoolean('paymentPending');
        $cart = null !== $cartId
            ? $this->cartRepository->findOneCartInProgressById($cartId, false)
            : $this->cartService->getCart();

        if (null === $cart && null !== $cartId && $paymentPending) {
            $existingCart = $this->cartRepository->find($cartId);

            if ($existingCart instanceof Cart && $existingCart->isCheckoutPayed()) {
                return $this->redirectToRoute('shop_checkout_success', [
                    '_locale' => $this->applyCartLocale($request, $existingCart),
                    'cartId' => $existingCart->getId()->toString(),
                ]);
            }
        }

        if (!$cart) {
            return $this->redirectToRoute('shop');
        }

        $this->applyCartLocale($request, $cart);

        if ($paymentPending) {
            $parameters = [
                '_locale' => $request->getLocale(),
                'cartId' => $cart->getId()->toString(),
                'startedAt' => time(),
            ];

            $stripeSessionId = $this->normalizeStripeSessionId($request->query->get('stripeSessionId'));
            if (null !== $stripeSessionId) {
                $parameters['stripeSessionId'] = $stripeSessionId;
            }

            return $this->redirectToRoute('shop_checkout_payment_pending', $parameters);
        }

        $this->matomoEcommerceTracker->trackInitiateCheckout($cart, $request);
        $this->metaConversionsApiTracker->trackInitiateCheckout($cart, $request);

        return $this->render('shop/checkout.html.twig', [
            'checkout_cart_id' => $cart->getId()->toString(),
            'disable_user_autofill' => $request->query->getBoolean('disableUserAutofill'),
            'payment_pending' => false,
        ]);
    }

    public function paymentPending(Request $request, string $cartId): Response
    {
        $this->clearCartSession($cartId);

        $startedAt = max(0, $request->query->getInt('startedAt'));
        if (0 === $startedAt) {
            return $this->redirectToRoute('shop_checkout_payment_pending', [
                '_locale' => $request->getLocale(),
                'cartId' => $cartId,
                'startedAt' => time(),
            ]);
        }

        $cart = $this->cartRepository->find($cartId);

        if (!$cart instanceof Cart) {
            return $this->redirectToRoute('shop');
        }

        $this->applyCartLocale($request, $cart);

        $stripeSessionId = $this->normalizeStripeSessionId($request->query->get('stripeSessionId'));
        if (!$cart->isCheckoutPayed() && $this->shouldSynchronizeStripeCart($cart)) {
            $this->stripePaymentStateSynchronizer->synchronizeCartById($cart->getId()->toString(), $stripeSessionId);
        }

        if ($cart->isCheckoutPayed()) {
            return $this->redirectToRoute('shop_checkout_success', [
                '_locale' => $request->getLocale(),
                'cartId' => $cart->getId()->toString(),
            ]);
        }

        if (time() - $startedAt >= 60) {
            return $this->redirectToRoute('shop_checkout_payment_unconfirmed', [
                '_locale' => $request->getLocale(),
                'cartId' => $cart->getId()->toString(),
            ]);
        }

        return $this->render('shop/payment_pending.html.twig', [
            'cart' => $cart,
            'started_at' => $startedAt,
            'stripe_session_id' => $stripeSessionId,
        ]);
    }

    public function paymentUnconfirmed(Request $request, string $cartId): Response
    {
        $this->clearCartSession($cartId);

        $cart = $this->cartRepository->find($cartId);

        if (!$cart instanceof Cart) {
            return $this->redirectToRoute('shop');
        }

        $this->applyCartLocale($request, $cart);

        if ($cart->isCheckoutPayed()) {
            return $this->redirectToRoute('shop_checkout_success', [
                '_locale' => $request->getLocale(),
                'cartId' => $cart->getId()->toString(),
            ]);
        }

        $contactTarget = $this->resolvePaymentUnconfirmedContactTarget($request);

        return $this->render('shop/payment_unconfirmed.html.twig', [
            'cart' => $cart,
            'contact_url' => $contactTarget['url'],
            'contact_email' => $contactTarget['email'],
        ]);
    }

    public function success(Request $request, string $cartId): Response
    {
        $this->clearCartSession($cartId);

        $cart = $this->cartRepository->find($cartId);

        if (!$cart instanceof Cart || !$this->canDisplayOrderDetails($cart)) {
            return $this->redirectToRoute('shop');
        }

        $this->applyCartLocale($request, $cart);

        $documentLabel = $cart->getDocumentLabelKey();

        return $this->render('shop/success.html.twig', [
            'cart' => $cart,
            'payment_method_label' => $this->resolvePaymentMethodLabel($cart->getPaymentMethod()),
            'document_label' => $documentLabel,
            'download_document_label' => $cart->getDownloadDocumentLabelKey(),
            'can_prompt_guest_account_creation' => $this->canPromptGuestAccountCreation($cart),
        ]);
    }

    private function clearCartSession(string $cartId): void
    {
        $this->cartService->forgetCartReference($cartId);
    }

    private function normalizeStripeSessionId(mixed $sessionId): ?string
    {
        return \is_string($sessionId) && str_starts_with($sessionId, 'cs_') ? $sessionId : null;
    }

    private function shouldSynchronizeStripeCart(Cart $cart): bool
    {
        $paymentMethod = $cart->getPaymentMethod();

        return $paymentMethod instanceof PaymentMethod && StripePayment::METHOD === $paymentMethod->getMethod();
    }

    /**
     * @return array{url: ?string, email: ?string}
     */
    private function resolvePaymentUnconfirmedContactTarget(Request $request): array
    {
        $routes = $this->router->getRouteCollection();
        $localizedContactRoute = 'contact.'.$request->getLocale();

        if (null !== $routes->get($localizedContactRoute)) {
            return [
                'url' => $this->generateUrl($localizedContactRoute),
                'email' => null,
            ];
        }

        if (null !== $routes->get('contact')) {
            return [
                'url' => $this->generateUrl('contact', ['_locale' => $request->getLocale()]),
                'email' => null,
            ];
        }

        $contact = $this->contactRepository->findOne();
        $email = $this->normalizeEmail($contact?->getEmailToShop());

        return [
            'url' => null,
            'email' => '' !== $email ? $email : null,
        ];
    }

    public function registerFromGuestOrder(string $cartId): Response
    {
        $cart = $this->cartRepository->find($cartId);

        if (!$cart instanceof Cart || !$this->canDisplayOrderDetails($cart) || !$this->canPromptGuestAccountCreation($cart)) {
            return $this->redirectToRoute('shop');
        }

        $email = $this->normalizeEmail($cart->getEmail());
        if ('' === $email) {
            $this->addFlash('error', $this->translator->trans('checkout_design.guest_account_missing_email'));

            return $this->redirectToRoute('shop_checkout_success', [
                'cartId' => $cart->getId()->toString(),
            ]);
        }

        if (null !== $this->userRepository->findVerifiedByEmail($email)) {
            $this->addFlash('info', $this->translator->trans('security.error.account_exists', [], 'messages'));

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

        $session = $this->requestStack->getSession();
        $session?->set(self::SESSION_REGISTER_EMAIL, $email);
        $session?->set(self::SESSION_REGISTER_MARKETING, false);

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

    public function purchaseOrderDownload(string $cartId): Response
    {
        $cart = $this->cartRepository->find($cartId);

        if (!$cart instanceof Cart || !$this->canDisplayOrderDetails($cart)) {
            return $this->redirectToRoute('shop');
        }

        $contact = $this->contactRepository->findOne();
        if (null === $contact) {
            throw $this->createNotFoundException('Aucun contact boutique configuré.');
        }

        $locale = $cart->getLocale();
        $filename = $this->pdf->createPdfCartDocument($contact, $cart, $locale);
        $filePath = $this->filesystem->absolutePath('order', $filename);

        return $this->file($filePath, $filename, ResponseHeaderBag::DISPOSITION_ATTACHMENT);
    }

    private function resolvePaymentMethodLabel(?PaymentMethod $paymentMethod): ?string
    {
        if (null === $paymentMethod) {
            return null;
        }

        $configuration = method_exists($paymentMethod, 'getConfiguration')
            ? $paymentMethod->getConfiguration()
            : [];

        $configuredName = $configuration['name'] ?? null;
        if (\is_string($configuredName) && '' !== $configuredName) {
            return $configuredName;
        }

        $method = $paymentMethod->getMethod();

        return match ($method) {
            'check' => $this->translator->trans('checkout_design.payment_check'),
            'bankTransfert', 'banktransfert' => $this->translator->trans('checkout_design.payment_banktransfert'),
            default => $paymentMethod->isCheckoutOnlineMethod()
                ? $this->translator->trans('checkout_design.payment_card')
                : $method,
        };
    }

    private function canDisplayOrderDetails(Cart $cart): bool
    {
        if ($cart->isCheckoutPayed()) {
            return true;
        }

        $checkout = $cart->getCartCheckout();
        $paymentMethod = $cart->getPaymentMethod();
        $user = $this->getUser();

        return $checkout?->getOrderedAt() instanceof \DateTimeImmutable
            && true === $paymentMethod?->isCheckoutOfflineMethod()
            && $user instanceof User
            && $cart->getUser() instanceof User
            && $cart->getUser()->getId()->toString() === $user->getId()->toString();
    }

    private function canPromptGuestAccountCreation(Cart $cart): bool
    {
        if (!$cart->isCheckoutPayed() || $cart->getUser() instanceof User) {
            return false;
        }

        return '' !== $this->normalizeEmail($cart->getEmail());
    }

    private function normalizeEmail(?string $email): string
    {
        return mb_strtolower(mb_trim((string) $email));
    }

    private function redirectToProductCategory(Product $product): Response
    {
        $category = $product->getCategory();

        return $this->redirectToRoute('shop', [
            'slug' => $category instanceof ProductCategory ? $category->getSlug() : null,
        ]);
    }

    private function applyCartLocale(Request $request, Cart $cart): string
    {
        $locale = mb_trim($cart->getLocale());

        if ('' === $locale) {
            $locale = 'fr';
        }

        $request->setLocale($locale);
        $request->attributes->set('_locale', $locale);
        $this->requestStack->getSession()?->set('_locale', $locale);

        if ($this->translator instanceof LocaleAwareInterface) {
            $this->translator->setLocale($locale);
        }

        return $locale;
    }
}
