# =============================================================================
# 企微IT智能服务台 — 紧急度评分 + 标记检测服务
# =============================================================================
# 说明：实现 PRD 中定义的紧急度评分公式和标记检测规则
#   1. 紧急度评分：基础分(关键词) + 情绪加成 + VIP加成 + 重复追问加成
#   2. 举手标记检测：关键词匹配
#   3. 需介入标记检测：追问轮次 > 阈值
#   4. 情绪标记检测：关键词规则
# 所有关键词配置存储在 system_configs 表中，可后台动态修改
# =============================================================================

import json
import logging
from typing import Dict, List, Optional
from uuid import UUID

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.message import Message
from app.models.system_config import SystemConfig

logger = logging.getLogger(__name__)


class ScoringService:
    """紧急度评分与标记检测服务。

    实现评分公式：紧急度 = 基础分 + 情绪加成 + VIP加成 + 重复追问加成
    评分范围 1-5，映射：1=低, 2=中, 3=高, 4=紧急, 5=最高

    所有关键词和阈值从 system_configs 表读取，支持后台动态修改。
    """

    # 默认配置（当数据库未配置时使用）
    DEFAULT_HAND_RAISE_KEYWORDS = [
        "转人工", "人工", "人工服务", "真人", "客服",
        "帮我转人工", "找人工", "要人工", "不要AI", "不要机器人",
    ]
    DEFAULT_EMOTION_KEYWORDS = {
        "angry": ["崩溃", "愤怒", "投诉", "差劲", "垃圾", "太差了", "受不了"],
        "urgent": ["急", "紧急", "马上", "立刻", "赶紧", "十万火急", "快点"],
        "worried": ["担心", "害怕", "出错", "丢失", "完蛋", "糟糕"],
    }
    DEFAULT_INTERVENE_THRESHOLD = 3
    DEFAULT_URGENCY_SCORES = {
        "base_keyword": 1,
        "emotion_bonus": 1,
        "vip_bonus": 1,
        "repeat_bonus": 1,
    }

    def __init__(self, db: AsyncSession):
        """初始化评分服务。

        Args:
            db: 异步数据库会话（用于读取 system_configs 配置）
        """
        self.db = db
        # 缓存配置（避免每次请求都查数据库）
        self._config_cache: Dict[str, str] = {}
        self._cache_loaded = False

    # --------------------------------------------------------------------------
    # 配置加载
    # --------------------------------------------------------------------------
    async def _load_configs(self) -> None:
        """从 system_configs 表加载所有配置项到内存缓存。"""
        if self._cache_loaded:
            return

        stmt = select(SystemConfig)
        result = await self.db.execute(stmt)
        configs = result.scalars().all()

        for config in configs:
            self._config_cache[config.config_key] = config.config_value

        self._cache_loaded = True
        logger.debug(f"评分配置加载完成: {len(self._config_cache)} 项")

    async def _get_config(self, key: str, default: str = "") -> str:
        """获取配置值。

        Args:
            key: 配置键
            default: 默认值

        Returns:
            str: 配置值字符串
        """
        await self._load_configs()
        return self._config_cache.get(key, default)

    async def _get_json_config(self, key: str, default: List[str]) -> List[str]:
        """获取 JSON 格式的配置值（解析为列表）。

        Args:
            key: 配置键
            default: 默认值列表

        Returns:
            List[str]: 解析后的字符串列表
        """
        value = await self._get_config(key)
        if not value:
            return default
        try:
            return json.loads(value)
        except json.JSONDecodeError:
            logger.warning(f"配置解析失败: key={key}, value={value}")
            return default

    # --------------------------------------------------------------------------
    # 举手标记检测
    # --------------------------------------------------------------------------
    def detect_hand_raise(self, content: str) -> bool:
        """检测消息中是否包含举手关键词。

        举手关键词如："转人工"、"人工"、"找真人" 等。
        命中任意一个关键词即触发举手标记。

        关键词配置：system_configs 表 hand_raise_keywords 键

        Args:
            content: 消息内容

        Returns:
            bool: 是否触发举手标记
        """
        # 由于 _get_json_config 是异步方法，这里使用默认关键词
        # 在 calculate_urgency 中统一异步加载配置
        keywords = self.DEFAULT_HAND_RAISE_KEYWORDS
        content_lower = content.lower()

        for keyword in keywords:
            if keyword.lower() in content_lower:
                logger.debug(f"举手关键词命中: keyword={keyword}")
                return True

        return False

    async def detect_hand_raise_async(self, content: str) -> bool:
        """异步版本的举手标记检测（从数据库读取配置）。

        Args:
            content: 消息内容

        Returns:
            bool: 是否触发举手标记
        """
        keywords = await self._get_json_config(
            "hand_raise_keywords", self.DEFAULT_HAND_RAISE_KEYWORDS
        )
        content_lower = content.lower()

        for keyword in keywords:
            if keyword.lower() in content_lower:
                logger.debug(f"举手关键词命中: keyword={keyword}")
                return True

        return False

    # --------------------------------------------------------------------------
    # 情绪标记检测
    # --------------------------------------------------------------------------
    def detect_emotion(self, content: str) -> str:
        """检测消息中的情绪标记。

        情绪分类：
        - angry: 愤怒（"崩溃"、"愤怒"、"投诉" 等）
        - urgent: 紧急（"急"、"紧急"、"马上" 等）
        - worried: 担忧（"担心"、"害怕"、"出错" 等）
        - neutral: 正常（无情绪关键词命中）

        检测优先级：angry > urgent > worried（因为愤怒最严重）

        关键词配置：
        - system_configs 表 emotion_keywords_angry 键
        - system_configs 表 emotion_keywords_urgent 键
        - system_configs 表 emotion_keywords_worried 键

        Args:
            content: 消息内容

        Returns:
            str: 情绪类型（angry/urgent/worried/neutral）
        """
        content_lower = content.lower()

        # 按优先级检测：angry > urgent > worried
        for emotion_type, keywords in self.DEFAULT_EMOTION_KEYWORDS.items():
            for keyword in keywords:
                if keyword.lower() in content_lower:
                    logger.debug(f"情绪关键词命中: emotion={emotion_type}, keyword={keyword}")
                    return emotion_type

        return "neutral"

    async def detect_emotion_async(self, content: str) -> str:
        """异步版本的情绪标记检测（从数据库读取配置）。

        Args:
            content: 消息内容

        Returns:
            str: 情绪类型
        """
        content_lower = content.lower()

        # 按优先级检测
        emotion_config_keys = {
            "angry": "emotion_keywords_angry",
            "urgent": "emotion_keywords_urgent",
            "worried": "emotion_keywords_worried",
        }

        for emotion_type, config_key in emotion_config_keys.items():
            keywords = await self._get_json_config(
                config_key, self.DEFAULT_EMOTION_KEYWORDS.get(emotion_type, [])
            )
            for keyword in keywords:
                if keyword.lower() in content_lower:
                    return emotion_type

        return "neutral"

    # --------------------------------------------------------------------------
    # 获取触发的情绪关键词列表
    # --------------------------------------------------------------------------
    def get_emotion_keywords(self, content: str, emotion: str) -> List[str]:
        """获取消息中触发了情绪标记的关键词列表。

        用于在会话标签中记录具体触发了哪些关键词。

        Args:
            content: 消息内容
            emotion: 情绪类型

        Returns:
            List[str]: 触发的关键词列表
        """
        content_lower = content.lower()
        keywords = self.DEFAULT_EMOTION_KEYWORDS.get(emotion, [])
        matched = [kw for kw in keywords if kw in content_lower]
        return matched

    # --------------------------------------------------------------------------
    # 需介入标记检测
    # --------------------------------------------------------------------------
    async def detect_need_intervene(
        self, conversation_id: UUID, db: AsyncSession
    ) -> bool:
        """检测会话是否需要介入（员工追问超过阈值轮次）。

        检测逻辑：
        1. 统计该会话中员工连续发送的消息数（中间无坐席回复）
        2. 如果连续追问轮次 > 阈值（默认3），触发需介入标记

        阈值配置：system_configs 表 intervene_round_threshold 键

        Args:
            conversation_id: 会话ID
            db: 数据库会话

        Returns:
            bool: 是否需要介入
        """
        # 获取阈值
        threshold_str = await self._get_config(
            "intervene_round_threshold", str(self.DEFAULT_INTERVENE_THRESHOLD)
        )
        try:
            threshold = int(threshold_str)
        except ValueError:
            threshold = self.DEFAULT_INTERVENE_THRESHOLD

        # 查询该会话最近的员工消息数
        # 简化逻辑：查询会话中员工发送的总消息数
        stmt = select(func.count(Message.id)).where(
            Message.conversation_id == conversation_id,
            Message.sender_type == "employee",
        )
        result = await db.execute(stmt)
        employee_msg_count = result.scalar() or 0

        # 如果员工消息数 > 阈值，触发需介入
        # 注意：这里是累计消息数，不是连续追问数
        # 更精确的实现应该检查最后 N 条消息是否都是员工发的
        is_need = employee_msg_count > threshold
        if is_need:
            logger.debug(
                f"需介入检测: conv_id={conversation_id}, "
                f"employee_msgs={employee_msg_count}, threshold={threshold}"
            )

        return is_need

    # --------------------------------------------------------------------------
    # 紧急度评分
    # --------------------------------------------------------------------------
    async def calculate_urgency(
        self,
        content: str = "",
        tags: Optional[Dict] = None,
        is_vip: bool = False,
    ) -> int:
        """计算会话紧急度评分。

        公式：紧急度 = 基础分(关键词) + 情绪加成 + VIP加成 + 重复追问加成
        评分范围 1-5，最终结果 clamp 到 [1, 5]

        各项说明：
        - 基础分：消息中是否包含举手/情绪关键词，命中则加分
        - 情绪加成：有情绪标记（非neutral）则加分
        - VIP加成：VIP 员工加分
        - 重复追问加成：追问轮次超过阈值则加分

        分值配置：
        - urgency_base_keyword_score: 关键词基础加分（默认1）
        - urgency_emotion_bonus: 情绪加成分（默认1）
        - urgency_vip_bonus: VIP加成分（默认1）
        - urgency_repeat_bonus: 重复追问加成分（默认1）

        Args:
            content: 消息内容
            tags: 会话标签字典
            is_vip: 是否 VIP 员工

        Returns:
            int: 紧急度评分（1-5）
        """
        if tags is None:
            tags = {}

        # 从配置读取各项分值
        base_keyword_score = int(
            await self._get_config(
                "urgency_base_keyword_score",
                str(self.DEFAULT_URGENCY_SCORES["base_keyword"]),
            )
        )
        emotion_bonus = int(
            await self._get_config(
                "urgency_emotion_bonus",
                str(self.DEFAULT_URGENCY_SCORES["emotion_bonus"]),
            )
        )
        vip_bonus = int(
            await self._get_config(
                "urgency_vip_bonus",
                str(self.DEFAULT_URGENCY_SCORES["vip_bonus"]),
            )
        )
        repeat_bonus = int(
            await self._get_config(
                "urgency_repeat_bonus",
                str(self.DEFAULT_URGENCY_SCORES["repeat_bonus"]),
            )
        )

        # 计算基础分：举手或情绪关键词命中则加分
        score = 1  # 起始分
        if tags.get("hand_raise", False) or tags.get("emotion", "neutral") != "neutral":
            score += base_keyword_score

        # 情绪加成：有情绪标记（非neutral）则加分
        if tags.get("emotion", "neutral") != "neutral":
            score += emotion_bonus

        # VIP 加成
        if is_vip:
            score += vip_bonus

        # 重复追问加成：追问轮次超过阈值则加分
        intervene_threshold = int(
            await self._get_config(
                "intervene_round_threshold",
                str(self.DEFAULT_INTERVENE_THRESHOLD),
            )
        )
        repeat_count = tags.get("repeat_count", 0)
        if repeat_count > intervene_threshold:
            score += repeat_bonus

        # Clamp 到 [1, 5]
        score = max(1, min(5, score))

        logger.debug(
            f"紧急度评分: base={base_keyword_score}, emotion={emotion_bonus}, "
            f"vip={vip_bonus}, repeat={repeat_bonus}, total={score}"
        )
        return score

    # --------------------------------------------------------------------------
    # 重置配置缓存（后台修改配置后调用）
    # --------------------------------------------------------------------------
    def reset_cache(self) -> None:
        """重置配置缓存。

        当管理员在后台修改 system_configs 表后调用，
        下次请求时会重新从数据库加载配置。
        """
        self._config_cache = {}
        self._cache_loaded = False
        logger.info("评分配置缓存已重置")
