<?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\FiltersAwareInterface;
use MonsieurBiz\SyliusSearchPlugin\Model\Document\FilterValue;
use MonsieurBiz\SyliusSearchPlugin\Model\Document\ResultSetFilters;

class FiltersFactory implements FiltersFactoryInterface
{
    public function create(ElasticaResultSet $resultSet = null): FiltersAwareInterface
    {
        $filters = new ResultSetFilters();
        if (null !== $resultSet) {
            $filters->setFilters($this->buildFilters($resultSet));
        }

        return $filters;
    }

    private function buildFilters(ElasticaResultSet $resultSet): array
    {
        $aggregations = $resultSet->getAggregations();
        // No aggregation so don't perform filters
        if (0 === count($aggregations)) {
            return [];
        }

        $filters = [];

        // Retrieve filters labels in aggregations
        $attributes = [];
        $attributeAggregations = $aggregations['attributes'] ?? [];
        unset($attributeAggregations['doc_count']);
        $attributeCodeBuckets = $attributeAggregations['codes']['buckets'] ?? [];
        foreach ($attributeCodeBuckets as $attributeCodeBucket) {
            $attributeCode = $attributeCodeBucket['key'];
            $attributeNameBuckets = $attributeCodeBucket['names']['buckets'] ?? [];
            foreach ($attributeNameBuckets as $attributeNameBucket) {
                $attributeName = $attributeNameBucket['key'];
                $attributes[$attributeCode] = $attributeName;

                break;
            }
        }

        // Retrieve filters values in aggregations
        $filterAggregations = $aggregations['filters'] ?? [];
        unset($filterAggregations['doc_count']);
        foreach ($filterAggregations as $field => $aggregation) {
            if (0 === $aggregation['doc_count']) {
                continue;
            }
            $filter = new Filter($field, $attributes[$field] ?? $field, $aggregation['doc_count']);
            $buckets = $aggregation['values']['buckets'] ?? [];
            foreach ($buckets as $bucket) {
                if (isset($bucket['key'], $bucket['doc_count'])) {
                    $filterValue = new FilterValue($bucket['key'], \urlencode($bucket['key']), $bucket['doc_count']);
                    $filter->addValue($filterValue);
                }
            }
            $filters[] = $filter;
        }

        $this->sortFilters($filters);

        return $filters;
    }

    private function sortFilters(array &$filters): void
    {
        usort($filters, static function ($filter1, $filter2) {
            /** @var Filter $filter1 */
            /** @var Filter $filter2 */

            // If same count we display the filters with more values before
            if ($filter1->getCount() === $filter2->getCount()) {
                return \count($filter2->getValues()) > \count($filter1->getValues());
            }

            return $filter2->getCount() > $filter1->getCount();
        });
    }
}
