<?php

declare(strict_types=1);

namespace KupShop\ComponentsBundle\Twig\Components\Search;

use Elastica\Multi\Search as MultiSearch;
use Elastica\Result;
use Elastica\ResultSet;
use KupShop\CatalogBundle\ProductList\Builder\FilteredProductListInterface;
use KupShop\CatalogBundle\Search\FulltextElastic;
use KupShop\CatalogBundle\View\CategoryRequestHandler;
use KupShop\CatalogElasticBundle\Contracts\ElasticSectionEventDispatcher;
use KupShop\CatalogElasticBundle\Contracts\SectionResponseInterface;
use KupShop\CatalogElasticBundle\Util\ElasticQueryClient;
use KupShop\ComponentsBundle\Attributes\Component;
use KupShop\ComponentsBundle\Attributes\Version;
use KupShop\ComponentsBundle\GTM\PostMount\GTMViewData;
use KupShop\ComponentsBundle\Twig\Components\FilteredProductList;
use KupShop\GTMBundle\PageType\SearchPageType;
use Symfony\Contracts\Service\Attribute\Required;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
use Symfony\UX\TwigComponent\Attribute\PostMount;

#[AsTwigComponent(template: '@Components/components/Search/View/View.1.html.twig')]
#[Component(1, [
    new Version(1, newJs: null),
])]
class View extends FilteredProductList
{
    use GTMViewData;
    #[Required]
    public SearchPageType $gtmClass;

    private const SEARCH_SETTINGS = [
        FulltextElastic::INDEX_ARTICLES => ['fields' => []],
    ];

    private FulltextElastic $fulltextElastic;
    private ElasticQueryClient $elasticQueryClient;

    private bool $searchInjected = false;

    public function mount(): void
    {
        $this->baseFilterParams = \FilterParams::createDefault();
        $this->baseFilterParams->setSectionsFilter([CategoryRequestHandler::SEARCH_CATEGORY_ID]);
    }

    public function getArticleIds(): array
    {
        $results = $this->getSearchResult(FulltextElastic::INDEX_ARTICLES)->getResults();

        return array_map(fn (Result $result) => (int) $result->getId(), $results);
    }

    public function getSections(): array
    {
        $results = $this->getSearchResult(FulltextElastic::INDEX_SECTIONS)->getResults();

        return array_map(fn (Result $result) => $result->getSource(), $results);
    }

    public function getProducers(): array
    {
        $results = $this->getSearchResult(FulltextElastic::INDEX_PRODUCERS)->getResults();

        return array_map(fn (Result $result) => $result->getSource() + ['id' => $result->getId()], $results);
    }

    public function getPages(): array
    {
        $results = $this->getSearchResult(FulltextElastic::INDEX_PAGES)->getResults();

        return array_map(fn (Result $result) => $result->getSource(), $results);
    }

    #[\Override]
    protected function getResult(): FilteredProductListInterface&SectionResponseInterface
    {
        $result = parent::getResult();

        if (!$this->searchInjected) {
            $searches = [];
            foreach ($this->fulltextElastic->getIndexTypes() as $searchTopic) {
                if ($searchTopic === FulltextElastic::INDEX_PRODUCTS) {
                    continue;
                }

                $options = self::SEARCH_SETTINGS[$searchTopic] ?? [];
                $search = $this->elasticQueryClient->createGenericSearch(
                    ...$options,
                    searchTerm: $this->getSearchTerm(),
                    index: $searchTopic,
                );

                $searches[$searchTopic] = $search;
            }

            if (!($result instanceof ElasticSectionEventDispatcher)) {
                throw new \RuntimeException(sprintf(
                    'Could not inject other search queries into ES request(%s).',
                    \get_class($result),
                ));
            }

            $result->onBeforeExecute(function (MultiSearch $multiSearch) use ($searches) {
                $multiSearch->addSearches($searches);
            });

            $this->searchInjected = true;
        }

        return $result;
    }

    private function getSearchResult(string $resultSetName): ResultSet
    {
        return $this->getResult()->getResponse()[$resultSetName];
    }

    public function getSearchTerm(): string
    {
        return $this->filterParams->getSearch();
    }

    #[Required]
    final public function _setFulltextElastic(FulltextElastic $fulltextElastic): void
    {
        $this->fulltextElastic = $fulltextElastic;
    }

    #[Required]
    final public function _setElasticQueryClient(ElasticQueryClient $elasticQueryClient): void
    {
        $this->elasticQueryClient = $elasticQueryClient;
    }

    public function getGTMPlaceholders(): array
    {
        return ['item_list_name' => 'search: '.$this->getSearchTerm(), 'item_list_id' => ''];
    }

    #[PostMount]
    public function addGTMDataAttribute(array $componentsData): array
    {
        if ($gtmData = $this->fetchGTMData()) {
            $componentsData['data-tracking-default'] = htmlentities(json_encode($gtmData, JSON_NUMERIC_CHECK | JSON_HEX_APOS));
        }

        if ($this->getSearchTerm()) {
            $results = [];
            $products = [];
            $this->getProducts()->fetchProducers();
            $this->getProducts()->forAll(function ($key, $value) use (&$products) {
                $products[] = [
                    'id' => $value->id,
                    'name' => $value->title,
                    'price' => $value->getProductPrice()->getPriceWithoutVat(),
                    'brand' => $value->producer['name'] ?? null,
                ];

                return true;
            });
            $results['products'] = $products;

            foreach ($this->getSections() as $section) {
                $results['categories'][] = [
                    'id' => $section['id'],
                    'name' => $section['title'],
                    'path' => $section['path'],
                    'label' => $section['title'],
                    'image_url' => $section['photo'],
                ];
            }

            foreach ($this->getArticleIds() as $articleId) {
                $results['articles'][] = ['id' => $articleId];
            }

            foreach ($this->getProducers() as $producer) {
                $results['producers'][] = [
                    'id' => $producer['id'],
                    'name' => $producer['title'],
                    'label' => $producer['title'],
                ];
            }

            foreach ($this->getPages() as $page) {
                $results['pages'][] = [
                    'id' => $page['id'],
                    'name' => $page['name'],
                    'path' => $page['url'],
                ];
            }

            $searchData = ['event' => 'search', 'search_term' => $this->getSearchTerm(), 'results' => $results];
            $componentsData['data-tracking-search'] = htmlentities(json_encode($searchData, JSON_NUMERIC_CHECK | JSON_HEX_APOS));
        }

        return $componentsData;
    }
}
