<?php

namespace External\SprintBundle\Synchronizers;

use Doctrine\DBAL\Connection;
use External\MSSQLBundle\Synchronizers\BaseSynchronizer;
use External\SprintBundle\Util\HeliosAPI;
use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
use KupShop\OrderingBundle\Util\Order\OrderItemInfo;
use KupShop\OrderingBundle\Util\Order\OrderUtil;
use Psr\Log\LoggerInterface;
use Query\Operator;

class OrderSynchronizer extends BaseSynchronizer
{
    public const ORDER_STATUS_NEW = 0;
    public const ORDER_STATUS_IN_PROGRESS = 1;
    public const ORDER_STATUS_FINISHED = 2;

    protected static $type = 'order';

    /** @var SentryLogger */
    private $sentryLogger;

    /** @var HeliosAPI */
    private $heliosAPI;

    /** @var OrderItemInfo */
    private $orderItemInfo;

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

    protected function process(?int $lastSyncTime = null): void
    {
        $timeLog[] = ['time' => date('d-m-Y-H-i-s'), 'message' => 'Synchronizace objednávek z Heliosu'];
        $this->processFromHelios();
        $timeLog[] = ['time' => date('d-m-Y-H-i-s'), 'message' => 'Synchronizace objednávek do Heliosu'];
        $this->processToHelios();
        $this->connection->close();
        $timeLog[] = ['time' => date('d-m-Y-H-i-s'), 'message' => 'Synchronizace objednávek dokončena'];
        addActivityLog(ActivityLog::SEVERITY_SUCCESS,
            ActivityLog::TYPE_SYNC,
            'Synchronizace objednávek',
            $timeLog);
    }

    protected function processToHelios($fromCron = true): void
    {
        $qb = sqlQueryBuilder()
            ->select('o.id, mo.status as helios_status, mo.id_money as id_helios')
            ->from('orders', 'o')
            ->leftJoin('o', 'money_orders', 'mo', 'mo.id_order = o.id')
            ->andWhere(Operator::not(Operator::equals(['source' => 'import'])))
            ->andWhere(Operator::equals(['o.status_storno' => 0]))
            ->andWhere('o.date_created >= NOW() - INTERVAL 30 DAY')
            ->andWhere(
                Operator::not(
                    Operator::inIntArray([
                        OrderSynchronizer::ORDER_STATUS_FINISHED,
                        OrderSynchronizer::ORDER_STATUS_IN_PROGRESS,
                    ],
                        'o.status')
                )
            )
            ->forUpdate()->groupBy('o.id');

        if ($fromCron) {
            $qb->andWhere('o.date_created <= NOW() - INTERVAL 1 HOUR');
        }

        foreach ($qb->execute() as $item) {
            try {
                $order = new \Order();
                $order->createFromDB($item['id']);

                // Nechceme nahrávat objednávky s chybou
                if ($order->getFlags()['SE'] ?? false) {
                    continue;
                }

                $heliosId = $item['id_helios'] ?: (sqlQueryBuilder()->select('id_money')->from('money_orders')->andWhere(Operator::equals(['id_order' => $item['id']]))->forUpdate()->execute()->fetchOne() ?: null);
                if ($heliosId == null) {
                    if ($result = $this->heliosAPI->insertOrder($order)) {
                        $order->logHistory('Objednávka nahrána do Heliosu ID: '.$result['id']);
                        $this->createMapping($result['id'], $order->id, 'order');
                        $this->updateSQL('money_orders', ['status' => self::ORDER_STATUS_NEW], ['id_order' => $order->id]);
                        $heliosId = (int) $result['id'];
                    } else {
                        $order->logHistory('Objednávka se nepovedlo nahrát do Heliosu');
                        $orderUtil = ServiceContainer::getService(OrderUtil::class);
                        $orderUtil->addFlag($order, 'SE');
                    }
                }

                if (!empty($heliosId)) {
                    $this->heliosAPI->update('TabDokladyZbozi_EXT', ['_EC_StavUhrady' => $order->isPaid()], ['ID', $heliosId]);
                }
            } catch (\Exception $e) {
                if (isDevelopment()) {
                    throw $e;
                }

                $this->sentryLogger->captureException($e);
            }
        }
    }

    public function processFromHelios($idOrder = null): void
    {
        if ($idOrder || $orderNumbers = $this->getOrderIdsForControl()) {
            $qb = $this->connection->getQueryBuilder()
                ->select('ID')
                ->from('hvw_ASOL_API_MerchandiseDocuments', 'md')->andWhere('DatPorizeni >= :last_sync_date')
                ->setParameter('last_sync_date', (new \DateTime())->modify('-3 months')->format('Y-m-d h:i:s'));

            if ($idOrder) {
                $qb->andWhere('md.ID = :id')
                    ->setParameter('id', $idOrder);
            } else {
                $qb->andWhere('md.NavaznaObjednavka IN (:ids)')
                    ->setParameter('ids', $orderNumbers, Connection::PARAM_STR_ARRAY);
            }

            foreach ($qb->execute() as $item) {
                // Vytahnu obj, ktere nemají stejný status na eshopu jako v heliosu
                if ($idOrder = sqlQueryBuilder()->select('id_order')
                    ->from('money_orders', 'mo')
                    ->innerJoin('mo', 'orders', 'o', 'mo.id_order=o.id')
                    ->where(Operator::equals(['mo.id_money' => $item['ID']]))
                    ->execute()->fetchOne()) {
                    $order = new \Order();
                    $order->createFromDB($idOrder);
                    $changeStatus = false;
                    $status = null;

                    if ($order['status'] == 0 || $order['status'] == 3) {
                        if ($this->connection->getQueryBuilder()
                            ->select('ID')
                            ->from('hvw_ASOL_API_MerchandiseDocuments', 'md')
                            ->andWhere('md.DruhPohybuZbo = 2')
                            ->andWhere('md.NavaznaObjednavka = (:id)')
                            ->setParameter('id', $order['order_no'])->execute()->fetchOne() ?? false) {
                            // Zpracovává se
                            $status = $changeStatus = 1;
                        }
                    } elseif ($order['status'] == 1) {
                        if ($this->connection->getQueryBuilder()
                            ->select('ID')
                            ->from('hvw_ASOL_API_MerchandiseDocuments', 'md')
                            ->andWhere('md.DruhPohybuZbo = 13')
                            ->andWhere('md.Realizovano = 1')
                            ->andWhere('md.NavaznaObjednavka = (:id)')
                            ->setParameter('id', $order['order_no'])->execute()->fetchOne() ?? false) {
                            // Vyřízeno
                            $this->processBalikobotFromHelios($order['order_no']);
                            $status = $changeStatus = 2;
                        }
                    }
                    if (!$changeStatus && $this->connection->getQueryBuilder()
                        ->select('ID')
                        ->from('hvw_ASOL_API_MerchandiseDocuments', 'md')
                        ->andWhere('md.DruhPohybuZbo = 9')
                        ->andWhere('md.Splneno = 1')
                        ->andWhere('md.NavaznaObjednavka = (:id)')
                        ->setParameter('id', $order['order_no'])->execute()->fetchOne() ?? false) {
                        $order->storno(false);
                    }
                    if ($changeStatus && !empty($status)) {
                        $order->changeStatus($status);
                    }
                }
            }
        }
    }

    private function getOrderIdsForControl(): ?array
    {
        return array_map(
            function ($x) {
                return $x['id'];
            },
            sqlQueryBuilder()
                ->select('o.order_no as id')
                ->from('orders', 'o')
                ->join('o', 'money_orders', 'mo', 'mo.id_order = o.id')
                ->andWhere(
                    Operator::not(
                        Operator::inIntArray([
                            OrderSynchronizer::ORDER_STATUS_FINISHED],
                            'o.status')
                    )
                )
                ->andWhere(Operator::equals(['o.status_storno' => 0]))
                ->andWhere(Operator::not(Operator::equals(['o.source' => 'import'])))
                ->execute()->fetchAll()
        );
    }

    public function processBalikobotFromHelios($orderNumber = null): void
    {
        foreach ($this->connection->getQueryBuilder()
                     ->select('bb.ID as id, bb.carrier_id as package_id, bb.real_order_id as order_no')
                     ->from('hvw_BalikobotBaliky_ReadOnly', 'bb')
                     ->join('bb', 'hvw_BalikobotZasilky', 'bz', 'bz.ID = bb.IdZasilky')
                     ->andWhere('bb.real_order_id like :ids')
                     ->setParameter('ids', '%'.$orderNumber.'/001')->execute() as $item) {
            if ($orderId = sqlQueryBuilder()->select('o.id')
                ->from('orders', 'o')
                ->join('o', 'money_orders', 'mo', 'mo.id_order = o.id')
                ->andWhere(Operator::equals(['o.order_no' => $orderNumber]))->execute()->fetchOne()) {
                $order = new \Order();
                $order->createFromDB($orderId);

                $data['package_id'] = $item['package_id'];

                if ($order['package_id'] == null || $order['package_id'] != $data['package_id']) {
                    sqlQueryBuilder()
                        ->update('orders')
                        ->directValues($data)
                        ->where(Operator::equals(['id' => $orderId]))->execute();
                    if ($order['package_id'] || $order['package_id'] != $data['package_id']) {
                        $order->logHistory('Číslo balíku: '.$item['package_id']);
                    }
                }
            }
        }
    }

    /**
     * @required
     */
    public function setSentryLogger(SentryLogger $sentryLogger): void
    {
        $this->sentryLogger = $sentryLogger;
    }

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

    /**
     * @required
     */
    public function setHeliosAPI(HeliosAPI $heliosAPI): void
    {
        $this->heliosAPI = $heliosAPI;
    }

    /**
     * @required
     */
    public function setOrderItemInfo(OrderItemInfo $orderItemInfo): void
    {
        $this->orderItemInfo = $orderItemInfo;
    }
}
