"""Tests for markitect.llm.openrouter."""

from unittest import mock

import pytest

from markitect.llm.openrouter import OpenRouterAdapter
from markitect.llm.config import LLMConfig
from markitect.llm.exceptions import LLMRateLimitError, LLMAPIError
from markitect.prompts.execution.models import RunConfig, LLMResponse


def _make_api_response(content="hello world", model="test-model"):
    return {
        "id": "gen-abc123",
        "model": model,
        "choices": [
            {
                "message": {"role": "assistant", "content": content},
                "finish_reason": "stop",
            }
        ],
        "usage": {
            "prompt_tokens": 10,
            "completion_tokens": 5,
            "total_tokens": 15,
        },
    }


class TestOpenRouterAdapter:
    def _adapter(self, **kwargs):
        defaults = {"api_key": "sk-test", "model": "test/model"}
        defaults.update(kwargs)
        return OpenRouterAdapter(**defaults)

    @mock.patch("markitect.llm.openrouter.post_json")
    def test_execute_prompt_success(self, mock_post):
        mock_post.return_value = _make_api_response("Generated text")
        adapter = self._adapter()
        config = RunConfig(model_name="test/model", temperature=0.5, max_tokens=100)

        resp = adapter.execute_prompt("Write something", config)

        assert isinstance(resp, LLMResponse)
        assert resp.content == "Generated text"
        assert resp.usage["prompt_tokens"] == 10
        assert resp.usage["completion_tokens"] == 5
        assert resp.metadata["provider"] == "openrouter"
        assert "latency_seconds" in resp.metadata

    @mock.patch("markitect.llm.openrouter.post_json")
    def test_payload_structure(self, mock_post):
        mock_post.return_value = _make_api_response()
        adapter = self._adapter(system_prompt="You are helpful")
        config = RunConfig(temperature=0.3, max_tokens=500)

        adapter.execute_prompt("Test prompt", config)

        call_args = mock_post.call_args
        payload = call_args[0][1]  # second positional arg
        assert payload["model"] == "test/model"
        assert payload["temperature"] == 0.3
        assert payload["max_tokens"] == 500
        assert len(payload["messages"]) == 2
        assert payload["messages"][0]["role"] == "system"
        assert payload["messages"][1]["role"] == "user"

    @mock.patch("markitect.llm.openrouter.post_json")
    def test_no_system_prompt(self, mock_post):
        mock_post.return_value = _make_api_response()
        adapter = self._adapter()
        config = RunConfig()

        adapter.execute_prompt("Test", config)

        payload = mock_post.call_args[0][1]
        assert len(payload["messages"]) == 1
        assert payload["messages"][0]["role"] == "user"

    @mock.patch("markitect.llm.openrouter.post_json")
    @mock.patch("markitect.llm.openrouter.time.sleep")
    def test_retry_on_429(self, mock_sleep, mock_post):
        mock_post.side_effect = [
            LLMRateLimitError("rate limited", status_code=429),
            _make_api_response("after retry"),
        ]
        adapter = self._adapter(max_retries=2)
        config = RunConfig()

        resp = adapter.execute_prompt("Test", config)
        assert resp.content == "after retry"
        assert mock_sleep.call_count == 1

    @mock.patch("markitect.llm.openrouter.post_json")
    @mock.patch("markitect.llm.openrouter.time.sleep")
    def test_retry_on_5xx(self, mock_sleep, mock_post):
        mock_post.side_effect = [
            LLMAPIError("server error", status_code=502),
            _make_api_response("recovered"),
        ]
        adapter = self._adapter(max_retries=2)
        config = RunConfig()

        resp = adapter.execute_prompt("Test", config)
        assert resp.content == "recovered"

    @mock.patch("markitect.llm.openrouter.post_json")
    def test_no_retry_on_4xx(self, mock_post):
        mock_post.side_effect = LLMAPIError("bad request", status_code=400)
        adapter = self._adapter(max_retries=2)
        config = RunConfig()

        with pytest.raises(LLMAPIError) as exc_info:
            adapter.execute_prompt("Test", config)
        assert exc_info.value.status_code == 400

    @mock.patch("markitect.llm.openrouter.post_json")
    @mock.patch("markitect.llm.openrouter.time.sleep")
    def test_exhausted_retries_raises(self, mock_sleep, mock_post):
        mock_post.side_effect = LLMRateLimitError("rate limited", status_code=429)
        adapter = self._adapter(max_retries=1)
        config = RunConfig()

        with pytest.raises(LLMRateLimitError):
            adapter.execute_prompt("Test", config)

    def test_validate_config_no_key(self):
        adapter = OpenRouterAdapter(api_key=None, model="test")
        # Force key to None
        adapter._api_key = None
        config = RunConfig()
        assert adapter.validate_config(config) is False

    def test_validate_config_ok(self):
        adapter = self._adapter()
        config = RunConfig(temperature=0.5)
        assert adapter.validate_config(config) is True

    def test_validate_config_bad_temperature(self):
        adapter = self._adapter()
        config = RunConfig(temperature=3.0)
        assert adapter.validate_config(config) is False
