"""
Tests for Batch Ask-Back Service.
[AC-MRS-SLOT-ASKBACK-01] 批量追问测试
"""

import time
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from app.services.mid.batch_ask_back_service import (
    AskBackSlot,
    BatchAskBackConfig,
    BatchAskBackResult,
    BatchAskBackService,
    create_batch_ask_back_service,
)


class TestAskBackSlot:
    """AskBackSlot 测试"""

    def test_init(self):
        """测试初始化"""
        slot = AskBackSlot(
            slot_key="region",
            label="地区",
            ask_back_prompt="请告诉我您的地区",
            priority=100,
            is_required=True,
        )
        assert slot.slot_key == "region"
        assert slot.label == "地区"
        assert slot.priority == 100
        assert slot.is_required is True


class TestBatchAskBackConfig:
    """BatchAskBackConfig 测试"""

    def test_default_config(self):
        """测试默认配置"""
        config = BatchAskBackConfig()
        assert config.max_ask_back_slots_per_turn == 2
        assert config.prefer_required is True
        assert config.prefer_scene_relevant is True
        assert config.avoid_recent_asked is True
        assert config.recent_asked_threshold_seconds == 60.0
        assert config.merge_prompts is True

    def test_custom_config(self):
        """测试自定义配置"""
        config = BatchAskBackConfig(
            max_ask_back_slots_per_turn=3,
            prefer_required=False,
            merge_prompts=False,
        )
        assert config.max_ask_back_slots_per_turn == 3
        assert config.prefer_required is False
        assert config.merge_prompts is False


class TestBatchAskBackResult:
    """BatchAskBackResult 测试"""

    def test_has_ask_back(self):
        """测试是否有追问"""
        result = BatchAskBackResult(ask_back_count=2)
        assert result.has_ask_back() is True
        
        result = BatchAskBackResult(ask_back_count=0)
        assert result.has_ask_back() is False

    def test_get_prompt_with_merged(self):
        """测试获取合并后的提示"""
        result = BatchAskBackResult(
            merged_prompt="请告诉我您的地区和产品",
            prompts=["请告诉我您的地区", "请告诉我您的产品"],
            ask_back_count=2,
        )
        assert result.get_prompt() == "请告诉我您的地区和产品"

    def test_get_prompt_without_merged(self):
        """测试获取未合并的提示"""
        result = BatchAskBackResult(
            prompts=["请告诉我您的地区"],
            ask_back_count=1,
        )
        assert result.get_prompt() == "请告诉我您的地区"

    def test_get_prompt_empty(self):
        """测试空结果的提示"""
        result = BatchAskBackResult()
        assert "请提供更多信息" in result.get_prompt()


class TestBatchAskBackService:
    """BatchAskBackService 测试"""

    @pytest.fixture
    def mock_session(self):
        """创建 mock session"""
        return AsyncMock()

    @pytest.fixture
    def config(self):
        """创建配置"""
        return BatchAskBackConfig(
            max_ask_back_slots_per_turn=2,
            prefer_required=True,
            merge_prompts=True,
        )

    @pytest.fixture
    def service(self, mock_session, config):
        """创建服务实例"""
        return BatchAskBackService(
            session=mock_session,
            tenant_id="tenant_1",
            session_id="session_1",
            config=config,
        )

    def test_calculate_priority_required(self, service):
        """测试必填槽位优先级"""
        priority = service._calculate_priority(is_required=True, scene_relevance=0.0)
        assert priority == 100

    def test_calculate_priority_scene_relevant(self, service):
        """测试场景相关优先级"""
        priority = service._calculate_priority(is_required=False, scene_relevance=1.0)
        assert priority == 50

    def test_calculate_priority_both(self, service):
        """测试必填且场景相关优先级"""
        priority = service._calculate_priority(is_required=True, scene_relevance=1.0)
        assert priority == 150

    def test_select_slots_for_ask_back(self, service):
        """测试选择追问槽位"""
        slots = [
            AskBackSlot(slot_key="a", label="A", priority=50),
            AskBackSlot(slot_key="b", label="B", priority=100),
            AskBackSlot(slot_key="c", label="C", priority=75),
        ]
        
        selected = service._select_slots_for_ask_back(slots)
        
        assert len(selected) == 2
        assert selected[0].slot_key == "b"
        assert selected[1].slot_key == "c"

    def test_filter_recently_asked(self, service):
        """测试过滤最近追问过的槽位"""
        current_time = time.time()
        asked_history = {
            "recently_asked": current_time - 30,
            "old_asked": current_time - 120,
        }
        
        slots = [
            AskBackSlot(slot_key="recently_asked", label="最近追问过"),
            AskBackSlot(slot_key="old_asked", label="很久前追问过"),
            AskBackSlot(slot_key="never_asked", label="从未追问过"),
        ]
        
        filtered = service._filter_recently_asked(slots, asked_history)
        
        assert len(filtered) == 2
        slot_keys = [s.slot_key for s in filtered]
        assert "old_asked" in slot_keys
        assert "never_asked" in slot_keys
        assert "recently_asked" not in slot_keys

    def test_generate_prompts(self, service):
        """测试生成追问提示"""
        slots = [
            AskBackSlot(slot_key="region", label="地区", ask_back_prompt="请告诉我您的地区"),
            AskBackSlot(slot_key="product", label="产品", ask_back_prompt=None),
        ]
        
        prompts = service._generate_prompts(slots)
        
        assert len(prompts) == 2
        assert prompts[0] == "请告诉我您的地区"
        assert "产品" in prompts[1]

    def test_merge_prompts_single(self, service):
        """测试合并单个提示"""
        prompts = ["请告诉我您的地区"]
        merged = service._merge_prompts(prompts)
        assert merged == "请告诉我您的地区"

    def test_merge_prompts_two(self, service):
        """测试合并两个提示"""
        prompts = ["请告诉我您的地区", "请告诉我您的产品"]
        merged = service._merge_prompts(prompts)
        assert "地区" in merged
        assert "产品" in merged
        assert "以及" in merged

    def test_merge_prompts_multiple(self, service):
        """测试合并多个提示"""
        prompts = ["您的地区", "您的产品", "您的等级"]
        merged = service._merge_prompts(prompts)
        assert "地区" in merged
        assert "产品" in merged
        assert "等级" in merged
        assert "、" in merged
        assert "以及" in merged

    @pytest.mark.asyncio
    async def test_generate_batch_ask_back_empty(self, service):
        """测试空缺失槽位"""
        result = await service.generate_batch_ask_back(missing_slots=[])
        assert result.has_ask_back() is False

    @pytest.mark.asyncio
    async def test_generate_batch_ask_back_single(self, service):
        """测试单个缺失槽位"""
        missing_slots = [
            {
                "slot_key": "region",
                "label": "地区",
                "ask_back_prompt": "请告诉我您的地区",
            }
        ]
        
        with patch.object(service._slot_def_service, 'get_slot_definition_by_key') as mock_get:
            mock_get.return_value = MagicMock(required=True)
            
            result = await service.generate_batch_ask_back(missing_slots=missing_slots)
        
        assert result.has_ask_back() is True
        assert result.ask_back_count == 1
        assert "地区" in result.get_prompt()

    @pytest.mark.asyncio
    async def test_generate_batch_ask_back_multiple(self, service):
        """测试多个缺失槽位"""
        missing_slots = [
            {"slot_key": "region", "label": "地区", "ask_back_prompt": "您的地区"},
            {"slot_key": "product", "label": "产品", "ask_back_prompt": "您的产品"},
            {"slot_key": "grade", "label": "等级", "ask_back_prompt": "您的等级"},
        ]
        
        with patch.object(service._slot_def_service, 'get_slot_definition_by_key') as mock_get:
            mock_get.return_value = MagicMock(required=True)
            
            result = await service.generate_batch_ask_back(missing_slots=missing_slots)
        
        assert result.has_ask_back() is True
        assert result.ask_back_count == 2

    @pytest.mark.asyncio
    async def test_generate_batch_ask_back_prioritize_required(self, service):
        """测试优先追问必填槽位"""
        missing_slots = [
            {"slot_key": "optional", "label": "可选", "ask_back_prompt": "可选信息"},
            {"slot_key": "required", "label": "必填", "ask_back_prompt": "必填信息"},
        ]
        
        def mock_get_slot(tenant_id, slot_key):
            if slot_key == "required":
                return MagicMock(required=True)
            return MagicMock(required=False)
        
        with patch.object(service._slot_def_service, 'get_slot_definition_by_key', side_effect=mock_get_slot):
            result = await service.generate_batch_ask_back(missing_slots=missing_slots)
        
        assert result.has_ask_back() is True
        assert result.selected_slots[0].slot_key == "required"


class TestCreateBatchAskBackService:
    """create_batch_ask_back_service 工厂函数测试"""

    def test_create(self):
        """测试创建服务实例"""
        mock_session = AsyncMock()
        config = BatchAskBackConfig(max_ask_back_slots_per_turn=3)
        
        service = create_batch_ask_back_service(
            session=mock_session,
            tenant_id="tenant_1",
            session_id="session_1",
            config=config,
        )
        
        assert isinstance(service, BatchAskBackService)
        assert service._tenant_id == "tenant_1"
        assert service._session_id == "session_1"
        assert service._config.max_ask_back_slots_per_turn == 3


class TestAskBackHistory:
    """追问历史测试"""

    @pytest.fixture
    def service(self):
        """创建服务实例"""
        mock_session = AsyncMock()
        return BatchAskBackService(
            session=mock_session,
            tenant_id="tenant_1",
            session_id="session_1",
        )

    @pytest.mark.asyncio
    async def test_get_asked_history_empty(self, service):
        """测试获取空历史"""
        with patch.object(service._cache, '_get_client') as mock_client:
            mock_redis = AsyncMock()
            mock_redis.get = AsyncMock(return_value=None)
            mock_client.return_value = mock_redis
            
            history = await service._get_asked_history()
            assert history == {}

    @pytest.mark.asyncio
    async def test_get_asked_history_with_data(self, service):
        """测试获取有数据的历史"""
        import json
        
        history_data = {"region": 12345.0, "product": 12346.0}
        
        with patch.object(service._cache, '_get_client') as mock_client:
            mock_redis = AsyncMock()
            mock_redis.get = AsyncMock(return_value=json.dumps(history_data))
            mock_client.return_value = mock_redis
            
            history = await service._get_asked_history()
            assert history == history_data
