<?php

declare(strict_types=1);

namespace App\Framework\Search;

/**
 * Fluent builder for search queries
 */
final class SearchQueryBuilder
{
    private string $query = '*';

    private array $filters = [];

    private array $boosts = [];

    private array $fields = [];

    private array $highlightFields = [];

    private int $limit = 20;

    private int $offset = 0;

    private SearchSortBy $sortBy;

    private bool $enableHighlighting = true;

    private bool $enableFuzzyMatching = false;

    private float $minScore = 0.0;

    public function __construct(
        private readonly SearchEngine $engine,
        private readonly string $entityType
    ) {
        $this->sortBy = SearchSortBy::relevance();
    }

    /**
     * Set the search query string
     */
    public function query(string $query): self
    {
        $this->query = $query;

        return $this;
    }

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

        return $this;
    }

    /**
     * Add multiple filters at once
     */
    public function filters(array $filters): self
    {
        foreach ($filters as $field => $value) {
            if ($value instanceof SearchFilter) {
                $this->filters[$field] = $value;
            } else {
                $this->filters[$field] = SearchFilter::equals($value);
            }
        }

        return $this;
    }

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

        return $this;
    }

    /**
     * Restrict search to specific fields
     */
    public function fields(array $fields): self
    {
        $this->fields = $fields;

        return $this;
    }

    /**
     * Set fields to highlight
     */
    public function highlight(array $fields): self
    {
        $this->highlightFields = $fields;

        return $this;
    }

    /**
     * Set result limit
     */
    public function limit(int $limit): self
    {
        $this->limit = $limit;

        return $this;
    }

    /**
     * Set result offset for pagination
     */
    public function offset(int $offset): self
    {
        $this->offset = $offset;

        return $this;
    }

    /**
     * Set pagination (page-based)
     */
    public function page(int $page, int $perPage = 20): self
    {
        $this->limit = $perPage;
        $this->offset = ($page - 1) * $perPage;

        return $this;
    }

    /**
     * Sort by relevance (default)
     */
    public function sortByRelevance(): self
    {
        $this->sortBy = SearchSortBy::relevance();

        return $this;
    }

    /**
     * Sort by a field
     */
    public function sortBy(string $field, SearchSortDirection $direction = SearchSortDirection::ASC): self
    {
        $this->sortBy = SearchSortBy::field($field, $direction);

        return $this;
    }

    /**
     * Sort by multiple fields
     */
    public function sortByMultiple(SearchSortField ...$fields): self
    {
        $this->sortBy = SearchSortBy::multiple(...$fields);

        return $this;
    }

    /**
     * Enable/disable highlighting
     */
    public function highlighting(bool $enable): self
    {
        $this->enableHighlighting = $enable;

        return $this;
    }

    /**
     * Enable/disable fuzzy matching
     */
    public function fuzzy(bool $enable): self
    {
        $this->enableFuzzyMatching = $enable;

        return $this;
    }

    /**
     * Set minimum score threshold
     */
    public function minScore(float $minScore): self
    {
        $this->minScore = $minScore;

        return $this;
    }

    /**
     * Common filter shortcuts
     */
    public function where(string $field, mixed $value): self
    {
        return $this->filter($field, SearchFilter::equals($value));
    }

    public function whereIn(string $field, array $values): self
    {
        return $this->filter($field, SearchFilter::in($values));
    }

    public function whereRange(string $field, mixed $min, mixed $max): self
    {
        return $this->filter($field, SearchFilter::range($min, $max));
    }

    public function whereGreaterThan(string $field, mixed $value): self
    {
        return $this->filter($field, SearchFilter::greaterThan($value));
    }

    public function whereLessThan(string $field, mixed $value): self
    {
        return $this->filter($field, SearchFilter::lessThan($value));
    }

    public function whereContains(string $field, string $value): self
    {
        return $this->filter($field, SearchFilter::contains($value));
    }

    public function whereStartsWith(string $field, string $value): self
    {
        return $this->filter($field, SearchFilter::startsWith($value));
    }

    /**
     * Build and return the search query
     */
    public function build(): SearchQuery
    {
        return new SearchQuery(
            entityType: $this->entityType,
            query: $this->query,
            filters: $this->filters,
            boosts: $this->boosts,
            fields: $this->fields,
            highlightFields: $this->highlightFields,
            limit: $this->limit,
            offset: $this->offset,
            sortBy: $this->sortBy,
            enableHighlighting: $this->enableHighlighting,
            enableFuzzyMatching: $this->enableFuzzyMatching,
            minScore: $this->minScore
        );
    }

    /**
     * Build and execute the search query
     */
    public function search(): SearchResult
    {
        return $this->engine->search($this->build());
    }

    /**
     * Execute search and return only the first result
     */
    public function first(): ?SearchHit
    {
        $result = $this->limit(1)->search();

        return $result->hits[0] ?? null;
    }

    /**
     * Count total results without retrieving them
     */
    public function count(): int
    {
        $result = $this->limit(0)->search();

        return $result->total;
    }
}
