<?php

declare(strict_types=1);

namespace KupShop\CatalogBundle\Util;

use KupShop\CatalogBundle\Entity\Category;
use KupShop\CatalogBundle\Entity\Producer;
use KupShop\CatalogBundle\Entity\Section;
use KupShop\CatalogBundle\ProductList\DynamicFilterAttributes;
use KupShop\CatalogBundle\Repository\SectionsRepository;
use KupShop\I18nBundle\Translations\BlocksTranslation;
use KupShop\I18nBundle\Translations\ParametersListTranslation;
use KupShop\I18nBundle\Translations\ProducersTranslation;
use KupShop\I18nBundle\Translations\VariationsValuesTranslation;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Util\StringUtil;
use Query\Operator;
use Query\QueryBuilder;
use Query\Translation;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class SectionViewUtil
{
    public function __construct(
        protected readonly ActiveCategory $activeCategory,
        protected readonly IndexedFilterUtil $indexedFilterUtil,
        protected readonly FilterUtil $filterUtil,
    ) {
    }

    public function getListShowType(): mixed
    {
        $show = \Settings::getDefault()->cat_show_style;

        if (!empty($_COOKIE['cat_show'])) {
            $show = $_COOKIE['cat_show'];
        }

        if (!empty($_GET['show']) && preg_match('/^[[:digit:]]{1}$/i', $_GET['show'])) {
            $show = $_GET['show'];
            SetCookies('cat_show', $show);
        }

        return $show;
    }

    /**
     * @return array{noOnPage: int, page: int}
     */
    public function getPagerOptions(Request $request): array
    {
        $dbcfg = \Settings::getDefault();

        $noOnPage = $dbcfg['cat_show_products'];

        if (is_numeric(getVal('filter_onpage'))) {
            $noOnPage = getVal('filter_onpage');
            SetCookies('filter_onpage', $noOnPage);
        } elseif (!empty($_COOKIE['filter_onpage'])) {
            $noOnPage = intval($_COOKIE['filter_onpage']);
        }

        $page = (int) max(intval($request->get('page', default: 1)), 1);
        $noOnPage = (int) $noOnPage;

        return compact('noOnPage', 'page');
    }

    /**
     * @deprecated use {@see SectionsRepository}
     */
    public function getCategory(int|array $categoryId, ?int $campaign = null): Category
    {
        $cfg = Config::get();
        global $catPath;

        $catPath = [];

        if (is_array($categoryId)) {
            $catPath = array_unique($categoryId);
            $categoryId = $catPath[0];
            $catPath = array_reverse($catPath);
        }

        $categoryId = (string) $categoryId;

        if (!empty($categoryId)) {
            $cat = $this->activeCategory->getSectionById($categoryId);
            if (!$cat) {
                throw new NotFoundHttpException('Section not found!');
            }
            if (($cat->getFigure() == 'N') && !getAdminUser()) {
                // skryta sekce - 404 (pro admina nezobrazovat 404, pokud je sekce skryta)
                throw new NotFoundHttpException('Section not found!');
            }
        }

        $this->activeCategory->setCategoryId($categoryId);

        if (!is_array($categoryId) && empty($catPath)) {
            $catPath = $this->activeCategory->getOpenedTreePath();
        }

        $this->activeCategory->validate([$categoryId]);
        $category = null;

        if (preg_match("/^-?[[:digit:]]+({$cfg['Products']['UrlFlags']})?$/i", $categoryId)) {
            if (!is_numeric($categoryId)) {
                $campaign = substr($categoryId, -1);
                $categoryId = substr($categoryId, 0, -1);
            }

            if ($categoryId >= 0) {
                // Tohle je demence, ale Honza tvrdí, že to je třeba kvůli Seznam remarketingu, bohužel :-(
                $cqb = sqlQueryBuilder()
                    ->select('s.*, fs.category_text as feed_seznam_category')
                    ->from('sections', 's')
                    ->leftJoin('s', 'kupshop_shared.feed_seznam', 'fs', 's.feed_seznam = fs.id')
                    ->where(Operator::equals(['s.id' => $categoryId]));
                $cqb->andWhere(
                    Translation::coalesceTranslatedFields(
                        \KupShop\I18nBundle\Translations\SectionsTranslation::class,
                        function ($columns) {
                            $columns['data'] = 'data_translated';

                            return $columns;
                        }
                    )
                );

                $category = $cqb->execute()
                    ->fetch();
                if ($category) {
                    $category['param'] = '';
                    $category['parents'] = $catPath;
                    $sectionData = array_replace_recursive(json_decode($category['data'] ?? '{}', true) ?: [], json_decode($category['data_translated'] ?? '{}', true) ?: []);
                    $category['data'] = $sectionData;
                    $category['date_updated'] = $category['date_updated'] ? new \DateTime($category['date_updated']) : null;
                } else {
                    throw new NotFoundHttpException('Section not found!');
                }
            } else {
                $category = [
                    'id' => 0,
                    'name' => '',
                    'descr' => '',
                    'behaviour' => 0,
                    'param' => '',
                    'lead_figure' => false,
                    'lead_text' => '',
                    'lead_products' => '',
                    'orderby' => 'title',
                    'orderdir' => 'ASC',
                    'producers_filter' => true,
                    'producers_to_title' => true,
                    'producers_indexing' => false,
                ];
            }
            $category['name_orig'] = $category['name'];

            $category['campaign'] = $campaign;

            $category = new Category($category);
        } else {
            redirection('REFERER');
        }

        return $category;
    }

    public function getDynamicFilterConfiguration(Section $section, ?int $producerId): DynamicFilterAttributes
    {
        if (FilterUtil::useOrderableFilters()) {
            $sectionFilters = $this->filterUtil->getSectionFilters(
                $section,
                $producerId,
            );

            return DynamicFilterAttributes::fromOrderedFilters($sectionFilters);
        }

        $parameters = [];
        if (findModule('producers') || findModule('products_sections')) {
            $qb = sqlQueryBuilder()
                ->select('pa.*')
                ->from('parameters', 'pa')
                ->leftJoin('pa', 'parameters_sections', 'ps', 'ps.id_section=:id_section AND ps.id_parameter=pa.id')
                ->setParameter('id_section', $section->getId())
                ->where(Operator::orX(
                    findModule('products_sections') ? Operator::andX(\Query\Parameter::inSections([$section->getId()]), Operator::equals(['ps.filter' => 'Y'])) : null,
                    findModule('producers') ? \Query\Parameter::inProducers([$producerId]) : null))
                ->groupBy('pa.id')
                ->orderBy('ps.weight')
                ->addOrderBy('pa.position');

            if (findModule(\Modules::INDEXED_FILTER)) {
                $qb->addSelect('ps.to_title, ps.indexing');
            }

            $qb->andWhere(Translation::coalesceTranslatedFields(\KupShop\I18nBundle\Translations\ParametersTranslation::class, ['name', 'descr']));
            $parameters = sqlFetchAll($qb->execute(), 'id');
        }

        $variations = [];
        if (findModule('products_variations')) {
            $qb = sqlQueryBuilder()
                ->select('pvcl.id, pvcl.label as name')
                ->from('products_variations_choices_labels', 'pvcl')
                ->where(\Query\Variation::labelsInSections([$section->getId()]))
                ->orderBy('pvs.weight');

            if (findModule(\Modules::INDEXED_FILTER)) {
                $qb->addSelect('to_title, indexing');
            }

            if (findModule(\Modules::CONVERTORS)) {
                $qb->addSelect('convertor');
            }

            $qb->andWhere(Translation::coalesceTranslatedFields(\KupShop\I18nBundle\Translations\VariationsLabelsTranslation::class, ['label' => 'name']));

            $variations = sqlFetchAll($qb->execute(), 'id');
        }
        $producers = true;
        if (findModule(\Modules::PRODUCERS)) {
            $producers = $section->getProducersFilter() == 'Y';
            if ($producers && findModule(\Modules::INDEXED_FILTER)) {
                $producers = [
                    'to_title' => $section->getProducersToTitle() === 'Y',
                    'indexing' => $section->getProducersIndexing() === 'Y',
                ];
            }
        }

        $labels = findModule(\Modules::LABELS) && $section->getLabelsFilter() === 'Y';

        return new DynamicFilterAttributes(
            parameters: $parameters,
            variations: $variations,
            producers: $producers,
            labels: $labels,
        );
    }

    public function getTitleParts(?Section $section, ?Producer $producer, bool $useSeo = true): array
    {
        $parts = [];

        if ($producer) {
            if ($useSeo && $producerMeta = $producer->getMetaTitle()) {
                $parts['producer'] = $producerMeta;
            } elseif ($producerName = $producer->getName()) {
                $parts['producer'] = $producerName;
            }
        }

        if ($section) {
            if ($section->getId() !== 0 || empty($parts['producer'])) {
                if ($useSeo && $categoryMetaTitle = $section->getMetaTitle()) {
                    $parts = array_merge(['category' => $categoryMetaTitle], $parts);
                } elseif ($categoryName = $section->getName()) {
                    $parts = array_merge(['category' => $categoryName], $parts);
                }
            }
        }

        return $parts;
    }

    public function getTitle(?Section $section, ?Producer $producer, string $separator = ' - ', bool $useSeo = true): string
    {
        $parts = $this->getTitleParts($section, $producer, $useSeo);

        return join($separator, array_filter($parts));
    }

    protected function getBlockContent(int $rootId): ?string
    {
        $block = sqlQueryBuilder()->from('blocks', 'b')
            ->where(Operator::equals(['id_root' => $rootId]))
            ->andWhere(Translation::coalesceTranslatedFields(BlocksTranslation::class, ['content']))
            ->execute()->fetchOne();

        if ($block) {
            return strip_tags($block);
        }

        return null;
    }

    public function getMetaDescriptionParts(?Section $section, ?Producer $producer): array
    {
        $parts = [];

        if ($categoryMeta = $section?->getMetaDescription()) {
            $parts['category'] = $categoryMeta;
        }

        if ($producerMeta = $producer?->getMetaDescription()) {
            $parts['producer'] = $producerMeta;
        }

        if (!isset($parts['category']) && ($sectionBlock = $section?->getIdBlock())) {
            if ($block = $this->getBlockContent($sectionBlock)) {
                $parts['category_block'] = $block;
            }
        }

        if (!isset($parts['producer']) && ($idProducerBlock = $producer?->getIdBlock())) {
            if ($block = $this->getBlockContent($idProducerBlock)) {
                $parts['producer_block'] = $block;
            }
        }

        return $parts;
    }

    public function createMetaDescription(?Section $section, ?Producer $producer, string $separator = ', '): string
    {
        if ($parts = $this->getMetaDescriptionParts($section, $producer)) {
            return StringUtil::cutToWords(join($separator, $parts), 160);
        }

        return $this->getTitle($section, $producer).' - '.\Settings::getDefault()->shop_description;
    }

    public function getDynamicTitleParts(DynamicFilterAttributes $dynamicFilter, \FilterParams $params, bool $includeProducer = true): array
    {
        if (!findModule(\Modules::INDEXED_FILTER)) {
            return [];
        }

        $variationNames = [];
        $parameterNames = [];
        $producerNames = [];

        // Variation names
        foreach ($params->getVariations() as $labelId => $values) {
            if (isset($dynamicFilter['variations'][$labelId])
                && $dynamicFilter['variations'][$labelId]['to_title'] == 'Y') {
                foreach ($values as $value) {
                    $data = sqlQueryBuilder()->select('pvcv.id')
                        ->from('products_variations_choices_values', 'pvcv')
                        ->where(Operator::equals(['pvcv.id' => $value]))
                        ->andWhere(Translation::coalesceTranslatedFields(VariationsValuesTranslation::class, ['value', 'title']))
                        ->execute()->fetch();
                    if ($data) {
                        $variationNames[] = (!empty($data['title']) ? $data['title'] : $data['value']);
                    }
                }
            }
        }

        // Parameter names
        foreach ($params->getParameters() as $id => $parameter) {
            if (isset($dynamicFilter['parameters'][$id])
                && $dynamicFilter['parameters'][$id]['to_title'] == 'Y') {
                if ($parameter['type'] == \Filter::PARAM_LIST) {
                    foreach ($parameter['values'] as $parameterListId) {
                        $data = sqlQueryBuilder()->select('pl.id')
                            ->from('parameters_list', 'pl')->where(Operator::equals(['pl.id' => $parameterListId]))
                            ->andWhere(Translation::coalesceTranslatedFields(ParametersListTranslation::class, ['value', 'title']))
                            ->execute()->fetch();
                        if ($data) {
                            $parameterNames[$id][] = (!empty($data['title']) ? $data['title'] : $data['value']);
                        }
                    }
                }
            }
        }

        if ($includeProducer
            && ($dynamicFilter['producers'] ?? false)
            && ($dynamicFilter['producers']['to_title'] ?? false) == 'Y'
        ) {
            foreach ($params->getProducers() as $producer) {
                $data = sqlQueryBuilder()->select('pr.id')
                    ->from('producers', 'pr')->where(Operator::equals(['pr.id' => $producer]))
                    ->andWhere(Translation::joinTranslatedFields(ProducersTranslation::class, function (QueryBuilder $qb, $columnName, $translatedField) {}, ['name', 'title']))
                    ->execute()->fetch();
                if ($data) {
                    $producerNames[] = (!empty($data['title']) ? $data['title'] : $data['name']);
                }
            }
        }

        foreach ($params->getVariationsSizeConvertion() as $labelId => $convertor) {
            if (($dynamicFilter['variations'][$labelId]['to_title'] ?? false) == 'Y') {
                foreach ($convertor['value'] as $paramId) {
                    $data = sqlQueryBuilder()->select('pl.id')
                        ->from('parameters_list', 'pl')->where(Operator::equals(['pl.id' => $paramId]))
                        ->andWhere(Translation::coalesceTranslatedFields(ParametersListTranslation::class, ['value', 'title']))
                        ->execute()->fetch();
                    if ($data) {
                        $variationNames[] = (!empty($data['title']) ? $data['title'] : $data['value']);
                    }
                }
            }
        }

        return compact('variationNames', 'parameterNames', 'producerNames');
    }

    public function createDynamicTitle(DynamicFilterAttributes $dynamicFilter, \FilterParams $params, bool $includeProducer = true): string
    {
        list(
            'variationNames' => $variationNames,
            'parameterNames' => $parameterNames,
            'producerNames' => $producerNames,
        ) = $this->getDynamicTitleParts($dynamicFilter, $params, $includeProducer);

        $parametersTitle = '';
        foreach ($parameterNames as $values) {
            $parametersTitle .= ' '.join(', ', $values);
        }

        $title = '';
        $title .= !empty($producerNames) ? ' '.join(', ', $producerNames) : '';
        $title .= $parametersTitle;
        $title .= !empty($variationNames) ? ' '.join(', ', $variationNames) : '';

        return $title;
    }

    public function createTitle(
        ?Section $section,
        ?Producer $producer,
        \FilterParams $filterParams,
        DynamicFilterAttributes $filterConfig,
        bool $useSeo = true,
    ): string {
        $categoryMeta = $this->getTitle($section, $producer, useSeo: $useSeo);
        $categoryDynamic = $this->createDynamicTitle(
            dynamicFilter: $filterConfig,
            params: $filterParams,
            includeProducer: $producer !== null,
        );

        return $categoryMeta.$categoryDynamic;
    }
}
