<?php

namespace KupShop\PohodaBundle\EventListener;

use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\MessengerBundle\Message\Envelope\AsyncEnvelope;
use KupShop\OrderingBundle\Cart;
use KupShop\OrderingBundle\Event\OrderEvent;
use KupShop\OrderingBundle\Util\Discount\DiscountGenerator;
use KupShop\OrderingBundle\Util\Order\OrderInfo;
use KupShop\PohodaBundle\Exceptions\PohodaException;
use KupShop\PohodaBundle\Messenger\OrderMessage;
use KupShop\PohodaBundle\Utils\Exporters\OrderInvoiceCorrectiveTaxExporter;
use KupShop\PohodaBundle\Utils\Exporters\OrdersDeleter;
use KupShop\PohodaBundle\Utils\Exporters\OrdersExporter;
use KupShop\PohodaBundle\Utils\PohodaBase;
use KupShop\PohodaBundle\Utils\Tools\PohodaProductHelper;
use KupShop\ReturnsBundle\Event\OrderCreateEvent;
use KupShop\ReturnsBundle\Event\ReturnsEvent;
use Query\Operator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Contracts\Service\Attribute\Required;

class OrderEventListener implements EventSubscriberInterface
{
    use PohodaBase;

    #[Required]
    public OrdersDeleter $ordersDeleter;
    #[Required]
    public OrderInvoiceCorrectiveTaxExporter $orderInvoiceCorrectiveTaxExporter;
    #[Required]
    public OrdersExporter $ordersExporter;
    #[Required]
    public MessageBusInterface $messageBus;
    #[Required]
    public PohodaProductHelper $pohodaProductHelper;
    #[Required]
    public DiscountGenerator $discountGenerator;
    #[Required]
    public Cart $cart;

    public static function getSubscribedEvents()
    {
        $events = [];

        if (findModule(\Modules::POHODA, \Modules::SUB_HANDLE_STORNO_CORRECTIVE_TAX_DOCUMENT)) {
            $events[OrderEvent::ORDER_STORNO][] = ['handleOrderCorrectiveTaxDocument', 201];
        }

        if (findModule(\Modules::POHODA, \Modules::SUB_DELETE_STORNO_ORDER)) {
            $events[OrderEvent::ORDER_STORNO][] = ['deleteOrderAfterStorno', 201];
        }

        if (findModule(\Modules::POHODA, \Modules::SUB_SYNC_COUPONS)) {
            $events[OrderEvent::ORDER_FINISHED][] = ['generateCoupon', 201];
        }

        if (findModule(\Modules::SYNCHRONIZATION)) {
            if (findModule(\Modules::POHODA, \Modules::SUB_INSTANT_ORDER_UPLOAD)) {
                $events[OrderEvent::ORDER_FINISHED][] = ['asyncSend', 200];
                $events[OrderCreateEvent::class][] = ['asyncSendReturn', 200];
                $events[OrderEvent::ORDER_PAID][] = ['asyncSend', 200];
            }
        }

        if (findModule(\Modules::POHODA, \Modules::SUB_MULTIPLE_SERVERS)) {
            $events[OrderEvent::ORDER_CREATED][] = ['addMServerLocation', 200];
        }

        if (findModule(\Modules::POHODA, \Modules::SUB_REUPLOAD_RETURN_ORDER)) {
            $events[ReturnsEvent::STATUS_CHANGED][] = ['reUploadOrder', 200];
        }

        return $events;
    }

    public function addMServerLocation(OrderEvent $orderEvent)
    {
        $order = $orderEvent->getOrder();
        $index = $this->pohodaProductHelper->findMServerIndexByPurchaseState($order->getPurchaseState());
        if (is_int(filter_var($index, FILTER_VALIDATE_INT))) {
            $order->setData('mServerIndex', $index);
        }
    }

    public function handleOrderCorrectiveTaxDocument(OrderEvent $orderEvent)
    {
        if ($orderEvent->getOrder()->source == OrderInfo::ORDER_SOURCE_POS) {
            return;
        }

        try {
            $order = $orderEvent->getOrder();
            // Opravný daňový doklad nemá smysl vytvářet, když není uhrazená a vyřízená
            if (in_array($order->status, getStatuses('handled')) && $order->isPaid() && !$order->getTotalPrice()->getPriceWithoutVat()->isZero()) {
                $this->orderInvoiceCorrectiveTaxExporter->setIDs([$order->id]);
                $this->orderInvoiceCorrectiveTaxExporter->processData();
            }
        } catch (PohodaException $pohodaException) {
            $this->orderInvoiceCorrectiveTaxExporter->pohodaConnector->addActivityLog(ActivityLog::SEVERITY_ERROR, $pohodaException->getMessage(), ['code' => $pohodaException->getCode()]);
        }
    }

    public function deleteOrderAfterStorno(OrderEvent $orderEvent)
    {
        if ($orderEvent->getOrder()->source == OrderInfo::ORDER_SOURCE_POS) {
            return;
        }

        try {
            $order = $orderEvent->getOrder();
            if (!in_array($order->status_previous, getStatuses('handled'))) {
                $this->ordersDeleter->setIDs([$order->id]);
                $this->ordersDeleter->processData();
            }
        } catch (PohodaException $pohodaException) {
            $this->ordersDeleter->pohodaConnector->addActivityLog(ActivityLog::SEVERITY_ERROR, $pohodaException->getMessage(), ['code' => $pohodaException->getCode()]);
        }
    }

    public function asyncSend(OrderEvent $orderEvent): void
    {
        $this->dispatchMessage($orderEvent->getOrder());
    }

    public function asyncSendReturn(OrderCreateEvent $orderCreateEvent): void
    {
        $this->dispatchMessage($orderCreateEvent->getOrder());
    }

    public function dispatchMessage($order)
    {
        if (!$this->pohodaConnector->isActive() || !empty($order->pohoda_id)) {
            return;
        }

        $orderMessage = new OrderMessage();
        $orderMessage->setIdOrder($order->id);

        $this->addRequestLog('async msg send', (array) $orderMessage);
        $this->messageBus->dispatch(new AsyncEnvelope(serialize($orderMessage)));
    }

    public function reUploadOrder(ReturnsEvent $event)
    {
        if ($event->getEntity()->getIdOrder()) {
            $order = new \Order();
            $order->createFromDB($event->getEntity()->getIdOrder());
            if (!empty($order->id) && !empty($order->pohoda_id)) {
                $id = $order->id;
                $this->ordersDeleter->setIDs([$id]);
                $this->ordersDeleter->processData();

                $results = $this->ordersDeleter->getResults();

                if (!($results[$id]['result'] ?? false) && ($results[$id]['errno'] != '115')) {
                    $error = $results[$id]['error'] ?? 'neznámý důvod';

                    $this->addActivityLog(ActivityLog::SEVERITY_ERROR, "{$results[$id]['errno']}: Objednávku {$order->order_no} se nepodařilo smazat - {$error}");
                }

                if (findModule(\Modules::POHODA, \Modules::SUB_GENERATE_INVOICES)) {
                    sqlQueryBuilder()
                        ->delete('orders_pohoda_invoices')
                        ->andWhere(Operator::equals(['id_order' => $id]))
                        ->execute();
                }

                $this->ordersExporter->setIDs([$id]);
                $this->ordersExporter->pohodaConnector->skipSaveLastSyncTime(true);
                $this->ordersExporter->processData();
            }
        }
    }

    public function generateCoupon(OrderEvent $event)
    {
        $order = $event->getOrder();

        // Rozdělení položek podle jednotlivých kupónů
        foreach ($order->fetchItems() as $item) {
            if (!empty($item['product'])) {
                $data = $item['product']->getData();
                if (!empty($data['generate_coupon']) && ($data['generate_coupon'] == 'Y') && !empty($data['generate_coupon_discount'])) {
                    if ($item['pieces'] > 1) {
                        $itemRow = sqlQueryBuilder()->select('*')->from('order_items')->where(Operator::equals(['id' => $item['id']]))->execute()->fetchAssociative();
                        unset($itemRow['id']);
                        $itemRow['pieces'] = $itemRow['pieces_reserved'] = 1;
                        $itemRow['total_price'] /= $item['pieces'];
                        for ($i = 1; $i < $item['pieces']; $i++) {
                            sqlQueryBuilder()
                                ->insert('order_items')
                                ->directValues($itemRow)
                                ->execute();
                        }

                        sqlQueryBuilder()
                            ->update('order_items')
                            ->directValues($itemRow)
                            ->where(Operator::equals(['id' => $item['id']]))
                            ->execute();
                    }
                }
            }
        }

        // Aby se znova načetly položky
        $order->items = [];
        $generated = $this->discountGenerator->generateCoupons($order, false);
    }
}
