"""
Policy Router for Mid Platform.
[AC-IDMP-02, AC-IDMP-05, AC-IDMP-16, AC-IDMP-20] Routes to agent/micro_flow/fixed/transfer based on policy.

Decision Matrix:
1. High-risk scenario (refund/complaint/privacy/transfer) -> micro_flow or transfer
2. Low confidence or missing key info -> micro_flow or fixed
3. Tool unavailable -> fixed
4. Human mode active -> transfer
5. Normal case -> agent

Intent Hint Integration:
- intent_hint provides soft signals (suggested_mode, confidence, high_risk_detected)
- policy_router consumes hints but retains final decision authority
- When hint suggests high_risk, policy_router validates and may override

High Risk Check Integration:
- high_risk_check provides structured risk detection result
- High-risk check takes priority over normal intent routing
- When high_risk_check matched, skip normal intent matching
"""

import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

from app.models.mid.schemas import (
    ExecutionMode,
    FeatureFlags,
    HighRiskScenario,
    PolicyRouterResult,
)

if TYPE_CHECKING:
    from app.models.mid.schemas import HighRiskCheckResult, IntentHintOutput

logger = logging.getLogger(__name__)

DEFAULT_HIGH_RISK_SCENARIOS: list[HighRiskScenario] = [
    HighRiskScenario.REFUND,
    HighRiskScenario.COMPLAINT_ESCALATION,
    HighRiskScenario.PRIVACY_SENSITIVE_PROMISE,
    HighRiskScenario.TRANSFER,
]

HIGH_RISK_KEYWORDS: dict[HighRiskScenario, list[str]] = {
    HighRiskScenario.REFUND: ["退款", "退货", "退钱", "退费", "还钱", "退款申请"],
    HighRiskScenario.COMPLAINT_ESCALATION: ["投诉", "升级投诉", "举报", "12315", "消费者协会"],
    HighRiskScenario.PRIVACY_SENSITIVE_PROMISE: ["承诺", "保证", "一定", "肯定能", "绝对", "担保"],
    HighRiskScenario.TRANSFER: ["转人工", "人工客服", "人工服务", "真人", "人工"],
}

LOW_CONFIDENCE_THRESHOLD = 0.3


@dataclass
class IntentMatch:
    """Intent match result."""
    intent_id: str
    intent_name: str
    confidence: float
    response_type: str
    target_kb_ids: list[str] | None = None
    flow_id: str | None = None
    fixed_reply: str | None = None
    transfer_message: str | None = None


class PolicyRouter:
    """
    [AC-IDMP-02, AC-IDMP-05, AC-IDMP-16, AC-IDMP-20] Policy router for execution mode decision.

    Decision Flow:
    1. Check feature flags (rollback_to_legacy -> fixed)
    2. Check session mode (HUMAN_ACTIVE -> transfer)
    3. Check high-risk scenarios -> micro_flow or transfer
    4. Check intent match confidence -> fallback if low
    5. Default -> agent
    """

    def __init__(
        self,
        high_risk_scenarios: list[HighRiskScenario] | None = None,
        low_confidence_threshold: float = LOW_CONFIDENCE_THRESHOLD,
    ):
        self._high_risk_scenarios = high_risk_scenarios or DEFAULT_HIGH_RISK_SCENARIOS
        self._low_confidence_threshold = low_confidence_threshold

    def route(
        self,
        user_message: str,
        session_mode: str = "BOT_ACTIVE",
        feature_flags: FeatureFlags | None = None,
        intent_match: IntentMatch | None = None,
        intent_hint: "IntentHintOutput | None" = None,
        context: dict[str, Any] | None = None,
    ) -> PolicyRouterResult:
        """
        [AC-IDMP-02] Route to appropriate execution mode.

        Args:
            user_message: User input message
            session_mode: Current session mode (BOT_ACTIVE/HUMAN_ACTIVE)
            feature_flags: Feature flags for grayscale control
            intent_match: Intent match result if available
            intent_hint: Soft signal from intent_hint tool (optional)
            context: Additional context for decision

        Returns:
            PolicyRouterResult with decided mode and metadata
        """
        logger.info(
            f"[AC-IDMP-02] PolicyRouter routing: session_mode={session_mode}, "
            f"feature_flags={feature_flags}, intent_match={intent_match}, "
            f"intent_hint_mode={intent_hint.suggested_mode if intent_hint else None}"
        )

        if feature_flags and feature_flags.rollback_to_legacy:
            logger.info("[AC-IDMP-17] Rollback to legacy requested, using fixed mode")
            return PolicyRouterResult(
                mode=ExecutionMode.FIXED,
                fallback_reason_code="rollback_to_legacy",
            )

        if session_mode == "HUMAN_ACTIVE":
            logger.info("[AC-IDMP-09] Session in HUMAN_ACTIVE mode, routing to transfer")
            return PolicyRouterResult(
                mode=ExecutionMode.TRANSFER,
                transfer_message="正在为您转接人工客服...",
            )

        if intent_hint and intent_hint.high_risk_detected:
            logger.info(
                f"[AC-IDMP-05, AC-IDMP-20] High-risk from hint: {intent_hint.fallback_reason_code}"
            )
            return self._handle_high_risk_from_hint(intent_hint, intent_match)

        high_risk_scenario = self._check_high_risk(user_message)
        if high_risk_scenario:
            logger.info(f"[AC-IDMP-05, AC-IDMP-20] High-risk scenario detected: {high_risk_scenario}")
            return self._handle_high_risk(high_risk_scenario, intent_match)

        if intent_hint and intent_hint.confidence < self._low_confidence_threshold:
            logger.info(
                f"[AC-IDMP-16] Low confidence from hint ({intent_hint.confidence}), "
                f"considering fallback"
            )
            if intent_hint.suggested_mode in (ExecutionMode.FIXED, ExecutionMode.MICRO_FLOW):
                return PolicyRouterResult(
                    mode=intent_hint.suggested_mode,
                    intent=intent_hint.intent,
                    confidence=intent_hint.confidence,
                    fallback_reason_code=intent_hint.fallback_reason_code or "low_confidence_hint",
                    target_flow_id=intent_hint.target_flow_id,
                )

        if intent_match:
            if intent_match.confidence < self._low_confidence_threshold:
                logger.info(
                    f"[AC-IDMP-16] Low confidence ({intent_match.confidence}), "
                    f"falling back from agent mode"
                )
                return self._handle_low_confidence(intent_match)

            if intent_match.response_type == "fixed":
                return PolicyRouterResult(
                    mode=ExecutionMode.FIXED,
                    intent=intent_match.intent_name,
                    confidence=intent_match.confidence,
                    fixed_reply=intent_match.fixed_reply,
                )

            if intent_match.response_type == "transfer":
                return PolicyRouterResult(
                    mode=ExecutionMode.TRANSFER,
                    intent=intent_match.intent_name,
                    confidence=intent_match.confidence,
                    transfer_message=intent_match.transfer_message or "正在为您转接人工客服...",
                )

            if intent_match.response_type == "flow":
                return PolicyRouterResult(
                    mode=ExecutionMode.MICRO_FLOW,
                    intent=intent_match.intent_name,
                    confidence=intent_match.confidence,
                    target_flow_id=intent_match.flow_id,
                )

        if feature_flags and not feature_flags.agent_enabled:
            logger.info("[AC-IDMP-17] Agent disabled by feature flag, using fixed mode")
            return PolicyRouterResult(
                mode=ExecutionMode.FIXED,
                fallback_reason_code="agent_disabled",
            )

        logger.info("[AC-IDMP-02] Default routing to agent mode")
        return PolicyRouterResult(
            mode=ExecutionMode.AGENT,
            intent=intent_match.intent_name if intent_match else None,
            confidence=intent_match.confidence if intent_match else None,
        )

    def _check_high_risk(self, message: str) -> HighRiskScenario | None:
        """
        [AC-IDMP-05, AC-IDMP-20] Check if message matches high-risk scenarios.

        Returns the first matched high-risk scenario or None.
        """
        message_lower = message.lower()

        for scenario in self._high_risk_scenarios:
            keywords = HIGH_RISK_KEYWORDS.get(scenario, [])
            for keyword in keywords:
                if keyword.lower() in message_lower:
                    return scenario

        return None

    def _handle_high_risk(
        self,
        scenario: HighRiskScenario,
        intent_match: IntentMatch | None,
    ) -> PolicyRouterResult:
        """
        [AC-IDMP-05] Handle high-risk scenario by routing to micro_flow or transfer.
        """
        if scenario == HighRiskScenario.TRANSFER:
            return PolicyRouterResult(
                mode=ExecutionMode.TRANSFER,
                high_risk_triggered=True,
                transfer_message="正在为您转接人工客服...",
            )

        if intent_match and intent_match.flow_id:
            return PolicyRouterResult(
                mode=ExecutionMode.MICRO_FLOW,
                intent=intent_match.intent_name,
                confidence=intent_match.confidence,
                high_risk_triggered=True,
                target_flow_id=intent_match.flow_id,
            )

        return PolicyRouterResult(
            mode=ExecutionMode.MICRO_FLOW,
            high_risk_triggered=True,
            fallback_reason_code=f"high_risk_{scenario.value}",
        )

    def _handle_high_risk_from_hint(
        self,
        intent_hint: "IntentHintOutput",
        intent_match: IntentMatch | None,
    ) -> PolicyRouterResult:
        """
        [AC-IDMP-05, AC-IDMP-20] Handle high-risk from intent_hint.

        Policy_router validates hint suggestion but may override.
        """
        if intent_hint.suggested_mode == ExecutionMode.TRANSFER:
            return PolicyRouterResult(
                mode=ExecutionMode.TRANSFER,
                high_risk_triggered=True,
                transfer_message="正在为您转接人工客服...",
            )

        if intent_match and intent_match.flow_id:
            return PolicyRouterResult(
                mode=ExecutionMode.MICRO_FLOW,
                intent=intent_match.intent_name,
                confidence=intent_match.confidence,
                high_risk_triggered=True,
                target_flow_id=intent_match.flow_id,
            )

        if intent_hint.target_flow_id:
            return PolicyRouterResult(
                mode=ExecutionMode.MICRO_FLOW,
                intent=intent_hint.intent,
                confidence=intent_hint.confidence,
                high_risk_triggered=True,
                target_flow_id=intent_hint.target_flow_id,
            )

        return PolicyRouterResult(
            mode=ExecutionMode.MICRO_FLOW,
            high_risk_triggered=True,
            fallback_reason_code=intent_hint.fallback_reason_code or "high_risk_hint",
        )

    def _handle_low_confidence(self, intent_match: IntentMatch) -> PolicyRouterResult:
        """
        [AC-IDMP-16] Handle low confidence by falling back to micro_flow or fixed.
        """
        if intent_match.flow_id:
            return PolicyRouterResult(
                mode=ExecutionMode.MICRO_FLOW,
                intent=intent_match.intent_name,
                confidence=intent_match.confidence,
                fallback_reason_code="low_confidence",
                target_flow_id=intent_match.flow_id,
            )

        if intent_match.fixed_reply:
            return PolicyRouterResult(
                mode=ExecutionMode.FIXED,
                intent=intent_match.intent_name,
                confidence=intent_match.confidence,
                fallback_reason_code="low_confidence",
                fixed_reply=intent_match.fixed_reply,
            )

        return PolicyRouterResult(
            mode=ExecutionMode.FIXED,
            fallback_reason_code="low_confidence_no_flow",
        )

    def get_active_high_risk_set(self) -> list[HighRiskScenario]:
        """[AC-IDMP-20] Get active high-risk scenario set."""
        return self._high_risk_scenarios

    def route_with_high_risk_check(
        self,
        user_message: str,
        high_risk_check_result: "HighRiskCheckResult | None",
        session_mode: str = "BOT_ACTIVE",
        feature_flags: FeatureFlags | None = None,
        intent_match: IntentMatch | None = None,
        intent_hint: "IntentHintOutput | None" = None,
        context: dict[str, Any] | None = None,
    ) -> PolicyRouterResult:
        """
        [AC-IDMP-05, AC-IDMP-20] Route with high_risk_check result.

        高风险优先于普通意图路由：
        1. 如果 high_risk_check 匹配，直接返回高风险路由结果
        2. 否则继续正常的路由决策

        Args:
            user_message: User input message
            high_risk_check_result: Result from high_risk_check tool
            session_mode: Current session mode (BOT_ACTIVE/HUMAN_ACTIVE)
            feature_flags: Feature flags for grayscale control
            intent_match: Intent match result if available
            intent_hint: Soft signal from intent_hint tool (optional)
            context: Additional context for decision

        Returns:
            PolicyRouterResult with decided mode and metadata
        """
        if high_risk_check_result and high_risk_check_result.matched:
            logger.info(
                f"[AC-IDMP-05, AC-IDMP-20] High-risk check matched: "
                f"scenario={high_risk_check_result.risk_scenario}, "
                f"rule_id={high_risk_check_result.rule_id}"
            )
            return self._handle_high_risk_check_result(
                high_risk_check_result, intent_match
            )

        return self.route(
            user_message=user_message,
            session_mode=session_mode,
            feature_flags=feature_flags,
            intent_match=intent_match,
            intent_hint=intent_hint,
            context=context,
        )

    def _handle_high_risk_check_result(
        self,
        high_risk_result: "HighRiskCheckResult",
        intent_match: IntentMatch | None,
    ) -> PolicyRouterResult:
        """
        [AC-IDMP-05] Handle high_risk_check result.

        高风险检测结果优先于普通意图路由。
        """
        recommended_mode = high_risk_result.recommended_mode or ExecutionMode.MICRO_FLOW
        risk_scenario = high_risk_result.risk_scenario

        if recommended_mode == ExecutionMode.TRANSFER:
            transfer_msg = "正在为您转接人工客服..."
            if risk_scenario:
                if risk_scenario == HighRiskScenario.COMPLAINT_ESCALATION:
                    transfer_msg = "检测到您可能需要投诉处理，正在为您转接人工客服..."
                elif risk_scenario == HighRiskScenario.REFUND:
                    transfer_msg = "您的退款请求需要人工处理，正在为您转接..."

            return PolicyRouterResult(
                mode=ExecutionMode.TRANSFER,
                high_risk_triggered=True,
                transfer_message=transfer_msg,
                fallback_reason_code=high_risk_result.rule_id,
            )

        if intent_match and intent_match.flow_id:
            return PolicyRouterResult(
                mode=ExecutionMode.MICRO_FLOW,
                intent=intent_match.intent_name,
                confidence=intent_match.confidence,
                high_risk_triggered=True,
                target_flow_id=intent_match.flow_id,
            )

        return PolicyRouterResult(
            mode=ExecutionMode.MICRO_FLOW,
            high_risk_triggered=True,
            fallback_reason_code=high_risk_result.rule_id
            or f"high_risk_{risk_scenario.value if risk_scenario else 'unknown'}",
        )
