<?php

declare(strict_types=1);

namespace MonsieurBiz\SyliusSearchPlugin\Factory\ResultSet;

use Elastica\ResultSet as ElasticaResultSet;
use MonsieurBiz\SyliusSearchPlugin\Model\Document\Filter;
use MonsieurBiz\SyliusSearchPlugin\Model\Document\FilterAwareInterface;
use MonsieurBiz\SyliusSearchPlugin\Model\Document\FilterInterface;
use MonsieurBiz\SyliusSearchPlugin\Model\Document\FilterValue;
use MonsieurBiz\SyliusSearchPlugin\Model\Document\ResultSetFilter;
use Sylius\Component\Core\Model\TaxonInterface;

class TaxonFilterFactory implements TaxonFilterFactoryInterface
{
    public function create(ElasticaResultSet $resultSet = null, ?TaxonInterface $taxon = null): FilterAwareInterface
    {
        $filter = new ResultSetFilter();
        if (null !== $resultSet) {
            $filter->setFilter($this->buildTaxonFilter($resultSet, $taxon));
        }

        return $filter;
    }

    private function buildTaxonFilter(ElasticaResultSet $resultSet, ?TaxonInterface $taxon): ?FilterInterface
    {
        $aggregations = $resultSet->getAggregations();

        $taxonAggregation = $aggregations['taxons'] ?? null;

        if (null === $taxonAggregation || $taxonAggregation['doc_count'] <= 0) {
            return null;
        }

        // Get current taxon level to retrieve only greater levels, in search we will take only the first level
        $currentTaxonLevel = null !== $taxon ? $taxon->getLevel() : 0;

        // Get children taxon if we have current taxon
        $childrenTaxon = [];
        if (null !== $taxon) {
            foreach ($taxon->getChildren() as $child) {
                $childrenTaxon[$child->getCode()] = $child->getLevel();
            }
        }

        $filter = new Filter('taxon', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count']);

        // Get taxon code in aggregation
        $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? [];
        foreach ($taxonCodeBuckets as $taxonCodeBucket) {
            if (0 === $taxonCodeBucket['doc_count']) {
                continue;
            }
            $taxonCode = $taxonCodeBucket['key'];
            $taxonName = null;

            // Get taxon level in aggregation
            $taxonLevelBuckets = $taxonCodeBucket['levels']['buckets'] ?? [];
            foreach ($taxonLevelBuckets as $taxonLevelBucket) {
                $level = $taxonLevelBucket['key'];
                if ($level === ($currentTaxonLevel + 1) && (null === $taxon || isset($childrenTaxon[$taxonCode]))) {
                    // Get taxon name in aggregation
                    $taxonNameBuckets = $taxonLevelBucket['names']['buckets'] ?? [];
                    foreach ($taxonNameBuckets as $taxonNameBucket) {
                        $taxonName = $taxonNameBucket['key'];
                        $label = $taxonName ?? $taxonCode;
                        $filterValue = new FilterValue($label, \urlencode($label), $taxonCodeBucket['doc_count']);
                        $filter->addValue($filterValue);

                        break 2;
                    }
                }
            }
        }

        // Put taxon filter in first if contains value
        if (count($filter->getValues()) > 0) {
            return $filter;
        }

        return null;
    }
}
