"""Tests for API settings utilities."""

from unittest.mock import patch

from local_deep_research.api.settings_utils import (
    InMemorySettingsManager,
    get_default_settings_snapshot,
    create_settings_snapshot,
    extract_setting_value,
)


class TestInMemorySettingsManager:
    """Tests for InMemorySettingsManager class."""

    def test_init_loads_defaults(self):
        """Test that initialization loads default settings."""
        with patch.object(
            InMemorySettingsManager, "_load_defaults"
        ) as mock_load:
            InMemorySettingsManager()
            mock_load.assert_called_once()

    def test_get_setting_existing_key(self):
        """Test getting an existing setting."""
        manager = InMemorySettingsManager()
        # llm.provider should be in defaults
        value = manager.get_setting("llm.provider")
        assert value is not None

    def test_get_setting_missing_key_returns_default(self):
        """Test getting a missing setting returns the default."""
        manager = InMemorySettingsManager()
        value = manager.get_setting("nonexistent.key", default="default_value")
        assert value == "default_value"

    def test_get_setting_none_default(self):
        """Test getting a missing setting with None default."""
        manager = InMemorySettingsManager()
        value = manager.get_setting("nonexistent.key")
        assert value is None

    def test_set_setting_existing_key(self):
        """Test setting an existing key."""
        manager = InMemorySettingsManager()
        result = manager.set_setting("llm.provider", "openai")
        assert result is True
        assert manager.get_setting("llm.provider") == "openai"

    def test_set_setting_nonexistent_key(self):
        """Test setting a nonexistent key returns False."""
        manager = InMemorySettingsManager()
        result = manager.set_setting("nonexistent.key", "value")
        assert result is False

    def test_get_all_settings_returns_dict(self):
        """Test get_all_settings returns a dictionary."""
        manager = InMemorySettingsManager()
        settings = manager.get_all_settings()
        assert isinstance(settings, dict)
        assert len(settings) > 0

    def test_get_all_settings_returns_copy(self):
        """Test get_all_settings returns a deep copy."""
        manager = InMemorySettingsManager()
        settings1 = manager.get_all_settings()
        settings2 = manager.get_all_settings()
        assert settings1 is not settings2

    def test_create_or_update_setting_with_dict(self):
        """Test creating a setting from a dict."""
        manager = InMemorySettingsManager()
        setting = {
            "key": "new.setting",
            "value": "test_value",
            "ui_element": "text",
        }
        result = manager.create_or_update_setting(setting)
        assert result is not None
        assert manager.get_setting("new.setting") == "test_value"

    def test_create_or_update_setting_without_key(self):
        """Test creating a setting without key returns None."""
        manager = InMemorySettingsManager()
        result = manager.create_or_update_setting({"value": "test"})
        assert result is None

    def test_delete_setting_existing(self):
        """Test deleting an existing setting."""
        manager = InMemorySettingsManager()
        # First ensure it exists
        manager.create_or_update_setting(
            {"key": "temp.setting", "value": "temp", "ui_element": "text"}
        )
        assert manager.get_setting("temp.setting") == "temp"

        result = manager.delete_setting("temp.setting")
        assert result is True
        assert manager.get_setting("temp.setting") is None

    def test_delete_setting_nonexistent(self):
        """Test deleting a nonexistent setting returns False."""
        manager = InMemorySettingsManager()
        result = manager.delete_setting("nonexistent.key")
        assert result is False

    def test_import_settings_basic(self):
        """Test importing settings."""
        manager = InMemorySettingsManager()
        new_settings = {
            "imported.setting": {
                "value": "imported_value",
                "ui_element": "text",
            },
        }
        manager.import_settings(new_settings)
        assert manager.get_setting("imported.setting") == "imported_value"

    def test_import_settings_with_delete_extra(self):
        """Test importing with delete_extra clears existing settings."""
        manager = InMemorySettingsManager()
        original_count = len(manager.get_all_settings())
        assert original_count > 0

        manager.import_settings(
            {"only.setting": {"value": "only", "ui_element": "text"}},
            delete_extra=True,
        )
        settings = manager.get_all_settings()
        assert len(settings) == 1
        assert "only.setting" in settings

    def test_import_settings_no_overwrite(self):
        """Test importing without overwriting existing settings."""
        manager = InMemorySettingsManager()
        manager.create_or_update_setting(
            {"key": "existing", "value": "original", "ui_element": "text"}
        )

        manager.import_settings(
            {"existing": {"value": "new_value", "ui_element": "text"}},
            overwrite=False,
        )
        assert manager.get_setting("existing") == "original"

    def test_get_typed_value_checkbox(self):
        """Test type conversion for checkbox."""
        manager = InMemorySettingsManager()
        setting_data = {"ui_element": "checkbox"}

        assert manager._get_typed_value(setting_data, "true") is True
        assert manager._get_typed_value(setting_data, "false") is False
        assert manager._get_typed_value(setting_data, "1") is True
        assert manager._get_typed_value(setting_data, "yes") is True

    def test_get_typed_value_number(self):
        """Test type conversion for number."""
        manager = InMemorySettingsManager()
        setting_data = {"ui_element": "number"}

        assert manager._get_typed_value(setting_data, "42") == 42.0
        assert manager._get_typed_value(setting_data, "3.14") == 3.14

    def test_get_typed_value_text(self):
        """Test type conversion for text."""
        manager = InMemorySettingsManager()
        setting_data = {"ui_element": "text"}

        assert manager._get_typed_value(setting_data, 123) == "123"
        assert manager._get_typed_value(setting_data, "hello") == "hello"

    def test_get_typed_value_unknown_type(self):
        """Test type conversion for unknown ui_element returns as-is."""
        manager = InMemorySettingsManager()
        setting_data = {"ui_element": "unknown_type"}

        result = manager._get_typed_value(setting_data, "value")
        assert result == "value"

    def test_load_from_defaults_file(self):
        """Test that load_from_defaults_file reloads defaults."""
        manager = InMemorySettingsManager()
        # Modify a setting
        manager.set_setting("llm.provider", "modified")
        assert manager.get_setting("llm.provider") == "modified"

        # Reload defaults
        manager.load_from_defaults_file()
        # Should be back to default
        # (exact value depends on defaults file)

    def test_env_var_override(self, monkeypatch):
        """Test that environment variables override defaults."""
        monkeypatch.setenv("LDR_LLM_PROVIDER", "env_provider")
        InMemorySettingsManager()
        # Check if env var was applied (depends on implementation)


class TestGetDefaultSettingsSnapshot:
    """Tests for get_default_settings_snapshot function."""

    def test_returns_dict(self):
        """Test that function returns a dictionary."""
        snapshot = get_default_settings_snapshot()
        assert isinstance(snapshot, dict)

    def test_contains_core_settings(self):
        """Test that snapshot contains core settings."""
        snapshot = get_default_settings_snapshot()
        # Check for some expected keys
        assert "llm.provider" in snapshot
        assert "llm.temperature" in snapshot or "llm.model" in snapshot

    def test_settings_have_value_key(self):
        """Test that settings have a 'value' key."""
        snapshot = get_default_settings_snapshot()
        for key, setting in snapshot.items():
            if isinstance(setting, dict):
                assert "value" in setting, f"Setting {key} missing 'value' key"


class TestCreateSettingsSnapshot:
    """Tests for create_settings_snapshot function."""

    def test_returns_dict(self):
        """Test that function returns a dictionary."""
        snapshot = create_settings_snapshot()
        assert isinstance(snapshot, dict)

    def test_without_overrides(self):
        """Test creating snapshot without overrides uses defaults."""
        snapshot = create_settings_snapshot()
        assert len(snapshot) > 0

    def test_with_overrides_dict(self):
        """Test creating snapshot with overrides dict."""
        snapshot = create_settings_snapshot(
            overrides={"llm.provider": "openai"}
        )
        assert snapshot["llm.provider"]["value"] == "openai"

    def test_with_overrides_as_first_arg(self):
        """Test creating snapshot with overrides as first positional arg."""
        snapshot = create_settings_snapshot({"llm.provider": "anthropic"})
        assert snapshot["llm.provider"]["value"] == "anthropic"

    def test_with_custom_base_settings(self):
        """Test creating snapshot with custom base settings."""
        base = {"custom.setting": {"value": "base_value", "ui_element": "text"}}
        snapshot = create_settings_snapshot(base_settings=base)
        assert "custom.setting" in snapshot
        assert snapshot["custom.setting"]["value"] == "base_value"

    def test_override_unknown_key(self):
        """Test overriding with unknown key creates new entry."""
        snapshot = create_settings_snapshot(
            overrides={"new.unknown.key": "new_value"}
        )
        assert "new.unknown.key" in snapshot
        assert snapshot["new.unknown.key"]["value"] == "new_value"

    def test_provider_kwarg(self):
        """Test provider kwarg shortcut."""
        snapshot = create_settings_snapshot(provider="openai")
        assert snapshot["llm.provider"]["value"] == "openai"

    def test_provider_with_api_key_kwarg(self):
        """Test provider and api_key kwargs together."""
        snapshot = create_settings_snapshot(
            provider="openai", api_key="sk-test-key"
        )
        assert snapshot["llm.provider"]["value"] == "openai"
        assert "llm.openai.api_key" in snapshot
        assert snapshot["llm.openai.api_key"]["value"] == "sk-test-key"

    def test_temperature_kwarg(self):
        """Test temperature kwarg shortcut."""
        snapshot = create_settings_snapshot(temperature=0.5)
        assert snapshot["llm.temperature"]["value"] == 0.5

    def test_max_search_results_kwarg(self):
        """Test max_search_results kwarg shortcut."""
        snapshot = create_settings_snapshot(max_search_results=50)
        assert snapshot["search.max_results"]["value"] == 50

    def test_infer_ui_element_bool(self):
        """Test ui_element inference for boolean values."""
        snapshot = create_settings_snapshot(overrides={"new.bool": True})
        assert snapshot["new.bool"]["ui_element"] == "checkbox"

    def test_infer_ui_element_number(self):
        """Test ui_element inference for numeric values."""
        snapshot = create_settings_snapshot(overrides={"new.number": 42})
        assert snapshot["new.number"]["ui_element"] == "number"

    def test_infer_ui_element_dict(self):
        """Test ui_element inference for dict values."""
        snapshot = create_settings_snapshot(
            overrides={"new.dict": {"nested": "value"}}
        )
        assert snapshot["new.dict"]["ui_element"] == "json"


class TestExtractSettingValue:
    """Tests for extract_setting_value function."""

    def test_extract_with_value_key(self):
        """Test extracting value from dict with 'value' key."""
        snapshot = {"test.key": {"value": "test_value"}}
        result = extract_setting_value(snapshot, "test.key")
        assert result == "test_value"

    def test_extract_direct_value(self):
        """Test extracting direct value (not dict)."""
        snapshot = {"test.key": "direct_value"}
        result = extract_setting_value(snapshot, "test.key")
        assert result == "direct_value"

    def test_extract_missing_key(self):
        """Test extracting missing key returns default."""
        snapshot = {"other.key": {"value": "value"}}
        result = extract_setting_value(
            snapshot, "missing.key", default="default"
        )
        assert result == "default"

    def test_extract_from_none_snapshot(self):
        """Test extracting from None snapshot returns default."""
        result = extract_setting_value(None, "any.key", default="default")
        assert result == "default"

    def test_extract_default_none(self):
        """Test default value is None when not specified."""
        snapshot = {}
        result = extract_setting_value(snapshot, "missing.key")
        assert result is None

    def test_extract_nested_dict_value(self):
        """Test extracting value that is itself a dict."""
        snapshot = {"complex.key": {"value": {"nested": "data"}}}
        result = extract_setting_value(snapshot, "complex.key")
        assert result == {"nested": "data"}
