<?php
/*
 *  Copyright 2023.  Baks.dev <admin@baks.dev>
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is furnished
 *  to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */

declare(strict_types=1);

namespace BaksDev\Orders\Order\Messenger\Products;

use BaksDev\Core\Deduplicator\DeduplicatorInterface;
use BaksDev\Core\Lock\AppLockInterface;
use BaksDev\Orders\Order\Entity\Event\OrderEvent;
use BaksDev\Orders\Order\Entity\Products\OrderProduct;
use BaksDev\Orders\Order\Messenger\OrderMessage;
use BaksDev\Orders\Order\Repository\CurrentOrderEvent\CurrentOrderEventInterface;
use BaksDev\Orders\Order\Repository\ExistOrderEventByStatus\ExistOrderEventByStatusInterface;
use BaksDev\Orders\Order\Type\Status\OrderStatus\OrderStatusNew;
use BaksDev\Orders\Order\UseCase\Admin\Edit\EditOrderDTO;
use BaksDev\Orders\Order\UseCase\Admin\Edit\Products\OrderProductDTO;
use BaksDev\Products\Product\Entity\Offers\Variation\Modification\Quantity\ProductModificationQuantity;
use BaksDev\Products\Product\Entity\Offers\Variation\Quantity\ProductVariationQuantity;
use BaksDev\Products\Product\Repository\CurrentQuantity\CurrentQuantityByEventInterface;
use BaksDev\Products\Product\Repository\CurrentQuantity\Modification\CurrentQuantityByModificationInterface;
use BaksDev\Products\Product\Repository\CurrentQuantity\Offer\CurrentQuantityByOfferInterface;
use BaksDev\Products\Product\Repository\CurrentQuantity\Variation\CurrentQuantityByVariationInterface;
use BaksDev\Products\Product\Repository\UpdateProductQuantity\AddProductQuantityInterface;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

/**
 * Ставит продукцию в резерв в карточке товара
 * @note Имеет самый высокий приоритет
 */
#[AsMessageHandler(priority: 100)]
final class OrderReserveProduct
{
    private LoggerInterface $logger;

    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly CurrentQuantityByModificationInterface $quantityByModification,
        private readonly CurrentQuantityByVariationInterface $quantityByVariation,
        private readonly CurrentQuantityByOfferInterface $quantityByOffer,
        private readonly CurrentQuantityByEventInterface $quantityByEvent,
        private readonly DeduplicatorInterface $deduplicator,
        private readonly AddProductQuantityInterface $addProductQuantity,
        LoggerInterface $ordersOrderLogger,
    ) {

        $this->logger = $ordersOrderLogger;
    }


    /**
     * Сообщение ставит продукцию в резерв в карточке товара
     */
    public function __invoke(OrderMessage $message): void
    {
        /** Новый заказ не имеет предыдущего события!!! */
        if($message->getLast())
        {
            return;
        }


        $OrderEvent = $this->entityManager
            ->getRepository(OrderEvent::class)
            ->find($message->getEvent());


        if(!$OrderEvent)
        {
            return;
        }

        $EditOrderDTO = new EditOrderDTO();
        $OrderEvent->getDto($EditOrderDTO);
        $this->entityManager->clear();


        /** Если заказ не является новым - завершаем обработчик */
        if(false === $EditOrderDTO->getStatus()->equals(OrderStatusNew::class))
        {
            return;
        }

        $Deduplicator = $this->deduplicator
            ->namespace('orders-order')
            ->deduplication([
                (string) $message->getId(),
                OrderStatusNew::STATUS,
                md5(self::class)
            ]);

        if($Deduplicator->isExecuted())
        {
            return;
        }

        $this->logger->info(sprintf('%s: Добавляем общий резерв продукции в карточке:', $EditOrderDTO->getInvariable()->getNumber()));

        /** @var OrderProductDTO $product */
        foreach($EditOrderDTO->getProduct() as $product)
        {
            /** Устанавливаем новый резерв продукции в заказе */
            $this->handle($product);
        }

        $Deduplicator->save();
    }

    /**
     * Метод добавляет новый резерв продукции в заказе
     */
    public function handle(OrderProductDTO $product): void
    {
        $Total = $product->getPrice()->getTotal();

        $this
            ->addProductQuantity
            ->forEvent($product->getProduct())
            ->forOffer($product->getOffer())
            ->forVariation($product->getVariation())
            ->forModification($product->getModification())
            ->addReserve($product->getPrice()->getTotal());







        $Quantity = null;

        /** Обновляем резерв модификации множественного варианта торгового предложения */
        if(!$Quantity && $product->getModification())
        {
            /** @var ProductModificationQuantity $Quantity */
            $Quantity = $this->quantityByModification->getModificationQuantity(
                $product->getProduct(),
                $product->getOffer(),
                $product->getVariation(),
                $product->getModification()
            );
        }

        /** Обновляем резерв множественного варианта торгового предложения */
        if(!$Quantity && $product->getVariation())
        {
            /** @var ProductVariationQuantity $Quantity */
            $Quantity = $this->quantityByVariation->getVariationQuantity(
                $product->getProduct(),
                $product->getOffer(),
                $product->getVariation()
            );
        }

        /** Обновляем резерв торгового предложения */
        if(!$Quantity && $product->getOffer())
        {
            $Quantity = $this->quantityByOffer->getOfferQuantity(
                $product->getProduct(),
                $product->getOffer(),
            );
        }

        /** Обновляем резерв продукта */
        if(!$Quantity && $product->getProduct())
        {
            $Quantity = $this->quantityByEvent->getQuantity(
                $product->getProduct()
            );
        }


        if($Quantity && $Quantity->addReserve($Total))
        {
            $this->entityManager->flush();

            $this->logger->info(
                sprintf('Добавили %s общий резерв продукции в карточке', $Total),
                [
                    self::class.':'.__LINE__,
                    'ProductEventUid' => (string) $product->getProduct(),
                    'ProductOfferUid' => (string) $product->getOffer(),
                    'ProductVariationUid' => (string) $product->getVariation(),
                    'ProductModificationUid' => (string) $product->getModification(),
                ]
            );

            return;
        }

        $this->logger->critical(
            'Невозможно добавить резерв на новый заказ: карточка не найдена либо недостаточное количество для резерва)',
            [
                self::class.':'.__LINE__,
                'total' => (string) $Total,
                'ProductEventUid' => (string) $product->getProduct(),
                'ProductOfferUid' => (string) $product->getOffer(),
                'ProductVariationUid' => (string) $product->getVariation(),
                'ProductModificationUid' => (string) $product->getModification(),
            ]
        );

    }
}
