<?php

namespace KupShop\BalikonosBundle\EventSubscriber;

use Doctrine\DBAL\Connection;
use KupShop\BalikonosBundle\Balikobot;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Event\CronEvent;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\HtmlBuilder\HTML;
use KupShop\OrderingBundle\Event\OrderDeliveryEvent;
use Psr\Log\LoggerInterface;
use Query\Operator;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class CronSubscriber implements EventSubscriberInterface
{
    use \DatabaseCommunication;

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

    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * @required
     */
    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

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

    public static function getSubscribedEvents()
    {
        $cfg = Config::get();
        $listeners = [];

        if (findModule(\Modules::BALIKONOS)
            && ($cfg['Modules'][\Modules::BALIKONOS]['provider'] ?? false) == 'balikobot'
        ) {
            $listeners[] = ['handleCheckPackages', 200];
        }

        if (!$listeners) {
            return [];
        }

        return [
            CronEvent::RUN_EXPENSIVE => $listeners,
        ];
    }

    public function handleCheckPackages()
    {
        $balikobot = ServiceContainer::getService(Balikobot::class);
        $dbcfg = \Settings::getDefault();

        if (($dbcfg['balikobot']['delivery_check'] ?? false) !== 'Y') {
            return;
        }

        $orderResults = sqlQueryBuilder()
            ->select('o.id, b.user as balikobot_user, dt.id_delivery AS delivery_type_id_delivery, o.package_id AS package_id, b.id AS balikonos_id')
            ->addSelect('DATE_SUB(NOW(), INTERVAL 2 WEEK) < o.date_handle AS order_not_old')
            ->from('orders', 'o')
            ->join('o', 'balikonos', 'b', 'b.id_order=o.id AND b.close = 2 AND b.id_delivery IS NOT NULL')
            ->join('o', 'delivery_type', 'dt', 'dt.id=o.id_delivery AND dt.id_delivery IN (:paired_delivery_ids)')
            ->where("b.id_delivery != ''")
            ->andWhere('o.status_storno != 1')
            ->setParameter('paired_delivery_ids', array_keys($dbcfg['balikobot']['delivery_type'] ?? []), Connection::PARAM_INT_ARRAY)
            ->groupBy('o.id')
            ->orderBy('o.date_accept', 'ASC')->execute();
        foreach ($orderResults as $orderResult) {
            $order = new \Order();
            $order->createFromDB($orderResult['id']);

            // -1 – Zásilka zatím nebyla předána přepravci.
            // 0 – Zásilka nebyla doručena – odmítnutí příjemcem. (případně „Zásilka nebyla doručena – chyba u přepravce.“)
            // 1 – Zásilka byla doručena příjemci.
            // 2 – Zásilka je doručována příjemci. (případně „Zásilka je připravena k výdeji.“)
            // 3 – Zásilka stornována.
            // 4 - Zásilka byla doručena zpět odesílateli.

            $saveToLog = true;
            if (isset($dbcfg['balikobot']['delivery_type'][$orderResult['delivery_type_id_delivery']]['carrier'])
                && !empty($orderResult['package_id'])
            ) {
                if ($balikobot->hasMultipleCollectionPlaces() && !empty($orderResult['balikobot_user'])) {
                    $balikobot->setCredentialsByName($orderResult['balikobot_user']);
                }

                $trackingInfo = $balikobot->trackSinglePackage(
                    $dbcfg['balikobot']['delivery_type'][$orderResult['delivery_type_id_delivery']]['carrier'],
                    $orderResult['package_id']
                );
                $trackingInfo = is_array($trackingInfo) ? reset($trackingInfo) : null;
                if ($trackingInfo && isset($trackingInfo['status_id'])) {
                    $strongEl = HTML::create('strong');
                    $date = !empty($trackingInfo['date']) ? new \DateTime($trackingInfo['date']) : null;
                    if ($trackingInfo['status_id'] == 1) {
                        $strongEl->text($trackingInfo['name'] ?? 'Zásilka byla doručena příjemci.');
                        $order->logHistory('Balíkobot: '.$strongEl, false, null, $date);
                        if (empty($date)) {
                            $this->logger->notice('Balikobot response', [
                                'order' => $order->id,
                                'trackingInfo' => $trackingInfo,
                            ]);
                        }
                        $this->eventDispatcher->dispatch(new OrderDeliveryEvent($order, $date));
                        $saveToLog = false;
                    } elseif (in_array($trackingInfo['status_id'], [0, 3])) {
                        $strongEl->text($trackingInfo['name'] ?? 'Zásilka stornována.');
                        $order->logHistory('Balíkobot: '.$strongEl, false, null, $date);
                        $saveToLog = false;
                    } elseif ($trackingInfo['status_id'] == 4) {
                        $strongEl->text($trackingInfo['name'] ?? 'Zásilka byla doručena zpět odesílateli.');
                        $order->logHistory('Balíkobot: '.$strongEl, false, null, $date);
                        $saveToLog = false;
                    }
                    $order->setData('balikobot_package_status', $trackingInfo['status_id']);
                    sqlQueryBuilder()->update('orders')
                        ->set('date_updated', 'NOW()')
                        ->where(Operator::equals(['id' => $order->id]))
                        ->execute();
                    // disable future processing of this order when package delivery status is final (0, 1 or 3)
                    if (!in_array($trackingInfo['status_id'], [0, 1, 3, 4])) {
                        continue;
                    }
                } else {
                    continue;
                }

                $this->updateSQL('balikonos', ['close' => 3], ['id' => $orderResult['balikonos_id']]);
                if ($saveToLog) {
                    $this->logger->notice('Balikobot close 3', [
                        'order' => $order,
                        'trackingInfo' => $trackingInfo ?? false,
                    ]);
                }
            }
        }
    }
}
