<?php

namespace KupShop\WarehouseBundle\EventListener;

use KupShop\OrderingBundle\Event\OrderEvent;
use KupShop\OrderingBundle\Event\OrderMergeEvent;
use KupShop\WarehouseBundle\Entity\StoreItem;
use KupShop\WarehouseBundle\Event\WarehouseOrderEvent;
use KupShop\WarehouseBundle\Util\Logger;
use KupShop\WarehouseBundle\Util\StoreItemWorker;
use Query\Operator;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class OrderListener implements EventSubscriberInterface
{
    /**
     * @var StoreItemWorker
     */
    private $worker;

    /**
     * @var Logger
     */
    private $logger;

    /**
     * @var EventDispatcherInterface
     */
    private $eventDispatcher;

    public function __construct(StoreItemWorker $worker, Logger $logger)
    {
        $this->worker = $worker;
        $this->logger = $logger;
    }

    public static function getSubscribedEvents()
    {
        return [
            OrderEvent::ORDER_STORNO => ['handleOrderStorno'],
            OrderMergeEvent::BEFORE_MERGE => ['checkBeforeMerge'],
        ];
    }

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

        $warehouse_order = sqlQueryBuilder()
            ->select('*')
            ->from('warehouse_orders')
            ->where(Operator::equals(['id_order' => $order->id]))
            ->andWhere('id_position IS NOT NULL')
            ->execute()
            ->fetch();

        if ($warehouse_order) {
            $this->stornoStocked($order, $warehouse_order['id_position']);
        } else {
            $this->stornoWholeOrder($order);
        }
    }

    protected function stornoWholeOrder(\Order $order)
    {
        $warehouse_order = sqlQueryBuilder()
            ->select('*')
            ->from('warehouse_orders')
            ->where(Operator::equals(['id_order' => $order->id]))
            ->andWhere('date_close IS NOT NULL')
            ->execute()
            ->fetch();

        // Return to store only if order processed in store
        if (!$warehouse_order) {
            return;
        }

        $this->logger->activateOrder($order->id, function () use ($order) {
            $id_position = $this->getOrderTablePosition($order);

            foreach ($order->fetchItems() as $item) {
                if ($item['id_product'] && $item['pieces'] > 0) {
                    $this->worker->createStoreItem(new StoreItem($item), $item['pieces'], $id_position, ['storno' => true], true);
                }
            }
        });
    }

    protected function stornoStocked(\Order $order, $id_box_position)
    {
        sqlGetConnection()->transactional(function () use ($order, $id_box_position) {
            $this->logger->activateOrder($order->id, function () use ($id_box_position, $order) {
                $items = $this->worker->getItemsFromOrdersBox($order->id);

                $id_position = $this->getOrderTablePosition($order);

                foreach ($items as $item) {
                    $this->worker->moveBetweenPositions(new StoreItem($item), $item['pieces'], $item['id_position'], $id_position);
                }

                $this->worker->cleanCompletedBox($id_box_position);
            });
        });
    }

    public function checkBeforeMerge(OrderMergeEvent $event)
    {
        if ($event->getError()) {
            return;
        }

        $order_old = $event->getOrderOld();
        $order_new = $event->getOrderNew();

        $warehouse_order = sqlQueryBuilder()
                ->select('o.order_no')
                ->from('warehouse_orders', 'wo')->leftJoin('wo', 'orders', 'o', 'o.id = wo.id_order')
                ->where(Operator::inIntArray([$order_old->id, $order_new->id], 'wo.id_order'))
                ->execute()
                ->fetch();

        if ($warehouse_order) {
            $event->setError(['message' => 'Objednávky nelze sloučit! Objednávka č. '.$warehouse_order['order_no'].' má záznam ve skladu.']);
        }
    }

    /**
     * @required
     */
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;
    }

    public function getOrderTablePosition($order)
    {
        $event = new WarehouseOrderEvent();
        $event->setOrder($order);
        $this->eventDispatcher->dispatch($event, WarehouseOrderEvent::STORNO_ORDER);

        return $this->worker->getPositionIdByCode($event->getReturnPosition());
    }
}
