"""High-level search service wrapping any backend."""

from __future__ import annotations

import logging
from collections.abc import Awaitable, Callable
from typing import Any

from search.backends.base import SearchBackend
from search.models import (
    IndexConfig,
    SearchDocument,
    SearchQuery,
    SearchResponse,
)
from errors import NotFoundError, ValidationError

logger = logging.getLogger(__name__)


class SearchService:
    """Convenience layer over a ``SearchBackend``.

    Provides document lifecycle operations (index, search, delete, reindex)
    with logging and consistent error handling.
    """

    def __init__(self, backend: SearchBackend) -> None:
        self._backend = backend

    @property
    def backend(self) -> SearchBackend:
        """Access the underlying search backend."""
        return self._backend

    async def create_index(self, config: IndexConfig) -> None:
        """Create or update a search index."""
        logger.info("Creating index %s", config.name)
        await self._backend.create_index(config)

    async def delete_index(self, index_name: str) -> None:
        """Delete an index and all its documents."""
        logger.info("Deleting index %s", index_name)
        await self._backend.delete_index(index_name)

    async def index_document(self, index_name: str, doc: dict[str, Any]) -> None:
        """Index a single document into *index_name*.

        Args:
            index_name: Target index.
            doc: Must include an ``"id"`` key. Remaining keys are the content.
        """
        doc_id = str(doc["id"])
        content = {k: v for k, v in doc.items() if k != "id"}
        document = SearchDocument(id=doc_id, content=content, index_name=index_name)
        await self._backend.index(document)
        logger.debug("Indexed document %s in %s", doc_id, index_name)

    async def bulk_index(self, index_name: str, docs: list[dict[str, Any]]) -> int:
        """Batch-index multiple documents.

        Args:
            index_name: Target index.
            docs: Each dict must include an ``"id"`` key.

        Returns:
            Number of documents indexed.
        """
        documents = [
            SearchDocument(
                id=str(d["id"]),
                content={k: v for k, v in d.items() if k != "id"},
                index_name=index_name,
            )
            for d in docs
        ]
        count = await self._backend.bulk_index(documents)
        logger.info("Bulk indexed %d documents in %s", count, index_name)
        return count

    async def search(self, index_name: str, query: SearchQuery) -> SearchResponse:
        """Execute a search against *index_name*."""
        response = await self._backend.search(index_name, query)
        logger.debug(
            "Search '%s' in %s: %d results in %.1fms",
            query.query,
            index_name,
            response.total,
            response.query_time_ms,
        )
        return response

    async def delete_document(self, index_name: str, doc_id: str) -> bool:
        """Remove a document from the index. Returns ``True`` if it existed."""
        deleted = await self._backend.delete(index_name, doc_id)
        if deleted:
            logger.debug("Deleted document %s from %s", doc_id, index_name)
        return deleted

    async def reindex(
        self,
        index_name: str,
        fetch_fn: Callable[[], Awaitable[list[dict[str, Any]]]],
    ) -> int:
        """Full reindex: delete all documents and re-import from *fetch_fn*.

        Args:
            index_name: Target index.
            fetch_fn: Async callable returning a list of documents (each with ``"id"``).

        Returns:
            Number of documents indexed.
        """
        logger.info("Starting reindex of %s", index_name)
        await self._backend.delete_index(index_name)

        docs = await fetch_fn()
        if not docs:
            logger.info("Reindex of %s: no documents returned by fetch_fn", index_name)
            return 0

        documents = [
            SearchDocument(
                id=str(d["id"]),
                content={k: v for k, v in d.items() if k != "id"},
                index_name=index_name,
            )
            for d in docs
        ]
        count = await self._backend.bulk_index(documents)
        logger.info("Reindex of %s complete: %d documents", index_name, count)
        return count
