"""
Tests for the check-sensitive-logging pre-commit hook.

Ensures the hook detects logging of passwords, API keys, tokens, and other
sensitive data that could leak user information to log files.
"""

import ast
import sys
from importlib import import_module
from pathlib import Path


HOOKS_DIR = Path(__file__).parent.parent.parent / ".pre-commit-hooks"
sys.path.insert(0, str(HOOKS_DIR))

hook_module = import_module("check-sensitive-logging")
SensitiveLoggingChecker = hook_module.SensitiveLoggingChecker


def _check_code(code: str, filename: str = "src/module.py") -> list:
    """Parse code and run the sensitive logging checker."""
    tree = ast.parse(code)
    checker = SensitiveLoggingChecker(filename)
    checker.visit(tree)
    return checker.errors


class TestDetectsPasswordLogging:
    """Ensures passwords are never logged."""

    def test_detects_password_in_fstring(self):
        code = 'logger.info(f"User login with password={password}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1
        assert any("password" in e.lower() for e in errors)

    def test_detects_user_password_variable(self):
        code = 'logger.info(f"DB access: {user_password}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1

    def test_detects_pwd_variable(self):
        code = 'logger.warning(f"Connection failed: pwd={pwd}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1


class TestDetectsApiKeyLogging:
    """Ensures API keys and tokens are never logged."""

    def test_detects_api_key(self):
        code = 'logger.info(f"Using API key: {api_key}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1

    def test_detects_access_token(self):
        code = 'logger.info(f"Auth with token: {access_token}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1

    def test_detects_secret_key(self):
        code = 'logger.debug(f"Secret: {secret_key}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1


class TestDetectsSensitiveDictLogging:
    """Ensures sensitive dicts (kwargs, credentials) are not logged."""

    def test_detects_kwargs_logging(self):
        code = 'logger.info(f"Calling with: {kwargs}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1

    def test_detects_credentials_logging(self):
        code = 'logger.warning(f"Auth data: {credentials}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1

    def test_detects_settings_snapshot_logging(self):
        code = 'logger.info(f"Settings: {settings_snapshot}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1

    def test_detects_connection_string_logging(self):
        code = 'logger.info(f"DB: {connection_string}")\n'
        errors = _check_code(code)
        assert len(errors) >= 1


class TestDetectsExcInfoOnWarning:
    """Ensures exc_info=True is not used on warning/error (leaks tracebacks)."""

    def test_detects_exc_info_on_warning(self):
        code = 'logger.warning("Failed", exc_info=True)\n'
        errors = _check_code(code)
        assert len(errors) >= 1
        assert any("exc_info" in e for e in errors)

    def test_detects_exc_info_on_error(self):
        code = 'logger.error("Crash", exc_info=True)\n'
        errors = _check_code(code)
        assert len(errors) >= 1

    def test_allows_exc_info_on_debug(self):
        code = 'logger.debug("Debug trace", exc_info=True)\n'
        errors = _check_code(code)
        assert len(errors) == 0


class TestDetectsExceptionVarInLog:
    """Ensures exception variables are not interpolated in non-debug logs."""

    def test_detects_exception_in_warning(self):
        code = """
try:
    do_work()
except Exception as e:
    logger.warning(f"Failed: {e}")
"""
        errors = _check_code(code)
        assert len(errors) >= 1
        assert any("exception variable" in e.lower() for e in errors)

    def test_allows_exception_in_debug(self):
        code = """
try:
    do_work()
except Exception as e:
    logger.debug(f"Failed: {e}")
"""
        errors = _check_code(code)
        # debug level is allowed
        exc_var_errors = [
            e for e in errors if "exception variable" in e.lower()
        ]
        assert len(exc_var_errors) == 0

    def test_allows_logger_exception(self):
        code = """
try:
    do_work()
except Exception as e:
    logger.exception(f"Failed: {e}")
"""
        errors = _check_code(code)
        exc_var_errors = [
            e for e in errors if "exception variable" in e.lower()
        ]
        assert len(exc_var_errors) == 0


class TestAllowsSafePatterns:
    """Ensures safe logging patterns are not flagged."""

    def test_allows_normal_string_logging(self):
        code = 'logger.info("Processing 10 results")\n'
        errors = _check_code(code)
        assert len(errors) == 0

    def test_allows_non_sensitive_variables(self):
        code = 'logger.info(f"Found {count} journals with score {score}")\n'
        errors = _check_code(code)
        assert len(errors) == 0

    def test_allows_prompt_tokens(self):
        """prompt_tokens is about LLM tokens, not auth tokens."""
        code = 'logger.info(f"Used {prompt_tokens} tokens")\n'
        errors = _check_code(code)
        assert len(errors) == 0

    def test_allows_max_tokens(self):
        code = 'logger.info(f"Max tokens: {max_tokens}")\n'
        errors = _check_code(code)
        assert len(errors) == 0

    def test_allows_test_files(self):
        """Test files should be allowed to log sensitive data for debugging."""
        code = 'logger.info(f"Testing with password={password}")\n'
        # Tests may have relaxed rules for specific vars
        # (depends on ALLOWED_LOGGING config)
        _check_code(code, filename="tests/test_auth.py")
