<?php

declare(strict_types=1);

namespace MonsieurBiz\SyliusSearchPlugin\Model\Config;

use MonsieurBiz\SyliusSearchPlugin\Exception\UnknownGridConfigType;
use Sylius\Component\Core\Model\TaxonInterface;
use Symfony\Component\HttpFoundation\Request;

class GridConfig implements GridConfigInterface
{
    private array $config;

    private bool $isInitialized = false;

    private ?string $type = null;

    private ?string $locale = null;

    private ?string $query = null;

    private ?int $page = null;

    /** @var int[] */
    private array $limits;

    private int $limit;

    /** @var string[]|null */
    private ?array $sorting = null;

    private ?TaxonInterface $taxon = null;

    private array $appliedFilters = [];

    private array $filters;

    public function __construct(array $config, array $filters)
    {
        $this->config = $config;
        $this->filters = $filters;
    }

    public function init(string $type, Request $request, ?TaxonInterface $taxon = null): void
    {
        if ($this->isInitialized) {
            return;
        }

        switch ($type) {
            case self::SEARCH_TYPE:
                // Set type, locale, page and query
                $this->type = $type;
                $this->locale = $request->getLocale();
                $this->page = max(1, (int) $request->get('page'));
                $this->query = htmlspecialchars(urldecode($request->get('query')));

                // Set sorting
                $availableSorting = $this->config['sorting']['search'] ?? [];
                $this->sorting = $this->cleanSorting($request->get('sorting'), $availableSorting);

                // Set limit
                $this->limit = max(1, (int) $request->get('limit'));
                $this->limits = $this->config['limits']['search'] ?? [];
                if (!\in_array($this->limit, $this->limits, true)) {
                    $this->limit = $this->config['default_limit']['search'] ?? self::FALLBACK_LIMIT;
                }

                // Set applied filters
                $this->appliedFilters = $request->get('attribute') ?? [];

                $priceFilter = $request->get('price');
                if (null !== $priceFilter) {
                    $this->appliedFilters['price'] = $priceFilter;
                }

                $this->isInitialized = true;

                break;
            case self::TAXON_TYPE:
                // Set type, locale, page and taxon
                $this->type = $type;
                $this->locale = $request->getLocale();
                $this->page = max(1, (int) $request->get('page'));
                $this->taxon = $taxon;

                // Set sorting
                $availableSorting = $this->config['sorting']['taxon'] ?? [];
                $this->sorting = $this->cleanSorting($request->get('sorting'), $availableSorting);
                if (count($this->sorting) === 0) {
                    $this->sorting['position'] = self::SORT_ASC;
                }

                // Set applied filters
                $this->appliedFilters = $request->get('attribute') ?? [];

                $priceFilter = $request->get('price');
                if (null !== $priceFilter) {
                    $this->appliedFilters['price'] = $priceFilter;
                }

                // Set limit
                $this->limit = max(1, (int) $request->get('limit'));
                $this->limits = $this->config['limits']['taxon'] ?? [];
                if (!\in_array($this->limit, $this->limits, true)) {
                    $this->limit = $this->config['default_limit']['taxon'] ?? self::FALLBACK_LIMIT;
                }
                $this->isInitialized = true;

                break;
            case self::INSTANT_TYPE:
                // Set type, locale, page and query
                $this->type = $type;
                $this->locale = $request->getLocale();
                $this->page = 1;
                $this->query = htmlspecialchars(urldecode($request->get('query')));

                // Set limit
                $this->limit = $this->config['default_limit']['instant'] ?? self::FALLBACK_LIMIT;
                $this->isInitialized = true;

                break;
            default:
                throw new UnknownGridConfigType();
        }
    }

    public function getConfig(): array
    {
        return $this->config;
    }

    public function getType(): string
    {
        return $this->type;
    }

    public function getLocale(): string
    {
        return $this->locale;
    }

    public function getQuery(): string
    {
        return $this->query;
    }

    public function getPage(): int
    {
        return $this->page;
    }

    /**
     * @return int[]
     */
    public function getLimits(): array
    {
        return $this->limits;
    }

    public function getLimit(): int
    {
        return $this->limit;
    }

    /**
     * @return string[]
     */
    public function getSorting(): array
    {
        return $this->sorting;
    }

    public function haveToApplyManuallyFilters(): bool
    {
        return $this->config['filters']['apply_manually'] ?? false;
    }

    public function useMainTaxonForFilter(): bool
    {
        return $this->config['filters']['use_main_taxon'] ?? false;
    }

    /**
     * @return string[]
     */
    public function getFilters(): array
    {
        return $this->filters;
    }

    public function getAppliedFilters(): array
    {
        return $this->appliedFilters;
    }

    public function getTaxon(): ?TaxonInterface
    {
        return $this->taxon;
    }

    private function cleanSorting(?array $sorting, array $availableSorting): array
    {
        if (!\is_array($sorting)) {
            return  [];
        }

        foreach ($sorting as $field => $order) {
            if (!\in_array($field, $availableSorting, true) || !\in_array($order, [self::SORT_ASC, self::SORT_DESC], true)) {
                unset($sorting[$field]);
            }
        }

        return $sorting;
    }
}
