"""
Retrieval Strategy - Unified Entry Point.
[AC-AISVC-RES-01~15] 检索策略统一入口。

整合：
- StrategyRouter: 策略路由（default/enhanced）
- ModeRouter: 模式路由（direct/react/auto）
- MetadataInferenceService: 元数据推断
- RollbackManager: 回退管理
"""

import logging
import time
from dataclasses import dataclass
from typing import Any

from sqlalchemy.ext.asyncio import AsyncSession

from app.services.retrieval.base import RetrievalContext, RetrievalResult
from app.services.retrieval.strategy.config import (
    RetrievalStrategyConfig,
    RuntimeMode,
    StrategyType,
)
from app.services.retrieval.strategy.default_pipeline import DefaultPipeline
from app.services.retrieval.strategy.enhanced_pipeline import EnhancedPipeline
from app.services.retrieval.strategy.metadata_inference import (
    InferenceContext,
    MetadataInferenceService,
)
from app.services.retrieval.strategy.mode_router import ModeDecision, ModeRouter
from app.services.retrieval.strategy.pipeline_base import (
    MetadataFilterResult,
    PipelineContext,
    PipelineResult,
)
from app.services.retrieval.strategy.rollback_manager import (
    RollbackManager,
    RollbackTrigger,
)
from app.services.retrieval.strategy.strategy_router import (
    RoutingDecision,
    StrategyRouter,
)

logger = logging.getLogger(__name__)


@dataclass
class RetrievalStrategyResult:
    """检索策略执行结果。"""
    
    retrieval_result: RetrievalResult
    strategy_used: StrategyType
    mode_used: RuntimeMode
    metadata_filter: MetadataFilterResult | None
    latency_ms: float
    diagnostics: dict[str, Any]


class RetrievalStrategy:
    """
    检索策略统一入口。【AC-AISVC-RES-01~15】
    
    整合所有策略组件：
    1. 元数据推断（MetadataInferenceService）
    2. 策略路由（StrategyRouter）
    3. 模式路由（ModeRouter）
    4. 回退管理（RollbackManager）
    
    使用方式：
    ```python
    strategy = RetrievalStrategy(session)
    result = await strategy.retrieve(
        tenant_id="tenant_1",
        query="用户问题",
        context={"user_id": "user_1"},
    )
    ```
    """
    
    def __init__(
        self,
        session: AsyncSession,
        config: RetrievalStrategyConfig | None = None,
    ):
        self._session = session
        self._config = config or RetrievalStrategyConfig()
        
        self._strategy_router = StrategyRouter(self._config)
        self._mode_router = ModeRouter(self._config.mode_router)
        self._rollback_manager = RollbackManager(self._config)
        self._metadata_inference: MetadataInferenceService | None = None
    
    async def retrieve(
        self,
        tenant_id: str,
        query: str,
        context: dict[str, Any] | None = None,
        kb_ids: list[str] | None = None,
        session_id: str | None = None,
        user_id: str | None = None,
        use_reranker: bool = False,
        use_react: bool = False,
    ) -> RetrievalStrategyResult:
        """
        执行检索策略。【AC-AISVC-RES-01~15】
        
        流程：
        1. 元数据推断
        2. 策略路由
        3. 模式路由
        4. 执行检索
        5. 检查是否需要回退
        
        Args:
            tenant_id: 租户 ID
            query: 查询文本
            context: 上下文信息
            kb_ids: 知识库 ID 列表
            session_id: 会话 ID
            user_id: 用户 ID
            use_reranker: 是否使用重排
            use_react: 是否使用 ReAct 模式
        
        Returns:
            RetrievalStrategyResult 包含检索结果和诊断信息
        """
        start_time = time.time()
        context = context or {}
        
        logger.info(
            f"[RetrievalStrategy] Starting retrieval: tenant={tenant_id}, "
            f"query={query[:50]}..."
        )
        
        try:
            metadata_filter = await self._infer_metadata(
                tenant_id=tenant_id,
                query=query,
                context=context,
                session_id=session_id,
                user_id=user_id,
            )
            
            routing_decision = await self._strategy_router.route(tenant_id, user_id)
            
            mode_decision = self._mode_router.decide(
                query=query,
                confidence=metadata_filter.confidence if metadata_filter else None,
            )
            
            retrieval_ctx = RetrievalContext(
                tenant_id=tenant_id,
                query=query,
                session_id=session_id,
                metadata_filter=metadata_filter.filter_dict if metadata_filter else None,
                kb_ids=kb_ids,
            )
            
            pipeline_ctx = PipelineContext(
                retrieval_ctx=retrieval_ctx,
                metadata_filter=metadata_filter,
                use_reranker=use_reranker or self._config.reranker.enabled,
                use_react=use_react or mode_decision.mode == RuntimeMode.REACT,
            )
            
            pipeline_result = await routing_decision.pipeline.retrieve(pipeline_ctx)
            
            if mode_decision.mode == RuntimeMode.DIRECT and self._mode_router.should_fallback_to_react(pipeline_result):
                logger.info("[RetrievalStrategy] Falling back to react mode")
                pipeline_ctx.use_react = True
                pipeline_result = await routing_decision.pipeline.retrieve(pipeline_ctx)
                mode_decision = ModeDecision(
                    mode=RuntimeMode.REACT,
                    reason="fallback_from_direct",
                )
            
            latency_ms = (time.time() - start_time) * 1000
            
            self._check_performance(latency_ms, tenant_id)
            
            logger.info(
                f"[RetrievalStrategy] Retrieval completed: strategy={routing_decision.strategy.value}, "
                f"mode={mode_decision.mode.value}, hits={len(pipeline_result.hits)}, "
                f"latency_ms={latency_ms:.2f}"
            )
            
            return RetrievalStrategyResult(
                retrieval_result=pipeline_result.retrieval_result,
                strategy_used=routing_decision.strategy,
                mode_used=mode_decision.mode,
                metadata_filter=metadata_filter,
                latency_ms=latency_ms,
                diagnostics={
                    "routing_reason": routing_decision.reason,
                    "mode_reason": mode_decision.reason,
                    "grayscale_hit": routing_decision.grayscale_hit,
                    **pipeline_result.diagnostics,
                },
            )
            
        except Exception as e:
            latency_ms = (time.time() - start_time) * 1000
            logger.error(
                f"[RetrievalStrategy] Retrieval error: {e}",
                exc_info=True
            )
            
            self._rollback_manager.rollback(
                trigger=RollbackTrigger.ERROR,
                reason=str(e),
                tenant_id=tenant_id,
            )
            
            return RetrievalStrategyResult(
                retrieval_result=RetrievalResult(
                    hits=[],
                    diagnostics={"error": str(e)},
                ),
                strategy_used=StrategyType.DEFAULT,
                mode_used=RuntimeMode.DIRECT,
                metadata_filter=None,
                latency_ms=latency_ms,
                diagnostics={"error": str(e)},
            )
    
    async def _infer_metadata(
        self,
        tenant_id: str,
        query: str,
        context: dict[str, Any],
        session_id: str | None = None,
        user_id: str | None = None,
    ) -> MetadataFilterResult | None:
        """执行元数据推断。"""
        try:
            if self._metadata_inference is None:
                self._metadata_inference = MetadataInferenceService(
                    self._session,
                    self._config.metadata_inference,
                )
            
            inference_ctx = InferenceContext(
                tenant_id=tenant_id,
                query=query,
                session_id=session_id,
                user_id=user_id,
                existing_context=context,
            )
            
            result = await self._metadata_inference.infer(inference_ctx)
            return result.filter_result
            
        except Exception as e:
            logger.warning(f"[RetrievalStrategy] Metadata inference failed: {e}")
            return None
    
    def _check_performance(self, latency_ms: float, tenant_id: str | None) -> None:
        """检查性能指标，必要时触发回退。"""
        self._rollback_manager.check_and_rollback(
            metrics={"latency_ms": latency_ms},
            tenant_id=tenant_id,
        )
    
    def get_config(self) -> RetrievalStrategyConfig:
        """获取当前配置。"""
        return self._config
    
    def update_config(self, config: RetrievalStrategyConfig) -> None:
        """更新配置。"""
        self._config = config
        self._strategy_router.update_config(config)
        self._mode_router.update_config(config.mode_router)
        self._rollback_manager.update_config(config)
    
    async def health_check(self) -> dict[str, bool]:
        """健康检查。"""
        results = {}
        
        try:
            default_pipeline = await self._strategy_router._get_default_pipeline()
            results["default_pipeline"] = await default_pipeline.health_check()
        except Exception:
            results["default_pipeline"] = False
        
        try:
            enhanced_pipeline = await self._strategy_router._get_enhanced_pipeline()
            results["enhanced_pipeline"] = await enhanced_pipeline.health_check()
        except Exception:
            results["enhanced_pipeline"] = False
        
        return results


async def create_retrieval_strategy(
    session: AsyncSession,
    config: RetrievalStrategyConfig | None = None,
) -> RetrievalStrategy:
    """创建 RetrievalStrategy 实例。"""
    return RetrievalStrategy(session, config)
