"""LLM Protocol 数据类测试"""

import pytest

from agentkit.llm.protocol import LLMProvider, LLMRequest, LLMResponse, TokenUsage, ToolCall


class TestTokenUsage:
    """TokenUsage 数据类测试"""

    def test_default_values(self):
        usage = TokenUsage()
        assert usage.prompt_tokens == 0
        assert usage.completion_tokens == 0
        assert usage.total_tokens == 0

    def test_custom_values(self):
        usage = TokenUsage(prompt_tokens=100, completion_tokens=50)
        assert usage.prompt_tokens == 100
        assert usage.completion_tokens == 50
        assert usage.total_tokens == 150

    def test_total_tokens_computed(self):
        usage = TokenUsage(prompt_tokens=100, completion_tokens=50)
        assert usage.total_tokens == 150


class TestToolCall:
    """ToolCall 数据类测试"""

    def test_tool_call_creation(self):
        tc = ToolCall(id="call_123", name="get_weather", arguments={"city": "Beijing"})
        assert tc.id == "call_123"
        assert tc.name == "get_weather"
        assert tc.arguments == {"city": "Beijing"}

    def test_tool_call_with_empty_arguments(self):
        tc = ToolCall(id="call_456", name="list_items", arguments={})
        assert tc.arguments == {}


class TestLLMRequest:
    """LLMRequest 数据类测试"""

    def test_basic_request(self):
        request = LLMRequest(
            messages=[{"role": "user", "content": "Hello"}],
            model="gpt-4o-mini",
        )
        assert len(request.messages) == 1
        assert request.model == "gpt-4o-mini"
        assert request.tools is None
        assert request.tool_choice == "auto"
        assert request.temperature == 0.7
        assert request.max_tokens == 2000

    def test_request_with_tools(self):
        tools = [
            {
                "type": "function",
                "function": {
                    "name": "get_weather",
                    "description": "Get weather",
                    "parameters": {"type": "object", "properties": {"city": {"type": "string"}}},
                },
            }
        ]
        request = LLMRequest(
            messages=[{"role": "user", "content": "What's the weather?"}],
            model="gpt-4o",
            tools=tools,
            tool_choice="auto",
            temperature=0.0,
            max_tokens=1000,
        )
        assert request.tools is not None
        assert len(request.tools) == 1
        assert request.temperature == 0.0
        assert request.max_tokens == 1000

    def test_request_with_extra_kwargs(self):
        request = LLMRequest(
            messages=[{"role": "user", "content": "Hello"}],
            model="gpt-4o",
            top_p=0.9,
        )
        assert request.model == "gpt-4o"


class TestLLMResponse:
    """LLMResponse 数据类测试"""

    def test_basic_response(self):
        usage = TokenUsage(prompt_tokens=10, completion_tokens=20)
        response = LLMResponse(content="Hello!", model="gpt-4o-mini", usage=usage)
        assert response.content == "Hello!"
        assert response.model == "gpt-4o-mini"
        assert response.usage.total_tokens == 30
        assert response.tool_calls == []
        assert response.latency_ms == 0.0

    def test_response_with_tool_calls(self):
        usage = TokenUsage(prompt_tokens=10, completion_tokens=20)
        tool_calls = [
            ToolCall(id="call_1", name="get_weather", arguments={"city": "Beijing"})
        ]
        response = LLMResponse(
            content="", model="gpt-4o", usage=usage, tool_calls=tool_calls, latency_ms=150.5
        )
        assert len(response.tool_calls) == 1
        assert response.tool_calls[0].name == "get_weather"
        assert response.latency_ms == 150.5

    def test_has_tool_calls_true(self):
        usage = TokenUsage(prompt_tokens=10, completion_tokens=20)
        tool_calls = [ToolCall(id="call_1", name="search", arguments={"q": "test"})]
        response = LLMResponse(content="", model="gpt-4o", usage=usage, tool_calls=tool_calls)
        assert response.has_tool_calls is True

    def test_has_tool_calls_false(self):
        usage = TokenUsage(prompt_tokens=10, completion_tokens=20)
        response = LLMResponse(content="Hello!", model="gpt-4o-mini", usage=usage)
        assert response.has_tool_calls is False


class TestLLMProvider:
    """LLMProvider ABC 测试"""

    def test_cannot_instantiate_directly(self):
        with pytest.raises(TypeError):
            LLMProvider()

    def test_subclass_must_implement_chat(self):
        class IncompleteProvider(LLMProvider):
            pass

        with pytest.raises(TypeError):
            IncompleteProvider()

    async def test_subclass_with_chat_works(self):
        class DummyProvider(LLMProvider):
            async def chat(self, request: LLMRequest) -> LLMResponse:
                usage = TokenUsage(prompt_tokens=5, completion_tokens=10)
                return LLMResponse(content="hi", model=request.model, usage=usage)

        provider = DummyProvider()
        request = LLMRequest(messages=[{"role": "user", "content": "hi"}], model="test")
        response = await provider.chat(request)
        assert response.content == "hi"
