# This file was auto-generated by Fern from our API Definition.

import typing
from json.decoder import JSONDecodeError

from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.http_response import AsyncHttpResponse, HttpResponse
from ..core.jsonable_encoder import jsonable_encoder
from ..core.pydantic_utilities import parse_obj_as
from ..core.request_options import RequestOptions
from ..core.serialization import convert_and_respect_annotation_metadata
from ..errors.bad_request_error import BadRequestError
from ..errors.forbidden_error import ForbiddenError
from ..errors.not_found_error import NotFoundError
from ..types.bad_request_error_body import BadRequestErrorBody
from ..types.corpus_key import CorpusKey
from ..types.error import Error
from ..types.field_query import FieldQuery
from ..types.metadata_query_response import MetadataQueryResponse
from ..types.not_found_error_body import NotFoundErrorBody
from .types.metadata_query_request_level import MetadataQueryRequestLevel

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class RawMetadataClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper

    def query_metadata(
        self,
        corpus_key: CorpusKey,
        *,
        queries: typing.Sequence[FieldQuery],
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        level: typing.Optional[MetadataQueryRequestLevel] = OMIT,
        metadata_filter: typing.Optional[str] = OMIT,
        limit: typing.Optional[int] = OMIT,
        offset: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[MetadataQueryResponse]:
        """
        Query for documents in a specific corpus using fuzzy matching across specified metadata fields. The search first applies any exact metadata filters to narrow the results, then performs fuzzy matching on the remaining documents using the specified field queries.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to search for metadata.

        queries : typing.Sequence[FieldQuery]
            List of field-specific queries to apply fuzzy matching.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        level : typing.Optional[MetadataQueryRequestLevel]
            Whether to search document-level or part-level metadata. Document-level returns unique documents, part-level can return multiple parts from the same document.

        metadata_filter : typing.Optional[str]
            Optional filter expression to narrow down results before fuzzy matching is applied.
            This uses the same expression format as document listing filters and applies exact matching.

        limit : typing.Optional[int]
            Sets the maximum number of documents to return.

        offset : typing.Optional[int]
            Starting position for pagination.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[MetadataQueryResponse]
            List of matching documents with relevance scores and field-level match details.
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/metadata_query",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "level": level,
                "queries": convert_and_respect_annotation_metadata(
                    object_=queries, annotation=typing.Sequence[FieldQuery], direction="write"
                ),
                "metadata_filter": metadata_filter,
                "limit": limit,
                "offset": offset,
            },
            headers={
                "content-type": "application/json",
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    MetadataQueryResponse,
                    parse_obj_as(
                        type_=MetadataQueryResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)


class AsyncRawMetadataClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def query_metadata(
        self,
        corpus_key: CorpusKey,
        *,
        queries: typing.Sequence[FieldQuery],
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        level: typing.Optional[MetadataQueryRequestLevel] = OMIT,
        metadata_filter: typing.Optional[str] = OMIT,
        limit: typing.Optional[int] = OMIT,
        offset: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[MetadataQueryResponse]:
        """
        Query for documents in a specific corpus using fuzzy matching across specified metadata fields. The search first applies any exact metadata filters to narrow the results, then performs fuzzy matching on the remaining documents using the specified field queries.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to search for metadata.

        queries : typing.Sequence[FieldQuery]
            List of field-specific queries to apply fuzzy matching.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        level : typing.Optional[MetadataQueryRequestLevel]
            Whether to search document-level or part-level metadata. Document-level returns unique documents, part-level can return multiple parts from the same document.

        metadata_filter : typing.Optional[str]
            Optional filter expression to narrow down results before fuzzy matching is applied.
            This uses the same expression format as document listing filters and applies exact matching.

        limit : typing.Optional[int]
            Sets the maximum number of documents to return.

        offset : typing.Optional[int]
            Starting position for pagination.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[MetadataQueryResponse]
            List of matching documents with relevance scores and field-level match details.
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/metadata_query",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "level": level,
                "queries": convert_and_respect_annotation_metadata(
                    object_=queries, annotation=typing.Sequence[FieldQuery], direction="write"
                ),
                "metadata_filter": metadata_filter,
                "limit": limit,
                "offset": offset,
            },
            headers={
                "content-type": "application/json",
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    MetadataQueryResponse,
                    parse_obj_as(
                        type_=MetadataQueryResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
