<?php

namespace KupShop\StoresBundle\EventListener;

use KupShop\KupShopBundle\Util\LoggingContext;
use KupShop\OrderingBundle\Cart;
use KupShop\OrderingBundle\Event\OrderItemEvent;
use KupShop\POSBundle\Util\PosEntity;
use KupShop\StoresBundle\Utils\StoresInStore;
use Query\Operator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\Service\Attribute\Required;

class OrderItemListener implements EventSubscriberInterface
{
    use \DatabaseCommunication;

    /**
     * @var StoresInStore
     */
    protected $storeInStore;

    #[Required]
    public Cart $cart;

    #[Required]
    public LoggingContext $loggingContext;

    public static function getSubscribedEvents()
    {
        return [
            OrderItemEvent::ITEM_CREATED => [
                ['updateStore', 200],
                ['updateStoreSets', 200],
            ],
            OrderItemEvent::ITEM_UPDATED => [
                ['updateStore', 200],
            ],
        ];
    }

    public function updateStore(OrderItemEvent $event)
    {
        if (findModule(\Modules::STORES, \Modules::SUB_DISABLE_MOVEMENTS) || \Settings::getDefault()->prod_subtract_from_store == 'N') {
            return;
        }

        $order = $event->getOrder();
        if (!$order) {
            return;
        }

        $qb = sqlQueryBuilder()
            ->select('s.*')
            ->from('stores', 's');

        if (findModule(\Modules::NEW_POS) && $order->pos > 0) {
            $posEntity = new PosEntity();
            $posEntity->createFromDB($order->pos);
            $qb->andWhere(Operator::equals(['s.id' => $posEntity->getStores()[0] ?? 0]));
        } else {
            $storeId = $order->getStore();
            $qb->andWhere(Operator::equals(['s.id' => $storeId]));
        }

        $store = $qb->execute()->fetch();

        if ($store && !empty($store['id'])) {
            $SQLfields = [
                'id_store' => $store['id'],
                'id_product' => $event->getProduct()->id,
                'id_variation' => $event->getVariationId(),
                'quantity' => -$event->getPieces(),
            ];

            $this->externalStoreHack($event, $store['id']);

            $this->loggingContext->activateOrder($order->id, function () use ($SQLfields) {
                $this->storeInStore->updateStoreItem($SQLfields);
            });
        }
    }

    public function updateStoreSets(OrderItemEvent $event)
    {
        $order = $event->getOrder();
        if (!$order) {
            return;
        }

        $storeId = $order->getStore();
        if (!$storeId) {
            return;
        }

        $product = $event->getProduct();
        if ($sets = $product->fetchSets()) {
            $quantity = -$event->getPieces();
            if ($note = $event->getData()['row']['note'] ?? null) {
                $note = $product->parseNote($note);
            }
            /** @var \Product $set_product */
            foreach ($sets as $set_product) {
                if (!($variation = $this->checkSetVariations($set_product, $note))) {
                    continue;
                }

                $SQLfields = [
                    'id_store' => $storeId,
                    'id_product' => $set_product->id,
                    'id_variation' => $variation['id'],
                    'quantity' => $set_product['set_pieces'] * $quantity,
                ];
                $this->externalStoreHack($event, $storeId);

                $this->loggingContext->activateOrder($order->id, function () use ($SQLfields) {
                    $this->storeInStore->updateStoreItem($SQLfields);
                });
            }
        }
    }

    protected function checkSetVariations($set_product, $note)
    {
        $variation = ['title' => '', 'id' => null];

        if ($set_product->hasVariations()) {
            $set_product->fetchVariations();
            $set_variation = $note['set_variation'][$set_product->id] ?? null;
            if ($set_variation && is_array($set_variation)) {
                $set_variation = $set_variation[$set_product['set_id']]['id_variation'] ?? null;
            }
            if (empty($set_variation) || $set_variation == -1) {
                return false;
            }

            $variation['id'] = $set_variation;
            $variation['title'] = ' '.$set_product->findVariation($variation['id'])['title'];
        }

        if (!empty($note['set_variation'][$set_product->id]) && $note['set_variation'][$set_product->id] == -1) {
            return false;
        }

        return $variation;
    }

    private function externalStoreHack($event, $storeId)
    {
        $store = sqlQueryBuilder()->select('type')->from('stores')
            ->where(Operator::equals(['id' => $storeId]))
            ->execute()->fetchOne();

        if ($store == StoresInStore::TYPE_EXTERNAL_STORE) {
            // Neni to moc hezky, ale melo by to bejt bezpecny.
            // Az budeme mit sell pres eventu, tak by se tohle melo hodit pred to, aby uz se
            // neodecitalo v pripade externiho skladu i z webovyho
            if (sqlGetConnection()->isTransactionActive()) {
                $p = $event->getProduct();
                $p->sell($event->getVariationId(), -$event->getPieces());
            } else {
                throw new \Exception('Maji se pricist kusy zpatky kvuli externimu skladu a neni to v transakci!!');
            }
        }
    }

    /**
     * @required
     */
    public function setStoreInStore(StoresInStore $storeInStore): void
    {
        $this->storeInStore = $storeInStore;
    }
}
