"""
Slot Strategy Executor.
[AC-MRS-07-UPGRADE] 槽位提取策略链执行器

按顺序执行提取策略链，直到成功提取并通过校验。
支持失败分类和详细日志追踪。
"""

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

from app.models.entities import ExtractFailureType

logger = logging.getLogger(__name__)


class ExtractStrategyType(str, Enum):
    """提取策略类型"""
    RULE = "rule"
    LLM = "llm"
    USER_INPUT = "user_input"


@dataclass
class StrategyStepResult:
    """单步策略执行结果"""
    strategy: str
    success: bool
    value: Any = None
    failure_type: ExtractFailureType | None = None
    failure_reason: str = ""
    execution_time_ms: float = 0.0


@dataclass
class StrategyChainResult:
    """策略链执行结果"""
    slot_key: str
    success: bool
    final_value: Any = None
    final_strategy: str | None = None
    steps: list[StrategyStepResult] = field(default_factory=list)
    total_execution_time_ms: float = 0.0
    ask_back_prompt: str | None = None

    def to_dict(self) -> dict[str, Any]:
        """转换为字典"""
        return {
            "slot_key": self.slot_key,
            "success": self.success,
            "final_value": self.final_value,
            "final_strategy": self.final_strategy,
            "steps": [
                {
                    "strategy": step.strategy,
                    "success": step.success,
                    "value": step.value if step.success else None,
                    "failure_type": step.failure_type.value if step.failure_type else None,
                    "failure_reason": step.failure_reason,
                    "execution_time_ms": step.execution_time_ms,
                }
                for step in self.steps
            ],
            "total_execution_time_ms": self.total_execution_time_ms,
            "ask_back_prompt": self.ask_back_prompt,
        }


@dataclass
class ExtractContext:
    """提取上下文"""
    tenant_id: str
    slot_key: str
    user_input: str
    slot_type: str
    validation_rule: str | None = None
    history: list[dict[str, str]] | None = None
    session_id: str | None = None


class SlotStrategyExecutor:
    """
    [AC-MRS-07-UPGRADE] 槽位提取策略链执行器
    
    职责：
    1. 按策略链顺序执行提取
    2. 某一步成功且校验通过 -> 停止并返回结果
    3. 当前策略失败 -> 记录失败原因，继续下一策略
    4. 全部失败 -> 返回结构化失败结果
    5. 提供可追踪日志（slot_key、strategy、reason）
    """

    def __init__(
        self,
        rule_extractor: Callable[[ExtractContext], Any] | None = None,
        llm_extractor: Callable[[ExtractContext], Any] | None = None,
        user_input_extractor: Callable[[ExtractContext], Any] | None = None,
    ):
        """
        初始化执行器
        
        Args:
            rule_extractor: 规则提取器函数
            llm_extractor: LLM提取器函数
            user_input_extractor: 用户输入提取器函数
        """
        self._extractors: dict[str, Callable[[ExtractContext], Any]] = {
            ExtractStrategyType.RULE.value: rule_extractor or self._default_rule_extract,
            ExtractStrategyType.LLM.value: llm_extractor or self._default_llm_extract,
            ExtractStrategyType.USER_INPUT.value: user_input_extractor or self._default_user_input_extract,
        }

    async def execute_chain(
        self,
        strategies: list[str],
        context: ExtractContext,
        ask_back_prompt: str | None = None,
    ) -> StrategyChainResult:
        """
        执行提取策略链
        
        Args:
            strategies: 策略链，如 ["user_input", "rule", "llm"]
            context: 提取上下文
            ask_back_prompt: 追问提示语（全部失败时使用）
            
        Returns:
            StrategyChainResult: 执行结果
        """
        import time
        
        start_time = time.time()
        steps: list[StrategyStepResult] = []
        
        logger.info(
            f"[SlotStrategyExecutor] Starting strategy chain for slot '{context.slot_key}': "
            f"strategies={strategies}, tenant={context.tenant_id}"
        )
        
        for idx, strategy in enumerate(strategies):
            step_start = time.time()
            
            logger.info(
                f"[SlotStrategyExecutor] Executing step {idx + 1}/{len(strategies)}: "
                f"slot_key={context.slot_key}, strategy={strategy}"
            )
            
            step_result = await self._execute_single_strategy(strategy, context)
            step_result.execution_time_ms = (time.time() - step_start) * 1000
            steps.append(step_result)
            
            if step_result.success:
                total_time = (time.time() - start_time) * 1000
                logger.info(
                    f"[SlotStrategyExecutor] Strategy chain succeeded at step {idx + 1}: "
                    f"slot_key={context.slot_key}, strategy={strategy}, "
                    f"total_time_ms={total_time:.2f}"
                )
                return StrategyChainResult(
                    slot_key=context.slot_key,
                    success=True,
                    final_value=step_result.value,
                    final_strategy=strategy,
                    steps=steps,
                    total_execution_time_ms=total_time,
                )
            else:
                logger.warning(
                    f"[SlotStrategyExecutor] Step {idx + 1} failed: "
                    f"slot_key={context.slot_key}, strategy={strategy}, "
                    f"failure_type={step_result.failure_type}, "
                    f"reason={step_result.failure_reason}"
                )
        
        # 全部策略失败
        total_time = (time.time() - start_time) * 1000
        logger.warning(
            f"[SlotStrategyExecutor] All strategies failed for slot '{context.slot_key}': "
            f"attempted={len(strategies)}, total_time_ms={total_time:.2f}"
        )
        
        return StrategyChainResult(
            slot_key=context.slot_key,
            success=False,
            final_value=None,
            final_strategy=None,
            steps=steps,
            total_execution_time_ms=total_time,
            ask_back_prompt=ask_back_prompt,
        )

    async def _execute_single_strategy(
        self,
        strategy: str,
        context: ExtractContext,
    ) -> StrategyStepResult:
        """
        执行单个提取策略
        
        Args:
            strategy: 策略类型
            context: 提取上下文
            
        Returns:
            StrategyStepResult: 单步执行结果
        """
        extractor = self._extractors.get(strategy)
        
        if not extractor:
            return StrategyStepResult(
                strategy=strategy,
                success=False,
                failure_type=ExtractFailureType.EXTRACT_RUNTIME_ERROR,
                failure_reason=f"Unknown strategy: {strategy}",
            )
        
        try:
            # 执行提取
            value = await extractor(context)
            
            # 检查结果是否为空
            if value is None or value == "":
                return StrategyStepResult(
                    strategy=strategy,
                    success=False,
                    failure_type=ExtractFailureType.EXTRACT_EMPTY,
                    failure_reason="Extracted value is empty",
                )
            
            # 执行校验（如果有校验规则）
            if context.validation_rule:
                is_valid, error_msg = self._validate_value(value, context)
                if not is_valid:
                    return StrategyStepResult(
                        strategy=strategy,
                        success=False,
                        failure_type=ExtractFailureType.EXTRACT_VALIDATION_FAIL,
                        failure_reason=f"Validation failed: {error_msg}",
                    )
            
            return StrategyStepResult(
                strategy=strategy,
                success=True,
                value=value,
            )
            
        except Exception as e:
            logger.exception(
                f"[SlotStrategyExecutor] Runtime error in strategy '{strategy}' "
                f"for slot '{context.slot_key}': {e}"
            )
            return StrategyStepResult(
                strategy=strategy,
                success=False,
                failure_type=ExtractFailureType.EXTRACT_RUNTIME_ERROR,
                failure_reason=f"Runtime error: {str(e)}",
            )

    def _validate_value(self, value: Any, context: ExtractContext) -> tuple[bool, str]:
        """
        校验提取的值
        
        Args:
            value: 提取的值
            context: 提取上下文
            
        Returns:
            Tuple of (是否通过, 错误信息)
        """
        import re
        
        validation_rule = context.validation_rule
        if not validation_rule:
            return True, ""
        
        try:
            # 尝试作为正则表达式校验
            if validation_rule.startswith("^") or validation_rule.endswith("$"):
                if re.match(validation_rule, str(value)):
                    return True, ""
                return False, f"Value '{value}' does not match pattern '{validation_rule}'"
            
            # 其他校验规则可以在这里扩展
            return True, ""
            
        except re.error as e:
            logger.warning(f"[SlotStrategyExecutor] Invalid validation rule pattern: {e}")
            return True, ""  # 正则错误时放行

    async def _default_rule_extract(self, context: ExtractContext) -> Any:
        """默认规则提取实现（占位）"""
        # 实际项目中应该调用 VariableExtractor 或其他规则引擎
        logger.debug(f"[SlotStrategyExecutor] Default rule extract for '{context.slot_key}'")
        return None

    async def _default_llm_extract(self, context: ExtractContext) -> Any:
        """默认LLM提取实现（占位）"""
        logger.debug(f"[SlotStrategyExecutor] Default LLM extract for '{context.slot_key}'")
        return None

    async def _default_user_input_extract(self, context: ExtractContext) -> Any:
        """默认用户输入提取实现"""
        # user_input 策略通常表示需要向用户询问，这里返回空表示需要追问
        logger.debug(f"[SlotStrategyExecutor] User input required for '{context.slot_key}'")
        return None


# 便捷函数
async def execute_extract_strategies(
    strategies: list[str],
    tenant_id: str,
    slot_key: str,
    user_input: str,
    slot_type: str = "string",
    validation_rule: str | None = None,
    ask_back_prompt: str | None = None,
    history: list[dict[str, str]] | None = None,
    rule_extractor: Callable[[ExtractContext], Any] | None = None,
    llm_extractor: Callable[[ExtractContext], Any] | None = None,
) -> StrategyChainResult:
    """
    便捷函数：执行提取策略链
    
    Args:
        strategies: 策略链
        tenant_id: 租户ID
        slot_key: 槽位键名
        user_input: 用户输入
        slot_type: 槽位类型
        validation_rule: 校验规则
        ask_back_prompt: 追问提示语
        history: 对话历史
        rule_extractor: 规则提取器
        llm_extractor: LLM提取器
        
    Returns:
        StrategyChainResult: 执行结果
    """
    executor = SlotStrategyExecutor(
        rule_extractor=rule_extractor,
        llm_extractor=llm_extractor,
    )
    
    context = ExtractContext(
        tenant_id=tenant_id,
        slot_key=slot_key,
        user_input=user_input,
        slot_type=slot_type,
        validation_rule=validation_rule,
        history=history,
    )
    
    return await executor.execute_chain(strategies, context, ask_back_prompt)
