"""Tests for configuration module."""

import pytest
import yaml

from oclawma.config import (
    Config,
    ConfigurationError,
    ContextBudgetConfig,
    FallbackConfig,
    ModelProfile,
)


class TestModelProfile:
    """Test ModelProfile dataclass."""

    def test_default_creation(self):
        """Test creating a profile with defaults."""
        profile = ModelProfile(name="test", provider="ollama", model="test-model")

        assert profile.name == "test"
        assert profile.provider == "ollama"
        assert profile.model == "test-model"
        assert profile.temperature == 0.7
        assert profile.top_p == 1.0
        assert profile.enabled is True

    def test_get_api_key_from_config(self):
        """Test getting API key from config."""
        profile = ModelProfile(
            name="test",
            provider="kimi",
            model="k2.5",
            api_key="secret-key",
        )

        assert profile.get_api_key() == "secret-key"

    def test_get_api_key_from_env(self, monkeypatch):
        """Test getting API key from environment variable."""
        monkeypatch.setenv("TEST_API_KEY", "env-secret-key")

        profile = ModelProfile(
            name="test",
            provider="kimi",
            model="k2.5",
            api_key_env="TEST_API_KEY",
        )

        assert profile.get_api_key() == "env-secret-key"

    def test_get_api_key_config_takes_precedence(self, monkeypatch):
        """Test that config api_key takes precedence over env."""
        monkeypatch.setenv("TEST_API_KEY", "env-secret-key")

        profile = ModelProfile(
            name="test",
            provider="kimi",
            model="k2.5",
            api_key="config-secret-key",
            api_key_env="TEST_API_KEY",
        )

        assert profile.get_api_key() == "config-secret-key"

    def test_to_dict(self):
        """Test converting profile to dictionary."""
        profile = ModelProfile(
            name="test",
            provider="ollama",
            model="llama3.2",
            base_url="http://localhost:11434",
        )

        data = profile.to_dict()

        assert data["name"] == "test"
        assert data["provider"] == "ollama"
        assert data["model"] == "llama3.2"
        assert data["base_url"] == "http://localhost:11434"

    def test_from_dict(self):
        """Test creating profile from dictionary."""
        data = {
            "provider": "kimi",
            "model": "kimi-k2-5",
            "api_key_env": "KIMI_API_KEY",
            "temperature": 0.5,
        }

        profile = ModelProfile.from_dict("cloud", data)

        assert profile.name == "cloud"
        assert profile.provider == "kimi"
        assert profile.model == "kimi-k2-5"
        assert profile.api_key_env == "KIMI_API_KEY"
        assert profile.temperature == 0.5


class TestContextBudgetConfig:
    """Test ContextBudgetConfig dataclass."""

    def test_defaults(self):
        """Test default budget configuration."""
        budget = ContextBudgetConfig()

        assert budget.total_budget == 8192
        assert budget.warning_threshold == 6144
        assert budget.critical_threshold == 7168
        assert budget.strict_mode is True

    def test_from_dict(self):
        """Test creating from dictionary."""
        data = {"total_budget": 16384, "strict_mode": False}

        budget = ContextBudgetConfig.from_dict(data)

        assert budget.total_budget == 16384
        assert budget.warning_threshold == 6144  # default
        assert budget.strict_mode is False


class TestFallbackConfig:
    """Test FallbackConfig dataclass."""

    def test_defaults(self):
        """Test default fallback configuration."""
        fallback = FallbackConfig()

        assert fallback.enabled is True
        assert fallback.primary_profile == "local"
        assert fallback.fallback_profile == "cloud"
        assert fallback.trigger_on_context_overflow is True
        assert fallback.trigger_on_connection_error is True


class TestConfig:
    """Test Config class."""

    def test_default_profiles_created(self):
        """Test that default profiles are created."""
        config = Config()

        assert "local" in config.profiles
        assert "cloud" in config.profiles
        assert "fallback" in config.profiles

    def test_get_active_profile(self):
        """Test getting active profile."""
        config = Config()
        config.active_profile = "cloud"

        profile = config.get_active_profile()

        assert profile.name == "cloud"
        assert profile.provider == "kimi"

    def test_get_active_profile_not_found(self):
        """Test error when active profile not found."""
        config = Config()
        config.active_profile = "nonexistent"

        with pytest.raises(ConfigurationError) as exc_info:
            config.get_active_profile()

        assert "nonexistent" in str(exc_info.value)

    def test_set_active_profile(self):
        """Test setting active profile."""
        config = Config()

        config.set_active_profile("cloud")

        assert config.active_profile == "cloud"

    def test_add_profile(self):
        """Test adding a new profile."""
        config = Config()

        new_profile = ModelProfile(
            name="custom",
            provider="openai",
            model="gpt-4",
        )
        config.add_profile(new_profile)

        assert "custom" in config.profiles
        assert config.profiles["custom"].provider == "openai"

    def test_remove_profile(self):
        """Test removing a profile."""
        config = Config()

        result = config.remove_profile("cloud")

        assert result is True
        assert "cloud" not in config.profiles

    def test_remove_profile_not_found(self):
        """Test removing non-existent profile."""
        config = Config()

        result = config.remove_profile("nonexistent")

        assert result is False

    def test_remove_active_profile_resets(self):
        """Test that removing active profile resets to another."""
        config = Config()
        config.active_profile = "cloud"

        config.remove_profile("cloud")

        assert config.active_profile == "local"

    def test_to_dict(self):
        """Test converting config to dictionary."""
        config = Config()
        config.active_profile = "cloud"

        data = config.to_dict()

        assert data["active_profile"] == "cloud"
        assert "profiles" in data
        assert "budget" in data
        assert "fallback" in data

    def test_from_dict(self):
        """Test creating config from dictionary."""
        data = {
            "active_profile": "custom",
            "profiles": {
                "custom": {
                    "name": "custom",
                    "provider": "openai",
                    "model": "gpt-4",
                    "base_url": None,
                    "api_key": None,
                    "api_key_env": None,
                    "temperature": 0.7,
                    "max_tokens": None,
                    "top_p": 1.0,
                    "enabled": True,
                }
            },
            "budget": {"total_budget": 16384},
            "fallback": {"enabled": False},
        }

        config = Config.from_dict(data)

        assert config.active_profile == "custom"
        assert "custom" in config.profiles
        assert config.budget.total_budget == 16384
        assert config.fallback.enabled is False

    def test_save_and_load(self, tmp_path):
        """Test saving and loading config."""
        config_path = tmp_path / "config.yaml"
        config = Config()
        config.active_profile = "cloud"

        config.save(config_path)
        loaded = Config.load(config_path)

        assert loaded.active_profile == "cloud"
        assert "local" in loaded.profiles
        assert "cloud" in loaded.profiles

    def test_load_nonexistent_returns_defaults(self, tmp_path):
        """Test loading non-existent file returns defaults."""
        config_path = tmp_path / "nonexistent.yaml"

        config = Config.load(config_path)

        assert config.active_profile == "local"
        assert "local" in config.profiles

    def test_ensure_exists_creates_file(self, tmp_path):
        """Test ensure_exists creates file if not exists."""
        config_path = tmp_path / ".oclawma" / "config.yaml"

        config = Config.ensure_exists(config_path)

        assert config_path.exists()
        assert config.active_profile == "local"

    def test_ensure_exists_loads_existing(self, tmp_path):
        """Test ensure_exists loads existing file."""
        config_path = tmp_path / "config.yaml"

        # Create config first
        original = Config()
        original.active_profile = "cloud"
        original.save(config_path)

        # Ensure exists should load it
        loaded = Config.ensure_exists(config_path)

        assert loaded.active_profile == "cloud"


class TestConfigYAMLFormat:
    """Test YAML format of saved config."""

    def test_yaml_structure(self, tmp_path):
        """Test that saved YAML has expected structure."""
        config_path = tmp_path / "config.yaml"
        config = Config()
        config.save(config_path)

        with open(config_path) as f:
            data = yaml.safe_load(f)

        assert "active_profile" in data
        assert "profiles" in data
        assert "local" in data["profiles"]
        assert "cloud" in data["profiles"]
        assert "budget" in data
        assert "fallback" in data

    def test_profile_yaml_format(self, tmp_path):
        """Test profile format in YAML."""
        config_path = tmp_path / "config.yaml"
        config = Config()
        config.save(config_path)

        with open(config_path) as f:
            data = yaml.safe_load(f)

        local_profile = data["profiles"]["local"]
        assert local_profile["provider"] == "ollama"
        assert local_profile["model"] == "qwen2.5:3b"
        assert local_profile["base_url"] == "http://localhost:11434"
