<?php

declare(strict_types=1);

namespace External\PompoBundle\DRS\Synchronizer;

use KupShop\CatalogBundle\Product\ProductFinder;
use KupShop\StoresBundle\Utils\StoresInStore;
use Query\Operator;

/**
 * Synchronizace skladu prodejen.
 */
class StockSynchronizer extends AbstractSynchronizer
{
    protected static $type = 'stock';

    protected bool $logging = false;

    /** @var ProductFinder */
    private $productFinder;

    /** @var StoresInStore */
    private $storesInStore;

    private bool $isFullStockSync = false;

    /**
     * @required
     */
    public function setProductFinder(ProductFinder $productFinder): void
    {
        $this->productFinder = $productFinder;
    }

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

    public function syncItemsByCode($codes): void
    {
        if (!is_array($codes)) {
            $codes = [$codes];
        }

        foreach ($this->drsApi->getStock($codes) as $item) {
            $this->processItem($item);
        }
    }

    public function syncFullStock(): int
    {
        $this->isFullStockSync = true;

        $count = 0;
        foreach ($this->drsApi->getStockWithPagination(onlyNonZeroItems: true) as $item) {
            $this->processItem($item);
            $count++;
        }

        $this->isFullStockSync = false;

        // nastavim 0 vsem polozkam, ktere nebyly aktualizovant vice jak 12 hodin
        sqlQueryBuilder()
            ->update('stores_items')
            ->directValues(['quantity' => 0])
            ->where('TIMESTAMPDIFF(HOUR, last_sync, NOW()) > 24')
            ->execute();

        return $count;
    }

    protected function processItem(array $item): void
    {
        // najdu produkt
        if (!($productId = $this->productFinder->getProductIdByCode($item['VART']))) {
            return;
        }

        // nactu is ID skladu podle cisla prodejny v DRSu
        $storeId = $this->getStoreId((int) $item['Shop']);

        $updateData = [
            'id_store' => $storeId,
            'id_product' => $productId,
            'id_variation' => null,
            'quantity' => (float) $item['Quantity'],
        ];

        if ($this->isFullStockSync) {
            $updateData['last_sync'] = (new \DateTime())->format('Y-m-d H:i:s');
        }

        // aktualizuju skladovost
        $this->updateStoreItem($updateData);
    }

    protected function getItems(): iterable
    {
        if ($this->mode === self::MODE_FULL) {
            return $this->drsApi->getStock();
        }

        // nactu si zmeny a jakmile se vsechno projde a zpracuje, tak zavolam deleteStockChanges, kterej promaze
        // promaze tabulku se zmenama
        return $this->drsApi->getStockChanges(function ($changes, $maxId) {
            $this->drsApi->deleteStockChanges($changes, $maxId);
        });
    }

    private function updateStoreItem(array $item): void
    {
        $storeItem = sqlQueryBuilder()
            ->select('id')
            ->from('stores_items')
            ->andWhere(Operator::equalsNullable(
                [
                    'id_product' => $item['id_product'],
                    'id_variation' => $item['id_variation'],
                    'id_store' => $item['id_store'],
                ]))
            ->execute()
            ->fetchAssociative();

        if ($storeItem) {
            $updateData = [
                'quantity' => $item['quantity'],
            ];

            if ($item['last_sync'] ?? false) {
                $updateData['last_sync'] = $item['last_sync'];
            }

            sqlQueryBuilder()
                ->update('stores_items')
                ->directValues($updateData)
                ->where(Operator::equals(['id' => $storeItem['id']]))
                ->execute();
        } else {
            sqlQueryBuilder()
                ->insert('stores_items')
                ->directValues($item)
                ->execute();
        }
    }

    private function getStoreId(int $branchId): int
    {
        static $stores;

        if (!$stores) {
            $qb = sqlQueryBuilder()
                ->select('id, data')
                ->from('stores');

            foreach ($qb->execute() as $item) {
                $data = json_decode($item['data'] ?? '', true) ?? [];
                if (!empty($data['branchId'])) {
                    $stores[$data['branchId']] = (int) $item['id'];
                }
            }
        }

        if (!($stores[$branchId] ?? false)) {
            $stores[$branchId] = sqlGetConnection()->transactional(function () use ($branchId) {
                sqlQueryBuilder()
                    ->insert('stores')
                    ->directValues(
                        [
                            'name' => 'Shop '.$branchId,
                            'type' => StoresInStore::TYPE_EXTERNAL_STORE,
                            'data' => json_encode(['branchId' => $branchId]),
                        ]
                    )->execute();

                return (int) sqlInsertId();
            });
        }

        return $stores[$branchId];
    }
}
