"""Async search method for AsyncRecords."""

from __future__ import annotations

from typing import Any

import msgspec

from pinecone._internal.validation import require_in_range, require_non_empty
from pinecone.models.records.search import SearchRecordsResponse


_DECODER = msgspec.json.Decoder(SearchRecordsResponse)


async def search(
    self: Any,
    *,
    namespace: str,
    top_k: int,
    inputs: dict[str, str] | None = None,
    vector: list[float] | None = None,
    id: str | None = None,
    filter: dict[str, Any] | None = None,
    fields: list[str] | None = None,
    rerank: dict[str, Any] | None = None,
) -> SearchRecordsResponse:
    """Search records in a namespace.

    Search with text (for indexes with integrated embedding), a query
    vector, or an existing record ID. Optionally rerank results.

    Args:
        namespace (str): Target namespace to search.
        top_k (int): Number of results to return (1-10000).
        inputs (dict[str, str] | None): Text inputs for integrated embedding
            search, e.g., ``{"text": "search query"}``.
        vector (list[float] | None): Query vector values for direct
            vector search.
        id (str | None): ID of an existing record to use as the query.
        filter (dict[str, Any] | None): Metadata filter to narrow results.
        fields (list[str] | None): Fields to return in results. If not
            specified, all fields are returned.
        rerank (dict[str, Any] | None): Reranking configuration with
            ``model`` (str, required), ``rank_fields`` (list[str], required),
            and optionally ``top_n`` (int), ``query`` (str), and
            ``parameters`` (dict).

    Returns:
        SearchRecordsResponse containing hits and usage information.

    Raises:
        ValueError: If namespace is empty or top_k is out of range.
        PineconeApiException: If the API request fails.

    Examples:
        Search with text (integrated embedding):

        >>> response = await index.records.search(
        ...     namespace="my-ns",
        ...     top_k=10,
        ...     inputs={"text": "search query"},
        ... )
        >>> for hit in response.hits:
        ...     print(f"{hit.id}: {hit.score}")

        Search with a vector:

        >>> response = await index.records.search(
        ...     namespace="my-ns",
        ...     top_k=5,
        ...     vector=[0.1, 0.2, 0.3],
        ... )

        Search with reranking:

        >>> response = await index.records.search(
        ...     namespace="my-ns",
        ...     top_k=100,
        ...     inputs={"text": "query"},
        ...     rerank={"model": "bge-reranker-v2-m3", "rank_fields": ["text"], "top_n": 10},
        ... )
    """
    require_non_empty("namespace", namespace)
    require_in_range("top_k", top_k, 1, 10000)

    query: dict[str, Any] = {"top_k": top_k}
    if inputs is not None:
        query["inputs"] = inputs
    if vector is not None:
        query["vector"] = vector
    if id is not None:
        query["id"] = id
    if filter is not None:
        query["filter"] = filter

    body: dict[str, Any] = {"query": query}
    if fields is not None:
        body["fields"] = fields
    if rerank is not None:
        body["rerank"] = rerank

    data = await self._make_data_plane_request(
        "POST",
        f"/records/namespaces/{namespace}/search",
        body=body,
    )
    return _DECODER.decode(data)
