<?php

declare(strict_types=1);

namespace External\HannahBundle\Util\Product;

use KupShop\CatalogBundle\ProductList\FilterParams;
use KupShop\CatalogBundle\ProductList\ProductCollection;
use Query\Operator;

readonly class ProductRelatedVariationsUtil
{
    public function __construct(
        private FilterParams $filterParams,
    ) {
    }

    public function fetchForProduct(\Product $product, int $variationId): void
    {
        $variationValue = sqlQueryBuilder()
            ->select('id_label, id_value')
            ->from('products_variations_combination')
            ->where(Operator::equals(['id_variation' => $variationId]))
            ->execute()->fetchAssociative();

        if (!$variationValue) {
            return;
        }

        $filterParams = clone $this->filterParams;
        $filterParams->setVariation($variationValue['id_label'], [$variationValue['id_value']]);

        $this->fetchProductsRelatedVariationIdByFilterParams(new ProductCollection([$product->id => $product]), $filterParams);
    }

    /**
     * Načte ke kolekci produktů `relatedVariationId` podle `FilterParams`.
     *
     *   - provede načtení `relatedVariationId` i na všechny produkty v kolekcích dané kolekce produktů.
     */
    public function fetchProductsRelatedVariationIdByFilterParams(ProductCollection $products, FilterParams $filterParams): void
    {
        if ($products->isEmpty()) {
            return;
        }

        if ($productVariationsData = $this->getVariationsData($products, $filterParams)) {
            $this->fetchProductsRelatedVariationId($products, $productVariationsData);
        }

        $products->_ocCollectionProducts = $products->fetchCollections();

        if ($collectionVariationsData = $this->getVariationsData($products->_ocCollectionProducts, $filterParams)) {
            $this->fetchProductsRelatedVariationId($products->_ocCollectionProducts, $collectionVariationsData);
        }
    }

    /**
     * K produktum nacteme `relatedVariationId`, ktere odpovida variante se stejnou hodnotou jmenovky jako je v poli `$variationValueIds`.
     *
     * Pouziva se v katalogu a na detailu produktu.
     *    - pokud se v katalogu zafiltruje podle varianty, tak pri prokliknuti na produkt se predvybere varianta - tohle nacita ID, ktere nasledne sablony pridaji do URL
     *    - pokud se na detailu produktu vybere varianta, tak se pri prekliknuti na jiny produkt v kolekci drzi vybrana varianta z predesleho produktu
     *
     * @param array<int, int[]> $variationsData
     */
    public function fetchProductsRelatedVariationId(ProductCollection $products, array $variationsData): void
    {
        if ($products->isEmpty()) {
            return;
        }

        $orX = [];

        foreach ($variationsData as $productId => $valueIds) {
            $orX[] = Operator::andX(
                Operator::equals(['pv.id_product' => $productId]),
                Operator::inIntArray($valueIds, 'pvc.id_value')
            );
        }

        $qb = sqlQueryBuilder()
            ->select('pv.id_product, pvc.id_variation')
            ->from('products_variations_combination', 'pvc')
            ->join('pvc', 'products_variations_choices_values', 'pvcv', 'pvc.id_value = pvcv.id')
            ->join('pvc', 'products_variations', 'pv', 'pv.id = pvc.id_variation')
            ->andWhere(Operator::orX($orX))
            ->andWhere(Operator::inIntArray($products->getProductIds(), 'pv.id_product'))
            // pouze viditelne varianty
            ->andWhere(Operator::equals(['pv.figure' => 'Y']))
            // pouze varianty skladem
            ->andWhere('pv.in_store > 0')
            ->groupBy('pv.id_product')
            ->orderBy('pvcv.sort', 'ASC');

        $data = $qb->execute()->fetchAllKeyValue();

        foreach ($products as $product) {
            $product->relatedVariationId = $data[$product->id] ?? null;
        }
    }

    /**
     * @return array<int, int[]>
     */
    public function getVariationsData(ProductCollection $products, FilterParams $filterParams): array
    {
        if ($products->isEmpty()) {
            return [];
        }

        $variationsData = [];

        // filtr podle jmenovek variant
        foreach ($filterParams->getVariations() as $values) {
            foreach ($values as $value) {
                $qb = sqlQueryBuilder()
                    ->select('pv.id_product, pvc.id_value')
                    ->from('products_variations_combination', 'pvc')
                    ->join('pvc', 'products_variations', 'pv', 'pv.id = pvc.id_variation')
                    ->andWhere(Operator::inIntArray($products->getProductIds(), 'pv.id_product'))
                    ->andWhere(Operator::equals(['pvc.id_value' => $value]))
                    ->groupBy('pv.id_product');

                foreach ($qb->execute() as $item) {
                    $variationsData[$item['id_product']][] = $item['id_value'];
                }
            }
        }

        // filtr podle prevodnich tabulek
        foreach ($filterParams->getVariationsSizeConvertion() as $item) {
            foreach ($item['value'] ?? [] as $value) {
                $qb = sqlQueryBuilder()->select('p.id as id_product, pvc.id_value')
                    ->from('convertors_table_cache', 'ctc')
                    ->join('ctc', 'convertors_table_values', 'ctv', 'ctc.id_table = ctv.id_table')
                    ->join('ctc', 'products_variations', 'pv', 'pv.id_product = ctc.id_product')
                    ->join('pv', 'products_variations_combination', 'pvc', 'pvc.id_variation = pv.id and pvc.id_value = ctv.id_label_value')
                    ->join('pv', 'products', 'p', 'p.id = pv.id_product')
                    ->andWhere(Operator::equals(['ctv.id_parameter_value' => $value]))
                    ->andWhere(Operator::inIntArray($products->getProductIds(), 'p.id'))
                    ->groupBy('p.id');

                foreach ($qb->execute() as $variationValue) {
                    $variationsData[$variationValue['id_product']][] = $variationValue['id_value'];
                }
            }
        }

        return $variationsData;
    }
}
