"""
Behavioral tests for security/security_settings module.

Tests type conversion, bounds validation, and security default loading.
"""

import os
from unittest.mock import patch

import pytest


class TestConvertValue:
    """Tests for _convert_value function."""

    def test_converts_bool_true_values(self):
        """Converts 'true', '1', 'yes', 'on' to True."""
        from local_deep_research.security.security_settings import (
            _convert_value,
        )

        for val in ("true", "1", "yes", "on"):
            assert _convert_value(val, bool, "test") is True

    def test_converts_bool_false_values(self):
        """Converts 'false', '0', 'no', 'off' to False."""
        from local_deep_research.security.security_settings import (
            _convert_value,
        )

        for val in ("false", "0", "no", "off"):
            assert _convert_value(val, bool, "test") is False

    def test_bool_is_case_insensitive(self):
        """Bool conversion is case-insensitive."""
        from local_deep_research.security.security_settings import (
            _convert_value,
        )

        assert _convert_value("TRUE", bool, "test") is True
        assert _convert_value("True", bool, "test") is True
        assert _convert_value("Yes", bool, "test") is True

    def test_converts_int(self):
        """Converts string to int."""
        from local_deep_research.security.security_settings import (
            _convert_value,
        )

        assert _convert_value("42", int, "test") == 42

    def test_converts_negative_int(self):
        """Converts negative string to int."""
        from local_deep_research.security.security_settings import (
            _convert_value,
        )

        assert _convert_value("-5", int, "test") == -5

    def test_converts_float(self):
        """Converts string to float."""
        from local_deep_research.security.security_settings import (
            _convert_value,
        )

        assert _convert_value("3.14", float, "test") == pytest.approx(3.14)

    def test_converts_str(self):
        """Converts to string (identity)."""
        from local_deep_research.security.security_settings import (
            _convert_value,
        )

        assert _convert_value("hello", str, "test") == "hello"

    def test_returns_none_for_invalid_int(self):
        """Returns None for non-numeric int conversion."""
        from local_deep_research.security.security_settings import (
            _convert_value,
        )

        assert _convert_value("abc", int, "test") is None

    def test_returns_none_for_invalid_float(self):
        """Returns None for non-numeric float conversion."""
        from local_deep_research.security.security_settings import (
            _convert_value,
        )

        assert _convert_value("not_a_number", float, "test") is None


class TestValidateBounds:
    """Tests for _validate_bounds function."""

    def test_value_within_bounds_unchanged(self):
        """Value within bounds is unchanged."""
        from local_deep_research.security.security_settings import (
            _validate_bounds,
        )

        assert _validate_bounds(50, 0, 100, "test") == 50

    def test_clamps_below_minimum(self):
        """Value below minimum is clamped to minimum."""
        from local_deep_research.security.security_settings import (
            _validate_bounds,
        )

        assert _validate_bounds(-5, 0, 100, "test") == 0

    def test_clamps_above_maximum(self):
        """Value above maximum is clamped to maximum."""
        from local_deep_research.security.security_settings import (
            _validate_bounds,
        )

        assert _validate_bounds(150, 0, 100, "test") == 100

    def test_none_min_allows_any_low_value(self):
        """None min_value allows any low value."""
        from local_deep_research.security.security_settings import (
            _validate_bounds,
        )

        assert _validate_bounds(-1000, None, 100, "test") == -1000

    def test_none_max_allows_any_high_value(self):
        """None max_value allows any high value."""
        from local_deep_research.security.security_settings import (
            _validate_bounds,
        )

        assert _validate_bounds(99999, 0, None, "test") == 99999

    def test_both_none_returns_value_unchanged(self):
        """Both None bounds returns value unchanged."""
        from local_deep_research.security.security_settings import (
            _validate_bounds,
        )

        assert _validate_bounds(42, None, None, "test") == 42

    def test_exact_minimum_accepted(self):
        """Value equal to minimum is accepted."""
        from local_deep_research.security.security_settings import (
            _validate_bounds,
        )

        assert _validate_bounds(0, 0, 100, "test") == 0

    def test_exact_maximum_accepted(self):
        """Value equal to maximum is accepted."""
        from local_deep_research.security.security_settings import (
            _validate_bounds,
        )

        assert _validate_bounds(100, 0, 100, "test") == 100

    def test_works_with_floats(self):
        """Works with float values and bounds."""
        from local_deep_research.security.security_settings import (
            _validate_bounds,
        )

        assert _validate_bounds(0.5, 0.0, 1.0, "test") == pytest.approx(0.5)
        assert _validate_bounds(-0.1, 0.0, 1.0, "test") == pytest.approx(0.0)
        assert _validate_bounds(1.5, 0.0, 1.0, "test") == pytest.approx(1.0)


class TestGetSecurityDefault:
    """Tests for get_security_default function."""

    def test_returns_default_when_no_env_or_json(self):
        """Returns default value when no env var or JSON setting."""
        from local_deep_research.security.security_settings import (
            get_security_default,
            _load_security_settings,
        )

        # Clear the cache to ensure fresh load
        _load_security_settings.cache_clear()

        # Use a key that won't exist
        result = get_security_default("nonexistent.setting.key_xyz_12345", 42)
        assert result == 42

    def test_env_var_overrides_default(self):
        """Environment variable overrides default value."""
        from local_deep_research.security.security_settings import (
            get_security_default,
            _load_security_settings,
        )

        _load_security_settings.cache_clear()

        env_key = "LDR_TEST_ENV_OVERRIDE_ABC"
        with patch.dict(os.environ, {env_key: "99"}):
            result = get_security_default("test.env_override_abc", 42)
            assert result == 99

    def test_env_var_key_format(self):
        """Environment variable uses LDR_ prefix with dots replaced by underscores."""
        from local_deep_research.security.security_settings import (
            get_security_default,
            _load_security_settings,
        )

        _load_security_settings.cache_clear()

        # Key "security.session_days" -> env var "LDR_SECURITY_SESSION_DAYS"
        with patch.dict(os.environ, {"LDR_MY_CUSTOM_KEY": "hello"}):
            result = get_security_default("my.custom_key", "default")
            assert result == "hello"

    def test_bool_default_returns_bool(self):
        """Bool default causes bool return type."""
        from local_deep_research.security.security_settings import (
            get_security_default,
            _load_security_settings,
        )

        _load_security_settings.cache_clear()

        result = get_security_default("nonexistent.bool_setting_xyz", False)
        assert result is False

    def test_env_var_bool_conversion(self):
        """Environment variable with bool default converts to bool."""
        from local_deep_research.security.security_settings import (
            get_security_default,
            _load_security_settings,
        )

        _load_security_settings.cache_clear()

        with patch.dict(os.environ, {"LDR_TEST_BOOL_ENV_XYZ": "true"}):
            result = get_security_default("test.bool_env_xyz", False)
            assert result is True

    def test_invalid_env_var_falls_through(self):
        """Invalid env var value falls through to default."""
        from local_deep_research.security.security_settings import (
            get_security_default,
            _load_security_settings,
        )

        _load_security_settings.cache_clear()

        with patch.dict(os.environ, {"LDR_TEST_BADINT_XYZ": "not_a_number"}):
            result = get_security_default("test.badint_xyz", 10)
            assert result == 10


class TestLoadSecuritySettings:
    """Tests for _load_security_settings function."""

    def test_returns_dict(self):
        """Returns a dictionary."""
        from local_deep_research.security.security_settings import (
            _load_security_settings,
        )

        _load_security_settings.cache_clear()
        result = _load_security_settings()
        assert isinstance(result, dict)

    def test_is_cached(self):
        """Subsequent calls return cached result."""
        from local_deep_research.security.security_settings import (
            _load_security_settings,
        )

        _load_security_settings.cache_clear()
        result1 = _load_security_settings()
        result2 = _load_security_settings()
        assert result1 is result2

    def test_returns_empty_dict_for_missing_file(self):
        """Returns empty dict if settings file doesn't exist."""
        from local_deep_research.security.security_settings import (
            _load_security_settings,
            _SETTINGS_PATH,
        )

        _load_security_settings.cache_clear()

        with patch.object(type(_SETTINGS_PATH), "exists", return_value=False):
            _load_security_settings.cache_clear()
            result = _load_security_settings()
            assert result == {}
