"""
Vector retriever for AI Service.
[AC-AISVC-16, AC-AISVC-17] Qdrant-based vector retrieval with score threshold filtering.
"""

import logging
from typing import Any

from app.core.config import get_settings
from app.core.qdrant_client import QdrantClient, get_qdrant_client
from app.services.retrieval.base import (
    BaseRetriever,
    RetrievalContext,
    RetrievalHit,
    RetrievalResult,
)

logger = logging.getLogger(__name__)

settings = get_settings()


class VectorRetriever(BaseRetriever):
    """
    [AC-AISVC-16, AC-AISVC-17] Vector-based retriever using Qdrant.
    Supports score threshold filtering and tenant isolation.
    """

    def __init__(
        self,
        qdrant_client: QdrantClient | None = None,
        top_k: int | None = None,
        score_threshold: float | None = None,
        min_hits: int | None = None,
    ):
        self._qdrant_client = qdrant_client
        self._top_k = top_k or settings.rag_top_k
        self._score_threshold = score_threshold or settings.rag_score_threshold
        self._min_hits = min_hits or settings.rag_min_hits

    async def _get_client(self) -> QdrantClient:
        """Get Qdrant client instance."""
        if self._qdrant_client is None:
            self._qdrant_client = await get_qdrant_client()
        return self._qdrant_client

    async def retrieve(self, ctx: RetrievalContext) -> RetrievalResult:
        """
        [AC-AISVC-16, AC-AISVC-17] Retrieve documents from vector store.

        Steps:
        1. Generate embedding for query (placeholder - requires embedding provider)
        2. Search in tenant's collection
        3. Filter by score threshold
        4. Return structured result

        Args:
            ctx: Retrieval context with tenant_id and query.

        Returns:
            RetrievalResult with filtered hits.
        """
        logger.info(
            f"[AC-AISVC-16] Starting vector retrieval for tenant={ctx.tenant_id}, "
            f"query={ctx.query[:50]}..."
        )
        logger.info(
            f"[AC-AISVC-16] Retrieval config: top_k={self._top_k}, "
            f"score_threshold={self._score_threshold}, min_hits={self._min_hits}"
        )

        try:
            client = await self._get_client()
            logger.info(f"[AC-AISVC-16] Got Qdrant client: {type(client).__name__}")

            logger.info("[AC-AISVC-16] Generating embedding for query...")
            query_vector = await self._get_embedding(ctx.query)
            logger.info(f"[AC-AISVC-16] Embedding generated: dim={len(query_vector)}")

            logger.info(f"[AC-AISVC-16] Searching in tenant collections: tenant_id={ctx.tenant_id}")
            if ctx.kb_ids:
                logger.info(f"[AC-AISVC-16] Restricting search to KB IDs: {ctx.kb_ids}")
                hits = await client.search(
                    tenant_id=ctx.tenant_id,
                    query_vector=query_vector,
                    limit=self._top_k,
                    score_threshold=self._score_threshold,
                    vector_name="full",
                    metadata_filter=ctx.metadata_filter,
                    kb_ids=ctx.kb_ids,
                )
            else:
                hits = await client.search(
                    tenant_id=ctx.tenant_id,
                    query_vector=query_vector,
                    limit=self._top_k,
                    score_threshold=self._score_threshold,
                    vector_name="full",
                    metadata_filter=ctx.metadata_filter,
                )

            logger.info(f"[AC-AISVC-16] Search returned {len(hits)} hits")
            
            retrieval_hits = [
                RetrievalHit(
                    text=hit.get("payload", {}).get("text", ""),
                    score=hit.get("score", 0.0),
                    source=hit.get("payload", {}).get("source", "vector"),
                    metadata=hit.get("payload", {}),
                )
                for hit in hits
                if hit.get("score", 0.0) >= self._score_threshold
            ]

            is_insufficient = len(retrieval_hits) < self._min_hits

            diagnostics = {
                "query_length": len(ctx.query),
                "top_k": self._top_k,
                "score_threshold": self._score_threshold,
                "min_hits": self._min_hits,
                "total_candidates": len(hits),
                "filtered_hits": len(retrieval_hits),
                "is_insufficient": is_insufficient,
                "max_score": max((h.score for h in retrieval_hits), default=0.0),
            }

            logger.info(
                f"[AC-AISVC-17] Retrieval complete: {len(retrieval_hits)} hits, "
                f"insufficient={is_insufficient}, max_score={diagnostics['max_score']:.3f}"
            )

            if len(retrieval_hits) == 0:
                logger.warning(
                    f"[AC-AISVC-17] No hits found! tenant={ctx.tenant_id}, "
                    f"query={ctx.query[:50]}..., raw_hits={len(hits)}, threshold={self._score_threshold}"
                )

            return RetrievalResult(
                hits=retrieval_hits,
                diagnostics=diagnostics,
            )

        except Exception as e:
            logger.error(f"[AC-AISVC-16] Retrieval error: {e}", exc_info=True)
            return RetrievalResult(
                hits=[],
                diagnostics={"error": str(e), "is_insufficient": True},
            )

    def _apply_metadata_filter(
        self,
        hits: list[dict[str, Any]],
        metadata_filter: dict[str, Any],
    ) -> list[dict[str, Any]]:
        """
        应用元数据过滤条件。
        
        支持的操作:
        - {"$eq": value} : 等于
        - {"$in": [values]} : 在列表中
        """
        filtered = []
        for hit in hits:
            payload = hit.get("payload", {})
            hit_metadata = payload.get("metadata", {})
            
            match = True
            for field_key, condition in metadata_filter.items():
                hit_value = hit_metadata.get(field_key)
                
                if isinstance(condition, dict):
                    if "$eq" in condition:
                        if hit_value != condition["$eq"]:
                            match = False
                            break
                    elif "$in" in condition:
                        if hit_value not in condition["$in"]:
                            match = False
                            break
                else:
                    # 直接值比较
                    if hit_value != condition:
                        match = False
                        break
            
            if match:
                filtered.append(hit)
        
        return filtered

    async def _get_embedding(self, text: str) -> list[float]:
        """
        Generate embedding for text using pluggable embedding provider.
        [AC-AISVC-29] Uses configured embedding provider.
        """
        from app.services.embedding import get_embedding_provider

        provider = await get_embedding_provider()
        return await provider.embed(text)

    async def health_check(self) -> bool:
        """
        [AC-AISVC-16] Check if Qdrant connection is healthy.
        """
        try:
            client = await self._get_client()
            qdrant = await client.get_client()
            await qdrant.get_collections()
            return True
        except Exception as e:
            logger.error(f"[AC-AISVC-16] Health check failed: {e}")
            return False


_vector_retriever: VectorRetriever | None = None


async def get_vector_retriever() -> VectorRetriever:
    """Get or create VectorRetriever instance."""
    global _vector_retriever
    if _vector_retriever is None:
        _vector_retriever = VectorRetriever()
    return _vector_retriever
