"""Data models for records search operations."""

from __future__ import annotations

from typing import Any

from msgspec import field

from pinecone.models._base import BaseModel
from pinecone.models._display import render_table


class Hit(BaseModel, kw_only=True):
    """A single search result from a records search query.

    Attributes:
        id: Unique record identifier.
        score: Relevance score for this result.
        fields: Record fields returned with this hit.
    """

    id: str = field(name="_id")
    score: float = field(name="_score")
    fields: dict[str, Any] = field(default_factory=dict)

    def __repr__(self) -> str:
        return f"Hit(id={self.id!r}, score={self.score})"

    def _repr_pretty_(self, p: Any, cycle: bool) -> None:
        """IPython pretty-printer support."""
        if cycle:
            p.text("Hit(...)")
            return

        p.text("Hit(")
        with p.group(2, "", ")"):
            p.breakable()
            p.text(f"id={self.id!r},")
            p.breakable()
            p.text(f"score={self.score}")
            if self.fields:
                p.breakable()
                p.text(f"fields={self.fields!r}")


class SearchUsage(BaseModel, kw_only=True):
    """Usage information for a records search operation.

    Attributes:
        read_units: Number of read units consumed.
        embed_total_tokens: Total tokens used for embedding (if applicable).
        rerank_units: Number of rerank units consumed (if applicable).
    """

    read_units: int
    embed_total_tokens: int | None = None
    rerank_units: int | None = None


class _SearchResult(BaseModel, kw_only=True):
    """Internal wrapper for the search result hits array.

    Attributes:
        hits: List of matching records.
    """

    hits: list[Hit]


class SearchRecordsResponse(BaseModel, kw_only=True):
    """Response from a records search query.

    Attributes:
        result: Search result containing hits.
        usage: Usage statistics for the search operation.

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

    result: _SearchResult
    usage: SearchUsage

    @property
    def hits(self) -> list[Hit]:
        """Convenience accessor for search result hits.

        Returns:
            List of matching records.
        """
        return self.result.hits

    def __repr__(self) -> str:
        return f"SearchRecordsResponse(hits={len(self.result.hits)}, usage={self.usage!r})"

    def __dir__(self) -> list[str]:
        """Return public API, hiding internal attributes."""
        attrs = set(super().__dir__())
        public = {name for name in attrs if not name.startswith("_")}
        return sorted(public)

    def _repr_pretty_(self, p: Any, cycle: bool) -> None:
        """IPython pretty-printer support."""
        if cycle:
            p.text("SearchRecordsResponse(...)")
            return

        p.text("SearchRecordsResponse(")
        with p.group(2, "", ")"):
            p.breakable()
            p.text(f"hits={len(self.result.hits)},")
            p.breakable()
            p.text(f"usage={self.usage!r}")

    def _repr_html_(self) -> str:
        """Jupyter notebook HTML representation."""
        rows: list[tuple[str, str | int]] = [
            ("Hits:", len(self.result.hits)),
            ("Read Units:", self.usage.read_units),
        ]

        if self.usage.embed_total_tokens is not None:
            rows.append(("Embed Tokens:", self.usage.embed_total_tokens))
        if self.usage.rerank_units is not None:
            rows.append(("Rerank Units:", self.usage.rerank_units))

        return render_table("SearchRecordsResponse", rows)
