"""
Coze 服务层单元测试
"""

import asyncio
import pytest
from datetime import datetime
from unittest.mock import Mock, AsyncMock, patch, MagicMock

from cozepy import ChatEventType

from app.services.ai.coze.service import CozeService, get_coze_service
from app.services.ai.coze.models import (
    SessionType, MessageRole, ContentType, StreamEventType,
    CreateSessionRequest, SendMessageRequest, EndSessionRequest,
    CozeSession, CozeMessage, StreamEvent
)
from app.services.ai.coze.exceptions import CozeAPIError


@pytest.fixture
def coze_service():
    """创建测试用的服务实例"""
    with patch("app.services.ai.coze.service.get_coze_client"):
        service = CozeService()
        service.bot_config = {
            "course_chat": "chat-bot-id",
            "training": "training-bot-id",
            "exam": "exam-bot-id"
        }
        service.workspace_id = "test-workspace"
        return service


@pytest.mark.asyncio
class TestCozeService:
    """测试 Coze 服务"""
    
    async def test_create_course_chat_session(self, coze_service):
        """测试创建课程对话会话"""
        # Mock Coze client
        mock_conversation = Mock(id="conv-123")
        coze_service.client.conversations.create = Mock(return_value=mock_conversation)
        
        request = CreateSessionRequest(
            session_type=SessionType.COURSE_CHAT,
            user_id="user-123",
            course_id="course-456"
        )
        
        response = await coze_service.create_session(request)
        
        # 验证结果
        assert response.conversation_id == "conv-123"
        assert response.bot_id == "chat-bot-id"
        assert isinstance(response.session_id, str)
        assert isinstance(response.created_at, datetime)
        
        # 验证会话已保存
        session = coze_service._sessions[response.session_id]
        assert session.session_type == SessionType.COURSE_CHAT
        assert session.user_id == "user-123"
        assert session.metadata["course_id"] == "course-456"
    
    async def test_create_training_session(self, coze_service):
        """测试创建陪练会话"""
        mock_conversation = Mock(id="conv-456")
        coze_service.client.conversations.create = Mock(return_value=mock_conversation)
        
        request = CreateSessionRequest(
            session_type=SessionType.TRAINING,
            user_id="user-789",
            training_topic="客诉处理"
        )
        
        response = await coze_service.create_session(request)
        
        assert response.conversation_id == "conv-456"
        assert response.bot_id == "training-bot-id"
        
        session = coze_service._sessions[response.session_id]
        assert session.session_type == SessionType.TRAINING
        assert session.metadata["training_topic"] == "客诉处理"
    
    async def test_send_message_with_stream(self, coze_service):
        """测试发送消息（流式响应）"""
        # 创建测试会话
        session = CozeSession(
            session_id="test-session",
            conversation_id="conv-123",
            session_type=SessionType.COURSE_CHAT,
            user_id="user-123",
            bot_id="chat-bot-id"
        )
        coze_service._sessions["test-session"] = session
        coze_service._messages["test-session"] = []
        
        # Mock 流式响应
        mock_events = [
            Mock(
                event=ChatEventType.CONVERSATION_MESSAGE_DELTA,
                conversation_id="conv-123",
                message=Mock(content="Hello ")
            ),
            Mock(
                event=ChatEventType.CONVERSATION_MESSAGE_DELTA,
                conversation_id="conv-123",
                message=Mock(content="world!")
            ),
            Mock(
                event=ChatEventType.CONVERSATION_MESSAGE_COMPLETED,
                conversation_id="conv-123",
                message=Mock(content="Hello world!"),
                usage={"tokens": 10}
            )
        ]
        
        coze_service.client.chat.stream = Mock(return_value=iter(mock_events))
        
        request = SendMessageRequest(
            session_id="test-session",
            content="Hi there",
            stream=True
        )
        
        # 收集事件
        events = []
        async for event in coze_service.send_message(request):
            events.append(event)
        
        # 验证事件
        assert len(events) == 4  # 2 delta + 1 completed + 1 done
        assert events[0].event == StreamEventType.MESSAGE_DELTA
        assert events[0].content == "Hello "
        assert events[1].event == StreamEventType.MESSAGE_DELTA
        assert events[1].content == "world!"
        assert events[2].event == StreamEventType.MESSAGE_COMPLETED
        assert events[2].content == "Hello world!"
        assert events[3].event == StreamEventType.DONE
        
        # 验证消息已保存
        messages = coze_service._messages["test-session"]
        assert len(messages) == 2  # 用户消息 + 助手消息
        assert messages[0].role == MessageRole.USER
        assert messages[0].content == "Hi there"
        assert messages[1].role == MessageRole.ASSISTANT
        assert messages[1].content == "Hello world!"
    
    async def test_send_message_error_handling(self, coze_service):
        """测试发送消息错误处理"""
        # 不存在的会话
        request = SendMessageRequest(
            session_id="nonexistent",
            content="Test"
        )
        
        with pytest.raises(CozeAPIError, match="会话不存在"):
            async for _ in coze_service.send_message(request):
                pass
    
    async def test_end_session(self, coze_service):
        """测试结束会话"""
        # 创建测试会话和消息
        created_at = datetime.now()
        session = CozeSession(
            session_id="test-session",
            conversation_id="conv-123",
            session_type=SessionType.TRAINING,
            user_id="user-123",
            bot_id="training-bot-id",
            created_at=created_at
        )
        coze_service._sessions["test-session"] = session
        coze_service._messages["test-session"] = [
            Mock(), Mock(), Mock()  # 3条消息
        ]
        
        request = EndSessionRequest(
            reason="用户主动结束",
            feedback={"rating": 5, "comment": "很有帮助"}
        )
        
        response = await coze_service.end_session("test-session", request)
        
        # 验证响应
        assert response.session_id == "test-session"
        assert isinstance(response.ended_at, datetime)
        assert response.message_count == 3
        assert response.duration_seconds > 0
        
        # 验证会话元数据
        assert session.metadata["end_reason"] == "用户主动结束"
        assert session.metadata["feedback"]["rating"] == 5
    
    async def test_end_nonexistent_session(self, coze_service):
        """测试结束不存在的会话"""
        request = EndSessionRequest()
        
        with pytest.raises(CozeAPIError, match="会话不存在"):
            await coze_service.end_session("nonexistent", request)
    
    async def test_get_session_messages(self, coze_service):
        """测试获取会话消息历史"""
        # 创建测试消息
        messages = [
            CozeMessage(
                message_id=f"msg-{i}",
                session_id="test-session",
                role=MessageRole.USER if i % 2 == 0 else MessageRole.ASSISTANT,
                content=f"Message {i}"
            )
            for i in range(10)
        ]
        coze_service._messages["test-session"] = messages
        
        # 测试分页
        result = await coze_service.get_session_messages("test-session", limit=5, offset=2)
        
        assert len(result) == 5
        assert result[0].content == "Message 2"
        assert result[4].content == "Message 6"
    
    async def test_stream_with_card_content(self, coze_service):
        """测试流式响应中的卡片内容"""
        # 创建测试会话
        session = CozeSession(
            session_id="test-session",
            conversation_id="conv-123",
            session_type=SessionType.EXAM,
            user_id="user-123",
            bot_id="exam-bot-id"
        )
        coze_service._sessions["test-session"] = session
        coze_service._messages["test-session"] = []
        
        # Mock 包含卡片的流式响应
        mock_events = [
            Mock(
                event=ChatEventType.CONVERSATION_MESSAGE_DELTA,
                conversation_id="conv-123",
                message=Mock(content='{"question": "测试题目"}', content_type="card")
            ),
            Mock(
                event=ChatEventType.CONVERSATION_MESSAGE_COMPLETED,
                conversation_id="conv-123",
                message=Mock(content='{"question": "测试题目"}', content_type="card")
            )
        ]
        
        coze_service.client.chat.stream = Mock(return_value=iter(mock_events))
        
        request = SendMessageRequest(
            session_id="test-session",
            content="生成一道考题"
        )
        
        events = []
        async for event in coze_service.send_message(request):
            events.append(event)
        
        # 验证卡片类型被正确识别
        assert events[0].content_type == ContentType.CARD
        assert events[1].content_type == ContentType.CARD
        
        # 验证消息保存时的内容类型
        messages = coze_service._messages["test-session"]
        assert messages[1].content_type == ContentType.CARD


def test_get_coze_service_singleton():
    """测试服务单例"""
    service1 = get_coze_service()
    service2 = get_coze_service()
    assert service1 is service2
