<?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\Doctrine\EventSubscriber;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Utils\Mailer;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartCheckout;
use CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Cart\Model\Invoice;
use CpCreation\VitiCore\Cart\Repository\InvoiceRepository;
use CpCreation\VitiCore\Contact\Model\Contact;
use CpCreation\VitiCore\Product\Model\ProductGift;
use CpCreation\VitiCore\Product\Repository\ProductGiftRepository;
use CpCreation\VitiCore\Shop\Repository\ShopConfigRepository;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Symfony\Component\HttpFoundation\RequestStack;

#[AsDoctrineListener(event: Events::prePersist)]
#[AsDoctrineListener(event: Events::onFlush)]
readonly class CartCheckoutSubscriber
{
    public function __construct(
        private RequestStack $requestStack,
        private Mailer $mailer,
        private ShopConfigRepository $shopConfigRepository,
        private ProductGiftRepository $productGiftRepository,
        private InvoiceRepository $invoiceRepository,
    ) {
    }

    public function prePersist(LifecycleEventArgs $event): void
    {
        $entity = $event->getObject();

        if (!$entity instanceof CartCheckout) {
            return;
        }

        $cart = $entity->getCart();

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

        $request = $this->requestStack->getCurrentRequest() ?? $this->requestStack->getMainRequest();

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

        $cart->setLocale($request->getLocale());
        $cart->setIpClient($request->getClientIp());
    }

    public function onFlush(OnFlushEventArgs $eventArgs): void
    {
        $em = $eventArgs->getObjectManager();
        $uow = $em->getUnitOfWork();

        $hasGift = false;
        $nextDebitNumber = null;

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            if (!$entity instanceof CartCheckout) {
                continue;
            }

            $changes = $uow->getEntityChangeSet($entity);

            // payedAt vient d'être renseigné
            if (isset($changes['payedAt']) && null !== $changes['payedAt'][1]) {
                $cart = $entity->getCart();
                $items = $entity->getCart()->getItems();
                $cartCheckoutPayedAt = \DateTime::createFromInterface($entity->getPayedAt());

                if (null === $entity->getOrderedAt()) {
                    $entity->setOrderedAt(\DateTimeImmutable::createFromInterface($entity->getPayedAt()));
                    $uow->recomputeSingleEntityChangeSet($em->getClassMetadata($entity::class), $entity);
                }

                $shopConfig = $this->shopConfigRepository->findOne();
                if ($shopConfig?->isInvoice()) {
                    $cart->promoteToInvoiceDocument();
                    $uow->recomputeSingleEntityChangeSet($em->getClassMetadata(Cart::class), $cart);
                }

                if ($shopConfig?->isInvoice() && null === $cart->getInvoice()) {
                    $nextDebitNumber ??= $this->invoiceRepository->getNextDebitNumber();
                    $this->createInvoiceForCart($cart, $nextDebitNumber, $em, $uow);
                    ++$nextDebitNumber;
                }

                if (null !== $shopConfig && null !== $shopConfig->getGiftCardValidTime()) {
                    $cartCheckoutPayedAt->add($shopConfig->getGiftCardValidTime());
                } else {
                    $cartCheckoutPayedAt->add(new \DateInterval('P12M'));
                }

                /** @var CartItem $item */
                foreach ($items as $item) {
                    if (!$item->getVariant()->getType()->isGift()) {
                        continue;
                    }

                    for ($i = 0; $i < $item->getQuantity(); ++$i) {
                        $gift = new ProductGift();
                        $gift->setValidUntilDate($cartCheckoutPayedAt);

                        // suppose que addGift() gère la relation owning side correctement
                        $item->addGift($gift);

                        // Persist du nouveau gift
                        $em->persist($gift);
                        $metadata = $em->getClassMetadata($item::class);
                        $uow->computeChangeSet($metadata, $item);
                        $uow->persist($item);
                        $hasGift = true;
                    }
                }

                if ($hasGift) {
                    $contact = $em->getRepository(Contact::class)->findOne();
                    if (null !== $contact) {
                        $this->mailer->sendGiftCard($contact, $entity->getCart());
                    }
                }
                $cart = $entity->getCart();
                $giftUsed = $this->productGiftRepository->findGiftFromCart($cart);
                if ($giftUsed instanceof ProductGift) {
                    $giftUsed->setUsedAt(new \DateTime());
                    $em->persist($giftUsed);
                }
            }

            // payedAt a été annulé (redevient null) -> suppression des gifts
            if (isset($changes['payedAt']) && null === $changes['payedAt'][1]) {
                $items = $entity->getCart()->getItems();

                /** @var CartItem $item */
                foreach ($items as $item) {
                    if (!$item->getVariant()->getType()->isGift()) {
                        continue;
                    }

                    foreach ($item->getGifts() as $gift) {
                        $em->remove($gift);
                        $uow->scheduleForDelete($gift);
                    }

                    $uow->recomputeSingleEntityChangeSet($em->getClassMetadata($item::class), $item);
                }
            }
        }
    }

    private function createInvoiceForCart(Cart $cart, int $numberDebit, EntityManagerInterface $em, UnitOfWork $uow): void
    {
        $invoice = new Invoice('shop');
        $invoice->setNumberDebit($numberDebit);
        $cart->setInvoice($invoice);

        $em->persist($invoice);
        $uow->computeChangeSet($em->getClassMetadata(Invoice::class), $invoice);
        $uow->recomputeSingleEntityChangeSet($em->getClassMetadata(Cart::class), $cart);
    }
}
