<?php

declare(strict_types=1);

namespace External\PompoBundle\Util\Order;

use External\PompoBundle\DataGo\Util\DataGoApi;
use External\PompoBundle\DRS\Synchronizer\OrderSynchronizer;
use External\PompoBundle\DRS\Util\DRSApi;
use External\PompoBundle\Util\PompoUtil;
use KupShop\OrderingBundle\Entity\Order\OrderItem;
use Query\Operator;

class PompoOrderUtil
{
    public const SYNCHRONIZER_DATAGO = 'dataGo';
    public const SYNCHRONIZER_DRS = 'drs';

    private DRSApi $drsApi;
    private DataGoApi $dataGoApi;
    private PompoUtil $pompoUtil;
    private OrderSynchronizer $drsOrderSynchronizer;

    public function __construct(DRSApi $drsApi, DataGoApi $dataGoApi, PompoUtil $pompoUtil, OrderSynchronizer $drsOrderSynchronizer)
    {
        $this->drsApi = $drsApi;
        $this->dataGoApi = $dataGoApi;
        $this->pompoUtil = $pompoUtil;
        $this->drsOrderSynchronizer = $drsOrderSynchronizer;
    }

    public function getOrderSynchronizerType(\Order $order): ?string
    {
        // pokud je to rezervace, tak jde objednavka pres DRS
        if (($order->getFlags()['RZ'] ?? false) || ($order->getFlags()['RT'] ?? false)) {
            return self::SYNCHRONIZER_DRS;
        }

        // jinak jde objednavka pres DataGo
        return self::SYNCHRONIZER_DATAGO;
    }

    public function getOrderItemChargeCode(OrderItem $item): ?string
    {
        $chargeId = ($item->getNote()['charge']['id_charge'] ?? null) ?: ($item->getNote()['id_charge'] ?? null);
        if (!$chargeId) {
            return null;
        }

        $data = sqlQueryBuilder()
            ->select('data')
            ->from('charges')
            ->where(Operator::equals(['id' => $chargeId]))
            ->execute()->fetchOne();

        $data = json_decode($data ?: '', true) ?: [];

        if (!empty($data['pompo_sync_code'])) {
            return $data['pompo_sync_code'];
        }

        return null;
    }

    public function getOrderItemDifference(\Order $order, array $orderItems = []): array
    {
        $currentItems = [];
        $orderItems = empty($orderItems) ? $this->getOrderItemsCounts($order) : $orderItems;

        try {
            if ($order->getData('pompoCreateItemDiffFromLocalData')) {
                // projdu polozky objednavky a vlozim si je do $currentItems
                foreach ($order->fetchItems() as $item) {
                    $currentItems[$item['id']] = (float) $item['pieces'];
                }
                // pokud nejaka polozka v $currentItems chybi, ale v puvodnim stavu byla ($orderItems), tak ji doplnim
                // do $currentItems s nulovym poctem kusu
                foreach ($orderItems as $key => $quantity) {
                    if (!isset($currentItems[$key])) {
                        $currentItems[$key] = 0;
                    }
                }
            } elseif ($this->getOrderSynchronizerType($order) === self::SYNCHRONIZER_DRS) {
                $currentItems = $this->drsApi->getOrderCurrentItems(
                    $order->order_no,
                    $this->drsOrderSynchronizer->getLoginBranch($order),
                    array_keys($orderItems)
                );
            } else {
                $dataGoId = sqlQueryBuilder()
                    ->select('id_datago')
                    ->from('datago_orders')
                    ->where(Operator::equals(['id_order' => $order->id]))
                    ->execute()->fetchOne();

                if ($dataGoId) {
                    [$user, $pass] = $this->pompoUtil->getDataGoCredentialsByOrder($order);

                    $currentItems = $this->dataGoApi->setCredentials($user, $pass)
                        ->getOrderCurrentItems($dataGoId);
                }
            }
        } catch (\Throwable $e) {
        }

        return $this->getOrderItemDifferenceResult($orderItems, $currentItems);
    }

    public function getOrderItemDifferenceResult(array $orderItems, array $dataGoItems): array
    {
        $hasDiff = false;
        foreach ($orderItems as $id => $pieces) {
            if (($dataGoItems[$id] ?? null) === null || ($dataGoItems[$id] == $pieces)) {
                continue;
            }

            $hasDiff = true;
            break;
        }

        $dataGoItems = empty($dataGoItems) ? [] : $dataGoItems;

        return [
            'hasDiff' => $hasDiff,
            'items' => $dataGoItems,
        ];
    }

    public function getOrderChangedItems(\Order $order): array
    {
        $originalStates = $order->getData('orderOriginalStates') ?: [];
        $originalState = reset($originalStates);
        $orderItems = [];
        if (!empty($originalState)) {
            foreach ($originalState as $itemId => $originalItem) {
                $orderItems[$itemId] = $originalItem['pieces'];
            }
        } else {
            // pokud nemam original state, tak ho vyrobim
            foreach ($this->getOrderItems($order) as $item) {
                if ($item['id_product']) {
                    $originalState[$item->getId()] = $this->prepareSimpleOrderItem($item);
                }
            }
        }

        $result = $this->getOrderItemDifference($order, $orderItems);

        $changedItems = [];
        if ($result['hasDiff']) {
            foreach ($originalState as $originalItemId => $originalItem) {
                $newPieces = $result['items'][$originalItemId] ?? null;
                if ($newPieces === null || $newPieces == $originalItem['pieces']) {
                    continue;
                }

                $changedItems[] = array_merge($originalItem, [
                    'oldPieces' => $originalItem['pieces'],
                    'newPieces' => $newPieces,
                ]);
            }
        }

        return $changedItems;
    }

    public function updateOrderByDifference(\Order $order, array $diffData): void
    {
        $newItems = $diffData['items'] ?? [];
        if (empty($newItems)) {
            return;
        }

        $originalState = [];
        $changeLog = [];
        foreach ($this->getOrderItems($order) as $item) {
            if (!$item['id_product']) {
                continue;
            }

            $originalState[$item->getId()] = $this->prepareSimpleOrderItem($item);

            $newPieces = $newItems[$item->getId()] ?? 0;
            // nic se nezmenilo
            if ($item->getPieces() == $newPieces) {
                continue;
            }

            // aktualizuju polozky, ktera se zmenila
            if ($newPieces > 0) {
                $changeLog[] = sprintf('Změna počtu kusů u položky <strong>%s</strong> (%s): %s -> %s', $item->getDescr(), $item->getId(), $item->getPieces(), $newPieces);
                $order->updateItem($item->getId(), $newPieces);
            } else {
                $changeLog[] = sprintf('Odebrána položka <strong>%s</strong> (%s): %s -> %s', $item->getDescr(), $item->getId(), $item->getPieces(), $newPieces);
                $order->updateItem($item->getId(), 0);
            }
        }

        if (!empty($changeLog)) {
            // save original state
            $originalStates = $order->getData('orderOriginalStates') ?: [];
            $originalStates[] = $originalState;
            $order->setData('orderOriginalStates', $originalStates);

            if ($this->getOrderSynchronizerType($order) === self::SYNCHRONIZER_DRS) {
                $logPrefix = '[DRS]';
            } else {
                $logPrefix = '[DataGo]';
            }

            // log info to order history
            $changeLog = [-1 => '<strong>'.$logPrefix.' Změny položek v objednávce:</strong>'] + array_map(fn ($x) => ' - '.$x, $changeLog);

            $order->logHistory(
                implode('<br>', $changeLog)
            );
        }
    }

    public function getOrderItemsCounts(\Order $order): array
    {
        $orderItems = [];
        foreach ($order->fetchItems() as $item) {
            if ($item['id_product']) {
                $orderItems[$item['id']] = $item['pieces'];
            }
        }

        return $orderItems;
    }

    public function prepareSimpleOrderItem(OrderItem $item): array
    {
        return [
            'id' => $item->getId(),
            'productId' => $item->getProductId(),
            'variationId' => $item->getVariationId(),
            'pieces' => $item->getPieces(),
            'price' => $item->getTotalPrice()->getPriceWithVat()->asFloat(),
            'tax' => $item->getVat(),
            'name' => $item->getDescr(),
        ];
    }

    /**
     * @return OrderItem[]
     */
    private function getOrderItems(\Order $order): array
    {
        return $order->getItems();
    }
}
