"""LLM Provider (OpenAI Compatible) 测试"""

import json

import pytest
from pytest_httpx import HTTPXMock

from agentkit.core.exceptions import LLMProviderError
from agentkit.llm.protocol import LLMRequest, LLMResponse, TokenUsage
from agentkit.llm.providers.openai import OpenAICompatibleProvider


class TestOpenAICompatibleProviderBasic:
    """基本 chat 功能测试"""

    async def test_chat_returns_llm_response(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            url="https://api.openai.com/v1/chat/completions",
            json={
                "id": "chatcmpl-123",
                "model": "gpt-4o-mini",
                "choices": [
                    {
                        "index": 0,
                        "message": {"role": "assistant", "content": "Hello! How can I help?"},
                        "finish_reason": "stop",
                    }
                ],
                "usage": {"prompt_tokens": 10, "completion_tokens": 6, "total_tokens": 16},
            },
        )

        provider = OpenAICompatibleProvider(api_key="test-key")
        request = LLMRequest(
            messages=[{"role": "user", "content": "Hi"}],
            model="gpt-4o-mini",
        )
        response = await provider.chat(request)

        assert isinstance(response, LLMResponse)
        assert response.content == "Hello! How can I help?"
        assert response.model == "gpt-4o-mini"
        assert response.usage.prompt_tokens == 10
        assert response.usage.completion_tokens == 6
        assert response.usage.total_tokens == 16

    async def test_chat_with_custom_base_url(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            url="https://api.deepseek.com/v1/chat/completions",
            json={
                "id": "chatcmpl-456",
                "model": "deepseek-chat",
                "choices": [
                    {
                        "index": 0,
                        "message": {"role": "assistant", "content": "DeepSeek response"},
                        "finish_reason": "stop",
                    }
                ],
                "usage": {"prompt_tokens": 5, "completion_tokens": 3, "total_tokens": 8},
            },
        )

        provider = OpenAICompatibleProvider(
            api_key="test-key",
            base_url="https://api.deepseek.com/v1",
            default_model="deepseek-chat",
        )
        request = LLMRequest(
            messages=[{"role": "user", "content": "Hi"}],
            model="deepseek-chat",
        )
        response = await provider.chat(request)

        assert response.content == "DeepSeek response"
        assert response.model == "deepseek-chat"


class TestOpenAICompatibleProviderToolCalls:
    """Function Calling (tool_calls) 测试"""

    async def test_response_contains_tool_calls(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            url="https://api.openai.com/v1/chat/completions",
            json={
                "id": "chatcmpl-789",
                "model": "gpt-4o",
                "choices": [
                    {
                        "index": 0,
                        "message": {
                            "role": "assistant",
                            "content": None,
                            "tool_calls": [
                                {
                                    "id": "call_abc",
                                    "type": "function",
                                    "function": {
                                        "name": "get_weather",
                                        "arguments": '{"city": "Beijing"}',
                                    },
                                }
                            ],
                        },
                        "finish_reason": "tool_calls",
                    }
                ],
                "usage": {"prompt_tokens": 20, "completion_tokens": 15, "total_tokens": 35},
            },
        )

        provider = OpenAICompatibleProvider(api_key="test-key")
        request = LLMRequest(
            messages=[{"role": "user", "content": "What's the weather in Beijing?"}],
            model="gpt-4o",
            tools=[
                {
                    "type": "function",
                    "function": {
                        "name": "get_weather",
                        "description": "Get weather",
                        "parameters": {
                            "type": "object",
                            "properties": {"city": {"type": "string"}},
                        },
                    },
                }
            ],
        )
        response = await provider.chat(request)

        assert response.has_tool_calls is True
        assert len(response.tool_calls) == 1
        assert response.tool_calls[0].id == "call_abc"
        assert response.tool_calls[0].name == "get_weather"
        assert response.tool_calls[0].arguments == {"city": "Beijing"}

    async def test_response_without_tool_calls(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            url="https://api.openai.com/v1/chat/completions",
            json={
                "id": "chatcmpl-101",
                "model": "gpt-4o-mini",
                "choices": [
                    {
                        "index": 0,
                        "message": {"role": "assistant", "content": "Just a text response"},
                        "finish_reason": "stop",
                    }
                ],
                "usage": {"prompt_tokens": 5, "completion_tokens": 5, "total_tokens": 10},
            },
        )

        provider = OpenAICompatibleProvider(api_key="test-key")
        request = LLMRequest(
            messages=[{"role": "user", "content": "Hello"}],
            model="gpt-4o-mini",
        )
        response = await provider.chat(request)

        assert response.has_tool_calls is False
        assert response.content == "Just a text response"


class TestOpenAICompatibleProviderErrors:
    """API 错误处理测试"""

    async def test_api_error_raises_provider_error(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            url="https://api.openai.com/v1/chat/completions",
            status_code=401,
            json={"error": {"message": "Invalid API key", "type": "invalid_request_error"}},
        )

        provider = OpenAICompatibleProvider(api_key="bad-key")
        request = LLMRequest(
            messages=[{"role": "user", "content": "Hi"}],
            model="gpt-4o-mini",
        )

        with pytest.raises(LLMProviderError):
            await provider.chat(request)

    async def test_api_rate_limit_raises_provider_error(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            url="https://api.openai.com/v1/chat/completions",
            status_code=429,
            json={"error": {"message": "Rate limit exceeded", "type": "rate_limit_error"}},
        )

        provider = OpenAICompatibleProvider(api_key="test-key")
        request = LLMRequest(
            messages=[{"role": "user", "content": "Hi"}],
            model="gpt-4o-mini",
        )

        with pytest.raises(LLMProviderError):
            await provider.chat(request)
