from collections.abc import Iterable, Iterator
from typing import Any, overload

from django.db import models
from django.db.models import QuerySet
from wagtail.search.query import SearchQuery

class FilterError(Exception): ...

class FieldError(Exception):
    field_name: str | None
    def __init__(self, *args: Any, field_name: str | None = None, **kwargs: Any) -> None: ...

class SearchFieldError(FieldError): ...
class FilterFieldError(FieldError): ...
class OrderByFieldError(FieldError): ...

class BaseSearchQueryCompiler:
    DEFAULT_OPERATOR: str
    HANDLES_ORDER_BY_EXPRESSIONS: bool
    queryset: QuerySet[models.Model]
    query: SearchQuery
    fields: list[str] | None
    order_by_relevance: bool
    def __init__(
        self,
        queryset: QuerySet[models.Model],
        query: SearchQuery | str | None,
        fields: list[str] | None = None,
        operator: str | None = None,
        order_by_relevance: bool = True,
    ) -> None: ...
    def check(self) -> None: ...

class BaseSearchResults:
    supports_facet: bool
    backend: BaseSearchBackend | None
    query_compiler: BaseSearchQueryCompiler | None
    prefetch_related: list[str] | None
    start: int
    stop: int | None
    model: type[models.Model] | None
    def __init__(
        self,
        backend: BaseSearchBackend | None,
        query_compiler: BaseSearchQueryCompiler | None,
        prefetch_related: list[str] | None = None,
    ) -> None: ...
    def results(self) -> list[models.Model]: ...
    def count(self) -> int: ...
    @overload
    def __getitem__(self, key: slice) -> BaseSearchResults: ...
    @overload
    def __getitem__(self, key: int) -> models.Model: ...
    def __iter__(self) -> Iterator[models.Model]: ...
    def __len__(self) -> int: ...
    def annotate_score(self, field_name: str) -> BaseSearchResults: ...
    def facet(self, field_name: str) -> dict[str, int]: ...

class EmptySearchResults(BaseSearchResults):
    def __init__(self) -> None: ...

class BaseIndex:
    def __init__(self, backend: BaseSearchBackend) -> None: ...
    def get_key(self) -> str: ...
    def add_model(self, model: type) -> None: ...
    def refresh(self) -> None: ...
    def reset(self) -> None: ...
    def add_item(self, obj: models.Model) -> None: ...
    def add_items(self, model: type, items: Iterable[models.Model]) -> None: ...
    def delete_item(self, item: models.Model) -> None: ...

class BaseSearchBackend:
    query_compiler_class: type[BaseSearchQueryCompiler] | None
    autocomplete_query_compiler_class: type[BaseSearchQueryCompiler] | None
    results_class: type[BaseSearchResults] | None
    rebuilder_class: type | None
    index_class: type[BaseIndex]
    catch_indexing_errors: bool
    def __init__(self, params: dict[str, Any]) -> None: ...
    def get_index_for_model(self, model: type) -> BaseIndex: ...
    def get_index_for_object(self, obj: models.Model) -> BaseIndex: ...
    def all_indexes(self) -> list[BaseIndex]: ...
    def reset_indexes(self) -> None: ...
    def refresh_indexes(self) -> None: ...
    def add(self, obj: models.Model) -> None: ...
    def add_bulk(self, model: type, obj_list: Iterable[models.Model]) -> None: ...
    def delete(self, obj: models.Model) -> None: ...
    def search(
        self,
        query: SearchQuery | str | None,
        model_or_queryset: type | QuerySet[models.Model],
        fields: list[str] | None = None,
        operator: str | None = None,
        order_by_relevance: bool = True,
    ) -> BaseSearchResults: ...
    def autocomplete(
        self,
        query: SearchQuery | str | None,
        model_or_queryset: type | QuerySet[models.Model],
        fields: list[str] | None = None,
        operator: str | None = None,
        order_by_relevance: bool = True,
    ) -> BaseSearchResults: ...

def get_model_root(model: type[models.Model]) -> type[models.Model]: ...
