"""
Comprehensive tests for settings logger module.

Tests cover:
- log_settings function with all log levels
- redact_sensitive_keys function
- create_settings_summary function
- get_settings_log_level function
"""

import os
import pytest
from unittest.mock import patch

# Import the functions we want to test
# We need to patch the module-level SETTINGS_LOG_LEVEL before importing
# So we'll import the module itself and access functions through it


class TestLogSettingsNoneLevel:
    """Tests for log_settings with none level."""

    def test_log_settings_none_level_skips_logging(self):
        """Test that log_settings does nothing when level is 'none'."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "none"}):
            # Re-import to get new SETTINGS_LOG_LEVEL
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)

            with patch.object(settings_logger.logger, "info") as mock_info:
                with patch.object(
                    settings_logger.logger, "debug"
                ) as mock_debug:
                    settings_logger.log_settings({"key": "value"})

                    mock_info.assert_not_called()
                    mock_debug.assert_not_called()


class TestLogSettingsSummaryLevel:
    """Tests for log_settings with summary level."""

    def test_log_settings_summary_level(self):
        """Test that log_settings outputs summary at INFO level."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "summary"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)

            with patch.object(settings_logger.logger, "info") as mock_info:
                settings_logger.log_settings(
                    {"key": "value"}, message="Test message"
                )

                mock_info.assert_called_once()
                call_args = mock_info.call_args[0][0]
                assert "Test message" in call_args


class TestLogSettingsDebugLevel:
    """Tests for log_settings with debug level."""

    def test_log_settings_debug_level(self):
        """Test that log_settings outputs full settings with redaction at DEBUG level."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "debug"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)

            with patch.object(settings_logger.logger, "debug") as mock_debug:
                settings_logger.log_settings(
                    {"api_key": "secret123", "normal": "value"},
                    message="Test message",
                )

                mock_debug.assert_called_once()
                call_args = mock_debug.call_args[0][0]
                assert "redacted" in call_args.lower()


class TestLogSettingsDebugUnsafeLevel:
    """Tests for debug_unsafe deprecation — maps to 'none'."""

    def test_debug_unsafe_maps_to_none(self):
        """Test that debug_unsafe is deprecated and maps to 'none'."""
        import warnings

        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "debug_unsafe"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter("always")
                importlib.reload(settings_logger)

                # Should have emitted a DeprecationWarning
                deprecation_warnings = [
                    x for x in w if issubclass(x.category, DeprecationWarning)
                ]
                assert len(deprecation_warnings) >= 1
                assert (
                    "deprecated" in str(deprecation_warnings[0].message).lower()
                )

            assert settings_logger.get_settings_log_level() == "none"

    @pytest.mark.parametrize("env_value", ["unsafe", "raw"])
    def test_unsafe_aliases_map_to_none(self, env_value):
        """Test that 'unsafe' and 'raw' also map to 'none'."""
        import warnings

        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": env_value}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            with warnings.catch_warnings(record=True):
                warnings.simplefilter("always")
                importlib.reload(settings_logger)

            assert settings_logger.get_settings_log_level() == "none"

    def test_debug_unsafe_does_not_log_secrets(self):
        """Test that debug_unsafe no longer logs unredacted settings."""
        import warnings

        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "debug_unsafe"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            with warnings.catch_warnings(record=True):
                warnings.simplefilter("always")
                importlib.reload(settings_logger)

            with patch.object(settings_logger.logger, "debug") as mock_debug:
                settings_logger.log_settings(
                    {"api_key": "secret123"}, message="Test message"
                )

                # Should NOT have logged anything (level is "none")
                mock_debug.assert_not_called()


class TestLogSettingsForcedLevel:
    """Tests for log_settings with forced level override."""

    def test_log_settings_force_level_overrides_env(self):
        """Test that force_level parameter overrides environment setting."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "none"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)

            with patch.object(settings_logger.logger, "info") as mock_info:
                settings_logger.log_settings(
                    {"key": "value"}, force_level="summary"
                )

                # Should have logged despite env being 'none'
                mock_info.assert_called_once()


class TestLogSettingsEdgeCases:
    """Tests for edge cases in log_settings."""

    def test_log_settings_with_empty_settings(self):
        """Test log_settings handles empty dict gracefully."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "summary"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)

            with patch.object(settings_logger.logger, "info") as mock_info:
                # Should not raise
                settings_logger.log_settings({})

                mock_info.assert_called_once()
                call_args = mock_info.call_args[0][0]
                assert "0 total settings" in call_args

    def test_log_settings_with_non_dict_settings(self):
        """Test log_settings handles non-dict input."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "summary"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)

            with patch.object(settings_logger.logger, "info") as mock_info:
                # Should not raise
                settings_logger.log_settings("not a dict")

                mock_info.assert_called_once()


class TestRedactSensitiveKeys:
    """Tests for redact_sensitive_keys function."""

    @pytest.fixture(autouse=True)
    def setup_module(self):
        """Import the module for tests."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "none"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)
            self.settings_logger = settings_logger

    def test_redact_api_key_pattern(self):
        """Test that 'api_key' pattern is redacted."""
        result = self.settings_logger.redact_sensitive_keys(
            {"api_key": "secret123"}
        )
        assert result["api_key"] == "***REDACTED***"

    def test_redact_password_pattern(self):
        """Test that 'password' pattern is redacted."""
        result = self.settings_logger.redact_sensitive_keys(
            {"password": "mypassword", "db_password": "dbpass"}
        )
        assert result["password"] == "***REDACTED***"
        assert result["db_password"] == "***REDACTED***"

    def test_redact_token_pattern(self):
        """Test that 'token' pattern is redacted."""
        result = self.settings_logger.redact_sensitive_keys(
            {"token": "abc123", "access_token": "xyz789"}
        )
        assert result["token"] == "***REDACTED***"
        assert result["access_token"] == "***REDACTED***"

    def test_redact_secret_pattern(self):
        """Test that 'secret' pattern is redacted."""
        result = self.settings_logger.redact_sensitive_keys(
            {"secret": "shh", "secret_key": "shhh"}
        )
        assert result["secret"] == "***REDACTED***"
        assert result["secret_key"] == "***REDACTED***"

    def test_redact_nested_sensitive_keys(self):
        """Test that nested sensitive keys are redacted."""
        result = self.settings_logger.redact_sensitive_keys(
            {"outer": {"api_key": "nested_secret", "normal": "value"}}
        )
        assert result["outer"]["api_key"] == "***REDACTED***"
        assert result["outer"]["normal"] == "value"

    def test_redact_preserves_non_sensitive(self):
        """Test that non-sensitive keys are preserved."""
        result = self.settings_logger.redact_sensitive_keys(
            {"app_name": "MyApp", "debug": True, "count": 42}
        )
        assert result["app_name"] == "MyApp"
        assert result["debug"] is True
        assert result["count"] == 42

    def test_redact_setting_dict_with_value_key(self):
        """Test redaction of settings format with 'value' key."""
        result = self.settings_logger.redact_sensitive_keys(
            {"api_key": {"value": "secret", "type": "string"}}
        )
        assert result["api_key"]["value"] == "***REDACTED***"
        assert result["api_key"]["type"] == "string"


class TestCreateSettingsSummary:
    """Tests for create_settings_summary function."""

    @pytest.fixture(autouse=True)
    def setup_module(self):
        """Import the module for tests."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "none"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)
            self.settings_logger = settings_logger

    def test_create_settings_summary_counts(self):
        """Test that summary correctly counts different setting types."""
        result = self.settings_logger.create_settings_summary(
            {
                "search.engine.google": True,
                "search.engine.bing": True,
                "llm.provider": "openai",
                "llm.temperature": 0.7,
                "app.debug": False,
            }
        )

        assert "5 total settings" in result
        assert "search engines: 2" in result
        assert "LLM: 2" in result

    def test_create_settings_summary_empty_dict(self):
        """Test summary of empty settings."""
        result = self.settings_logger.create_settings_summary({})

        assert "0 total settings" in result

    def test_create_settings_summary_non_dict(self):
        """Test summary of non-dict input."""
        result = self.settings_logger.create_settings_summary("string_settings")

        assert "str" in result

    def test_create_settings_summary_with_object(self):
        """Test summary with object input."""

        class CustomSettings:
            pass

        result = self.settings_logger.create_settings_summary(CustomSettings())

        assert "CustomSettings" in result


class TestGetSettingsLogLevel:
    """Tests for get_settings_log_level function."""

    def test_get_settings_log_level_returns_current(self):
        """Test that get_settings_log_level returns current level."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "debug"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)

            result = settings_logger.get_settings_log_level()

            assert result == "debug"

    def test_get_settings_log_level_default(self):
        """Test that get_settings_log_level returns 'none' by default."""
        # Remove the env var if set
        env = os.environ.copy()
        env.pop("LDR_LOG_SETTINGS", None)

        with patch.dict(os.environ, env, clear=True):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)

            result = settings_logger.get_settings_log_level()

            assert result == "none"


class TestLogLevelMapping:
    """Tests for log level value mapping."""

    @pytest.mark.parametrize(
        "env_value,expected",
        [
            ("false", "none"),
            ("0", "none"),
            ("no", "none"),
            ("off", "none"),
            ("none", "none"),
            ("true", "summary"),
            ("1", "summary"),
            ("yes", "summary"),
            ("info", "summary"),
            ("summary", "summary"),
            ("debug", "debug"),
            ("full", "debug"),
            ("all", "debug"),
            ("debug_unsafe", "none"),
            ("unsafe", "none"),
            ("raw", "none"),
            ("invalid_value", "none"),  # Invalid defaults to none
        ],
    )
    def test_log_level_mapping(self, env_value, expected):
        """Test that various env values map to correct log levels."""
        import warnings

        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": env_value}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            with warnings.catch_warnings():
                warnings.simplefilter("ignore", DeprecationWarning)
                importlib.reload(settings_logger)

            result = settings_logger.get_settings_log_level()

            assert result == expected, (
                f"Expected {env_value} to map to {expected}, got {result}"
            )


class TestRedactCaseSensitivity:
    """Tests for case sensitivity in redaction."""

    @pytest.fixture(autouse=True)
    def setup_module(self):
        """Import the module for tests."""
        with patch.dict(os.environ, {"LDR_LOG_SETTINGS": "none"}):
            import importlib
            from local_deep_research.settings import logger as settings_logger

            importlib.reload(settings_logger)
            self.settings_logger = settings_logger

    def test_redact_case_insensitive_api_key(self):
        """Test that API_KEY (uppercase) is also redacted."""
        result = self.settings_logger.redact_sensitive_keys(
            {"API_KEY": "secret", "Api_Key": "secret2"}
        )
        assert result["API_KEY"] == "***REDACTED***"
        assert result["Api_Key"] == "***REDACTED***"

    def test_redact_case_insensitive_password(self):
        """Test that PASSWORD (uppercase) is also redacted."""
        result = self.settings_logger.redact_sensitive_keys(
            {"PASSWORD": "secret", "Password": "secret2"}
        )
        assert result["PASSWORD"] == "***REDACTED***"
        assert result["Password"] == "***REDACTED***"
