"""
Tests for LLM config provider instantiation.

Tests cover:
- Provider instantiation for various providers
"""

import pytest


class TestProviderInstantiation:
    """Tests for provider instantiation."""

    def test_anthropic_instantiation_with_api_key(self):
        """Anthropic instantiation with API key."""
        api_key = "sk-ant-test-key"  # pragma: allowlist secret
        model = "claude-3-opus-20240229"

        # Configuration check
        assert api_key is not None
        assert model.startswith("claude")

        params = {
            "model": model,
            "anthropic_api_key": api_key,
            "temperature": 0.7,
        }

        assert "anthropic_api_key" in params
        assert params["model"] == model

    def test_anthropic_instantiation_fallback_env(self):
        """Anthropic falls back to env var."""
        api_key_from_settings = None
        api_key_from_env = "sk-ant-env-key"  # pragma: allowlist secret

        api_key = api_key_from_settings or api_key_from_env

        assert api_key == api_key_from_env

    def test_openai_optional_params_api_base(self):
        """OpenAI accepts custom API base."""
        api_base = "https://custom.openai.com/v1"

        params = {
            "model": "gpt-4",
            "api_key": "sk-test",
        }  # pragma: allowlist secret

        if api_base:
            params["openai_api_base"] = api_base

        assert params["openai_api_base"] == api_base

    def test_openai_optional_params_organization(self):
        """OpenAI accepts organization ID."""
        organization = "org-12345"

        params = {
            "model": "gpt-4",
            "api_key": "sk-test",
        }  # pragma: allowlist secret

        if organization:
            params["openai_organization"] = organization

        assert params["openai_organization"] == organization

    def test_openai_optional_params_streaming(self):
        """OpenAI accepts streaming parameter."""
        streaming = True

        params = {
            "model": "gpt-4",
            "api_key": "sk-test",
        }  # pragma: allowlist secret

        if streaming is not None:
            params["streaming"] = streaming

        assert params["streaming"] is True

    def test_openai_endpoint_url_normalization(self):
        """OpenAI endpoint URL is normalized."""
        urls = [
            ("https://api.example.com/", "https://api.example.com"),
            ("https://api.example.com", "https://api.example.com"),
            ("http://localhost:8000/v1/", "http://localhost:8000/v1"),
        ]

        for raw_url, expected in urls:
            normalized = raw_url.rstrip("/")
            assert normalized == expected

    def test_lmstudio_chat_openai_wrapper(self):
        """LM Studio uses ChatOpenAI wrapper."""
        lmstudio_url = "http://localhost:1234/v1"
        model = "local-model"

        # LM Studio uses fake API key
        params = {
            "model": model,
            "api_key": "lm-studio",  # pragma: allowlist secret
            "base_url": lmstudio_url,
            "temperature": 0.7,
        }

        assert params["api_key"] == "lm-studio"  # pragma: allowlist secret
        assert params["base_url"] == lmstudio_url

    def test_llamacpp_uses_openai_compatible_endpoint(self):
        """llamacpp now talks to llama-server via ChatOpenAI; no in-process load."""
        from unittest.mock import patch, MagicMock

        def _settings(key, default=None, *a, **kw):
            return {
                "llm.llamacpp.url": "http://localhost:8080/v1",
                "llm.local_context_window_size": 8192,
                "llm.supports_max_tokens": True,
                "llm.max_tokens": 4096,
            }.get(key, default)

        with (
            patch(
                "local_deep_research.config.llm_config.is_llm_registered",
                return_value=False,
            ),
            patch(
                "local_deep_research.config.llm_config.get_setting_from_snapshot",
                side_effect=_settings,
            ),
            patch(
                "local_deep_research.config.llm_config.ChatOpenAI"
            ) as mock_chat_openai,
        ):
            mock_chat_openai.return_value = MagicMock()

            from local_deep_research.config.llm_config import get_llm

            get_llm(provider="llamacpp", model_name="my-loaded-model")

            mock_chat_openai.assert_called_once()
            call_kwargs = mock_chat_openai.call_args[1]
            assert call_kwargs["base_url"] == "http://localhost:8080/v1"
            assert call_kwargs["model"] == "my-loaded-model"


class TestProviderValidation:
    """Tests for provider validation."""

    def test_valid_providers_list(self):
        """VALID_PROVIDERS contains expected providers."""
        from local_deep_research.config.llm_config import VALID_PROVIDERS

        expected = [
            "ollama",
            "openai",
            "anthropic",
            "google",
            "openrouter",
            "openai_endpoint",
            "lmstudio",
            "llamacpp",
            "none",
        ]

        for provider in expected:
            assert provider in VALID_PROVIDERS

    def test_invalid_provider_raises_error(self):
        """Invalid provider raises ValueError."""
        provider = "invalid_provider"
        valid_providers = ["ollama", "openai", "anthropic"]

        if provider not in valid_providers:
            with pytest.raises(ValueError):
                raise ValueError(f"Invalid provider: {provider}")

    def test_provider_name_cleaning(self):
        """Provider name is cleaned of whitespace and quotes."""
        dirty_names = [
            '" ollama "',
            "'openai'",
            "  anthropic  ",
            '"google"',
        ]

        for name in dirty_names:
            cleaned = name.strip().strip("\"'").strip()
            assert cleaned in ["ollama", "openai", "anthropic", "google"]


class TestProviderAvailabilityChecks:
    """Tests for provider availability checks."""

    def test_openai_available_with_key(self):
        """OpenAI available when API key present."""
        api_key = "sk-test"  # pragma: allowlist secret

        is_available = bool(api_key)

        assert is_available

    def test_anthropic_available_with_key(self):
        """Anthropic available when API key present."""
        api_key = "sk-ant-test"  # pragma: allowlist secret

        is_available = bool(api_key)

        assert is_available

    def test_google_delegates_to_provider(self):
        """Google availability check delegates to GoogleProvider."""
        # Google provider has its own is_available method
        google_available = True  # Simulated

        assert google_available is not None

    def test_openrouter_delegates_to_provider(self):
        """OpenRouter availability check delegates to provider."""
        # OpenRouter provider has its own is_available method
        openrouter_available = True  # Simulated

        assert openrouter_available is not None
