# =============================================================================
# 企微IT智能服务台 — ScoringService 评分服务测试
# =============================================================================
# 测试覆盖：
#   1. 举手标记检测（关键词命中/未命中）
#   2. 情绪标记检测（angry > urgent > worried > neutral 优先级）
#   3. 需介入标记检测（追问轮次超阈值）
#   4. 紧急度评分公式（基础分 + 情绪加成 + VIP加成 + 重复追问加成）
#   5. 评分结果 clamp 到 [1, 5]
#   6. 配置缓存与重置
#   7. 情绪关键词提取
# =============================================================================

import json
import uuid
from datetime import datetime

import pytest
import pytest_asyncio
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.message import Message
from app.models.system_config import SystemConfig
from app.services.scoring_service import ScoringService
from tests.conftest import create_test_conversation


@pytest.fixture
def scoring_service(db_session):
    """创建评分服务实例。"""
    return ScoringService(db_session)


@pytest_asyncio.fixture
async def seeded_scoring_service(db_session):
    """创建带配置数据的评分服务。"""
    configs = [
        SystemConfig(config_key="hand_raise_keywords", config_value=json.dumps(["转人工", "人工", "人工服务", "真人", "客服", "不要AI"], ensure_ascii=False)),
        SystemConfig(config_key="emotion_keywords_angry", config_value=json.dumps(["崩溃", "愤怒", "投诉", "差劲", "垃圾"], ensure_ascii=False)),
        SystemConfig(config_key="emotion_keywords_urgent", config_value=json.dumps(["急", "紧急", "马上", "立刻", "赶紧"], ensure_ascii=False)),
        SystemConfig(config_key="emotion_keywords_worried", config_value=json.dumps(["担心", "害怕", "出错", "丢失", "完蛋"], ensure_ascii=False)),
        SystemConfig(config_key="intervene_round_threshold", config_value="3"),
        SystemConfig(config_key="urgency_base_keyword_score", config_value="1"),
        SystemConfig(config_key="urgency_emotion_bonus", config_value="1"),
        SystemConfig(config_key="urgency_vip_bonus", config_value="1"),
        SystemConfig(config_key="urgency_repeat_bonus", config_value="1"),
    ]
    db_session.add_all(configs)
    await db_session.flush()
    return ScoringService(db_session)


class TestDetectHandRaise:
    """测试举手标记检测。"""

    def test_hand_raise_with_keyword_转人工(self, scoring_service):
        """验证包含"转人工"关键词时触发举手标记。"""
        assert scoring_service.detect_hand_raise("我要转人工") is True

    def test_hand_raise_with_keyword_人工(self, scoring_service):
        """验证包含"人工"关键词时触发举手标记。"""
        assert scoring_service.detect_hand_raise("找人工客服") is True

    def test_hand_raise_with_keyword_真人(self, scoring_service):
        """验证包含"真人"关键词时触发举手标记。"""
        assert scoring_service.detect_hand_raise("我要找真人") is True

    def test_hand_raise_with_keyword_不要AI(self, scoring_service):
        """验证包含"不要AI"关键词时触发举手标记。"""
        assert scoring_service.detect_hand_raise("不要AI，找人") is True

    def test_no_hand_raise_normal_message(self, scoring_service):
        """验证普通消息不触发举手标记。"""
        assert scoring_service.detect_hand_raise("我的VPN连不上") is False

    def test_no_hand_raise_empty_message(self, scoring_service):
        """验证空消息不触发举手标记。"""
        assert scoring_service.detect_hand_raise("") is False


class TestDetectEmotion:
    """测试情绪标记检测。"""

    def test_detect_angry_emotion(self, scoring_service):
        """验证愤怒情绪关键词检测（最高优先级）。"""
        assert scoring_service.detect_emotion("崩溃了，系统太差劲了") == "angry"

    def test_detect_urgent_emotion(self, scoring_service):
        """验证紧急情绪关键词检测。"""
        assert scoring_service.detect_emotion("很急，赶紧帮我处理") == "urgent"

    def test_detect_worried_emotion(self, scoring_service):
        """验证担忧情绪关键词检测。"""
        assert scoring_service.detect_emotion("我担心数据丢失了") == "worried"

    def test_detect_neutral_no_keywords(self, scoring_service):
        """验证无情绪关键词时返回 neutral。"""
        assert scoring_service.detect_emotion("帮我重置密码") == "neutral"

    def test_emotion_priority_angry_over_urgent(self, scoring_service):
        """验证愤怒优先级高于紧急（同时包含两种关键词时）。"""
        # "紧急" 是 urgent 关键词，"垃圾" 是 angry 关键词
        result = scoring_service.detect_emotion("太垃圾了，紧急处理")
        assert result == "angry"

    def test_emotion_priority_urgent_over_worried(self, scoring_service):
        """验证紧急优先级高于担忧。"""
        # "急" 是 urgent 关键词，"担心" 是 worried 关键词
        result = scoring_service.detect_emotion("我很急，也担心出问题")
        assert result == "urgent"

    def test_detect_empty_message_returns_neutral(self, scoring_service):
        """验证空消息返回 neutral。"""
        assert scoring_service.detect_emotion("") == "neutral"


class TestGetEmotionKeywords:
    """测试情绪关键词提取。"""

    def test_get_matched_angry_keywords(self, scoring_service):
        """验证提取匹配的愤怒关键词。"""
        matched = scoring_service.get_emotion_keywords("崩溃了，太差劲了", "angry")
        assert "崩溃" in matched
        assert "差劲" in matched

    def test_get_no_matched_keywords_for_neutral(self, scoring_service):
        """验证 neutral 情绪无匹配关键词。"""
        matched = scoring_service.get_emotion_keywords("普通消息", "neutral")
        assert matched == []


class TestDetectNeedIntervene:
    """测试需介入标记检测。"""

    @pytest_asyncio.fixture
    async def conversation_with_messages(self, db_session):
        """创建带消息的会话。"""
        conv = create_test_conversation(employee_id="intervene_test_user")
        db_session.add(conv)
        await db_session.flush()

        # 添加 4 条员工消息（超过阈值 3）
        for i in range(4):
            msg = Message(
                conversation_id=conv.id,
                sender_type="employee",
                sender_id="intervene_test_user",
                content=f"第{i+1}条消息",
                msg_type="text",
                is_read=False,
            )
            db_session.add(msg)
        await db_session.flush()
        return conv

    @pytest.mark.asyncio
    async def test_need_intervene_when_messages_exceed_threshold(
        self, db_session, seeded_scoring_service, conversation_with_messages
    ):
        """验证员工消息数超过阈值时触发需介入标记。"""
        result = await seeded_scoring_service.detect_need_intervene(
            conversation_with_messages.id, db_session
        )
        assert result is True

    @pytest.mark.asyncio
    async def test_no_need_intervene_when_messages_below_threshold(
        self, db_session, seeded_scoring_service
    ):
        """验证员工消息数未超过阈值时不触发需介入标记。"""
        conv = create_test_conversation(employee_id="low_msg_user")
        db_session.add(conv)
        await db_session.flush()

        # 只添加 2 条消息（低于阈值 3）
        for i in range(2):
            msg = Message(
                conversation_id=conv.id,
                sender_type="employee",
                sender_id="low_msg_user",
                content=f"第{i+1}条消息",
                msg_type="text",
                is_read=False,
            )
            db_session.add(msg)
        await db_session.flush()

        result = await seeded_scoring_service.detect_need_intervene(conv.id, db_session)
        assert result is False


class TestCalculateUrgency:
    """测试紧急度评分计算。"""

    @pytest.mark.asyncio
    async def test_base_urgency_no_factors(self, seeded_scoring_service):
        """验证无任何加分因素时紧急度为 1（最低）。"""
        score = await seeded_scoring_service.calculate_urgency(
            content="普通消息",
            tags={},
            is_vip=False,
        )
        assert score == 1

    @pytest.mark.asyncio
    async def test_urgency_with_hand_raise(self, seeded_scoring_service):
        """验证举手标记增加紧急度。"""
        score = await seeded_scoring_service.calculate_urgency(
            content="转人工",
            tags={"hand_raise": True},
            is_vip=False,
        )
        # 1(基础) + 1(hand_raise关键词加分) = 2
        assert score == 2

    @pytest.mark.asyncio
    async def test_urgency_with_emotion(self, seeded_scoring_service):
        """验证情绪标记增加紧急度。"""
        score = await seeded_scoring_service.calculate_urgency(
            content="太差劲了",
            tags={"emotion": "angry"},
            is_vip=False,
        )
        # 1(基础) + 1(关键词加分) + 1(情绪加成) = 3
        assert score == 3

    @pytest.mark.asyncio
    async def test_urgency_with_vip(self, seeded_scoring_service):
        """验证 VIP 标记增加紧急度。"""
        score = await seeded_scoring_service.calculate_urgency(
            content="普通消息",
            tags={"hand_raise": True},
            is_vip=True,
        )
        # 1(基础) + 1(关键词加分) + 1(VIP加成) = 3
        assert score == 3

    @pytest.mark.asyncio
    async def test_urgency_with_repeat_count(self, seeded_scoring_service):
        """验证重复追问超过阈值增加紧急度。"""
        score = await seeded_scoring_service.calculate_urgency(
            content="普通消息",
            tags={"repeat_count": 5},  # 超过阈值 3
            is_vip=False,
        )
        # 1(基础) + 1(重复追问加成) = 2
        assert score == 2

    @pytest.mark.asyncio
    async def test_urgency_max_clamp_to_5(self, seeded_scoring_service):
        """验证紧急度上限为 5。"""
        score = await seeded_scoring_service.calculate_urgency(
            content="转人工",
            tags={"hand_raise": True, "emotion": "angry", "repeat_count": 10},
            is_vip=True,
        )
        # 1 + 1(关键词) + 1(情绪) + 1(VIP) + 1(重复) = 5，超过上限 clamp 到 5
        assert score == 5

    @pytest.mark.asyncio
    async def test_urgency_min_clamp_to_1(self, seeded_scoring_service):
        """验证紧急度下限为 1。"""
        score = await seeded_scoring_service.calculate_urgency(
            content="普通消息",
            tags={},
            is_vip=False,
        )
        assert score >= 1

    @pytest.mark.asyncio
    async def test_urgency_all_factors_combined(self, seeded_scoring_service):
        """验证所有加分因素组合时的紧急度。"""
        score = await seeded_scoring_service.calculate_urgency(
            content="崩溃了，转人工",
            tags={"hand_raise": True, "emotion": "angry", "repeat_count": 5},
            is_vip=True,
        )
        # 1 + 1(关键词) + 1(情绪) + 1(VIP) + 1(重复) = 5
        assert score == 5


class TestConfigCache:
    """测试配置缓存机制。"""

    @pytest.mark.asyncio
    async def test_cache_loaded_once(self, seeded_scoring_service):
        """验证配置只加载一次（缓存机制）。"""
        # 第一次调用会加载配置
        await seeded_scoring_service._load_configs()
        assert seeded_scoring_service._cache_loaded is True

        # 第二次调用应直接返回（不再查数据库）
        cache_before = seeded_scoring_service._config_cache.copy()
        await seeded_scoring_service._load_configs()
        assert seeded_scoring_service._config_cache == cache_before

    def test_reset_cache(self, seeded_scoring_service):
        """验证缓存重置功能。"""
        seeded_scoring_service._cache_loaded = True
        seeded_scoring_service._config_cache = {"test": "value"}

        seeded_scoring_service.reset_cache()

        assert seeded_scoring_service._cache_loaded is False
        assert seeded_scoring_service._config_cache == {}
