"""
用户测试：模拟原始会话中的两个异常场景，验证修复效果

场景 1：API 返回空错误消息 → 错误信息包含异常类型名（不再空）
场景 2：网络瞬断错误 → 自动重试 3 次后仍失败则终止
"""
import sys
import asyncio
from pathlib import Path
from unittest.mock import MagicMock, AsyncMock, patch

sys.path.insert(0, str(Path(__file__).parent.parent))


def print_header(title):
    print(f"\n{'='*60}")
    print(f"  {title}")
    print(f"{'='*60}")


async def test_scenario_1_empty_error_has_type_name():
    """
    场景 1：回放原始问题 —— 模型 API 返回空错误消息
    修复前：输出 "Model call failed: "   ← 冒号后面是空的
    修复后：输出 "Model call failed: [RemoteProtocolError] "  ← 有类型名
    """
    print_header("场景 1：空错误消息 → 现在包含异常类型名")

    from aacode.core.main_agent import MainAgent
    import httpx

    agent = MainAgent.__new__(MainAgent)
    agent.skills_manager = MagicMock()
    agent.skills_manager.list_enabled_skills = MagicMock(return_value=[])
    agent.session_manager = MagicMock()
    agent.context_manager = MagicMock()

    mock_req = MagicMock()

    # 模拟一个 str(e) 为空的 RemoteProtocolError
    try:
        exc = httpx.RemoteProtocolError("", request=mock_req)
    except TypeError:
        exc = httpx.RemoteProtocolError("")

    agent._call_openai_api = AsyncMock(side_effect=exc)

    model_caller = agent._create_model_caller({"api_key": "sk-test"})

    print("\n📡 模拟调用模型 API（空错误消息）...")
    try:
        await model_caller([{"role": "user", "content": "hi"}])
    except RuntimeError as e:
        msg = str(e)
        print(f"\n🔍 捕获到的错误信息:")
        print(f"   {msg}")

        if "RemoteProtocolError" in msg:
            print(f"\n✅ 通过！错误信息包含异常类型名 [RemoteProtocolError]")
            print(f"   修复前这里会是空字符串，无法定位问题。")
        else:
            print(f"\n❌ 失败！错误信息不包含异常类型名")


async def test_scenario_2_retry_then_succeed():
    """
    场景 2：网络瞬断 → 自动重试后成功
    修复前：第一次网络错误就直接终止任务
    修复后：自动重试，第 2 次成功，用户无感知
    """
    print_header("场景 2：网络瞬断 → 自动重试后成功")

    from aacode.core.main_agent import MainAgent
    import httpx

    agent = MainAgent.__new__(MainAgent)
    agent.skills_manager = MagicMock()
    agent.skills_manager.list_enabled_skills = MagicMock(return_value=[])
    agent.session_manager = MagicMock()
    agent.context_manager = MagicMock()

    mock_req = MagicMock()
    call_count = 0

    async def flaky_api(*args, **kwargs):
        nonlocal call_count
        call_count += 1
        if call_count < 2:
            raise httpx.RemoteProtocolError("Connection reset by peer", request=mock_req)
        return {"text": "OK, recovered!"}

    agent._call_openai_api = flaky_api

    model_caller = agent._create_model_caller({"api_key": "sk-test"})

    print("\n📡 模拟调用模型 API（第1次失败，第2次成功）...")
    result = await model_caller([{"role": "user", "content": "hi"}])

    print(f"\n🔍 返回结果: {result['text']}")
    print(f"   实际调用次数: {call_count}")

    if call_count == 2 and result["text"] == "OK, recovered!":
        print(f"\n✅ 通过！自动重试了 1 次后成功，用户无感知。")
        print(f"   修复前：第1次网络错误就直接杀死任务。")
    else:
        print(f"\n❌ 失败！")


async def test_scenario_3_retry_exhausted():
    """
    场景 3：持续故障 → 重试 3 次后报错（带类型名）
    """
    print_header("场景 3：持续故障 → 重试 3 次后终止（带错误类型）")

    from aacode.core.main_agent import MainAgent
    import httpx

    agent = MainAgent.__new__(MainAgent)
    agent.skills_manager = MagicMock()
    agent.skills_manager.list_enabled_skills = MagicMock(return_value=[])
    agent.session_manager = MagicMock()
    agent.context_manager = MagicMock()

    mock_req = MagicMock()
    agent._call_openai_api = AsyncMock(
        side_effect=httpx.RemoteProtocolError("Server disconnected", request=mock_req)
    )

    model_caller = agent._create_model_caller({"api_key": "sk-test"})

    print("\n📡 模拟调用模型 API（持续故障）...")
    try:
        await model_caller([{"role": "user", "content": "hi"}])
        print("\n❌ 失败！应该抛出异常但没抛出")
    except RuntimeError as e:
        msg = str(e)
        print(f"\n🔍 捕获到的错误信息:")
        print(f"   {msg}")
        calls = agent._call_openai_api.call_count
        print(f"   实际调用次数: {calls}")

        if calls == 3 and "RemoteProtocolError" in msg:
            print(f"\n✅ 通过！3 次重试后才报错，错误信息包含 [RemoteProtocolError]。")
        else:
            print(f"\n❌ 失败！期望重试 3 次后报错含类型名")


async def test_scenario_4_auth_error_no_retry():
    """
    场景 4：认证错误 → 不重试，直接终止
    """
    print_header("场景 4：认证错误 → 不重试直接终止")

    from aacode.core.main_agent import MainAgent
    import openai

    agent = MainAgent.__new__(MainAgent)
    agent.skills_manager = MagicMock()
    agent.skills_manager.list_enabled_skills = MagicMock(return_value=[])
    agent.session_manager = MagicMock()
    agent.context_manager = MagicMock()

    mock_resp = MagicMock()
    mock_resp.status_code = 401
    mock_resp.request = MagicMock()

    agent._call_openai_api = AsyncMock(
        side_effect=openai.AuthenticationError("Invalid API key", response=mock_resp, body={})
    )

    model_caller = agent._create_model_caller({"api_key": "sk-test"})

    print("\n📡 模拟调用模型 API（401 认证错误）...")
    try:
        await model_caller([{"role": "user", "content": "hi"}])
    except RuntimeError as e:
        msg = str(e)
        calls = agent._call_openai_api.call_count
        print(f"\n🔍 捕获到的错误信息:")
        print(f"   {msg}")
        print(f"   实际调用次数: {calls}")

        if calls == 1 and "AuthenticationError" in msg:
            print(f"\n✅ 通过！认证错误只调用了 1 次（不重试），错误信息含 [AuthenticationError]。")
        else:
            print(f"\n❌ 失败！期望重试 0 次，实际 {calls} 次")


async def main():
    print("╔══════════════════════════════════════════════════════╗")
    print("║  模型 API 重试 & 错误信息增强 —— 用户验收测试      ║")
    print("╚══════════════════════════════════════════════════════╝")

    results = []
    for test_name, test_func in [
        ("场景1: 空错误+类型名", test_scenario_1_empty_error_has_type_name),
        ("场景2: 重试后成功", test_scenario_2_retry_then_succeed),
        ("场景3: 重试耗尽终止", test_scenario_3_retry_exhausted),
        ("场景4: 认证不重试", test_scenario_4_auth_error_no_retry),
    ]:
        try:
            await test_func()
            results.append((test_name, True))
        except Exception as e:
            print(f"\n❌ 异常: {e}")
            import traceback
            traceback.print_exc()
            results.append((test_name, False))

    print_header("测试汇总")
    for name, passed in results:
        status = "✅ PASS" if passed else "❌ FAIL"
        print(f"  {status}  {name}")
    all_pass = all(r[1] for r in results)
    print(f"\n{'🎉 全部通过！' if all_pass else '⚠️ 有测试失败'}")

    return 0 if all_pass else 1


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