"""Configuration precedence tests for MCAConfig.

This module tests the configuration precedence chain to ensure
values are resolved in the correct order:
kwargs > env > YAML > defaults

Tests cover:
- Precedence chain validation
- Multi-source conflicts
- Precedence documentation alignment
"""

import os
import pytest
import tempfile

from mca_sdk.config.settings import MCAConfig
from mca_sdk.utils.exceptions import ConfigurationError


class TestConfigPrecedenceBasic:
    """Test basic precedence rules: kwargs > env > YAML > defaults."""

    def test_kwargs_overrides_env(self, monkeypatch):
        """Test that kwargs override environment variables."""
        monkeypatch.setenv("MCA_SERVICE_NAME", "env-service")
        monkeypatch.setenv("MCA_MODEL_ID", "env-model")

        config = MCAConfig.load(service_name="kwarg-service", model_id="kwarg-model")

        assert config.service_name == "kwarg-service"
        assert config.model_id == "kwarg-model"

    def test_kwargs_overrides_yaml(self, tmp_path):
        """Test that kwargs override YAML configuration."""
        config_file = tmp_path / "config.yaml"
        config_file.write_text("""
service_name: yaml-service
model_id: yaml-model
max_queue_size: 2000
""")

        config = MCAConfig.load(
            config_file=str(config_file),
            service_name="kwarg-service",
            max_queue_size=5000,
        )

        assert config.service_name == "kwarg-service"
        assert config.model_id == "yaml-model"  # From YAML
        assert config.max_queue_size == 5000  # Overridden by kwarg

    def test_env_overrides_yaml(self, tmp_path, monkeypatch):
        """Test that environment variables override YAML configuration."""
        config_file = tmp_path / "config.yaml"
        config_file.write_text("""
service_name: yaml-service
model_id: yaml-model
team_name: yaml-team
""")

        monkeypatch.setenv("MCA_SERVICE_NAME", "env-service")
        monkeypatch.setenv("MCA_TEAM_NAME", "env-team")

        config = MCAConfig.load(config_file=str(config_file))

        assert config.service_name == "env-service"  # Overridden by env
        assert config.model_id == "yaml-model"  # From YAML (no env)
        assert config.team_name == "env-team"  # Overridden by env

    def test_yaml_overrides_defaults(self, tmp_path, monkeypatch):
        """Test that YAML configuration overrides defaults."""
        # Clear all MCA_ env vars
        for key in list(os.environ.keys()):
            if key.startswith("MCA_"):
                monkeypatch.delenv(key, raising=False)
        monkeypatch.setenv("MCA_ENV", "staging")

        config_file = tmp_path / "config.yaml"
        config_file.write_text("""
service_name: yaml-service
max_queue_size: 3000
retry_attempts: 5
""")

        config = MCAConfig.load(config_file=str(config_file))

        assert config.service_name == "yaml-service"
        assert config.max_queue_size == 3000  # YAML overrides default 1000
        assert config.retry_attempts == 5  # YAML overrides default 3
        assert config.model_type == "regression"  # Default (not in YAML)

    def test_defaults_used_when_nothing_set(self, monkeypatch):
        """Test that defaults are used when no other source provides value."""
        # Clear all MCA_ env vars
        for key in list(os.environ.keys()):
            if key.startswith("MCA_"):
                monkeypatch.delenv(key, raising=False)
        monkeypatch.setenv("MCA_ENV", "staging")

        config = MCAConfig.load(service_name="test")

        # Check defaults are applied
        assert config.max_queue_size == 1000
        assert config.retry_attempts == 3
        assert config.base_delay == 1.0
        assert config.max_delay == 30.0
        assert config.backoff_multiplier == 2.0
        assert config.model_type == "regression"
        assert config.model_category == "internal"


class TestConfigPrecedenceFullChain:
    """Test full precedence chain with all sources present."""

    def test_full_precedence_chain(self, tmp_path, monkeypatch):
        """Test precedence when all sources (kwargs, env, YAML, defaults) are present."""
        # Setup YAML
        config_file = tmp_path / "config.yaml"
        config_file.write_text("""
service_name: yaml-service
model_id: yaml-model
team_name: yaml-team
max_queue_size: 2000
retry_attempts: 5
""")

        # Setup environment variables
        monkeypatch.setenv("MCA_SERVICE_NAME", "env-service")
        monkeypatch.setenv("MCA_MODEL_ID", "env-model")
        monkeypatch.setenv("MCA_MAX_QUEUE_SIZE", "3000")

        # Load with kwargs
        config = MCAConfig.load(
            config_file=str(config_file),
            service_name="kwarg-service",
            max_queue_size=4000,
        )

        # Verify precedence: kwargs > env > YAML > defaults
        assert config.service_name == "kwarg-service"  # kwargs wins
        assert config.model_id == "env-model"  # env wins (no kwarg)
        assert config.team_name == "yaml-team"  # YAML wins (no env or kwarg)
        assert config.max_queue_size == 4000  # kwargs wins
        assert config.retry_attempts == 5  # YAML wins (no env or kwarg)
        assert config.base_delay == 1.0  # Default (not in any source)


class TestConfigPrecedenceBooleanFields:
    """Test precedence for boolean fields (special parsing)."""

    def test_boolean_precedence_kwargs_over_env(self, monkeypatch):
        """Test boolean field precedence: kwargs > env."""
        monkeypatch.setenv("MCA_SERVICE_NAME", "test")
        monkeypatch.setenv("MCA_BUFFERING_ENABLED", "true")
        monkeypatch.setenv("MCA_DEBUG", "true")

        config = MCAConfig.load(buffering_enabled=False, debug_mode=False)

        assert config.buffering_enabled is False  # kwargs wins
        assert config.debug_mode is False  # kwargs wins

    def test_boolean_precedence_env_over_yaml(self, tmp_path, monkeypatch):
        """Test boolean field precedence: env > YAML."""
        config_file = tmp_path / "config.yaml"
        config_file.write_text("""
service_name: test
buffering_enabled: false
""")

        monkeypatch.setenv("MCA_BUFFERING_ENABLED", "true")
        # debug_mode is not present in YAML, so this doesn't test precedence

        config = MCAConfig.load(config_file=str(config_file))

        assert config.buffering_enabled is True  # env wins over YAML false

    def test_boolean_env_parsing_variations(self, monkeypatch):
        """Test that boolean environment variables parse correctly (true, 1, yes)."""
        monkeypatch.setenv("MCA_SERVICE_NAME", "test")

        # Test "true"
        monkeypatch.setenv("MCA_BUFFERING_ENABLED", "true")
        config = MCAConfig.from_env()
        assert config.buffering_enabled is True

        # Test "1"
        monkeypatch.setenv("MCA_DEBUG", "1")
        config = MCAConfig.from_env()
        assert config.debug_mode is True

        # Test "yes"
        monkeypatch.setenv("MCA_STRICT_VALIDATION", "yes")
        config = MCAConfig.from_env()
        assert config.strict_validation is True

        # Test "false"
        monkeypatch.setenv("MCA_BUFFERING_ENABLED", "false")
        config = MCAConfig.from_env()
        assert config.buffering_enabled is False

        # Test "0"
        monkeypatch.setenv("MCA_BUFFERING_ENABLED", "0")
        config = MCAConfig.from_env()
        assert config.buffering_enabled is False

        # Test "no"
        monkeypatch.setenv("MCA_BUFFERING_ENABLED", "no")
        config = MCAConfig.from_env()
        assert config.buffering_enabled is False

    def test_invalid_boolean_env_value_raises_error(self, monkeypatch):
        """Test that invalid boolean env values raise ConfigurationError."""
        monkeypatch.setenv("MCA_SERVICE_NAME", "test")
        monkeypatch.setenv("MCA_BUFFERING_ENABLED", "maybe")

        with pytest.raises(ConfigurationError, match="Invalid boolean value.*maybe"):
            MCAConfig.from_env()


class TestConfigPrecedenceNumericFields:
    """Test precedence for numeric fields (int and float)."""

    def test_int_precedence_kwargs_over_env(self, monkeypatch):
        """Test integer field precedence: kwargs > env."""
        monkeypatch.setenv("MCA_SERVICE_NAME", "test")
        monkeypatch.setenv("MCA_MAX_QUEUE_SIZE", "2000")
        monkeypatch.setenv("MCA_COLLECTOR_TIMEOUT", "20")

        config = MCAConfig.load(max_queue_size=5000, collector_timeout=30)

        assert config.max_queue_size == 5000  # kwargs wins
        assert config.collector_timeout == 30  # kwargs wins

    def test_float_precedence_env_over_yaml(self, tmp_path, monkeypatch):
        """Test float field precedence: env > YAML."""
        config_file = tmp_path / "config.yaml"
        config_file.write_text("""
service_name: test
base_delay: 2.0
max_delay: 60.0
backoff_multiplier: 3.0
""")

        monkeypatch.setenv("MCA_BASE_DELAY", "1.5")
        monkeypatch.setenv("MCA_MAX_DELAY", "45.0")

        config = MCAConfig.load(config_file=str(config_file))

        assert config.base_delay == 1.5  # env wins
        assert config.max_delay == 45.0  # env wins
        assert config.backoff_multiplier == 3.0  # YAML wins (no env)


class TestConfigPrecedenceOptionalFields:
    """Test precedence for optional fields (None values)."""

    def test_none_in_kwargs_defers_to_lower_precedence(self, monkeypatch):
        """Test that explicit None in kwargs defers to lower precedence sources.

        This enables wrapper functions to work correctly:
        def init(model_id=None):
            return MCAConfig.load(model_id=model_id)

        When model_id=None is passed, it means "not provided", so we should
        defer to environment variables and YAML instead of overriding them.
        """
        monkeypatch.setenv("MCA_SERVICE_NAME", "test")
        monkeypatch.setenv("MCA_MODEL_ID", "env-model")

        config = MCAConfig.load(model_id=None)

        # None in kwargs defers to env var (fixed behavior)
        assert config.model_id == "env-model"

    def test_optional_field_precedence(self, tmp_path, monkeypatch):
        """Test precedence for optional fields."""
        config_file = tmp_path / "config.yaml"
        config_file.write_text("""
service_name: test
team_name: yaml-team
model_id: yaml-model
""")

        monkeypatch.setenv("MCA_TEAM_NAME", "env-team")

        config = MCAConfig.load(config_file=str(config_file), model_id="kwarg-model")

        assert config.model_id == "kwarg-model"  # kwargs wins
        assert config.team_name == "env-team"  # env wins
        # registry_url not in any source, defaults to None
        assert config.registry_url is None


class TestConfigPrecedenceDocumentation:
    """Test that precedence behavior matches documentation."""

    def test_precedence_matches_docstring(self, tmp_path, monkeypatch):
        """Test that precedence chain matches MCAConfig.load() docstring.

        Docstring states: kwargs > environment variables > YAML file > defaults
        """
        config_file = tmp_path / "config.yaml"
        config_file.write_text("""
service_name: yaml-service
collector_timeout: 15
""")

        monkeypatch.setenv("MCA_SERVICE_NAME", "env-service")
        monkeypatch.setenv("MCA_COLLECTOR_TIMEOUT", "20")

        config = MCAConfig.load(config_file=str(config_file), service_name="kwarg-service")

        # Verify precedence chain from docstring
        assert config.service_name == "kwarg-service"  # kwargs (highest)
        assert config.collector_timeout == 20  # env (overrides YAML)
        assert config.model_type == "regression"  # default (lowest)

    def test_precedence_matches_class_docstring(self):
        """Test that precedence matches MCAConfig class docstring.

        Class docstring states: kwargs > environment variables > YAML file > defaults
        """
        # This is a documentation check - verifies behavior matches claims
        # Same as previous test but explicitly for class-level docs
        config = MCAConfig.load(service_name="test")
        assert config.max_queue_size == 1000  # Default from class docstring


class TestConfigPrecedenceEdgeCases:
    """Test edge cases in precedence resolution."""

    def test_empty_string_vs_none_precedence(self, monkeypatch):
        """Test that empty string in higher precedence source is used."""
        monkeypatch.setenv("MCA_SERVICE_NAME", "test")
        monkeypatch.setenv("MCA_MODEL_ID", "env-model")

        # Empty string should be treated as a value, not "not provided"
        config = MCAConfig.load(team_name="")
        assert config.team_name == ""  # kwargs empty string wins

    def test_zero_vs_none_precedence(self, monkeypatch):
        """Test that zero in higher precedence source is used (not treated as falsy)."""
        monkeypatch.setenv("MCA_SERVICE_NAME", "test")
        monkeypatch.setenv("MCA_COLLECTOR_TIMEOUT", "10")

        # Zero is a valid value and should win
        # NOTE: collector_timeout validation requires positive, so this would fail
        # Testing with max_queue_size instead (but it also requires positive)
        # Using retry_attempts which allows 0
        monkeypatch.setenv("MCA_RETRY_ATTEMPTS", "5")
        config = MCAConfig.load(retry_attempts=0)
        assert config.retry_attempts == 0  # kwargs zero wins

    def test_false_vs_none_precedence(self, monkeypatch):
        """Test that False in higher precedence source is used (not treated as None)."""
        monkeypatch.setenv("MCA_SERVICE_NAME", "test")
        monkeypatch.setenv("MCA_BUFFERING_ENABLED", "true")

        config = MCAConfig.load(buffering_enabled=False)
        assert config.buffering_enabled is False  # kwargs False wins (not env True)


class TestConfigPrecedenceMultipleConflicts:
    """Test precedence with multiple conflicting sources."""

    def test_multiple_sources_multiple_fields(self, tmp_path, monkeypatch):
        """Test precedence when multiple sources set multiple fields."""
        config_file = tmp_path / "config.yaml"
        config_file.write_text("""
service_name: yaml-service
model_id: yaml-model
team_name: yaml-team
max_queue_size: 2000
retry_attempts: 5
base_delay: 2.0
buffering_enabled: true
""")

        monkeypatch.setenv("MCA_SERVICE_NAME", "env-service")
        monkeypatch.setenv("MCA_MODEL_ID", "env-model")
        monkeypatch.setenv("MCA_MAX_QUEUE_SIZE", "3000")
        monkeypatch.setenv("MCA_BASE_DELAY", "1.5")

        config = MCAConfig.load(
            config_file=str(config_file),
            service_name="kwarg-service",
            max_queue_size=4000,
        )

        # Verify each field resolved correctly
        assert config.service_name == "kwarg-service"  # kwargs
        assert config.model_id == "env-model"  # env
        assert config.team_name == "yaml-team"  # YAML
        assert config.max_queue_size == 4000  # kwargs
        assert config.retry_attempts == 5  # YAML
        assert config.base_delay == 1.5  # env
        assert config.buffering_enabled is True  # YAML
        assert config.model_type == "regression"  # default
