#!/usr/bin/env python3
"""全面的网关回归测试和端到端测试"""

import asyncio
import os
import sys
from pathlib import Path
from unittest.mock import Mock, AsyncMock, patch
import json

# 添加项目路径
sys.path.insert(0, str(Path(__file__).parent))

class TestGatewayComprehensive:
    """全面的网关测试套件"""
    
    def __init__(self):
        self.test_results = []
        self.passed = 0
        self.failed = 0
    
    def log_test(self, name, success, message=""):
        """记录测试结果"""
        result = {
            "name": name,
            "success": success,
            "message": message
        }
        self.test_results.append(result)
        if success:
            self.passed += 1
            print(f"✅ {name}: 通过")
            if message:
                print(f"   ℹ️  {message}")
        else:
            self.failed += 1
            print(f"❌ {name}: 失败 - {message}")
    
    async def test_1_openai_gateway_initialization(self):
        """测试1: OpenAI网关初始化"""
        try:
            from core.main_agent import MainAgent
            
            # 模拟依赖
            mock_context = AsyncMock()
            mock_safety = Mock()
            
            model_config = {
                "name": "gpt-4",
                "gateway": "openai",
                "api_key": "test_openai_key",
                "base_url": "https://api.openai.com/v1",
                "temperature": 0.1,
                "max_tokens": 100
            }
            
            agent = MainAgent(
                project_path=Path.cwd(),
                context_manager=mock_context,
                safety_guard=mock_safety,
                model_config=model_config,
            )
            
            # 验证网关类型
            if hasattr(agent, 'model_caller'):
                self.log_test("OpenAI网关初始化", True, "网关类型: openai")
                return True
            else:
                self.log_test("OpenAI网关初始化", False, "缺少model_caller属性")
                return False
                
        except Exception as e:
            self.log_test("OpenAI网关初始化", False, f"异常: {str(e)}")
            return False
    
    async def test_2_anthropic_gateway_initialization(self):
        """测试2: Anthropic网关初始化"""
        try:
            from core.main_agent import MainAgent
            
            # 模拟依赖
            mock_context = AsyncMock()
            mock_safety = Mock()
            
            model_config = {
                "name": "MiniMax-M2.5",
                "gateway": "anthropic",
                "api_key": "test_anthropic_key",
                "base_url": "https://api.minimax.chat/v1",
                "temperature": 0.1,
                "max_tokens": 100
            }
            
            agent = MainAgent(
                project_path=Path.cwd(),
                context_manager=mock_context,
                safety_guard=mock_safety,
                model_config=model_config,
            )
            
            # 验证网关类型
            if hasattr(agent, 'model_caller'):
                self.log_test("Anthropic网关初始化", True, "网关类型: anthropic")
                return True
            else:
                self.log_test("Anthropic网关初始化", False, "缺少model_caller属性")
                return False
                
        except Exception as e:
            self.log_test("Anthropic网关初始化", False, f"异常: {str(e)}")
            return False
    
    async def test_3_gateway_auto_detection(self):
        """测试3: 网关自动检测"""
        try:
            from config import Settings
            
            # 测试不同模型的自动检测
            test_cases = [
                ("MiniMax-M2.5", "anthropic", "https://api.minimax.chat/v1"),
                ("minimax-m2.5", "anthropic", "https://api.minimax.chat/v1"),
                ("gpt-4", "openai", "https://api.openai.com/v1"),
                ("deepseek-chat", "openai", "https://api.deepseek.com/v1"),
                ("kimi-k2.5", "openai", "https://api.moonshot.cn/v1"),
            ]
            
            all_passed = True
            for model_name, expected_gateway, expected_url in test_cases:
                # 设置环境变量
                os.environ["LLM_MODEL_NAME"] = model_name
                os.environ.pop("LLM_GATEWAY", None)  # 清除显式设置
                os.environ.pop("LLM_API_URL", None)  # 清除显式设置
                
                # 重新加载配置
                settings = Settings()
                
                if settings.model.gateway == expected_gateway:
                    print(f"  ✅ {model_name}: 检测到网关 {settings.model.gateway}")
                else:
                    print(f"  ❌ {model_name}: 期望网关 {expected_gateway}, 实际 {settings.model.gateway}")
                    all_passed = False
                
                if expected_url in settings.model.base_url:
                    print(f"  ✅ {model_name}: 检测到URL {settings.model.base_url}")
                else:
                    print(f"  ⚠️  {model_name}: 期望URL包含 {expected_url}, 实际 {settings.model.base_url}")
            
            self.log_test("网关自动检测", all_passed, f"测试了{len(test_cases)}个模型")
            return all_passed
                
        except Exception as e:
            self.log_test("网关自动检测", False, f"异常: {str(e)}")
            return False
    
    async def test_4_message_format_conversion(self):
        """测试4: 消息格式转换测试"""
        try:
            from core.main_agent import MainAgent
            
            # 创建测试消息
            test_messages = [
                {"role": "system", "content": "你是一个助手"},
                {"role": "user", "content": "你好"},
                {"role": "assistant", "content": "你好！有什么可以帮助你的？"},
                {"role": "user", "content": "谢谢"}
            ]
            
            # 测试OpenAI格式
            mock_context = AsyncMock()
            mock_safety = Mock()
            
            model_config = {
                "name": "gpt-4",
                "gateway": "openai",
                "api_key": "test_key",
                "base_url": "https://api.openai.com/v1",
                "temperature": 0.1,
                "max_tokens": 100
            }
            
            agent = MainAgent(
                project_path=Path.cwd(),
                context_manager=mock_context,
                safety_guard=mock_safety,
                model_config=model_config,
            )
            
            # 模拟OpenAI响应
            with patch('openai.OpenAI') as mock_openai:
                mock_client = Mock()
                mock_openai.return_value = mock_client
                
                mock_stream = Mock()
                mock_chunk = Mock()
                mock_chunk.choices = [Mock()]
                mock_chunk.choices[0].delta = Mock(content="测试响应")
                mock_stream.__iter__ = Mock(return_value=iter([mock_chunk]))
                mock_client.chat.completions.create.return_value = mock_stream
                
                try:
                    response = await agent.model_caller(test_messages)
                    self.log_test("OpenAI消息格式", True, "消息格式转换成功")
                except Exception as e:
                    self.log_test("OpenAI消息格式", False, f"OpenAI格式异常: {str(e)}")
                    return False
            
            # 测试Anthropic格式
            model_config["gateway"] = "anthropic"
            model_config["name"] = "MiniMax-M2.5"
            model_config["base_url"] = "https://api.minimax.chat/v1"
            
            agent2 = MainAgent(
                project_path=Path.cwd(),
                context_manager=mock_context,
                safety_guard=mock_safety,
                model_config=model_config,
            )
            
            # 模拟Anthropic响应
            with patch('anthropic.Anthropic') as mock_anthropic:
                mock_client = Mock()
                mock_anthropic.return_value = mock_client
                
                mock_messages = Mock()
                mock_client.messages = mock_messages
                
                mock_stream = Mock()
                mock_stream.text_stream = iter(["测试", "响应"])
                mock_stream.__enter__ = Mock(return_value=mock_stream)
                mock_stream.__exit__ = Mock(return_value=None)
                mock_messages.stream.return_value = mock_stream
                
                try:
                    # 使用run_in_executor模拟异步
                    with patch('asyncio.get_event_loop') as mock_loop:
                        mock_loop_instance = Mock()
                        mock_loop.return_value = mock_loop_instance
                        mock_loop_instance.run_in_executor.return_value = asyncio.Future()
                        mock_loop_instance.run_in_executor.return_value.set_result("测试响应")
                        
                        response = await agent2.model_caller(test_messages)
                        self.log_test("Anthropic消息格式", True, "消息格式转换成功")
                except Exception as e:
                    self.log_test("Anthropic消息格式", False, f"Anthropic格式异常: {str(e)}")
                    return False
            
            return True
                
        except Exception as e:
            self.log_test("消息格式转换", False, f"异常: {str(e)}")
            return False
    
    async def test_5_error_handling(self):
        """测试5: 错误处理机制"""
        try:
            from core.main_agent import MainAgent
            
            # 模拟依赖
            mock_context = AsyncMock()
            mock_safety = Mock()
            
            # 测试认证错误
            model_config = {
                "name": "gpt-4",
                "gateway": "openai",
                "api_key": "invalid_key",
                "base_url": "https://api.openai.com/v1",
                "temperature": 0.1,
                "max_tokens": 100
            }
            
            agent = MainAgent(
                project_path=Path.cwd(),
                context_manager=mock_context,
                safety_guard=mock_safety,
                model_config=model_config,
            )
            
            test_messages = [{"role": "user", "content": "测试"}]
            
            # 模拟认证错误
            with patch('openai.OpenAI') as mock_openai:
                mock_client = Mock()
                mock_openai.return_value = mock_client
                mock_client.chat.completions.create.side_effect = Exception("401 Authentication Failed")
                
                try:
                    await agent.model_caller(test_messages)
                    self.log_test("认证错误处理", False, "应该抛出异常但未抛出")
                    return False
                except RuntimeError as e:
                    if "API认证失败" in str(e):
                        self.log_test("认证错误处理", True, "正确捕获认证错误")
                    else:
                        self.log_test("认证错误处理", False, f"错误类型不正确: {str(e)}")
                        return False
                except Exception as e:
                    self.log_test("认证错误处理", False, f"未捕获到RuntimeError: {type(e).__name__}: {str(e)}")
                    return False
            
            # 测试网络错误
            with patch('openai.OpenAI') as mock_openai:
                mock_client = Mock()
                mock_openai.return_value = mock_client
                mock_client.chat.completions.create.side_effect = Exception("Connection failed")
                
                try:
                    await agent.model_caller(test_messages)
                    self.log_test("网络错误处理", False, "应该抛出异常但未抛出")
                    return False
                except RuntimeError as e:
                    if "网络连接失败" in str(e):
                        self.log_test("网络错误处理", True, "正确捕获网络错误")
                    else:
                        self.log_test("网络错误处理", False, f"错误类型不正确: {str(e)}")
                        return False
            
            # 测试Anthropic网关后备机制
            model_config["gateway"] = "anthropic"
            model_config["name"] = "MiniMax-M2.5"
            
            agent2 = MainAgent(
                project_path=Path.cwd(),
                context_manager=mock_context,
                safety_guard=mock_safety,
                model_config=model_config,
            )
            
            # 模拟Anthropic格式失败，触发OpenAI后备
            with patch('anthropic.Anthropic') as mock_anthropic:
                mock_client = Mock()
                mock_anthropic.return_value = mock_client
                mock_messages = Mock()
                mock_client.messages = mock_messages
                mock_messages.stream.side_effect = Exception("404 page not found")
                
                # 模拟OpenAI成功响应
                with patch('openai.OpenAI') as mock_openai2:
                    mock_openai_client = Mock()
                    mock_openai2.return_value = mock_openai_client
                    
                    mock_stream = Mock()
                    mock_chunk = Mock()
                    mock_chunk.choices = [Mock()]
                    mock_chunk.choices[0].delta = Mock(content="后备响应")
                    mock_stream.__iter__ = Mock(return_value=iter([mock_chunk]))
                    mock_openai_client.chat.completions.create.return_value = mock_stream
                    
                    try:
                        response = await agent2.model_caller(test_messages)
                        self.log_test("Anthropic后备机制", True, "成功触发OpenAI后备")
                    except Exception as e:
                        self.log_test("Anthropic后备机制", False, f"后备机制失败: {str(e)}")
                        return False
            
            return True
                
        except Exception as e:
            self.log_test("错误处理机制", False, f"异常: {str(e)}")
            return False
    
    async def test_6_streaming_output(self):
        """测试6: 流式输出功能"""
        try:
            from core.main_agent import MainAgent
            
            # 模拟依赖
            mock_context = AsyncMock()
            mock_safety = Mock()
            
            # 测试OpenAI流式输出
            model_config = {
                "name": "gpt-4",
                "gateway": "openai",
                "api_key": "test_key",
                "base_url": "https://api.openai.com/v1",
                "temperature": 0.1,
                "max_tokens": 100
            }
            
            agent = MainAgent(
                project_path=Path.cwd(),
                context_manager=mock_context,
                safety_guard=mock_safety,
                model_config=model_config,
            )
            
            test_messages = [{"role": "user", "content": "测试流式输出"}]
            
            # 模拟流式响应
            with patch('openai.OpenAI') as mock_openai:
                mock_client = Mock()
                mock_openai.return_value = mock_client
                
                # 创建模拟的流式响应
                class MockStream:
                    def __init__(self):
                        self.chunks = [
                            Mock(choices=[Mock(delta=Mock(content="流式"))]),
                            Mock(choices=[Mock(delta=Mock(content="输出"))]),
                            Mock(choices=[Mock(delta=Mock(content="测试"))]),
                        ]
                        self.index = 0
                    
                    def __iter__(self):
                        return self
                    
                    def __next__(self):
                        if self.index < len(self.chunks):
                            chunk = self.chunks[self.index]
                            self.index += 1
                            return chunk
                        raise StopIteration
                
                mock_stream = MockStream()
                mock_client.chat.completions.create.return_value = mock_stream
                
                # 捕获打印输出
                import io
                from contextlib import redirect_stdout
                
                f = io.StringIO()
                with redirect_stdout(f):
                    response = await agent.model_caller(test_messages)
                
                output = f.getvalue()
                if "流式输出测试" in response:
                    self.log_test("OpenAI流式输出", True, "流式输出功能正常")
                else:
                    self.log_test("OpenAI流式输出", False, f"响应不完整: {response}")
                    return False
            
            # 测试Anthropic流式输出
            model_config["gateway"] = "anthropic"
            model_config["name"] = "MiniMax-M2.5"
            
            agent2 = MainAgent(
                project_path=Path.cwd(),
                context_manager=mock_context,
                safety_guard=mock_safety,
                model_config=model_config,
            )
            
            with patch('anthropic.Anthropic') as mock_anthropic:
                mock_client = Mock()
                mock_anthropic.return_value = mock_client
                mock_messages = Mock()
                mock_client.messages = mock_messages
                
                # 模拟Anthropic流式响应
                class MockAnthropicStream:
                    def __init__(self):
                        self.texts = ["Anthropic", "流式", "输出"]
                        self.index = 0
                    
                    @property
                    def text_stream(self):
                        return self
                    
                    def __iter__(self):
                        return self
                    
                    def __next__(self):
                        if self.index < len(self.texts):
                            text = self.texts[self.index]
                            self.index += 1
                            return text
                        raise StopIteration
                    
                    def __enter__(self):
                        return self
                    
                    def __exit__(self, *args):
                        pass
                
                mock_stream = MockAnthropicStream()
                mock_messages.stream.return_value = mock_stream
                
                # 模拟run_in_executor
                with patch('asyncio.get_event_loop') as mock_loop:
                    mock_loop_instance = Mock()
                    mock_loop.return_value = mock_loop_instance
                    
                    def sync_call():
                        result = ""
                        with mock_stream as stream:
                            for text in stream.text_stream:
                                result += text
                        return result
                    
                    mock_loop_instance.run_in_executor.return_value = asyncio.Future()
                    mock_loop_instance.run_in_executor.return_value.set_result("Anthropic流式输出")
                    
                    f = io.StringIO()
                    with redirect_stdout(f):
                        response = await agent2.model_caller(test_messages)
                    
                    if "Anthropic流式输出" in response:
                        self.log_test("Anthropic流式输出", True, "流式输出功能正常")
                    else:
                        self.log_test("Anthropic流式输出", False, f"响应不完整: {response}")
                        return False
            
            return True
                
        except Exception as e:
            self.log_test("流式输出功能", False, f"异常: {str(e)}")
            return False
    
    async def test_7_end_to_end_integration(self):
        """测试7: 端到端集成测试"""
        try:
            # 测试完整的Agent执行流程
            from core.main_agent import MainAgent
            
            # 模拟所有依赖
            mock_context = AsyncMock()
            mock_context.add_context = AsyncMock()
            mock_context.get_context = AsyncMock(return_value="")
            
            mock_safety = Mock()
            mock_safety.check_command = Mock(return_value=True)
            mock_safety.check_file_access = Mock(return_value=True)
            
            # 使用OpenAI网关
            model_config = {
                "name": "gpt-4",
                "gateway": "openai",
                "api_key": "test_key",
                "base_url": "https://api.openai.com/v1",
                "temperature": 0.1,
                "max_tokens": 100
            }
            
            # 创建Agent - 使用patch完全模拟API调用
            with patch('openai.OpenAI') as mock_openai:
                mock_client = Mock()
                mock_openai.return_value = mock_client
                
                # 模拟流式响应
                mock_stream = Mock()
                mock_chunk = Mock()
                mock_chunk.choices = [Mock()]
                mock_chunk.choices[0].delta = Mock(content="模拟响应")
                mock_stream.__iter__ = Mock(return_value=iter([mock_chunk]))
                mock_client.chat.completions.create.return_value = mock_stream
                
                # 创建Agent
                agent = MainAgent(
                    project_path=Path.cwd(),
                    context_manager=mock_context,
                    safety_guard=mock_safety,
                    model_config=model_config,
                    max_iterations=3  # 限制迭代次数
                )
                
                # 模拟工具执行
                mock_tools = {
                    "read_file": AsyncMock(return_value={"success": True, "content": "测试文件内容"}),
                    "list_files": AsyncMock(return_value={"success": True, "files": ["test.py"]}),
                }
                
                # 替换工具
                agent.tools = mock_tools
                
                # 执行任务
                try:
                    result = await agent.execute(
                        task="测试端到端流程",
                        init_instructions="这是一个测试任务",
                        max_iterations=2
                    )
                    
                    if result and "session_id" in result:
                        self.log_test("端到端集成测试", True, f"任务执行成功，会话ID: {result['session_id']}")
                        return True
                    else:
                        self.log_test("端到端集成测试", False, "任务执行结果不完整")
                        return False
                        
                except Exception as e:
                    self.log_test("端到端集成测试", False, f"执行异常: {str(e)}")
                    return False
                
        except Exception as e:
            self.log_test("端到端集成测试", False, f"初始化异常: {str(e)}")
            return False
    
    async def test_8_config_validation(self):
        """测试8: 配置验证"""
        try:
            from config import Settings
            
            # 测试有效配置
            os.environ["LLM_API_KEY"] = "valid_key"
            os.environ.pop("LLM_MODEL_NAME", None)
            
            settings = Settings()
            errors = settings.validate()
            
            if not errors:
                self.log_test("配置验证-有效配置", True, "有效配置通过验证")
            else:
                self.log_test("配置验证-有效配置", False, f"不应该有错误但得到: {errors}")
                return False
            
            # 测试无效配置（缺少API密钥）
            os.environ.pop("LLM_API_KEY", None)
            os.environ.pop("OPENAI_API_KEY", None)
            
            settings2 = Settings()
            settings2.model.api_key = None  # 确保没有API密钥
            errors2 = settings2.validate()
            
            if errors2 and any("API密钥" in error for error in errors2):
                self.log_test("配置验证-无效配置", True, "正确检测到缺少API密钥")
            else:
                self.log_test("配置验证-无效配置", False, f"应该检测到API密钥错误但得到: {errors2}")
                return False
            
            return True
                
        except Exception as e:
            self.log_test("配置验证", False, f"异常: {str(e)}")
            return False
    
    def print_summary(self):
        """打印测试摘要"""
        print("\n" + "="*60)
        print("测试摘要")
        print("="*60)
        
        for result in self.test_results:
            status = "✅" if result["success"] else "❌"
            print(f"{status} {result['name']}")
            if result["message"] and not result["success"]:
                print(f"   原因: {result['message']}")
        
        print("\n" + "="*60)
        print(f"总计: {len(self.test_results)} 个测试")
        print(f"通过: {self.passed}")
        print(f"失败: {self.failed}")
        print("="*60)
        
        return self.failed == 0
    
    async def run_all_tests(self):
        """运行所有测试"""
        print("🚀 开始全面的网关回归测试和端到端测试")
        print("="*60)
        
        # 运行所有测试
        tests = [
            self.test_1_openai_gateway_initialization,
            self.test_2_anthropic_gateway_initialization,
            self.test_3_gateway_auto_detection,
            self.test_4_message_format_conversion,
            self.test_5_error_handling,
            self.test_6_streaming_output,
            self.test_7_end_to_end_integration,
            self.test_8_config_validation,
        ]
        
        for i, test in enumerate(tests, 1):
            print(f"\n📋 测试 {i}/{len(tests)}: {test.__doc__}")
            print("-"*40)
            await test()
        
        # 打印摘要
        success = self.print_summary()
        return success

async def main():
    """主函数"""
    tester = TestGatewayComprehensive()
    success = await tester.run_all_tests()
    
    if success:
        print("\n🎉 所有测试通过！网关功能正常。")
        return 0
    else:
        print("\n⚠️  部分测试失败，请检查问题。")
        return 1

if __name__ == "__main__":
    # 设置环境变量避免干扰
    os.environ.pop("LLM_API_KEY", None)
    os.environ.pop("OPENAI_API_KEY", None)
    os.environ.pop("LLM_GATEWAY", None)
    os.environ.pop("LLM_MODEL_NAME", None)
    os.environ.pop("LLM_API_URL", None)
    
    exit_code = asyncio.run(main())
    sys.exit(exit_code)