"""Tests for evalkit.contrib.openai.OpenAIAgentTracker."""

from __future__ import annotations

import asyncio
from typing import Any
from unittest.mock import MagicMock

import pytest

from evalkit.contrib.tracker import BaseTracker
from evalkit.models import ToolCall


@pytest.fixture()
def _import_guard() -> None:
    """Skip if openai-agents is not installed."""
    pytest.importorskip("agents")


@pytest.mark.usefixtures("_import_guard")
class TestOpenAIAgentTracker:
    def _make_tracker(self) -> Any:
        from evalkit.contrib.openai import OpenAIAgentTracker

        return OpenAIAgentTracker()

    def _make_context(self, turn_input: Any = None, tool_arguments: str | None = None) -> MagicMock:
        ctx = MagicMock()
        ctx.turn_input = turn_input or []
        if tool_arguments is not None:
            ctx.tool_arguments = tool_arguments
        else:
            ctx.tool_arguments = None
        return ctx

    def _make_tool(self, name: str = "get_weather") -> MagicMock:
        tool = MagicMock()
        tool.name = name
        return tool

    def test_is_base_tracker(self) -> None:
        tracker = self._make_tracker()
        assert isinstance(tracker, BaseTracker)

    def test_initial_state(self) -> None:
        tracker = self._make_tracker()
        assert tracker.question == ""
        assert tracker.tool_calls == []
        assert tracker.context == []
        assert tracker.answer == ""

    def test_on_agent_start_captures_string_input(self) -> None:
        tracker = self._make_tracker()
        ctx = self._make_context(turn_input="What is the weather?")
        asyncio.run(tracker.on_agent_start(ctx, MagicMock()))
        assert tracker.question == "What is the weather?"

    def test_on_agent_start_captures_dict_input(self) -> None:
        tracker = self._make_tracker()
        ctx = self._make_context(turn_input=[{"role": "user", "content": "Hello world"}])
        asyncio.run(tracker.on_agent_start(ctx, MagicMock()))
        assert tracker.question == "Hello world"

    def test_on_agent_start_ignores_subsequent_calls(self) -> None:
        tracker = self._make_tracker()
        ctx1 = self._make_context(turn_input="First question")
        ctx2 = self._make_context(turn_input="Second question")
        asyncio.run(tracker.on_agent_start(ctx1, MagicMock()))
        asyncio.run(tracker.on_agent_start(ctx2, MagicMock()))
        assert tracker.question == "First question"

    def test_on_tool_end_captures_tool_call(self) -> None:
        tracker = self._make_tracker()
        ctx = self._make_context(tool_arguments='{"city": "Paris"}')
        tool = self._make_tool("get_weather")
        asyncio.run(tracker.on_tool_end(ctx, MagicMock(), tool, "Sunny in Paris"))
        assert tracker.tool_calls == [
            ToolCall(name="get_weather", parameters={"city": "Paris"}),
        ]

    def test_on_tool_end_captures_context(self) -> None:
        tracker = self._make_tracker()
        ctx = self._make_context(tool_arguments='{"q": "python"}')
        tool = self._make_tool("search")
        asyncio.run(tracker.on_tool_end(ctx, MagicMock(), tool, "Python is a language."))
        assert tracker.context == ["Python is a language."]

    def test_on_tool_end_handles_dict_arguments(self) -> None:
        tracker = self._make_tracker()
        ctx = self._make_context()
        ctx.tool_arguments = {"city": "Tokyo"}
        tool = self._make_tool("get_weather")
        asyncio.run(tracker.on_tool_end(ctx, MagicMock(), tool, "Sunny"))
        assert tracker.tool_calls[0].parameters == {"city": "Tokyo"}

    def test_on_tool_end_handles_no_arguments(self) -> None:
        tracker = self._make_tracker()
        ctx = self._make_context()
        tool = self._make_tool("ping")
        asyncio.run(tracker.on_tool_end(ctx, MagicMock(), tool, "pong"))
        assert tracker.tool_calls[0].parameters == {}

    def test_on_tool_end_handles_invalid_json(self) -> None:
        tracker = self._make_tracker()
        ctx = self._make_context(tool_arguments="not json")
        tool = self._make_tool("t")
        asyncio.run(tracker.on_tool_end(ctx, MagicMock(), tool, "result"))
        assert tracker.tool_calls[0].parameters == {}

    def test_on_agent_end_captures_string_output(self) -> None:
        tracker = self._make_tracker()
        ctx = self._make_context()
        asyncio.run(tracker.on_agent_end(ctx, MagicMock(), "The answer is 42."))
        assert tracker.answer == "The answer is 42."

    def test_on_agent_end_converts_non_string_output(self) -> None:
        tracker = self._make_tracker()
        ctx = self._make_context()
        asyncio.run(tracker.on_agent_end(ctx, MagicMock(), 42))
        assert tracker.answer == "42"

    def test_multiple_tool_calls(self) -> None:
        tracker = self._make_tracker()
        ctx1 = self._make_context(tool_arguments='{"city": "Paris"}')
        ctx2 = self._make_context(tool_arguments='{"q": "python"}')
        asyncio.run(tracker.on_tool_end(ctx1, MagicMock(), self._make_tool("weather"), "Sunny"))
        asyncio.run(tracker.on_tool_end(ctx2, MagicMock(), self._make_tool("search"), "Found"))
        assert len(tracker.tool_calls) == 2
        assert tracker.tool_calls[0].name == "weather"
        assert tracker.tool_calls[1].name == "search"

    def test_full_flow(self) -> None:
        tracker = self._make_tracker()
        agent = MagicMock()

        # Agent start
        ctx_start = self._make_context(turn_input="What is Python?")
        asyncio.run(tracker.on_agent_start(ctx_start, agent))

        # Tool call
        ctx_tool = self._make_context(tool_arguments='{"query": "python"}')
        tool = self._make_tool("search_kb")
        asyncio.run(tracker.on_tool_start(MagicMock(), agent, tool))
        asyncio.run(tracker.on_tool_end(ctx_tool, agent, tool, "Python is a programming language."))

        # Agent end
        ctx_end = self._make_context()
        asyncio.run(tracker.on_agent_end(ctx_end, agent, "Python is a high-level language."))

        assert tracker.question == "What is Python?"
        assert tracker.tool_calls == [
            ToolCall(name="search_kb", parameters={"query": "python"}),
        ]
        assert tracker.context == ["Python is a programming language."]
        assert tracker.answer == "Python is a high-level language."
