#!/usr/bin/env python3
"""
考培练系统 AI 功能完整测试脚本

测试所有 AI 功能是否正常：
1. AI 基础服务连通性（4sapi + OpenRouter）
2. 试题生成功能（V2 引擎）
3. 答案判断功能（V2 引擎）
4. 知识点分析功能（V2 引擎）
5. 课程对话功能（V2 引擎）
6. 陪练场景提取功能（V2 引擎）
7. 能力分析功能（V2 引擎）

测试方式：通过 HTTP API 调用后端服务
"""

import asyncio
import httpx
import json
import sys
import time
from datetime import datetime
from typing import Optional, Dict, Any, List

# ==================== 配置 ====================

# 测试基础URL（演示版后端）
BASE_URL = "http://localhost:8000/api/v1"

# 测试账户（从数据库获取有效用户）
TEST_USERNAME = "admin"
TEST_PASSWORD = "admin123"

# 测试超时时间（秒）
TIMEOUT = 120

# 测试结果统计
test_results: List[Dict[str, Any]] = []

# ==================== 工具函数 ====================

def print_header(title: str):
    """打印标题"""
    print("\n" + "=" * 60)
    print(f"  {title}")
    print("=" * 60)

def print_result(test_name: str, success: bool, message: str, duration_ms: int = 0):
    """打印测试结果"""
    status = "✅ 通过" if success else "❌ 失败"
    duration_str = f" ({duration_ms}ms)" if duration_ms > 0 else ""
    print(f"  {status} | {test_name}{duration_str}")
    if not success:
        print(f"       └─ {message}")
    
    # 记录结果
    test_results.append({
        "name": test_name,
        "success": success,
        "message": message,
        "duration_ms": duration_ms,
        "timestamp": datetime.now().isoformat()
    })

async def get_auth_token(client: httpx.AsyncClient) -> Optional[str]:
    """获取认证 token"""
    try:
        response = await client.post(
            f"{BASE_URL}/auth/login",
            data={"username": TEST_USERNAME, "password": TEST_PASSWORD}
        )
        if response.status_code == 200:
            data = response.json()
            return data.get("data", {}).get("access_token")
        else:
            print(f"  ⚠️ 登录失败: {response.status_code} - {response.text}")
            return None
    except Exception as e:
        print(f"  ⚠️ 登录异常: {e}")
        return None

# ==================== 测试用例 ====================

async def test_1_ai_basic_connectivity():
    """测试1: AI 基础服务连通性"""
    print_header("测试1: AI 基础服务连通性")
    
    # 直接测试 4sapi
    async with httpx.AsyncClient(timeout=30) as client:
        # 4sapi 测试
        start = time.time()
        try:
            response = await client.post(
                "https://4sapi.com/v1/chat/completions",
                headers={
                    "Authorization": "Bearer sk-9yMCXjRGANbacz20kJY8doSNy6Rf446aYwmgGIuIXQ7DAyBw",
                    "Content-Type": "application/json"
                },
                json={
                    "model": "gemini-3-flash-preview",
                    "messages": [{"role": "user", "content": "你好，请回复'测试成功'"}],
                    "max_tokens": 50
                }
            )
            duration = int((time.time() - start) * 1000)
            
            if response.status_code == 200:
                data = response.json()
                content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
                print_result("4sapi.com 连通性", True, f"响应: {content[:50]}...", duration)
            else:
                print_result("4sapi.com 连通性", False, f"HTTP {response.status_code}: {response.text[:100]}", duration)
        except Exception as e:
            duration = int((time.time() - start) * 1000)
            print_result("4sapi.com 连通性", False, str(e), duration)
        
        # OpenRouter 测试
        start = time.time()
        try:
            response = await client.post(
                "https://openrouter.ai/api/v1/chat/completions",
                headers={
                    "Authorization": "Bearer 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",
                    "Content-Type": "application/json"
                },
                json={
                    "model": "google/gemini-3-flash-preview",
                    "messages": [{"role": "user", "content": "你好，请回复'测试成功'"}],
                    "max_tokens": 50
                }
            )
            duration = int((time.time() - start) * 1000)
            
            if response.status_code == 200:
                data = response.json()
                content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
                print_result("OpenRouter 连通性", True, f"响应: {content[:50]}...", duration)
            else:
                print_result("OpenRouter 连通性", False, f"HTTP {response.status_code}: {response.text[:100]}", duration)
        except Exception as e:
            duration = int((time.time() - start) * 1000)
            print_result("OpenRouter 连通性", False, str(e), duration)

async def test_2_exam_generation():
    """测试2: 试题生成功能"""
    print_header("测试2: 试题生成功能 (V2 引擎)")
    
    async with httpx.AsyncClient(timeout=TIMEOUT) as client:
        # 获取 token
        token = await get_auth_token(client)
        if not token:
            print_result("试题生成", False, "无法获取认证 token")
            return
        
        headers = {"Authorization": f"Bearer {token}"}
        
        # 先查询一个有知识点的课程
        try:
            response = await client.get(f"{BASE_URL}/courses", headers=headers)
            if response.status_code != 200:
                print_result("试题生成 - 查询课程", False, f"HTTP {response.status_code}")
                return
            
            courses = response.json().get("data", {}).get("items", [])
            if not courses:
                print_result("试题生成", False, "没有可用的课程")
                return
            
            course_id = courses[0].get("id")
            print(f"  📝 使用课程ID: {course_id}")
        except Exception as e:
            print_result("试题生成 - 查询课程", False, str(e))
            return
        
        # 测试试题生成 API
        start = time.time()
        try:
            response = await client.post(
                f"{BASE_URL}/exams/generate?engine=v2",
                headers=headers,
                json={
                    "course_id": course_id,
                    "single_choice_count": 2,
                    "multiple_choice_count": 1,
                    "true_false_count": 1,
                    "fill_blank_count": 0,
                    "essay_count": 0,
                    "difficulty_level": 3,
                    "current_round": 1
                }
            )
            duration = int((time.time() - start) * 1000)
            
            if response.status_code == 200:
                data = response.json()
                result = data.get("data", {}).get("result", "")
                try:
                    questions = json.loads(result) if result else []
                    print_result("试题生成 (V2)", True, f"生成了 {len(questions)} 道题目", duration)
                except:
                    print_result("试题生成 (V2)", True, f"返回结果: {result[:100]}...", duration)
            else:
                print_result("试题生成 (V2)", False, f"HTTP {response.status_code}: {response.text[:200]}", duration)
        except Exception as e:
            duration = int((time.time() - start) * 1000)
            print_result("试题生成 (V2)", False, str(e), duration)

async def test_3_answer_judge():
    """测试3: 答案判断功能"""
    print_header("测试3: 答案判断功能 (V2 引擎)")
    
    async with httpx.AsyncClient(timeout=TIMEOUT) as client:
        # 获取 token
        token = await get_auth_token(client)
        if not token:
            print_result("答案判断", False, "无法获取认证 token")
            return
        
        headers = {"Authorization": f"Bearer {token}"}
        
        # 测试正确答案
        start = time.time()
        try:
            response = await client.post(
                f"{BASE_URL}/exams/judge-answer?engine=v2",
                headers=headers,
                json={
                    "question": "玻尿酸的主要作用是什么？",
                    "correct_answer": "保湿、填充、塑形",
                    "user_answer": "保湿和填充",
                    "analysis": "玻尿酸具有强大的保水能力，可用于皮肤保湿和面部填充"
                }
            )
            duration = int((time.time() - start) * 1000)
            
            if response.status_code == 200:
                data = response.json()
                is_correct = data.get("data", {}).get("is_correct")
                print_result("答案判断 (V2)", True, f"判断结果: {'正确' if is_correct else '错误'}", duration)
            else:
                print_result("答案判断 (V2)", False, f"HTTP {response.status_code}: {response.text[:200]}", duration)
        except Exception as e:
            duration = int((time.time() - start) * 1000)
            print_result("答案判断 (V2)", False, str(e), duration)

async def test_4_knowledge_analysis():
    """测试4: 知识点分析功能"""
    print_header("测试4: 知识点分析功能 (V2 引擎)")
    
    async with httpx.AsyncClient(timeout=TIMEOUT) as client:
        # 获取 token
        token = await get_auth_token(client)
        if not token:
            print_result("知识点分析", False, "无法获取认证 token")
            return
        
        headers = {"Authorization": f"Bearer {token}"}
        
        # 先查询课程和资料
        try:
            response = await client.get(f"{BASE_URL}/courses", headers=headers)
            if response.status_code != 200:
                print_result("知识点分析 - 查询课程", False, f"HTTP {response.status_code}")
                return
            
            courses = response.json().get("data", {}).get("items", [])
            if not courses:
                print_result("知识点分析", False, "没有可用的课程")
                return
            
            course_id = courses[0].get("id")
            
            # 查询课程资料
            response = await client.get(f"{BASE_URL}/courses/{course_id}/materials", headers=headers)
            if response.status_code != 200:
                print_result("知识点分析 - 查询资料", False, f"HTTP {response.status_code}")
                return
            
            materials = response.json().get("data", {}).get("items", [])
            if not materials:
                print_result("知识点分析", False, "课程没有可用的资料")
                return
            
            material_id = materials[0].get("id")
            print(f"  📝 使用课程ID: {course_id}, 资料ID: {material_id}")
        except Exception as e:
            print_result("知识点分析 - 查询资料", False, str(e))
            return
        
        # 测试知识点分析 API
        start = time.time()
        try:
            response = await client.post(
                f"{BASE_URL}/courses/{course_id}/materials/{material_id}/analyze?engine=v2",
                headers=headers
            )
            duration = int((time.time() - start) * 1000)
            
            if response.status_code == 200:
                data = response.json()
                kps = data.get("data", {}).get("knowledge_points", [])
                print_result("知识点分析 (V2)", True, f"提取了 {len(kps)} 个知识点", duration)
            else:
                # 可能返回 400 因为没有文件内容
                error_detail = response.json().get("detail", response.text[:200])
                print_result("知识点分析 (V2)", False, f"HTTP {response.status_code}: {error_detail}", duration)
        except Exception as e:
            duration = int((time.time() - start) * 1000)
            print_result("知识点分析 (V2)", False, str(e), duration)

async def test_5_course_chat():
    """测试5: 课程对话功能"""
    print_header("测试5: 课程对话功能 (V2 引擎)")
    
    async with httpx.AsyncClient(timeout=TIMEOUT) as client:
        # 获取 token
        token = await get_auth_token(client)
        if not token:
            print_result("课程对话", False, "无法获取认证 token")
            return
        
        headers = {"Authorization": f"Bearer {token}"}
        
        # 先查询课程
        try:
            response = await client.get(f"{BASE_URL}/courses", headers=headers)
            if response.status_code != 200:
                print_result("课程对话 - 查询课程", False, f"HTTP {response.status_code}")
                return
            
            courses = response.json().get("data", {}).get("items", [])
            if not courses:
                print_result("课程对话", False, "没有可用的课程")
                return
            
            course_id = courses[0].get("id")
            print(f"  📝 使用课程ID: {course_id}")
        except Exception as e:
            print_result("课程对话 - 查询课程", False, str(e))
            return
        
        # 测试课程对话 API（流式响应）
        start = time.time()
        try:
            # 注意：课程对话是流式响应，需要特殊处理
            async with client.stream(
                "POST",
                f"{BASE_URL}/course/chat?engine=v2",
                headers=headers,
                json={
                    "course_id": course_id,
                    "query": "请简单介绍一下这门课程的主要内容"
                }
            ) as response:
                if response.status_code == 200:
                    content = ""
                    async for line in response.aiter_lines():
                        if line.startswith("data:"):
                            try:
                                data = json.loads(line[5:].strip())
                                if data.get("event") == "message_chunk":
                                    content += data.get("content", "")
                            except:
                                pass
                    
                    duration = int((time.time() - start) * 1000)
                    if content:
                        print_result("课程对话 (V2)", True, f"响应: {content[:100]}...", duration)
                    else:
                        print_result("课程对话 (V2)", True, "收到流式响应", duration)
                else:
                    duration = int((time.time() - start) * 1000)
                    body = await response.aread()
                    print_result("课程对话 (V2)", False, f"HTTP {response.status_code}: {body.decode()[:200]}", duration)
        except Exception as e:
            duration = int((time.time() - start) * 1000)
            print_result("课程对话 (V2)", False, str(e), duration)

async def test_6_practice_scene():
    """测试6: 陪练场景提取功能"""
    print_header("测试6: 陪练场景提取功能 (V2 引擎)")
    
    async with httpx.AsyncClient(timeout=TIMEOUT) as client:
        # 获取 token
        token = await get_auth_token(client)
        if not token:
            print_result("陪练场景提取", False, "无法获取认证 token")
            return
        
        headers = {"Authorization": f"Bearer {token}"}
        
        # 先查询课程
        try:
            response = await client.get(f"{BASE_URL}/courses", headers=headers)
            if response.status_code != 200:
                print_result("陪练场景提取 - 查询课程", False, f"HTTP {response.status_code}")
                return
            
            courses = response.json().get("data", {}).get("items", [])
            if not courses:
                print_result("陪练场景提取", False, "没有可用的课程")
                return
            
            course_id = courses[0].get("id")
            print(f"  📝 使用课程ID: {course_id}")
        except Exception as e:
            print_result("陪练场景提取 - 查询课程", False, str(e))
            return
        
        # 测试陪练场景提取 API
        start = time.time()
        try:
            response = await client.post(
                f"{BASE_URL}/practice/extract-scene?engine=v2",
                headers=headers,
                json={
                    "course_id": course_id,
                    "scene_type": "consultation"  # 咨询场景
                }
            )
            duration = int((time.time() - start) * 1000)
            
            if response.status_code == 200:
                data = response.json()
                scenes = data.get("data", {}).get("scenes", [])
                print_result("陪练场景提取 (V2)", True, f"提取了 {len(scenes)} 个场景", duration)
            else:
                error_detail = response.json().get("detail", response.text[:200])
                print_result("陪练场景提取 (V2)", False, f"HTTP {response.status_code}: {error_detail}", duration)
        except Exception as e:
            duration = int((time.time() - start) * 1000)
            print_result("陪练场景提取 (V2)", False, str(e), duration)

async def test_7_ability_analysis():
    """测试7: 能力分析功能"""
    print_header("测试7: 能力分析功能 (V2 引擎)")
    
    async with httpx.AsyncClient(timeout=TIMEOUT) as client:
        # 获取 token
        token = await get_auth_token(client)
        if not token:
            print_result("能力分析", False, "无法获取认证 token")
            return
        
        headers = {"Authorization": f"Bearer {token}"}
        
        # 测试能力分析 API（需要员工的言迹数据）
        start = time.time()
        try:
            # 先查询是否有员工数据
            response = await client.get(f"{BASE_URL}/users/me", headers=headers)
            if response.status_code != 200:
                print_result("能力分析 - 获取用户信息", False, f"HTTP {response.status_code}")
                return
            
            user = response.json().get("data", {})
            user_id = user.get("id")
            print(f"  📝 当前用户ID: {user_id}")
            
            # 测试能力分析 API
            response = await client.get(
                f"{BASE_URL}/ability/user/{user_id}/analysis?engine=v2",
                headers=headers
            )
            duration = int((time.time() - start) * 1000)
            
            if response.status_code == 200:
                data = response.json()
                dimensions = data.get("data", {}).get("dimensions", [])
                print_result("能力分析 (V2)", True, f"分析了 {len(dimensions)} 个能力维度", duration)
            elif response.status_code == 404:
                # 可能没有言迹数据
                print_result("能力分析 (V2)", True, "接口正常（无言迹数据）", duration)
            else:
                error_detail = response.json().get("detail", response.text[:200])
                print_result("能力分析 (V2)", False, f"HTTP {response.status_code}: {error_detail}", duration)
        except Exception as e:
            duration = int((time.time() - start) * 1000)
            print_result("能力分析 (V2)", False, str(e), duration)

async def test_backend_health():
    """测试后端健康状态"""
    print_header("后端服务健康检查")
    
    backends = [
        ("演示版 (aiedu)", "http://localhost:8000"),
        ("瑞小美 (kpl)", "http://localhost:8001"),
        ("华尔倍丽 (hua)", "http://localhost:8010"),
        ("杨扬宠物 (yy)", "http://localhost:8011"),
        ("武汉禾丽 (hl)", "http://localhost:8012"),
        ("芯颜定制 (xy)", "http://localhost:8013"),
        ("飞沃 (fw)", "http://localhost:8014"),
        ("恩喜成都 (ex)", "http://localhost:8015"),
        ("陪练试用版 (pl)", "http://localhost:8020"),
    ]
    
    async with httpx.AsyncClient(timeout=10) as client:
        for name, url in backends:
            start = time.time()
            try:
                response = await client.get(f"{url}/health")
                duration = int((time.time() - start) * 1000)
                
                if response.status_code == 200:
                    print_result(name, True, "健康", duration)
                else:
                    print_result(name, False, f"HTTP {response.status_code}", duration)
            except Exception as e:
                duration = int((time.time() - start) * 1000)
                print_result(name, False, str(e), duration)

def print_summary():
    """打印测试汇总"""
    print_header("测试汇总")
    
    total = len(test_results)
    passed = sum(1 for r in test_results if r["success"])
    failed = total - passed
    
    print(f"\n  总计: {total} 项测试")
    print(f"  ✅ 通过: {passed}")
    print(f"  ❌ 失败: {failed}")
    
    if failed > 0:
        print("\n  失败项目:")
        for r in test_results:
            if not r["success"]:
                print(f"    - {r['name']}: {r['message']}")
    
    # 计算平均延迟
    durations = [r["duration_ms"] for r in test_results if r["duration_ms"] > 0]
    if durations:
        avg_duration = sum(durations) / len(durations)
        print(f"\n  平均响应时间: {avg_duration:.0f}ms")
    
    print("\n" + "=" * 60)
    
    return failed == 0

async def main():
    """主测试入口"""
    print("\n" + "=" * 60)
    print("   考培练系统 AI 功能完整测试")
    print(f"   时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print("=" * 60)
    
    # 检查后端健康状态
    await test_backend_health()
    
    # 运行所有测试
    await test_1_ai_basic_connectivity()
    await test_2_exam_generation()
    await test_3_answer_judge()
    await test_4_knowledge_analysis()
    await test_5_course_chat()
    await test_6_practice_scene()
    await test_7_ability_analysis()
    
    # 打印汇总
    success = print_summary()
    
    sys.exit(0 if success else 1)

if __name__ == "__main__":
    asyncio.run(main())





