"""ReAct Engine 集成测试 - 完整 ReAct 循环"""

import pytest

from agentkit.llm.gateway import LLMGateway
from agentkit.llm.protocol import LLMResponse, TokenUsage, ToolCall
from agentkit.tools.base import Tool


class KnowledgeTool(Tool):
    """知识检索工具"""

    def __init__(self):
        super().__init__(
            name="retrieve_knowledge",
            description="Retrieve knowledge from the knowledge base",
        )

    async def execute(self, **kwargs) -> dict:
        query = kwargs.get("query", "")
        return {"knowledge": f"Knowledge about {query}", "relevance": 0.95}


class GenerateTool(Tool):
    """内容生成工具"""

    def __init__(self):
        super().__init__(
            name="generate_content",
            description="Generate content based on input",
        )

    async def execute(self, **kwargs) -> dict:
        topic = kwargs.get("topic", "")
        return {"content": f"Generated content about {topic}"}


class TestReActFullLoop:
    """完整 ReAct 循环：检索知识 → 生成内容 → 返回结果"""

    async def test_knowledge_then_generate_loop(self):
        from agentkit.core.react import ReActEngine, ReActResult

        from unittest.mock import AsyncMock, MagicMock

        knowledge_tool = KnowledgeTool()
        generate_tool = GenerateTool()

        gateway = MagicMock(spec=LLMGateway)
        gateway.chat = AsyncMock(side_effect=[
            # Step 1: LLM 决定检索知识
            LLMResponse(
                content="",
                model="test-model",
                usage=TokenUsage(prompt_tokens=50, completion_tokens=10),
                tool_calls=[ToolCall(id="tc_1", name="retrieve_knowledge", arguments={"query": "AI agents"})],
            ),
            # Step 2: LLM 决定生成内容
            LLMResponse(
                content="",
                model="test-model",
                usage=TokenUsage(prompt_tokens=80, completion_tokens=10),
                tool_calls=[ToolCall(id="tc_2", name="generate_content", arguments={"topic": "AI agents"})],
            ),
            # Step 3: LLM 返回最终答案
            LLMResponse(
                content="Based on the knowledge retrieved and content generated, here is the answer about AI agents.",
                model="test-model",
                usage=TokenUsage(prompt_tokens=100, completion_tokens=30),
            ),
        ])

        engine = ReActEngine(llm_gateway=gateway)
        result = await engine.execute(
            messages=[{"role": "user", "content": "Tell me about AI agents"}],
            tools=[knowledge_tool, generate_tool],
            system_prompt="You are a knowledgeable AI assistant.",
        )

        assert isinstance(result, ReActResult)
        assert result.total_steps == 3
        assert "AI agents" in result.output
        assert result.total_tokens == 50 + 10 + 80 + 10 + 100 + 30

        # 验证轨迹
        assert result.trajectory[0].tool_name == "retrieve_knowledge"
        assert result.trajectory[1].tool_name == "generate_content"
        assert result.trajectory[2].action == "final_answer"

    async def test_react_with_error_recovery(self):
        """带错误恢复的 ReAct 循环"""
        from agentkit.core.react import ReActEngine

        from unittest.mock import AsyncMock, MagicMock

        class FlakyTool(Tool):
            def __init__(self):
                super().__init__(name="flaky_api", description="A flaky API tool")
                self._call_count = 0

            async def execute(self, **kwargs) -> dict:
                self._call_count += 1
                if self._call_count == 1:
                    raise ConnectionError("API timeout")
                return {"data": "success on retry"}

        flaky_tool = FlakyTool()

        gateway = MagicMock(spec=LLMGateway)
        gateway.chat = AsyncMock(side_effect=[
            # Step 1: LLM 调用 flaky API（第一次失败）
            LLMResponse(
                content="",
                model="test-model",
                usage=TokenUsage(prompt_tokens=50, completion_tokens=10),
                tool_calls=[ToolCall(id="tc_1", name="flaky_api", arguments={})],
            ),
            # Step 2: LLM 收到错误后重试
            LLMResponse(
                content="",
                model="test-model",
                usage=TokenUsage(prompt_tokens=80, completion_tokens=10),
                tool_calls=[ToolCall(id="tc_2", name="flaky_api", arguments={})],
            ),
            # Step 3: LLM 返回最终答案
            LLMResponse(
                content="After retrying, I got the data successfully.",
                model="test-model",
                usage=TokenUsage(prompt_tokens=100, completion_tokens=20),
            ),
        ])

        engine = ReActEngine(llm_gateway=gateway)
        result = await engine.execute(
            messages=[{"role": "user", "content": "Call the flaky API"}],
            tools=[flaky_tool],
        )

        assert result.total_steps == 3
        # 第一次调用失败，但错误信息被包含在观察中
        assert "error" in str(result.trajectory[0].result).lower() or "failed" in str(result.trajectory[0].result).lower()
        # 第二次调用成功
        assert result.trajectory[1].result == {"data": "success on retry"}
        assert result.output == "After retrying, I got the data successfully."


class TestQualityGatePlaceholder:
    """Quality Gate 集成占位（将在 U5 实现）"""

    async def test_react_result_has_quality_metrics_placeholder(self):
        """验证 ReActResult 可扩展以支持 Quality Gate"""
        from agentkit.core.react import ReActResult, ReActStep

        result = ReActResult(
            output="test",
            trajectory=[ReActStep(step=1, action="final_answer", content="test")],
            total_steps=1,
            total_tokens=10,
        )
        # ReActResult 应是一个 dataclass，可以正常访问属性
        assert result.output == "test"
        assert result.total_steps == 1
        # 未来可以扩展添加 quality_score 等字段
