<?php

declare(strict_types=1);

namespace App\Framework\Search;

/**
 * Represents a search query with all parameters
 */
final readonly class SearchQuery
{
    /**
     * @param array<string, SearchFilter> $filters
     * @param array<string, float> $boosts
     * @param array<string> $fields
     * @param array<string> $highlightFields
     */
    public function __construct(
        public string $entityType,
        public string $query,
        public array $filters = [],
        public array $boosts = [],
        public array $fields = [],
        public array $highlightFields = [],
        public int $limit = 20,
        public int $offset = 0,
        public SearchSortBy $sortBy = new SearchSortBy(),
        public bool $enableHighlighting = true,
        public bool $enableFuzzyMatching = false,
        public float $minScore = 0.0
    ) {
    }

    public function hasFilters(): bool
    {
        return ! empty($this->filters);
    }

    public function hasBoosts(): bool
    {
        return ! empty($this->boosts);
    }

    public function hasFieldRestriction(): bool
    {
        return ! empty($this->fields);
    }

    public function withLimit(int $limit): self
    {
        return new self(
            $this->entityType,
            $this->query,
            $this->filters,
            $this->boosts,
            $this->fields,
            $this->highlightFields,
            $limit,
            $this->offset,
            $this->sortBy,
            $this->enableHighlighting,
            $this->enableFuzzyMatching,
            $this->minScore
        );
    }

    public function withOffset(int $offset): self
    {
        return new self(
            $this->entityType,
            $this->query,
            $this->filters,
            $this->boosts,
            $this->fields,
            $this->highlightFields,
            $this->limit,
            $offset,
            $this->sortBy,
            $this->enableHighlighting,
            $this->enableFuzzyMatching,
            $this->minScore
        );
    }

    public function withFilter(string $field, SearchFilter $filter): self
    {
        $filters = $this->filters;
        $filters[$field] = $filter;

        return new self(
            $this->entityType,
            $this->query,
            $filters,
            $this->boosts,
            $this->fields,
            $this->highlightFields,
            $this->limit,
            $this->offset,
            $this->sortBy,
            $this->enableHighlighting,
            $this->enableFuzzyMatching,
            $this->minScore
        );
    }

    public function withBoost(string $field, float $boost): self
    {
        $boosts = $this->boosts;
        $boosts[$field] = $boost;

        return new self(
            $this->entityType,
            $this->query,
            $this->filters,
            $boosts,
            $this->fields,
            $this->highlightFields,
            $this->limit,
            $this->offset,
            $this->sortBy,
            $this->enableHighlighting,
            $this->enableFuzzyMatching,
            $this->minScore
        );
    }

    public function withSortBy(SearchSortBy $sortBy): self
    {
        return new self(
            $this->entityType,
            $this->query,
            $this->filters,
            $this->boosts,
            $this->fields,
            $this->highlightFields,
            $this->limit,
            $this->offset,
            $sortBy,
            $this->enableHighlighting,
            $this->enableFuzzyMatching,
            $this->minScore
        );
    }

    public function toArray(): array
    {
        return [
            'entity_type' => $this->entityType,
            'query' => $this->query,
            'filters' => array_map(fn ($filter) => $filter->toArray(), $this->filters),
            'boosts' => $this->boosts,
            'fields' => $this->fields,
            'highlight_fields' => $this->highlightFields,
            'limit' => $this->limit,
            'offset' => $this->offset,
            'sort_by' => $this->sortBy->toArray(),
            'enable_highlighting' => $this->enableHighlighting,
            'enable_fuzzy_matching' => $this->enableFuzzyMatching,
            'min_score' => $this->minScore,
        ];
    }
}
