from typing import Any
from unittest.mock import MagicMock, patch
from src import ai_client


class MockUsage:
    def __init__(self) -> None:
        self.prompt_token_count = 10
        self.candidates_token_count = 5
        self.total_token_count = 15
        self.cached_content_token_count = 0


class MockPart:
    def __init__(self, text: Any, function_call: Any) -> None:
        self.text = text
        self.function_call = function_call


class MockContent:
    def __init__(self, parts: Any) -> None:
        self.parts = parts


class MockCandidate:
    def __init__(self, parts: Any) -> None:
        self.content = MockContent(parts)
        self.finish_reason = MagicMock()
        self.finish_reason.name = "STOP"


def test_ai_client_event_emitter_exists() -> None:
    assert hasattr(ai_client, "events")


def test_event_emission() -> None:
    callback = MagicMock()
    ai_client.events.on("test_event", callback)
    ai_client.events.emit("test_event", payload={"data": 123})
    callback.assert_called_once_with(payload={"data": 123})


def test_send_emits_events_proper() -> None:
    ai_client.reset_session()
    with (
        patch("src.ai_client._ensure_gemini_client"),
        patch("src.ai_client._gemini_client") as mock_client,
    ):
        mock_chat = MagicMock()
        mock_client.chats.create.return_value = mock_chat
        mock_response = MagicMock()
        mock_response.candidates = [MockCandidate([MockPart("gemini response", None)])]
        mock_response.usage_metadata = MockUsage()
        mock_response.text = "gemini response"
        mock_response.candidates[0].finish_reason.name = "STOP"
        mock_chat.send_message_stream.return_value = iter([mock_response])
        mock_chat.send_message.return_value = mock_response
        start_callback = MagicMock()
        response_callback = MagicMock()
        ai_client.events.on("request_start", start_callback)
        ai_client.events.on("response_received", response_callback)
        ai_client.set_provider("gemini", "gemini-2.5-flash-lite")
        ai_client.send("context", "message", )
        assert start_callback.called
        assert response_callback.called
        args, kwargs = start_callback.call_args
        assert kwargs["payload"]["provider"] == "gemini"


def test_send_emits_tool_events() -> None:
    ai_client.reset_session()
    with (
        patch("src.ai_client._ensure_gemini_client"),
        patch("src.ai_client._gemini_client") as mock_client,
        patch("src.mcp_client.dispatch") as mock_dispatch,
    ):
        mock_chat = MagicMock()
        mock_client.chats.create.return_value = mock_chat
        mock_fc = MagicMock()
        mock_fc.name = "read_file"
        mock_fc.args = {"path": "test.txt"}
        mock_response_with_tool = MagicMock()
        mock_response_with_tool.candidates = [
            MockCandidate([MockPart("tool call text", mock_fc)])
        ]
        mock_response_with_tool.usage_metadata = MockUsage()
        mock_response_with_tool.text = "tool call text"
        mock_response_final = MagicMock()
        mock_response_final.candidates = [
            MockCandidate([MockPart("final answer", None)])
        ]
        mock_response_final.usage_metadata = MockUsage()
        mock_response_final.text = "final answer"
        mock_chat.send_message_stream.side_effect = lambda *a, **kw: iter(
            [mock_response_with_tool]
        )
        mock_chat.send_message.side_effect = lambda *a, **kw: mock_response_with_tool
        mock_dispatch.return_value = "file content"
        ai_client.set_provider("gemini", "gemini-2.5-flash-lite")
        tool_callback = MagicMock()

        def debug_tool(*args, **kwargs):
            tool_callback(*args, **kwargs)

        ai_client.events.on("tool_execution", debug_tool)
        result = ai_client.send("context", "message", enable_tools=True)
        assert tool_callback.call_count >= 1
