<?php

namespace KupShop\PohodaBundle\Utils\Importers;

use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\CatalogBundle\Util\SetsUpdateStore;
use KupShop\PohodaBundle\Utils\PohodaBase;
use KupShop\PohodaBundle\Utils\Tools\CouponHelper;
use KupShop\PohodaBundle\Utils\Tools\PhotosHelper;
use KupShop\PohodaBundle\Utils\Tools\StoresHelper;
use Query\Operator;
use Symfony\Contracts\Service\Attribute\Required;

class ProductsImporter extends \AutomaticImport
{
    use PohodaBase;
    use StoresHelper;
    use PhotosHelper;
    use CouponHelper;

    #[Required]
    public SetsUpdateStore $setsUpdateStore;

    protected $type = \AutomaticImport::TYPE_XML;

    protected $request_template = 'requests/productsIn.tpl';
    protected $xslt = 'productsIn.xsl';

    protected $params = [
        'transaction' => false,
    ];

    protected $pairBy = self::CODE;
    protected $batchPairElement = self::CODE;

    public const CODE = 'code';
    public const EAN = 'ean';
    public const BOTH = 3;

    protected $modify_in_store = true;

    protected array $productCodes = [null]; // Chci vždycky alespoň jednu iteraci s prázdným kódem

    public function __construct()
    {
    }

    /**
     * @param array $params
     *
     * @return bool|false|string|string[]|null
     *
     * @throws \KupShop\PohodaBundle\Exceptions\PohodaException
     * @throws \SmartyException
     */
    public function getData($params = [])
    {
        sqlGetConnection()->close();
        $request = $this->pohodaConnector->getRequestContent($this->request_template, array_merge($params, ['full' => $this->isFullSync(), 'productCodes' => $this->productCodes]));

        $request = $this->replace($request);

        $this->addRequestLog('Products importer', $request);

        return $this->pohodaConnector->sendRequest($request);
    }

    /**
     * @param null  $xml
     * @param array $params
     *
     * @throws \KupShop\PohodaBundle\Exceptions\PohodaException
     * @throws \SmartyException
     */
    public function processData($xml = null, $params = [])
    {
        $this->pohodaConnector->prepareSyncTime();

        if (empty($params['memory'])) {
            ini_set('memory_limit', '512M');
        }

        if ($xml) {
            $this->processXML($xml);
        } else {
            if (findModule(\Modules::POHODA, \Modules::SUB_STORES)) {
                if ($params['storeId'] ?? false && $this->storeExists($params['storeId'])) {
                    $stores = [$params['storeId'] => $this->storeExists($params['storeId'])];
                } else {
                    $stores = $this->storeExists();
                }
                foreach ($stores as $storePohodaCode => $storeRow) {
                    $note = json_decode($storeRow['data'], true);
                    if (!findModule(\Modules::POHODA, \Modules::SUB_MULTIPLE_SERVERS) || ($this->pohodaConnector->getCurrentKey() == intval($note['mserver'] ?? 0))) {
                        echo date('Y-m-d H:i:s').": stahuji sklad {$storePohodaCode} \n";
                        $this->processXML($this->getData(['storeId' => $storePohodaCode]));
                        $this->pohodaConnector->addActivityLog(ActivityLog::SEVERITY_SUCCESS, "Sklad {$storePohodaCode}".($this->fullSync ? ' - plná' : ' - změny'), []);
                    }
                }
            } else {
                $this->processXML($this->getData());
            }
        }
        $this->finishSync();

        $this->pohodaConnector->setLastSyncTime('productsIn', $this->stats['products_updated']);

        return $this->stats['products_updated'];
    }

    public function processXML($xml)
    {
        echo date('Y-m-d H:i:s').": stazeno, transformuji \n";

        $resultXML = $this->transformXML($xml);

        $this->xml = $resultXML;

        echo date('Y-m-d H:i:s').": parsuji\n";

        if ($this->parseProducts()) {
            echo date('Y-m-d H:i:s').": importuji\n";
            $this->importProducts();
        }

        echo date('Y-m-d H:i:s').": importovani dokonceno\n";

        $this->products = [];
        $this->xml = null;
    }

    public function finishSync()
    {
        $this->recalcStores();

        // Clears affected products
        $this->updatedCreatedProducts = [];
        parent::clearCaches();
        parent::automaticUpdateFlagForNewProducts();
    }

    public function recalcStores()
    {
        if (findModule(\Modules::STORES)) {
            $this->storeService->recalcInStoreFromStores(function ($qb) {
                $qb->andWhere(Operator::inIntArray(array_column($this->updatedCreatedProducts, 'id_product'), 'p.id'));
            });
        } else {
            sqlQuery('
             UPDATE products p
                 SET p.in_store=(
                SELECT COALESCE(SUM(GREATEST(pv.in_store, 0)), p.in_store) 
                FROM products_variations pv
                WHERE pv.id_product=p.id AND pv.figure != "N"
                )
            ');
        }

        if (findModule('products_sets', 'calculate_stock')) {
            $this->setsUpdateStore->updateSetsStore();
        }
    }

    public function findProduct(&$product, &$item = null)
    {
        if (findModule(\Modules::POHODA, \Modules::SUB_SYNC_COUPONS)) {
            if ($this->tryFindCoupon($product, $item)) {
                return null;
            }
        }

        if (!($cond = $this->pairCond($product))) {
            return null;
        }

        if (!empty($product[$this->batchPairElement]) && $pb = ($this->productsBatch[$product[$this->batchPairElement]] ?? false)) {
            $product['id_product'] = $pb['id_product'];
            $product['id_variation'] = $pb['id_variation'];
            $product['sync'] = true;

            return;
        }

        $products = sqlQueryBuilder()
            ->select('p.id as id_product, p.figure="Y" as visible')
            ->from('products', 'p')
            ->andWhere($cond)
            ->execute()->fetchAll();

        if (count($products) > 1) {
            $product['error'][] = 'Danému kódu/ean odpovídá více položek.'.print_r($product['code'], true);

            return null;
        }

        if (findModule(\Modules::PRODUCTS_VARIATIONS) && count($products) <= 0) {
            $products = sqlQueryBuilder()->select('id as id_variation, id_product as id_product')
                ->from('products_variations')
                ->where($cond)
                ->execute()->fetchAll();
        }

        if (count($products) <= 0) {
            // Pokud je produkt nový, naimportuje se do kategorie, která je uvedená v elementu INIT_CATEGORY
            if ($item->INIT_CATEGORY ?? false) {
                if (empty($item->INIT_CATEGORY['empty'])) {
                    $item->CATEGORY = $item->INIT_CATEGORY;
                }

                if (!empty($item->INIT_VISIBILITY)) {
                    $item->HIDDEN = 'Y';
                }

                if ($this->display) {
                    $this->stats['products_created']++;
                    $this->updatedCreatedProducts[] = $product;
                }
                $product['status'] = 'nový';
                $product['sync'] = true;
            } else {
                $product['status'] = 'ignorován';
            }

            return null;
        } elseif ($this->add_new == 2) {
            $product['status'] = 'ignorován';

            return null;
        } else {
            $product['sync'] = true;
        }

        $product['id_product'] = $products[0]['id_product'];
        $product['id_variation'] = $products[0]['id_variation'] ?? null;
    }

    protected function pairCond(&$product)
    {
        $tmpProduct = $product;
        if ($product['code'] ?? false) {
            $tmpProduct['product_code'] = $product['product_code'] ?? $product['code']; // for backward compatibility
        }

        switch ($this->pairBy) {
            case self::BOTH:
                if (!empty($tmpProduct['ean']) && !empty($tmpProduct['product_code'])) {
                    return Operator::orX(
                        Operator::equals(['ean' => $tmpProduct['ean']]),
                        Operator::like(['code' => $tmpProduct['product_code']])
                    );
                }

                if (!empty($tmpProduct['ean'])) {
                    return Operator::equals(['ean' => $tmpProduct['ean']]);
                }

                if (!empty($tmpProduct['product_code'])) {
                    return Operator::like(['code' => $tmpProduct['product_code']]);
                }

                $tmpProduct['error'][] = 'Nelze najit udaj pro sparovani - EAN/KOD produktu.';

                return null;
            case self::EAN:
                if (empty($tmpProduct['ean'])) {
                    $tmpProduct['error'][] = 'Nelze najit udaj pro sparovani - EAN produktu.';

                    return null;
                }

                return Operator::equals(['ean' => $tmpProduct['ean']]);
            case self::CODE:
            default:
                if (empty($tmpProduct['product_code'])) {
                    $tmpProduct['error'][] = 'Nelze najit udaj pro sparovani - kod produktu.';

                    return null;
                }

                return Operator::like(['code' => $tmpProduct['product_code']]);
        }
    }

    public function findVariation(&$product, &$product_variation)
    {
        if (!empty($product_variation[$this->batchPairElement]) && $pb = ($this->productsBatch[$product_variation[$this->batchPairElement]] ?? false)) {
            $product_variation['id_product'] = $pb['id_product'];
            $product_variation['id_variation'] = $pb['id_variation'];
            $product_variation['sync'] = true;
            $product_variation['status'] = 'aktualizace';

            return true;
        }

        // Pokud se našla varianta v rámci hledání produktu
        if (!empty($product['id_variation'])) {
            $product_variation['sync'] = true;

            $product_variation['id_variation'] = $product['id_variation'];
            $product_variation['status'] = 'aktualizace';

            return true;
        }

        if (!($cond = $this->pairCond($product_variation))) {
            return null;
        }

        $variation = sqlQueryBuilder()->select('id as id_variation, id_product as id_product')
            ->from('products_variations')
            ->where($cond)
            ->execute()->fetchAll();

        if (count($variation) > 1) {
            $product_variation['error'][] = 'Danému kódu/ean odpovídá více položek.';

            return null;
        }

        if (count($variation) <= 0) {
            $this->stats['variations_created']++;

            if (!isset($product['sync'])) {
                $product['sync'] = false;
                $product_variation['status'] = 'ignorován';
            } else {
                $product_variation['status'] = 'nový';
                $product_variation['sync'] = true;
            }

            return null;
        }

        $product_variation['sync'] = true;

        $product['id_product'] = $variation[0]['id_product'];
        $product_variation['id_variation'] = $variation[0]['id_variation'];
        $product_variation['status'] = 'aktualizace';
    }

    public function transformXML(/* $xml */)
    {
        return $this->transformHelper->baseTransform(func_get_arg(0), $this->transformHelper->loadXslt($this->xslt));
    }

    protected function customProductUpdate($product, $variation = null)
    {
        $this->customUpdateSupplier($product, $variation);
    }

    public function customUpdateSupplier($product, $variation)
    {
        $supplier_in_store = getVal('supplier_in_store', $variation, getVal('supplier_in_store', $product));
        if (!is_array($supplier_in_store)) {
            return false;
        }
        foreach ($supplier_in_store as $id_supplier => $value) {
            if (empty($variation['product_code']) && empty($product['product_code'])) {
                return false;
            }
            $fields = [];
            $fields['id_supplier'] = $id_supplier;
            $fields['id_product'] = $product['id_product'];
            $fields['id_variation'] = $variation ? $variation['id_variation'] : getVal('id_variation', $product);
            $fields['code'] = $variation ? $variation['product_code'] : $product['product_code'];

            $fields['in_store'] = $value;

            $fields = queryCreate($fields, true);

            sqlQuery("INSERT INTO products_of_suppliers SET {$fields}, last_sync=NOW() ON DUPLICATE KEY UPDATE {$fields}, last_sync=NOW()");
        }
    }

    /**
     * @param $product - array of loaded product fields
     * @param $item    - xml product element
     */
    protected function addCustomProductLoad(&$product, $item)
    {
        $product['pohoda_sync_date'] = (new \DateTime())->format('Y-m-d H:i:s');
        if (findModule(\Modules::POHODA, \Modules::SUB_PHOTOS)) {
            $this->addPhotos($product, $item);
        }

        $this->addSupplierInStore($product, $item);
        $this->addResponseLog('Product Import', $product);
    }

    /**
     * @param $product_variation - array of loaded variation fields
     * @param $variation         - xml variation element
     */
    protected function addCustomVariationLoad(&$product_variation, $variation)
    {
        $product_variation['pohoda_sync_date'] = (new \DateTime())->format('Y-m-d H:i:s');
        if (findModule(\Modules::POHODA, \Modules::SUB_PHOTOS)) {
            $this->addPhotos($product_variation, $variation);
        }

        $this->addSupplierInStore($product_variation, $variation);
        $this->addResponseLog('Product Import', $product_variation);
    }

    protected function addSupplierInStore(&$outputItem, $inputItem)
    {
        if ($inputItem->SUPPLIERS_IN_STORE->SUPPLIER_IN_STORE ?? false) {
            foreach ($inputItem->SUPPLIERS_IN_STORE->SUPPLIER_IN_STORE as $value) {
                $outputItem['supplier_in_store'][intval($value['id'])] = intval(strval($value));
            }
        }
    }

    public function updateDB()
    {
    }

    public function updateSupplier($product, $variation)
    {
    }

    public function clearCaches()
    {
    }

    protected function automaticUpdateFlagForNewProducts()
    {
    }

    public function parseParams()
    {
        return true;
    }

    protected function productsBatchLoad($xml)
    {
        parent::productsBatchLoad($xml);

        if (!isFunctionalTests()) {
            echo date('Y-m-d H:i:s').": batch load dokonceno\n";
        }
    }

    protected function productsBatchQuery($chunk)
    {
        return sqlQueryBuilder()
                ->select('p.id as id_product, pv.id as id_variation', "COALESCE(pv.{$this->batchPairElement}, p.{$this->batchPairElement}) as {$this->batchPairElement}")
                ->from('products', 'p')
                ->leftjoin('p', 'products_variations', 'pv', 'pv.id_product = p.id')
                ->where(Operator::inStringArray(array_values($chunk), "COALESCE(pv.{$this->batchPairElement}, p.{$this->batchPairElement})"))
                ->groupBy('p.id, pv.id')
                ->execute();
    }

    protected function modifyFields(&$updateFields, &$variationFields)
    {
        $updateFields[] = 'pohoda_sync_date';
        $variationFields[] = 'pohoda_sync_date';
    }

    public function setIDs(array $IDs)
    {
        $this->productCodes = $IDs;
    }
}
