<?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\Award\Model\Award;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Context\GroupContext;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Order\CartItemType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Command\NotificationCommand;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Enum\PushLevel;
use CpCreation\VitiCore\Cart\CartService;
use CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Media\Model\Image as MediaImage;
use CpCreation\VitiCore\Product\Model\Product;
use CpCreation\VitiCore\Product\Model\ProductDegressive;
use CpCreation\VitiCore\Product\Model\ProductImage;
use CpCreation\VitiCore\Product\Model\ProductVariant;
use CpCreation\VitiCore\Product\Repository\ProductDegressiveRepository;
use CpCreation\VitiCore\Product\Repository\ProductPriceGroupRepository;
use CpCreation\VitiCore\Product\Repository\ProductVariantRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
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\LiveProp;
use Symfony\UX\LiveComponent\ComponentToolsTrait;
use Symfony\UX\LiveComponent\ComponentWithFormTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\TwigComponent\Attribute\PreMount;

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

    public function __construct(private readonly MessageBusInterface $commandSimpleBus,
        private readonly TranslatorInterface $translator,
        private readonly CartService $cartService,
        private readonly CartRepository $cartRepository,
        private readonly ProductDegressiveRepository $productDegressiveRepository,
        private readonly ProductPriceGroupRepository $productPriceGroupRepository,
        private readonly ProductVariantRepository $productVariantRepository,
        private readonly RequestStack $requestStack,
    )
    {
    }

    #[LiveProp(writable: false)]
    public Product $product;

    /**
     * @return list<MediaImage>
     */
    public function getGalleryImages(): array
    {
        $images = $this->product->getImages()->toArray();
        usort($images, static function (ProductImage $left, ProductImage $right): int {
            return ($left->getPosition() ?? PHP_INT_MAX) <=> ($right->getPosition() ?? PHP_INT_MAX);
        });

        $galleryImages = [];
        $seenPaths = [];

        $primaryImage = $this->product->getSecondImage() ?? $this->product->getImage();
        if ($primaryImage instanceof MediaImage && $primaryImage->getPath()) {
            $galleryImages[] = $primaryImage;
            $seenPaths[$primaryImage->getPath()] = true;
        }

        foreach ($images as $image) {
            $media = $image->getImage();

            if (!$media instanceof MediaImage || !$media->getPath()) {
                continue;
            }

            if (isset($seenPaths[$media->getPath()])) {
                continue;
            }

            $galleryImages[] = $media;
            $seenPaths[$media->getPath()] = true;
        }

        return $galleryImages;
    }

    public function getSelectedVariant(): ?ProductVariant
    {
        $selectedVariantId = $this->formValues['variant'] ?? null;

        if (\is_string($selectedVariantId) && '' !== $selectedVariantId) {
            foreach ($this->product->getVariants() as $variant) {
                if ($variant->getId()->toString() === $selectedVariantId) {
                    return $variant;
                }
            }
        }

        $variant = $this->product->getVariantByDefault();

        return $variant instanceof ProductVariant ? $variant : null;
    }

    public function isSelectedVariantHasDegressive(): bool
    {
        $variant = $this->getSelectedVariant();

        if (!$variant instanceof ProductVariant) {
            return false;
        }

        foreach ($this->productDegressiveRepository->findAllApplicableByProductAndByGroup($this->product, $this->resolvePriceGroupId()) as $rule) {
            if ($rule instanceof ProductDegressive && $rule->getTypes()->contains($variant->getType())) {
                return true;
            }
        }

        return false;
    }

    #[PreMount]
    public function preMount(array $data): array
    {
        $resolver = new OptionsResolver();
        $resolver->setRequired('product');

        return $resolver->resolve($data);
    }

    protected function instantiateForm(): FormInterface
    {
        $item = CartItem::create($this->product);

        if ($this->productVariantRepository->getVariantsByProduct($this->product)->count() > 1) {
            $item->setVariant(null);
        } elseif ($item->getVariant() instanceof ProductVariant && ($item->getVariant()->getLot() ?? 1) > 1) {
            $item->setQuantity($item->getVariant()->getLot());
        }

        $item->setCart($this->cartService->getCart());
        // we can extend AbstractController to get the normal shortcuts
        return $this->createForm(CartItemType::class, $item, [
            'product' => $this->product,
        ]);
    }

    #[LiveAction]
    public function save(): ?Response
    {
        try {
            $this->submitForm();
            /** @var CartItem $item */
            $item = $this->form->getData();

            $cart = $this->cartService->getCart();
            if (!$cart) {
                $cart = $this->cartService->createCart();
            }

            $variant = $item->getVariant();
            $cart->addItem($item);
            $this->cartRepository->save($cart);
            $notification = new NotificationCommand(
                message: $this->translator->trans('shop.success.added_to_cart', [
                    '%name%' => $variant->getFullName(),
                    '%quantity%' => $item->getQuantity(),
                ]),
                level: PushLevel::SUCCESS
            );
            $this->commandSimpleBus->dispatch($notification);
            $this->emit('cartUpdated');
        } 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);
            }
        }

        return null;
    }

    #[LiveAction]
    public function showDegressiveModal(#[LiveArg('variantId')] ?ProductVariant $variant = null): void
    {
        if (!$variant instanceof ProductVariant || $variant->getProduct()?->getId() !== $this->product->getId()) {
            $variant = $this->getSelectedVariant();
        }

        $payload = ['productId' => $this->product->getId()->toString()];

        if ($variant instanceof ProductVariant) {
            $payload['variantId'] = $variant->getId()->toString();
        }

        $this->emit('degressiveModal:open', $payload, 'modal:SimpleModalDegressivity');
    }

    #[LiveAction]
    public function showAwardModal(#[LiveArg('awardId')] ?Award $award = null): void
    {
        if (!$award instanceof Award || !$this->product->getAwards()->contains($award)) {
            return;
        }

        $this->emit('awardModal:open', [
            'productId' => $this->product->getId()->toString(),
            'awardId' => $award->getId()->toString(),
        ], 'modal:SimpleModalAward');
    }

    #[LiveAction]
    public function showProductDetailModal(): void
    {
        $this->emit('productDetailModal:open', [
            'productId' => $this->product->getId()->toString(),
        ], 'modal:SimpleModalProductDetail');
    }

    private function resolvePriceGroupId(): ?string
    {
        $groupId = GroupContext::$groupId;

        if (!\is_string($groupId) || '' === $groupId) {
            $groupId = $this->requestStack->getSession()?->get('groupPrice');
        }

        if (!\is_string($groupId) || '' === $groupId) {
            $groupId = $this->productPriceGroupRepository->getGroupByDefault()?->getId()?->toString();
        }

        return \is_string($groupId) && '' !== $groupId ? $groupId : null;
    }
}
