"""
Slot Backfill Service.
槽位回填服务 - 处理槽位值的提取、校验、确认、写回

[AC-MRS-SLOT-BACKFILL-01] 槽位值回填确认

职责：
1. 从用户回复提取候选槽位值
2. 调用 SlotManager 校验并归一化
3. 校验失败返回 ask_back_prompt 二次追问
4. 校验通过写入状态并标记 source/confidence
5. 对低置信度值增加确认话术
"""

import logging
from dataclasses import dataclass, field
from enum import Enum
from typing import Any

from sqlalchemy.ext.asyncio import AsyncSession

from app.models.mid.schemas import SlotSource
from app.services.mid.slot_manager import SlotManager, SlotWriteResult
from app.services.mid.slot_state_aggregator import SlotStateAggregator, SlotState
from app.services.mid.slot_strategy_executor import (
    ExtractContext,
    SlotStrategyExecutor,
    StrategyChainResult,
)
from app.services.slot_definition_service import SlotDefinitionService

logger = logging.getLogger(__name__)


class BackfillStatus(str, Enum):
    """回填状态"""
    SUCCESS = "success"
    VALIDATION_FAILED = "validation_failed"
    EXTRACTION_FAILED = "extraction_failed"
    NEEDS_CONFIRMATION = "needs_confirmation"
    NO_CANDIDATES = "no_candidates"


@dataclass
class BackfillResult:
    """
    回填结果
    
    Attributes:
        status: 回填状态
        slot_key: 槽位键名
        value: 最终值（校验通过后）
        normalized_value: 归一化后的值
        source: 值来源
        confidence: 置信度
        error_message: 错误信息
        ask_back_prompt: 追问提示
        confirmation_prompt: 确认提示（低置信度时）
        updated_state: 更新后的槽位状态
    """
    status: BackfillStatus
    slot_key: str | None = None
    value: Any = None
    normalized_value: Any = None
    source: str = "unknown"
    confidence: float = 0.0
    error_message: str | None = None
    ask_back_prompt: str | None = None
    confirmation_prompt: str | None = None
    updated_state: SlotState | None = None
    
    def is_success(self) -> bool:
        return self.status == BackfillStatus.SUCCESS
    
    def needs_ask_back(self) -> bool:
        return self.status in (
            BackfillStatus.VALIDATION_FAILED,
            BackfillStatus.EXTRACTION_FAILED,
        )
    
    def needs_confirmation(self) -> bool:
        return self.status == BackfillStatus.NEEDS_CONFIRMATION
    
    def to_dict(self) -> dict[str, Any]:
        return {
            "status": self.status.value,
            "slot_key": self.slot_key,
            "value": self.value,
            "normalized_value": self.normalized_value,
            "source": self.source,
            "confidence": self.confidence,
            "error_message": self.error_message,
            "ask_back_prompt": self.ask_back_prompt,
            "confirmation_prompt": self.confirmation_prompt,
        }


@dataclass
class BatchBackfillResult:
    """批量回填结果"""
    results: list[BackfillResult] = field(default_factory=list)
    success_count: int = 0
    failed_count: int = 0
    confirmation_needed_count: int = 0
    
    def add_result(self, result: BackfillResult) -> None:
        self.results.append(result)
        if result.is_success():
            self.success_count += 1
        elif result.needs_confirmation():
            self.confirmation_needed_count += 1
        else:
            self.failed_count += 1
    
    def get_ask_back_prompts(self) -> list[str]:
        """获取所有追问提示"""
        return [
            r.ask_back_prompt
            for r in self.results
            if r.ask_back_prompt
        ]
    
    def get_confirmation_prompts(self) -> list[str]:
        """获取所有确认提示"""
        return [
            r.confirmation_prompt
            for r in self.results
            if r.confirmation_prompt
        ]


class SlotBackfillService:
    """
    [AC-MRS-SLOT-BACKFILL-01] 槽位回填服务
    
    处理槽位值的提取、校验、确认、写回流程：
    1. 从用户回复提取候选槽位值
    2. SlotManager 校验并归一化
    3. 校验失败返回 ask_back_prompt 二次追问
    4. 校验通过写入状态并标记 source/confidence
    5. 对低置信度值增加确认话术
    """
    
    CONFIDENCE_THRESHOLD_LOW = 0.5
    CONFIDENCE_THRESHOLD_HIGH = 0.8
    
    def __init__(
        self,
        session: AsyncSession,
        tenant_id: str,
        session_id: str | None = None,
        slot_manager: SlotManager | None = None,
        strategy_executor: SlotStrategyExecutor | None = None,
    ):
        self._session = session
        self._tenant_id = tenant_id
        self._session_id = session_id
        self._slot_manager = slot_manager or SlotManager(session=session)
        self._strategy_executor = strategy_executor or SlotStrategyExecutor()
        self._slot_def_service = SlotDefinitionService(session)
        self._state_aggregator: SlotStateAggregator | None = None
    
    async def _get_state_aggregator(self) -> SlotStateAggregator:
        """获取状态聚合器"""
        if self._state_aggregator is None:
            self._state_aggregator = SlotStateAggregator(
                session=self._session,
                tenant_id=self._tenant_id,
                session_id=self._session_id,
            )
        return self._state_aggregator
    
    async def backfill_single_slot(
        self,
        slot_key: str,
        candidate_value: Any,
        source: str = "user_confirmed",
        confidence: float = 1.0,
        strategies: list[str] | None = None,
    ) -> BackfillResult:
        """
        回填单个槽位
        
        执行流程：
        1. 如果有提取策略，执行提取
        2. 校验候选值
        3. 根据校验结果决定下一步
        4. 写入状态
        
        Args:
            slot_key: 槽位键名
            candidate_value: 候选值
            source: 值来源
            confidence: 初始置信度
            strategies: 提取策略链（可选）
            
        Returns:
            BackfillResult: 回填结果
        """
        final_value = candidate_value
        final_source = source
        final_confidence = confidence
        
        if strategies:
            extracted_result = await self._extract_value(
                slot_key=slot_key,
                user_input=str(candidate_value),
                strategies=strategies,
            )
            
            if extracted_result.success:
                final_value = extracted_result.final_value
                final_source = extracted_result.final_strategy or source
                final_confidence = self._get_confidence_for_strategy(final_source)
            else:
                ask_back_prompt = await self._slot_manager.get_ask_back_prompt(
                    self._tenant_id, slot_key
                )
                return BackfillResult(
                    status=BackfillStatus.EXTRACTION_FAILED,
                    slot_key=slot_key,
                    value=candidate_value,
                    source=source,
                    confidence=confidence,
                    error_message=extracted_result.steps[-1].failure_reason if extracted_result.steps else "提取失败",
                    ask_back_prompt=ask_back_prompt,
                )
        
        write_result = await self._slot_manager.write_slot(
            tenant_id=self._tenant_id,
            slot_key=slot_key,
            value=final_value,
            source=SlotSource(final_source) if final_source in [s.value for s in SlotSource] else SlotSource.USER_CONFIRMED,
            confidence=final_confidence,
        )
        
        if not write_result.success:
            return BackfillResult(
                status=BackfillStatus.VALIDATION_FAILED,
                slot_key=slot_key,
                value=final_value,
                source=final_source,
                confidence=final_confidence,
                error_message=write_result.error.error_message if write_result.error else "校验失败",
                ask_back_prompt=write_result.ask_back_prompt,
            )
        
        normalized_value = write_result.value
        updated_state = None
        
        if self._session_id:
            aggregator = await self._get_state_aggregator()
            updated_state = await aggregator.update_slot(
                slot_key=slot_key,
                value=normalized_value,
                source=final_source,
                confidence=final_confidence,
            )
        
        result_status = BackfillStatus.SUCCESS
        confirmation_prompt = None
        
        if final_confidence < self.CONFIDENCE_THRESHOLD_LOW:
            result_status = BackfillStatus.NEEDS_CONFIRMATION
            confirmation_prompt = self._generate_confirmation_prompt(
                slot_key, normalized_value
            )
        
        return BackfillResult(
            status=result_status,
            slot_key=slot_key,
            value=final_value,
            normalized_value=normalized_value,
            source=final_source,
            confidence=final_confidence,
            confirmation_prompt=confirmation_prompt,
            updated_state=updated_state,
        )
    
    async def backfill_multiple_slots(
        self,
        candidates: dict[str, Any],
        source: str = "user_confirmed",
        confidence: float = 1.0,
    ) -> BatchBackfillResult:
        """
        批量回填槽位
        
        Args:
            candidates: 候选值字典 {slot_key: value}
            source: 值来源
            confidence: 初始置信度
            
        Returns:
            BatchBackfillResult: 批量回填结果
        """
        batch_result = BatchBackfillResult()
        
        for slot_key, value in candidates.items():
            result = await self.backfill_single_slot(
                slot_key=slot_key,
                candidate_value=value,
                source=source,
                confidence=confidence,
            )
            batch_result.add_result(result)
        
        return batch_result
    
    async def backfill_from_user_response(
        self,
        user_response: str,
        expected_slots: list[str],
        strategies: list[str] | None = None,
    ) -> BatchBackfillResult:
        """
        从用户回复中提取并回填槽位
        
        Args:
            user_response: 用户回复文本
            expected_slots: 期望提取的槽位列表
            strategies: 提取策略链
            
        Returns:
            BatchBackfillResult: 批量回填结果
        """
        batch_result = BatchBackfillResult()
        
        for slot_key in expected_slots:
            slot_def = await self._slot_def_service.get_slot_definition_by_key(
                self._tenant_id, slot_key
            )
            
            if not slot_def:
                continue
            
            extract_strategies = strategies or ["rule", "llm"]
            
            extracted_result = await self._extract_value(
                slot_key=slot_key,
                user_input=user_response,
                strategies=extract_strategies,
                slot_type=slot_def.type,
                validation_rule=slot_def.validation_rule,
            )
            
            if not extracted_result.success:
                ask_back_prompt = slot_def.ask_back_prompt or f"请提供{slot_key}信息"
                batch_result.add_result(BackfillResult(
                    status=BackfillStatus.EXTRACTION_FAILED,
                    slot_key=slot_key,
                    error_message="无法从回复中提取",
                    ask_back_prompt=ask_back_prompt,
                ))
                continue
            
            source = self._get_source_for_strategy(extracted_result.final_strategy)
            confidence = self._get_confidence_for_strategy(source)
            
            result = await self.backfill_single_slot(
                slot_key=slot_key,
                candidate_value=extracted_result.final_value,
                source=source,
                confidence=confidence,
            )
            batch_result.add_result(result)
        
        return batch_result
    
    async def _extract_value(
        self,
        slot_key: str,
        user_input: str,
        strategies: list[str],
        slot_type: str = "string",
        validation_rule: str | None = None,
    ) -> StrategyChainResult:
        """
        执行槽位值提取
        
        Args:
            slot_key: 槽位键名
            user_input: 用户输入
            strategies: 提取策略链
            slot_type: 槽位类型
            validation_rule: 校验规则
            
        Returns:
            StrategyChainResult: 提取结果
        """
        context = ExtractContext(
            tenant_id=self._tenant_id,
            slot_key=slot_key,
            user_input=user_input,
            slot_type=slot_type,
            validation_rule=validation_rule,
        )
        
        return await self._strategy_executor.execute_chain(
            strategies=strategies,
            context=context,
        )
    
    def _get_source_for_strategy(self, strategy: str | None) -> str:
        """根据策略获取来源"""
        strategy_source_map = {
            "rule": SlotSource.RULE_EXTRACTED.value,
            "llm": SlotSource.LLM_INFERRED.value,
            "user_input": SlotSource.USER_CONFIRMED.value,
        }
        return strategy_source_map.get(strategy or "", "unknown")
    
    def _get_confidence_for_strategy(self, source: str) -> float:
        """根据来源获取置信度"""
        confidence_map = {
            SlotSource.USER_CONFIRMED.value: 1.0,
            SlotSource.RULE_EXTRACTED.value: 0.9,
            SlotSource.LLM_INFERRED.value: 0.7,
            "context": 0.5,
            SlotSource.DEFAULT.value: 0.3,
        }
        return confidence_map.get(source, 0.5)
    
    def _generate_confirmation_prompt(
        self,
        slot_key: str,
        value: Any,
    ) -> str:
        """生成确认提示"""
        return f"我理解您说的是「{value}」，对吗？"
    
    async def confirm_low_confidence_slot(
        self,
        slot_key: str,
        confirmed: bool,
    ) -> BackfillResult:
        """
        确认低置信度槽位
        
        Args:
            slot_key: 槽位键名
            confirmed: 用户是否确认
            
        Returns:
            BackfillResult: 确认结果
        """
        if not self._session_id:
            return BackfillResult(
                status=BackfillStatus.SUCCESS,
                slot_key=slot_key,
            )
        
        aggregator = await self._get_state_aggregator()
        
        if confirmed:
            updated_state = await aggregator.update_slot(
                slot_key=slot_key,
                source=SlotSource.USER_CONFIRMED.value,
                confidence=1.0,
            )
            return BackfillResult(
                status=BackfillStatus.SUCCESS,
                slot_key=slot_key,
                source=SlotSource.USER_CONFIRMED.value,
                confidence=1.0,
                updated_state=updated_state,
            )
        else:
            await aggregator.clear_slot(slot_key)
            ask_back_prompt = await self._slot_manager.get_ask_back_prompt(
                self._tenant_id, slot_key
            )
            return BackfillResult(
                status=BackfillStatus.VALIDATION_FAILED,
                slot_key=slot_key,
                ask_back_prompt=ask_back_prompt or f"请重新提供{slot_key}信息",
            )


def create_slot_backfill_service(
    session: AsyncSession,
    tenant_id: str,
    session_id: str | None = None,
) -> SlotBackfillService:
    """
    创建槽位回填服务实例
    
    Args:
        session: 数据库会话
        tenant_id: 租户 ID
        session_id: 会话 ID
        
    Returns:
        SlotBackfillService: 槽位回填服务实例
    """
    return SlotBackfillService(
        session=session,
        tenant_id=tenant_id,
        session_id=session_id,
    )
