<?php

namespace KupShop\CatalogElasticBundle\Util;

use Elastica\Aggregation as Agg;
use Elastica\Query as Q;
use Elastica\Query\BoolQuery;
use KupShop\CatalogBundle\Enum\InStore;
use KupShop\CatalogElasticBundle\Elastica\Query;
use KupShop\RestrictionsBundle\Utils\Restrictions;

readonly class ElasticSectionsUtil
{
    protected const MAX_SECTION_COUNT = 10_000;

    public function __construct(
        protected ElasticaFactory $clientFactory,
        protected ElasticSpecs $elasticSpecs,
        protected ?Restrictions $restrictions,
    ) {
    }

    /** @return array{ count?: int, in_stock_count?: int, in_stock_suppliers_count?: int }[] */
    public function fetchProductCounts(array $sectionIds): array
    {
        $query = new Query();
        $query->setSource(false);
        $query->setQuery($filters = new BoolQuery());

        $filterParams = \FilterParams::createDefault();
        $filterParams->setSectionsFilter($sectionIds);
        foreach ($this->elasticSpecs->getFilters($filterParams) as $filter) {
            $filters->addFilter($filter);
        }

        if (findModule(\Modules::RESTRICTIONS)) {
            $this->elasticSpecs->applyRestrictionFilters($this->restrictions->getRestrictionFilterParams(), $filters);
        }

        $sectionsAgg = (new Agg\Terms('count'))
            ->setField('filter_section_recursive')
            ->setSize(self::MAX_SECTION_COUNT);

        // Count all (visible) products in section
        $query->addAggregation($sectionsAgg)
            // In products in_store (exclusive)
            ->addAggregation(
                (new Agg\Filter('in_stock_count'))
                    ->setFilter(new Q\Term(['in_store' => InStore::InStore->value]))
                    ->addAggregation($sectionsAgg),
            )
            // In products in_store at suppliers (exclusive) - will be summed up in self::createSectionFromArray
            ->addAggregation(
                (new Agg\Filter('in_stock_suppliers_count'))
                    ->setFilter(new Q\Term(['in_store' => InStore::InStoreSupplier->value]))
                    ->addAggregation($sectionsAgg),
            );

        $search = $this->clientFactory->createSearch();
        $search->setQuery($query);

        $searchResult = $search->search();

        $result = [];
        foreach ($searchResult->getAggregations() as $name => $aggregation) {
            if ($innerAgg = $aggregation['count'] ?? null) {
                foreach ($innerAgg['buckets'] as $bucket) {
                    $result[$bucket['key']][$name] = $bucket['doc_count'];
                }

                continue;
            }

            foreach ($aggregation['buckets'] as $bucket) {
                $result[$bucket['key']][$name] = $bucket['doc_count'];
            }
        }

        return $result;
    }
}
