"""
Coze API 网关单元测试
"""

import pytest
from unittest.mock import Mock, AsyncMock, patch
from fastapi.testclient import TestClient
from fastapi import FastAPI
from sse_starlette.sse import ServerSentEvent

from app.api.v1.coze_gateway import router
from app.services.ai.coze.models import (
    CreateSessionResponse, EndSessionResponse,
    StreamEvent, StreamEventType, ContentType, MessageRole
)
from app.services.ai.coze.exceptions import CozeAPIError, CozeAuthError


# 创建测试应用
app = FastAPI()
app.include_router(router)

client = TestClient(app)


@pytest.fixture
def mock_user():
    """模拟已登录用户"""
    with patch("app.api.v1.coze_gateway.get_current_user") as mock_get_user:
        mock_get_user.return_value = {
            "user_id": "test-user-123",
            "username": "test_user"
        }
        yield mock_get_user


@pytest.fixture
def mock_coze_service():
    """模拟 Coze 服务"""
    with patch("app.api.v1.coze_gateway.get_coze_service") as mock_get_service:
        mock_service = Mock()
        mock_get_service.return_value = mock_service
        yield mock_service


class TestCourseChat:
    """测试课程对话 API"""
    
    def test_create_course_chat_session_success(self, mock_user, mock_coze_service):
        """测试成功创建课程对话会话"""
        # Mock 服务响应
        mock_coze_service.create_session = AsyncMock(
            return_value=CreateSessionResponse(
                session_id="session-123",
                conversation_id="conv-123",
                bot_id="bot-123",
                created_at="2024-01-01T10:00:00"
            )
        )
        
        response = client.post(
            "/api/v1/course-chat/sessions",
            json={"course_id": "course-456"}
        )
        
        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        assert data["message"] == "success"
        assert data["data"]["session_id"] == "session-123"
        assert data["data"]["conversation_id"] == "conv-123"
    
    def test_create_course_chat_session_auth_error(self, mock_user, mock_coze_service):
        """测试认证错误"""
        mock_coze_service.create_session = AsyncMock(
            side_effect=CozeAuthError(
                message="认证失败",
                code="AUTH_ERROR",
                status_code=401
            )
        )
        
        response = client.post(
            "/api/v1/course-chat/sessions",
            json={"course_id": "course-456"}
        )
        
        assert response.status_code == 401
        data = response.json()
        assert data["detail"]["code"] == "AUTH_ERROR"
        assert data["detail"]["message"] == "认证失败"
    
    def test_create_course_chat_session_server_error(self, mock_user, mock_coze_service):
        """测试服务器错误"""
        mock_coze_service.create_session = AsyncMock(
            side_effect=Exception("Unexpected error")
        )
        
        response = client.post(
            "/api/v1/course-chat/sessions",
            json={"course_id": "course-456"}
        )
        
        assert response.status_code == 500
        data = response.json()
        assert data["detail"]["code"] == "INTERNAL_ERROR"


class TestTraining:
    """测试陪练 API"""
    
    def test_create_training_session_with_topic(self, mock_user, mock_coze_service):
        """测试创建带主题的陪练会话"""
        mock_coze_service.create_session = AsyncMock(
            return_value=CreateSessionResponse(
                session_id="training-123",
                conversation_id="conv-456",
                bot_id="training-bot",
                created_at="2024-01-01T11:00:00"
            )
        )
        
        response = client.post(
            "/api/v1/training/sessions",
            json={"training_topic": "客诉处理技巧"}
        )
        
        assert response.status_code == 200
        data = response.json()
        assert data["data"]["session_id"] == "training-123"
        
        # 验证服务调用
        call_args = mock_coze_service.create_session.call_args[0][0]
        assert call_args.training_topic == "客诉处理技巧"
    
    def test_create_training_session_without_topic(self, mock_user, mock_coze_service):
        """测试创建不带主题的陪练会话"""
        mock_coze_service.create_session = AsyncMock(
            return_value=CreateSessionResponse(
                session_id="training-456",
                conversation_id="conv-789",
                bot_id="training-bot",
                created_at="2024-01-01T12:00:00"
            )
        )
        
        response = client.post("/api/v1/training/sessions", json={})
        
        assert response.status_code == 200
        data = response.json()
        assert data["data"]["session_id"] == "training-456"
    
    def test_end_training_session_success(self, mock_user, mock_coze_service):
        """测试成功结束陪练会话"""
        mock_coze_service.end_session = AsyncMock(
            return_value=EndSessionResponse(
                session_id="training-123",
                ended_at="2024-01-01T13:00:00",
                duration_seconds=1800,
                message_count=25
            )
        )
        
        response = client.post(
            "/api/v1/training/sessions/training-123/end",
            json={
                "reason": "练习完成",
                "feedback": {"rating": 5, "helpful": True}
            }
        )
        
        assert response.status_code == 200
        data = response.json()
        assert data["data"]["duration_seconds"] == 1800
        assert data["data"]["message_count"] == 25
    
    def test_end_nonexistent_session(self, mock_user, mock_coze_service):
        """测试结束不存在的会话"""
        mock_coze_service.end_session = AsyncMock(
            side_effect=CozeAPIError(
                message="会话不存在",
                code="SESSION_NOT_FOUND",
                status_code=404
            )
        )
        
        response = client.post(
            "/api/v1/training/sessions/nonexistent/end",
            json={}
        )
        
        assert response.status_code == 404


class TestChatMessages:
    """测试消息发送 API"""
    
    def test_send_message_non_stream(self, mock_user, mock_coze_service):
        """测试非流式消息发送"""
        # Mock 异步生成器
        async def mock_generator():
            yield StreamEvent(
                event=StreamEventType.MESSAGE_DELTA,
                data={},
                content="Hello",
                content_type=ContentType.TEXT,
                role=MessageRole.ASSISTANT
            )
            yield StreamEvent(
                event=StreamEventType.MESSAGE_COMPLETED,
                data={"usage": {"tokens": 10}},
                message_id="msg-123",
                content="Hello, how can I help you?",
                content_type=ContentType.TEXT,
                role=MessageRole.ASSISTANT
            )
            yield StreamEvent(
                event=StreamEventType.DONE,
                data={"session_id": "session-123"}
            )
        
        mock_coze_service.send_message = AsyncMock(return_value=mock_generator())
        
        response = client.post(
            "/api/v1/chat/messages",
            json={
                "session_id": "session-123",
                "content": "Hello",
                "stream": False
            }
        )
        
        assert response.status_code == 200
        data = response.json()
        assert data["data"]["content"] == "Hello, how can I help you?"
        assert data["data"]["content_type"] == "text"
        assert data["data"]["role"] == "assistant"
    
    def test_send_message_with_files(self, mock_user, mock_coze_service):
        """测试带附件的消息发送"""
        async def mock_generator():
            yield StreamEvent(
                event=StreamEventType.MESSAGE_COMPLETED,
                data={},
                message_id="msg-456",
                content="File received",
                content_type=ContentType.TEXT,
                role=MessageRole.ASSISTANT
            )
            yield StreamEvent(
                event=StreamEventType.DONE,
                data={"session_id": "session-123"}
            )
        
        mock_coze_service.send_message = AsyncMock(return_value=mock_generator())
        
        response = client.post(
            "/api/v1/chat/messages",
            json={
                "session_id": "session-123",
                "content": "Please analyze this file",
                "file_ids": ["file-123", "file-456"],
                "stream": False
            }
        )
        
        assert response.status_code == 200
        
        # 验证服务调用
        call_args = mock_coze_service.send_message.call_args[0][0]
        assert call_args.file_ids == ["file-123", "file-456"]
    
    def test_get_message_history(self, mock_user, mock_coze_service):
        """测试获取消息历史"""
        from app.services.ai.coze.models import CozeMessage
        
        mock_messages = [
            CozeMessage(
                message_id="msg-1",
                session_id="session-123",
                role=MessageRole.USER,
                content="Hello"
            ),
            CozeMessage(
                message_id="msg-2",
                session_id="session-123",
                role=MessageRole.ASSISTANT,
                content="Hi there!"
            )
        ]
        
        mock_coze_service.get_session_messages = AsyncMock(
            return_value=mock_messages
        )
        
        response = client.get(
            "/api/v1/sessions/session-123/messages?limit=10&offset=0"
        )
        
        assert response.status_code == 200
        data = response.json()
        assert len(data["data"]["messages"]) == 2
        assert data["data"]["messages"][0]["content"] == "Hello"
        assert data["data"]["messages"][1]["content"] == "Hi there!"
        assert data["data"]["limit"] == 10
        assert data["data"]["offset"] == 0
