"""Tests for API key frontend settings functionality.

These tests verify that the API key settings used by the research form frontend
are correctly configured and can be saved/retrieved via the settings API.
"""

# Provider to API key setting mapping (must match research.js)
API_KEY_SETTINGS = {
    "OPENAI": "llm.openai.api_key",
    "ANTHROPIC": "llm.anthropic.api_key",
    "GOOGLE": "llm.google.api_key",
    "OPENROUTER": "llm.openrouter.api_key",
    "XAI": "llm.xai.api_key",
    "IONOS": "llm.ionos.api_key",
    "OPENAI_ENDPOINT": "llm.openai_endpoint.api_key",
    "OLLAMA": "llm.ollama.api_key",
}

# Providers that require API keys (cloud providers)
CLOUD_PROVIDERS = [
    "OPENAI",
    "ANTHROPIC",
    "GOOGLE",
    "OPENROUTER",
    "XAI",
    "IONOS",
]

# Providers with optional API keys (key is read from settings but missing
# is OK; falls back to a placeholder ChatOpenAI accepts).
OPTIONAL_API_KEY_PROVIDERS = [
    "OLLAMA",
    "OPENAI_ENDPOINT",
    "LMSTUDIO",
    "LLAMACPP",
]

# Providers that have no API key concept at all (api_key_setting=None).
# Currently empty after the LLM construction collapse — every built-in
# provider declares a real api_key_setting now.
LOCAL_PROVIDERS_NO_KEY: list[str] = []


class TestAPIKeyProviderMapping:
    """Test that provider implementations define correct API key settings."""

    def test_openai_provider_api_key_setting(self):
        """Test OpenAI provider uses correct API key setting."""
        from local_deep_research.llm.providers.implementations.openai import (
            OpenAIProvider,
        )

        assert OpenAIProvider.api_key_setting == "llm.openai.api_key"

    def test_anthropic_provider_api_key_setting(self):
        """Test Anthropic provider uses correct API key setting."""
        from local_deep_research.llm.providers.implementations.anthropic import (
            AnthropicProvider,
        )

        assert AnthropicProvider.api_key_setting == "llm.anthropic.api_key"

    def test_google_provider_api_key_setting(self):
        """Test Google provider uses correct API key setting."""
        from local_deep_research.llm.providers.implementations.google import (
            GoogleProvider,
        )

        assert GoogleProvider.api_key_setting == "llm.google.api_key"

    def test_openrouter_provider_api_key_setting(self):
        """Test OpenRouter provider uses correct API key setting."""
        from local_deep_research.llm.providers.implementations.openrouter import (
            OpenRouterProvider,
        )

        assert OpenRouterProvider.api_key_setting == "llm.openrouter.api_key"

    def test_xai_provider_api_key_setting(self):
        """Test xAI provider uses correct API key setting."""
        from local_deep_research.llm.providers.implementations.xai import (
            XAIProvider,
        )

        assert XAIProvider.api_key_setting == "llm.xai.api_key"

    def test_ionos_provider_api_key_setting(self):
        """Test IONOS provider uses correct API key setting."""
        from local_deep_research.llm.providers.implementations.ionos import (
            IONOSProvider,
        )

        assert IONOSProvider.api_key_setting == "llm.ionos.api_key"

    def test_openai_endpoint_provider_api_key_setting(self):
        """Test OpenAI Endpoint provider uses correct API key setting."""
        from local_deep_research.llm.providers.implementations.custom_openai_endpoint import (
            CustomOpenAIEndpointProvider,
        )

        assert (
            CustomOpenAIEndpointProvider.api_key_setting
            == "llm.openai_endpoint.api_key"
        )

    def test_ollama_provider_api_key_setting(self):
        """Test Ollama provider uses correct API key setting (optional)."""
        from local_deep_research.llm.providers.implementations.ollama import (
            OllamaProvider,
        )

        assert OllamaProvider.api_key_setting == "llm.ollama.api_key"

    def test_lmstudio_provider_optional_api_key(self):
        """LM Studio declares its API key setting as optional.

        Older versions of LDR set ``api_key_setting = None`` on LMStudio to
        signal "no auth needed". Now we model it as a real setting + the
        ``api_key_optional`` flag so ``has_api_key``/``is_available``
        semantics are uniform across providers.
        """
        from local_deep_research.llm.providers.implementations.lmstudio import (
            LMStudioProvider,
        )

        assert LMStudioProvider.api_key_setting == "llm.lmstudio.api_key"
        assert LMStudioProvider.api_key_optional is True

    def test_llamacpp_provider_optional_api_key(self):
        """llama.cpp declares its API key setting as optional (proxy auth)."""
        from local_deep_research.llm.providers.implementations.llamacpp import (
            LlamaCppProvider,
        )

        assert LlamaCppProvider.api_key_setting == "llm.llamacpp.api_key"
        assert LlamaCppProvider.api_key_optional is True


class TestAPIKeySettingsInMemory:
    """Test API key settings with InMemorySettingsManager."""

    def test_set_and_get_api_key(self):
        """Test setting and retrieving API key values."""
        from local_deep_research.api.settings_utils import (
            InMemorySettingsManager,
        )

        manager = InMemorySettingsManager()

        # Set an API key
        test_key = "sk-test-12345"
        result = manager.set_setting("llm.openai.api_key", test_key)
        assert result is True

        # Retrieve the API key
        retrieved = manager.get_setting("llm.openai.api_key")
        assert retrieved == test_key

    def test_api_key_defaults_to_empty(self):
        """Test that API keys default to empty string."""
        from local_deep_research.api.settings_utils import (
            InMemorySettingsManager,
        )

        manager = InMemorySettingsManager()

        for provider, setting_key in API_KEY_SETTINGS.items():
            value = manager.get_setting(setting_key)
            # API keys should default to empty string or None
            assert value in ("", None), (
                f"API key '{setting_key}' should default to empty, got '{value}'"
            )

    def test_multiple_api_keys_independent(self):
        """Test that setting one API key doesn't affect others."""
        from local_deep_research.api.settings_utils import (
            InMemorySettingsManager,
        )

        manager = InMemorySettingsManager()

        # Set OpenAI key
        manager.set_setting("llm.openai.api_key", "openai-key-123")

        # Set Anthropic key
        manager.set_setting("llm.anthropic.api_key", "anthropic-key-456")

        # Verify both are independent
        assert manager.get_setting("llm.openai.api_key") == "openai-key-123"
        assert (
            manager.get_setting("llm.anthropic.api_key") == "anthropic-key-456"
        )


class TestAPIKeySettingsSnapshot:
    """Test API key settings in settings snapshots."""

    def test_core_api_keys_in_snapshot(self):
        """Test that core API keys are included in settings snapshot."""
        from local_deep_research.api.settings_utils import (
            get_default_settings_snapshot,
        )

        snapshot = get_default_settings_snapshot()

        # Core API keys that should always be in snapshot
        core_api_keys = [
            "llm.openai.api_key",
            "llm.anthropic.api_key",
            "llm.openai_endpoint.api_key",
        ]

        for setting_key in core_api_keys:
            assert setting_key in snapshot, (
                f"API key '{setting_key}' not found in settings snapshot"
            )

    def test_create_snapshot_with_api_key(self):
        """Test creating snapshot with API key value."""
        from local_deep_research.api.settings_utils import (
            create_settings_snapshot,
        )

        snapshot = create_settings_snapshot(
            provider="openai", api_key="sk-test-snapshot-key"
        )

        # Check that the API key was set
        assert snapshot["llm.openai.api_key"]["value"] == "sk-test-snapshot-key"

    def test_snapshot_api_key_metadata(self):
        """Test that API key settings have proper metadata in snapshot."""
        from local_deep_research.api.settings_utils import (
            get_default_settings_snapshot,
        )

        snapshot = get_default_settings_snapshot()

        # Check metadata for core API keys that are in the snapshot
        core_api_keys = [
            "llm.openai.api_key",
            "llm.anthropic.api_key",
            "llm.openai_endpoint.api_key",
        ]

        for setting_key in core_api_keys:
            if setting_key in snapshot:
                setting = snapshot[setting_key]

                # Check required metadata fields
                assert "value" in setting, f"{setting_key} missing 'value'"
                assert "editable" in setting, (
                    f"{setting_key} missing 'editable'"
                )

                # API keys should be editable
                assert setting["editable"] is True, (
                    f"{setting_key} should be editable"
                )


class TestAPIKeyEnvironmentOverride:
    """Test that API keys can be overridden by environment variables."""

    def test_openai_api_key_env_override(self, monkeypatch):
        """Test OpenAI API key can be set via environment variable."""
        from local_deep_research.api.settings_utils import (
            InMemorySettingsManager,
        )

        test_key = "sk-env-override-key"
        monkeypatch.setenv("LDR_LLM_OPENAI_API_KEY", test_key)

        manager = InMemorySettingsManager()
        retrieved = manager.get_setting("llm.openai.api_key")

        assert retrieved == test_key

    def test_anthropic_api_key_env_override(self, monkeypatch):
        """Test Anthropic API key can be set via environment variable."""
        from local_deep_research.api.settings_utils import (
            InMemorySettingsManager,
        )

        test_key = "sk-ant-env-override"
        monkeypatch.setenv("LDR_LLM_ANTHROPIC_API_KEY", test_key)

        manager = InMemorySettingsManager()
        retrieved = manager.get_setting("llm.anthropic.api_key")

        assert retrieved == test_key

    def test_openai_endpoint_api_key_env_override(self, monkeypatch):
        """Test OpenAI Endpoint API key can be set via environment variable."""
        from local_deep_research.api.settings_utils import (
            InMemorySettingsManager,
        )

        test_key = "sk-endpoint-env-override"
        monkeypatch.setenv("LDR_LLM_OPENAI_ENDPOINT_API_KEY", test_key)

        manager = InMemorySettingsManager()
        retrieved = manager.get_setting("llm.openai_endpoint.api_key")

        assert retrieved == test_key


class TestFrontendAPIKeyMapping:
    """Test that frontend API key mapping matches backend."""

    def test_frontend_mapping_matches_providers(self):
        """Verify the frontend mapping in this test matches actual providers."""
        from local_deep_research.llm.providers.implementations.openai import (
            OpenAIProvider,
        )
        from local_deep_research.llm.providers.implementations.anthropic import (
            AnthropicProvider,
        )
        from local_deep_research.llm.providers.implementations.google import (
            GoogleProvider,
        )
        from local_deep_research.llm.providers.implementations.openrouter import (
            OpenRouterProvider,
        )
        from local_deep_research.llm.providers.implementations.xai import (
            XAIProvider,
        )
        from local_deep_research.llm.providers.implementations.ionos import (
            IONOSProvider,
        )
        from local_deep_research.llm.providers.implementations.custom_openai_endpoint import (
            CustomOpenAIEndpointProvider,
        )
        from local_deep_research.llm.providers.implementations.ollama import (
            OllamaProvider,
        )

        # Map provider classes to their expected settings
        provider_classes = {
            "OPENAI": OpenAIProvider,
            "ANTHROPIC": AnthropicProvider,
            "GOOGLE": GoogleProvider,
            "OPENROUTER": OpenRouterProvider,
            "XAI": XAIProvider,
            "IONOS": IONOSProvider,
            "OPENAI_ENDPOINT": CustomOpenAIEndpointProvider,
            "OLLAMA": OllamaProvider,
        }

        for provider_name, expected_setting in API_KEY_SETTINGS.items():
            provider_class = provider_classes.get(provider_name)
            if provider_class:
                actual_setting = provider_class.api_key_setting
                assert actual_setting == expected_setting, (
                    f"Frontend mapping for {provider_name} is '{expected_setting}' "
                    f"but provider class uses '{actual_setting}'"
                )
