from __future__ import annotations

import asyncio
from unittest.mock import AsyncMock, MagicMock, patch

import httpx

from aicd.config import AppConfig
from aicd.agent.llm import OpenAICompatibleClient, _is_retryable_llm_error
from openai import APIStatusError, APITimeoutError


def _req() -> httpx.Request:
    return httpx.Request("GET", "http://example.invalid/v1")


def test_is_retryable() -> None:
    assert _is_retryable_llm_error(APITimeoutError(request=_req()))
    r429 = httpx.Response(429, request=_req())
    assert _is_retryable_llm_error(APIStatusError("r", response=r429, body=None))
    r400 = httpx.Response(400, request=_req())
    assert not _is_retryable_llm_error(APIStatusError("b", response=r400, body=None))


def test_chat_retries_then_ok() -> None:
    cfg = AppConfig()
    cfg.llm.api_key = "k"
    cfg.llm.base_url = "http://test"
    cfg.llm.model = "m"
    cfg.llm.max_retries = 2
    cfg.llm.retry_base_delay_sec = 0.01
    client = OpenAICompatibleClient(cfg)

    ok_resp = MagicMock()
    ok_resp.choices = [MagicMock(message=MagicMock(content="hi"))]

    mock_create = AsyncMock(
        side_effect=[
            APITimeoutError(request=_req()),
            ok_resp,
        ]
    )
    mock_api = MagicMock()
    mock_api.chat.completions.create = mock_create

    async def _go() -> None:
        with patch.object(client, "_get_client", return_value=mock_api):
            r = await client.chat([{"role": "user", "content": "x"}], [])
        assert r is ok_resp
        assert mock_create.await_count == 2

    asyncio.run(_go())
