"""Стандартный API retrieval по RAG-сессии: эмбеддинг запроса и поиск по плану слоёв."""

from __future__ import annotations

import asyncio
from dataclasses import dataclass, field
from typing import TYPE_CHECKING

from app.core.rag.embedding.gigachat_embedder import GigaChatEmbedder

if TYPE_CHECKING:
    from app.core.rag.persistence.repository import RagRepository


@dataclass(slots=True)
class RetrievalPlan:
    """Параметры одного запроса к хранилищу: профиль, список слоёв и лимит строк."""

    profile: str
    layers: list[str]
    limit: int
    filters: dict[str, object] = field(default_factory=dict)


class RagSessionRetriever:
    """Выполняет retrieve через :class:`RagRepository` и эмбеддер; единая точка поиска по сессии."""

    def __init__(self, repository: RagRepository, embedder: GigaChatEmbedder) -> None:
        self._repository = repository
        self._embedder = embedder

    async def retrieve(self, rag_session_id: str, query_text: str, plan: RetrievalPlan) -> list[dict]:
        """Возвращает сырые строки чанков (как в RagRepository.retrieve), с полем layer."""
        if not plan.layers:
            return []
        query_embedding = await asyncio.to_thread(self._embed_query, query_text)
        kwargs = self._map_filters(plan.filters)
        return await asyncio.to_thread(
            self._repository.retrieve,
            rag_session_id,
            query_embedding,
            query_text=query_text,
            layers=plan.layers,
            limit=plan.limit,
            **kwargs,
        )

    async def retrieve_exact_files(
        self,
        rag_session_id: str,
        *,
        paths: list[str],
        layers: list[str] | None = None,
        limit: int = 200,
    ) -> list[dict]:
        return await asyncio.to_thread(
            self._repository.retrieve_exact_files,
            rag_session_id,
            paths=paths,
            layers=layers,
            limit=limit,
        )

    async def retrieve_chunks_by_path_substrings(
        self,
        rag_session_id: str,
        *,
        path_needles: list[str],
        layers: list[str] | None = None,
        limit: int = 200,
    ) -> list[dict]:
        return await asyncio.to_thread(
            self._repository.retrieve_chunks_by_path_substrings,
            rag_session_id,
            path_needles=path_needles,
            layers=layers,
            limit=limit,
        )

    async def list_docs_scope_rows(self, rag_session_id: str, *, limit: int = 8000) -> list[dict]:
        return await asyncio.to_thread(
            self._repository.list_docs_scope_index_rows,
            rag_session_id,
            limit=limit,
        )

    def _embed_query(self, query: str) -> list[float]:
        return self._embedder.embed([query])[0]

    def _map_filters(self, filters: dict[str, object]) -> dict:
        """Маппинг опциональных фильтров плана на kwargs RagRepository.retrieve."""
        out: dict = {}
        keys = (
            "path_prefixes",
            "exclude_path_prefixes",
            "exclude_like_patterns",
            "prefer_path_prefixes",
            "prefer_like_patterns",
            "prefer_non_tests",
        )
        for key in keys:
            if key in filters:
                out[key] = filters[key]
        if "metadata.domain" in filters:
            out["metadata_domain"] = filters["metadata.domain"]
        if "metadata.subdomain" in filters:
            out["metadata_subdomain"] = filters["metadata.subdomain"]
        if "metadata.type" in filters:
            out["metadata_type"] = filters["metadata.type"]
        if "metadata.doc_type" in filters:
            out["metadata_type"] = filters["metadata.doc_type"]
        return out
