<?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\Twig\Components\Cart;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Order\CartType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Command\NotificationCommand;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Enum\PushLevel;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Tracking\MatomoEcommerceTracker;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Tracking\MetaConversionsApiTracker;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Behavior\HasTemplateOverride;
use CpCreation\VitiCore\Cart\CartService;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Shop\CartQuantityProgressResolver;
use CpCreation\VitiCore\Shop\Repository\ShopConfigRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\ComponentToolsTrait;
use Symfony\UX\LiveComponent\ComponentWithFormTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent('cart:CartForm', template: '@components/cart/CartForm.html.twig')]
class CartForm extends AbstractController
{
    use ComponentToolsTrait;
    use ComponentWithFormTrait;
    use DefaultActionTrait;
    use HasTemplateOverride;

    #[LiveProp(writable: true, fieldName: 'cartModel')]
    public ?Cart $cart = null;

    public array $quantityProgressRules = [];

    public bool $shippingCountryRulePending = false;

    public function __construct(
        private readonly CartService $cartService,
        private readonly CartRepository $cartRepository,
        private readonly Security $security,
        private readonly MessageBusInterface $commandSimpleBus,
        private readonly TranslatorInterface $translator,
        private readonly CartQuantityProgressResolver $cartQuantityProgressResolver,
        private readonly ShopConfigRepository $shopConfigRepository,
        private readonly RequestStack $requestStack,
        private readonly MatomoEcommerceTracker $matomoEcommerceTracker,
        private readonly MetaConversionsApiTracker $metaConversionsApiTracker,
    ) {}

    public ?string $deleteItemId = null;

    protected function instantiateForm(): FormInterface
    {
        $this->cart = $this->cartService->getCart();
        $this->refreshQuantityProgress();

        return $this->createForm(CartType::class, $this->cart);
    }

    #[LiveListener('cartUpdated')]
    public function refreshForm(): void
    {
        $this->resetForm();
        $this->cart = $this->cartService->getCart();
        $this->refreshQuantityProgress();
    }

    #[LiveAction]
    public function deleteItem(#[LiveArg('id')] CartItem $item): void
    {
        $cart = $this->cartService->getCart();
        $willClearCart = $cart instanceof Cart && 1 === $cart->getItems()->count();

        if ($willClearCart) {
            $this->trackCartCleared($cart);
        }

        $cart->removeItem($item);
        $this->cartRepository->save($cart);
        $this->deleteItemId = $item->getId();
        if ($cart->getItems()->isEmpty()) {
            $this->cartService->removeCart();
        }
        $this->emitSelf('cartUpdated');
    }

    private function trackCartCleared(Cart $cart): void
    {
        $request = $this->requestStack->getCurrentRequest() ?? $this->requestStack->getMainRequest();

        $this->matomoEcommerceTracker->trackCartCleared($cart, $request);
        $this->metaConversionsApiTracker->trackCartCleared($cart, $request);
        $this->dispatchBrowserEvent('viti:cart-cleared', $this->buildCartClearedBrowserPayload($cart));
    }

    /**
     * @return array{
     *     params: array<string, mixed>,
     *     ecommerce: array<string, mixed>
     * }
     */
    private function buildCartClearedBrowserPayload(Cart $cart): array
    {
        $pricing = $cart->getPricing();
        $value = null !== $pricing ? round($pricing->totalTTC / 100, 2) : null;
        $items = [];

        if (null !== $pricing) {
            foreach ($pricing->lines as $line) {
                if ($line->quantity <= 0) {
                    continue;
                }

                $items[] = [
                    'item_id' => $line->productId ?? $line->itemId,
                    'item_name' => $line->productName ?? $line->variantFullName ?? $line->typeName ?? 'Produit',
                    'item_variant' => $line->variantFullName,
                    'price' => round($line->netTTC / 100, 2),
                    'quantity' => $line->quantity,
                ];
            }
        } else {
            foreach ($cart->getItems() as $cartItem) {
                $variant = $cartItem->getVariant();
                $items[] = [
                    'item_id' => $variant?->getId()?->toString(),
                    'item_name' => $variant?->getFullName() ?? 'Produit',
                    'item_variant' => $variant?->getFullName(),
                    'price' => round($cartItem->getFinalPriceTTC() / 100, 2),
                    'quantity' => $cartItem->getQuantity(),
                ];
            }
        }

        return [
            'params' => array_filter([
                'currency' => 'EUR',
                'value' => $value,
                'items' => $items,
            ], static fn(mixed $value): bool => null !== $value && '' !== $value),
            'ecommerce' => array_filter([
                'currency' => 'EUR',
                'value' => $value,
                'items' => $items,
            ], static fn(mixed $value): bool => null !== $value && '' !== $value),
        ];
    }

    #[LiveAction]
    public function submitCart(): ?RedirectResponse
    {
        $orderingBlockedReason = $this->getOrderingBlockedReason();

        if (null !== $orderingBlockedReason) {
            $this->commandSimpleBus->dispatch(new NotificationCommand(
                message: $orderingBlockedReason,
                level: PushLevel::ERROR,
            ));

            return null;
        }

        $this->refreshQuantityProgress();

        if ($this->hasInvalidQuantityRules()) {
            $this->notifyInvalidCartRules();
            $this->dispatchBrowserEvent('cart:scroll-to-rules');
            $this->emitSelf('cartUpdated');

            return null;
        }

        try {
            $this->submitForm();

            if (!$this->validateStockLimits()) {
                $this->emitSelf('cartUpdated');

                return null;
            }

            $this->cart = $this->cartService->hydrateCart($this->cart);
            $this->cartRepository->save($this->cart);

            return $this->redirectToRoute('shop_checkout');
        } catch (UnprocessableEntityHttpException $e) {
            if ($this->form->getErrors(true)->count() > 0) {
                $notification = new NotificationCommand(
                    message: $this->form->getErrors(true)->current()->getMessage(),
                    level: PushLevel::ERROR
                );
                $this->commandSimpleBus->dispatch($notification);
            }
            $this->emitSelf('cartUpdated');
        }

        return null;
    }

    private function getOrderingBlockedReason(): ?string
    {
        $config = $this->shopConfigRepository->findOne();

        if (null === $config) {
            return null;
        }

        if ($config->isOrderingDisabledForVacation()) {
            return $this->translator->trans('shop.orders_disabled_for_vacation');
        }

        $partnerCode = $this->requestStack->getSession()?->get('partnerCode');
        $partnerCode = \is_string($partnerCode) ? $partnerCode : null;

        return $config->shouldShowPrices($partnerCode)
            ? null
            : $this->translator->trans('shop.view_with_partner_code');
    }

    #[LiveAction]
    public function updateItemQuantity(): void
    {
        try {
            $this->submitForm();

            if (!$this->validateStockLimits()) {
                $this->emitSelf('cartUpdated');

                return;
            }

            $this->cart = $this->cartService->hydrateCart($this->cart);
            $this->cartRepository->save($this->cart);
            $this->refreshQuantityProgress();
            $this->refreshForm();
        } catch (UnprocessableEntityHttpException $e) {
            if ($this->form->getErrors(true)->count() > 0) {
                $notification = new NotificationCommand(
                    message: $this->form->getErrors(true)->current()->getMessage(),
                    level: PushLevel::ERROR
                );
                $this->commandSimpleBus->dispatch($notification);
            }
            $this->emitSelf('cartUpdated');
        }
    }

    private function refreshQuantityProgress(): void
    {
        if (!$this->cart instanceof Cart) {
            $this->quantityProgressRules = [];
            $this->shippingCountryRulePending = false;

            return;
        }

        $countryCode = $this->cart->getDeliveryAddress()?->getCountry();
        $this->shippingCountryRulePending = null === $countryCode || '' === $countryCode;
        $this->quantityProgressRules = $this->cartQuantityProgressResolver->resolve(
            $this->cart,
            $this->cart->getLocale(),
        );
    }

    private function hasInvalidQuantityRules(): bool
    {
        return [] !== array_filter(
            $this->quantityProgressRules,
            static fn(array|object $rule): bool => false === $rule->isComplete,
        );
    }

    private function notifyInvalidCartRules(): void
    {
        $this->commandSimpleBus->dispatch(new NotificationCommand(
            message: $this->translator->trans(
                'cart.progress.notification',
                [],
                null,
                $this->cart?->getLocale(),
            ),
            level: PushLevel::ERROR,
        ));
    }

    private function validateStockLimits(): bool
    {
        if (!$this->cart instanceof Cart) {
            return true;
        }

        foreach ($this->cart->getItems() as $item) {
            $stockViolation = $this->cartService->getStockViolationForCartItem(
                $item,
                $this->cart,
                $item,
                true,
            );

            if (null === $stockViolation) {
                continue;
            }

            $this->commandSimpleBus->dispatch(new NotificationCommand(
                message: $this->translator->trans('shop.error.stock_limit', [
                    '%name%' => $stockViolation['name'],
                    '%quantity%' => $stockViolation['requested_quantity'],
                    '%stock%' => $stockViolation['available_stock'],
                ], 'validators'),
                level: PushLevel::ERROR
            ));

            return false;
        }

        return true;
    }
}
