"""Tests for provider abstraction."""

from __future__ import annotations

from typing import Any

import pytest

from ai_qa.core.config import configure, reset_config
from ai_qa.exceptions import ProviderNotConfiguredError
from ai_qa.providers import ProviderRegistry, configure_provider
from ai_qa.providers.base import BaseProvider


class MockProvider(BaseProvider):
    """Mock provider for testing."""

    name = "mock"

    def judge(self, system: str, user: str) -> dict[str, Any]:
        """Return mock judgment."""
        return {
            "score": 0.8,
            "evidence": "Mock judgment",
            "cost_usd": 0.01,
            "tokens": {"input": 100, "output": 50},
        }

    def generate(self, prompt: str, system: str | None = None) -> str:
        """Return mock response."""
        return "Mock response"


def test_base_provider_is_abstract() -> None:
    """Test that BaseProvider cannot be instantiated."""
    with pytest.raises(TypeError):
        BaseProvider(model="test")  # type: ignore


def test_base_provider_has_judge_method() -> None:
    """Test that BaseProvider defines judge abstract method."""
    assert hasattr(BaseProvider, "judge")
    assert hasattr(BaseProvider, "generate")


def test_base_provider_repr() -> None:
    """Test BaseProvider __repr__."""
    provider = MockProvider(model="test-model")
    assert "test-model" in repr(provider)
    assert "MockProvider" in repr(provider)


def test_base_provider_config_storage() -> None:
    """Test that providers store config."""
    provider = MockProvider(model="test", max_tokens=2048, temperature=0.5)
    assert provider.config["max_tokens"] == 2048
    assert provider.config["temperature"] == 0.5


def test_base_provider_judge() -> None:
    """Test judge method on concrete provider."""
    provider = MockProvider(model="test")
    result = provider.judge("Judge this", "OUTPUT: test")

    assert result["score"] == 0.8
    assert "Mock judgment" in result["evidence"]
    assert result["cost_usd"] == 0.01
    assert result["tokens"]["input"] == 100


def test_base_provider_generate() -> None:
    """Test generate method on concrete provider."""
    provider = MockProvider(model="test")
    result = provider.generate("Say hello", system="You are helpful")

    assert result == "Mock response"


def test_provider_registry_list() -> None:
    """Test ProviderRegistry.list()."""
    providers = ProviderRegistry.list()
    assert "anthropic" in providers
    assert "openai" in providers
    assert "local" in providers


def test_provider_registry_get() -> None:
    """Test ProviderRegistry.get() for known providers."""
    from ai_qa.providers import AnthropicProvider, OpenAIProvider, LocalProvider

    assert ProviderRegistry.get("anthropic") == AnthropicProvider
    assert ProviderRegistry.get("openai") == OpenAIProvider
    assert ProviderRegistry.get("local") == LocalProvider


def test_provider_registry_get_unknown() -> None:
    """Test ProviderRegistry.get() raises for unknown provider."""
    with pytest.raises(ProviderNotConfiguredError) as exc_info:
        ProviderRegistry.get("unknown")
    assert "unknown" in str(exc_info.value).lower()


def test_provider_registry_register() -> None:
    """Test ProviderRegistry.register()."""
    ProviderRegistry.register("test-mock", MockProvider)
    assert ProviderRegistry.get("test-mock") == MockProvider


def test_provider_registry_instance_management() -> None:
    """Test ProviderRegistry instance management."""
    reset_config()

    provider = MockProvider(model="test")
    ProviderRegistry.set_instance(provider)

    assert ProviderRegistry.get_instance() == provider

    ProviderRegistry.set_instance(None)  # type: ignore
    assert ProviderRegistry.get_instance() is None


def test_configure_provider() -> None:
    """Test configure_provider() function."""
    reset_config()

    provider = configure_provider("local", "test-model")

    assert provider.model == "test-model"
    assert provider.name == "local"
    assert ProviderRegistry.get_instance() == provider


def test_configure_provider_with_kwargs() -> None:
    """Test configure_provider() with custom config."""
    reset_config()

    provider = configure_provider(
        "local",
        "test-model",
        base_url="http://localhost:9000",
        max_tokens=4096,
    )

    assert provider.base_url == "http://localhost:9000"
    assert provider.config["max_tokens"] == 4096


def test_get_provider_not_configured_raises() -> None:
    """Test get_provider() raises if not configured."""
    from ai_qa.providers import get_provider

    reset_config()

    with pytest.raises(ProviderNotConfiguredError) as exc_info:
        get_provider()
    assert "No provider configured" in str(exc_info.value)


def test_get_provider_missing_model_raises() -> None:
    """Test get_provider() raises if model not set."""
    from ai_qa.providers import get_provider

    reset_config()

    with pytest.raises(ProviderNotConfiguredError) as exc_info:
        configure(provider="local")
        get_provider()
    assert "No model configured" in str(exc_info.value)


def test_get_provider_uses_global_config() -> None:
    """Test get_provider() uses global Config."""
    from ai_qa.providers import get_provider

    reset_config()

    configure(provider="local", model="llama2")
    provider = get_provider()

    assert provider.name == "local"
    assert provider.model == "llama2"


def test_get_provider_caches_instance() -> None:
    """Test get_provider() caches the provider instance."""
    from ai_qa.providers import get_provider

    reset_config()

    configure(provider="local", model="llama2")
    provider1 = get_provider()
    provider2 = get_provider()

    assert provider1 is provider2


def test_local_provider_attributes() -> None:
    """Test LocalProvider has required attributes."""
    from ai_qa.providers import LocalProvider

    assert LocalProvider.name == "local"
    provider = LocalProvider(model="llama2")
    assert provider.model == "llama2"
    assert provider.base_url == "http://localhost:11434"


def test_local_provider_custom_base_url() -> None:
    """Test LocalProvider accepts custom base_url."""
    from ai_qa.providers import LocalProvider

    provider = LocalProvider(model="llama2", base_url="http://localhost:8000")
    assert provider.base_url == "http://localhost:8000"
