<?php

declare(strict_types=1);

namespace KupShop\GraphQLBundle\ApiPublic\Controller;

use Elastica\Query\Ids;
use Elastica\Result;
use KupShop\AdminBundle\Util\SystemImageUtils;
use KupShop\CatalogBundle\Search\Exception\FulltextException;
use KupShop\CatalogBundle\Search\FulltextElastic;
use KupShop\CatalogElasticBundle\Util\ElasticaFactory;
use KupShop\CatalogElasticBundle\Util\ElasticQueryClient;
use KupShop\GraphQLBundle\ApiAdmin\Annotation\Module;
use KupShop\GraphQLBundle\ApiPublic\Types\Search\AutocompleteProductResultItem;
use KupShop\GraphQLBundle\ApiPublic\Types\Search\AutocompleteResult;
use KupShop\GraphQLBundle\ApiPublic\Types\Search\AutocompleteResultItem;
use KupShop\I18nBundle\Util\PriceConverter;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\Contexts;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use TheCodingMachine\GraphQLite\Annotations\Query;

class SearchController
{
    private const RESULTS_COUNT = 6;

    private const ALLOWED_PRESEARCH_TYPES = [
        FulltextElastic::INDEX_PRODUCTS,
        FulltextElastic::INDEX_SECTIONS,
        FulltextElastic::INDEX_PRODUCERS,
    ];

    public function __construct(
        private readonly FulltextElastic $fulltext,
        private readonly SystemImageUtils $systemImageUtils,
        private readonly ?ElasticaFactory $searchClientFactory,
        private readonly ?ElasticQueryClient $elasticQueryClient,
        private readonly CurrencyContext $currencyContext,
        private readonly PriceConverter $priceConverter,
    ) {
    }

    /**
     * @param string[] $types
     *
     * @return \Generator<AutocompleteResult>
     *
     * @throws FulltextException
     */
    #[Query]
    #[Module(\Modules::PRODUCTS_SECTIONS, \Modules::SUB_ELASTICSEARCH)]
    public function autocomplete(string $term, array $types): \Generator
    {
        $invalidTypes = array_diff($types, $this->fulltext->getIndexTypes());
        if ($invalidTypes) {
            throw new \Exception('Invalid types:'.implode(', ', $invalidTypes));
        }

        $multiSearch = $this->searchClientFactory->createMultiSearch();
        foreach ($types as $searchTopic) {
            $pager = new \Pager();
            $pager->setOnPage(self::RESULTS_COUNT);
            $pager->setPageNumber(1);

            $search = $this->elasticQueryClient->createGenericSearch(
                searchTerm: $term,
                index: $searchTopic,
                pager: $pager,
            );

            $multiSearch->addSearch($search, $searchTopic);
        }

        $response = $multiSearch->search();
        foreach ($response->getResultSets() as $type => $result) {
            $items = array_map(
                static fn (Result $r) => array_merge($r->getSource(), ['id' => $r->getId()]),
                $result->getResults(),
            );

            $items = $this->processFulltextItems($type, $items);
            yield new AutocompleteResult($type, $items, $result->getTotalHits());
        }
    }

    /**
     * @param string[] $types
     *
     * @return \Generator<AutocompleteResult>
     *
     * @throws BadRequestHttpException
     */
    #[Query]
    #[Module(\Modules::PRODUCTS_SECTIONS, \Modules::SUB_ELASTICSEARCH)]
    public function presearch(array $types): \Generator
    {
        $dbcfg = \Settings::getDefault();
        $config = $dbcfg->fulltext['preload'] ?? [];

        if ($invalidTypes = array_diff($types, static::ALLOWED_PRESEARCH_TYPES)) {
            throw new BadRequestHttpException('Invalid types: '.implode(', ', $invalidTypes));
        }

        $multiSearch = $this->searchClientFactory->createMultiSearch();
        foreach ($types as $type) {
            if (empty($config[$type])) {
                continue;
            }

            $query = new \KupShop\CatalogElasticBundle\Elastica\Query();
            $query->setQuery(new Ids($config[$type]));

            $search = $this->searchClientFactory->createSearch($type)->setQuery($query);
            $multiSearch->addSearch($search, $type);
        }

        if (empty($multiSearch->getSearches())) {
            return yield from [];
        }

        $resultSet = $multiSearch->search();

        foreach ($resultSet->getResultSets() as $type => $result) {
            $items = $this->processFulltextItems($type, array_map(fn (Result $x) => ['id' => $x->getId(), ...$x->getSource()], $result->getResults()));
            yield new AutocompleteResult($type, $items, 0);
        }
    }

    protected function processFulltextItems(string $type, array $items)
    {
        $cfg = \Settings::getDefault();
        $lang = Contexts::get(LanguageContext::class)->getActiveId();
        $defaultImagePath = $this->systemImageUtils->getSystemImagePlaceholderPath();
        if (!str_starts_with($defaultImagePath, '/')) {
            $defaultImagePath = '/'.$defaultImagePath;
        }

        if (!is_array($cfg['Photo']['placeholder'] ?? null)) {
            $placeholder = getVal('placeholder', $cfg['Photo'], $defaultImagePath);
        }

        if (empty($placeholder)) {
            $placeholder = $cfg['Photo']['placeholder']['lang'][$lang] ?? $cfg['Photo']['placeholder']['default'] ?? $defaultImagePath;
        }

        if ($type == FulltextElastic::INDEX_PRODUCTS) {
            foreach ($items as $item) {
                yield new AutocompleteProductResultItem($type, $item, $placeholder, $this->currencyContext, $this->priceConverter);
            }

            return;
        }

        foreach ($items as $item) {
            yield new AutocompleteResultItem($type, $item, $placeholder);
        }
    }
}
