"""
Behavioral tests for security patterns.

These tests verify the logic of security patterns like authentication,
authorization, input sanitization, token management, and access control
without making actual system calls.
"""

import base64
import hashlib
import hmac
import re
import secrets
import time
from datetime import datetime, timedelta


class TestPasswordHashing:
    """Tests for password hashing patterns."""

    def test_hash_password(self):
        """Test password hashing with salt."""

        def hash_password(password, salt=None):
            if salt is None:
                salt = secrets.token_hex(16)
            combined = f"{salt}{password}"
            hashed = hashlib.sha256(combined.encode()).hexdigest()
            return f"{salt}${hashed}"

        result = hash_password("mypassword")
        parts = result.split("$")
        assert len(parts) == 2
        assert len(parts[0]) == 32  # Salt is 16 bytes = 32 hex chars
        assert len(parts[1]) == 64  # SHA256 = 64 hex chars

    def test_verify_password(self):
        """Test password verification."""

        def hash_password(password, salt=None):
            if salt is None:
                salt = secrets.token_hex(16)
            combined = f"{salt}{password}"
            hashed = hashlib.sha256(combined.encode()).hexdigest()
            return f"{salt}${hashed}"

        def verify_password(password, stored_hash):
            salt = stored_hash.split("$")[0]
            return hash_password(password, salt) == stored_hash

        stored = hash_password("correctpassword")
        assert verify_password("correctpassword", stored) is True
        assert verify_password("wrongpassword", stored) is False

    def test_password_strength_check(self):
        """Test password strength validation."""

        def check_password_strength(password):
            score = 0
            if len(password) >= 8:
                score += 1
            if len(password) >= 12:
                score += 1
            if re.search(r"[A-Z]", password):
                score += 1
            if re.search(r"[a-z]", password):
                score += 1
            if re.search(r"\d", password):
                score += 1
            if re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
                score += 1
            return {
                "score": score,
                "strength": (
                    "weak" if score < 3 else "medium" if score < 5 else "strong"
                ),
            }

        assert check_password_strength("abc")["strength"] == "weak"
        assert check_password_strength("abcd1234")["strength"] == "medium"
        assert check_password_strength("Abcd1234!@#")["strength"] == "strong"

    def test_common_password_check(self):
        """Test common password detection."""

        def is_common_password(password):
            common = {
                "password",
                "123456",
                "qwerty",
                "abc123",
                "letmein",
                "welcome",
                "admin",
                "password1",
            }
            return password.lower() in common

        assert is_common_password("password") is True
        assert is_common_password("123456") is True
        assert is_common_password("xK9#mNp2$vL") is False

    def test_password_similarity_check(self):
        """Test password similarity to username/email."""

        def is_too_similar(password, username, email=None):
            password_lower = password.lower()
            if username.lower() in password_lower:
                return True
            if email:
                email_local = email.split("@")[0].lower()
                if email_local in password_lower:
                    return True
            return False

        assert is_too_similar("john123", "john") is True
        assert (
            is_too_similar("johndoe@pass", "john", "johndoe@example.com")
            is True
        )
        assert (
            is_too_similar("secure#Pass1", "john", "johndoe@example.com")
            is False
        )


class TestTokenGeneration:
    """Tests for token generation patterns."""

    def test_generate_random_token(self):
        """Test random token generation."""

        def generate_token(length=32):
            return secrets.token_hex(length // 2)

        token = generate_token(32)
        assert len(token) == 32
        assert all(c in "0123456789abcdef" for c in token)

    def test_generate_url_safe_token(self):
        """Test URL-safe token generation."""

        def generate_url_safe_token(length=32):
            return secrets.token_urlsafe(length)

        token = generate_url_safe_token(32)
        # URL-safe base64 uses these characters
        assert all(
            c
            in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
            for c in token
        )

    def test_token_uniqueness(self):
        """Test that generated tokens are unique."""

        def generate_token():
            return secrets.token_hex(16)

        tokens = {generate_token() for _ in range(100)}
        assert len(tokens) == 100  # All should be unique

    def test_token_expiry_encoding(self):
        """Test encoding expiry time in token."""

        def create_timed_token(data, expires_in_seconds):
            expires_at = int(time.time()) + expires_in_seconds
            token_data = f"{data}|{expires_at}"
            return base64.urlsafe_b64encode(token_data.encode()).decode()

        def decode_timed_token(token):
            decoded = base64.urlsafe_b64decode(token.encode()).decode()
            data, expires_at = decoded.rsplit("|", 1)
            return data, int(expires_at)

        token = create_timed_token("user123", 3600)
        data, expires_at = decode_timed_token(token)
        assert data == "user123"
        assert expires_at > time.time()

    def test_signed_token(self):
        """Test creating a signed token."""

        def create_signed_token(data, secret):
            encoded = base64.urlsafe_b64encode(data.encode()).decode()
            signature = hmac.new(
                secret.encode(), encoded.encode(), hashlib.sha256
            ).hexdigest()
            return f"{encoded}.{signature}"

        def verify_signed_token(token, secret):
            try:
                encoded, signature = token.rsplit(".", 1)
                expected_sig = hmac.new(
                    secret.encode(), encoded.encode(), hashlib.sha256
                ).hexdigest()
                if not hmac.compare_digest(signature, expected_sig):
                    return None
                return base64.urlsafe_b64decode(encoded.encode()).decode()
            except Exception:
                return None

        token = create_signed_token("user123", "secret")
        assert verify_signed_token(token, "secret") == "user123"
        assert verify_signed_token(token, "wrong_secret") is None
        assert verify_signed_token("tampered", "secret") is None


class TestAccessControl:
    """Tests for access control patterns."""

    def test_role_based_access(self):
        """Test role-based access control."""

        def has_permission(user_roles, required_role):
            role_hierarchy = {
                "admin": 100,
                "moderator": 50,
                "editor": 30,
                "viewer": 10,
            }
            user_level = max(role_hierarchy.get(r, 0) for r in user_roles)
            required_level = role_hierarchy.get(required_role, 0)
            return user_level >= required_level

        assert has_permission(["admin"], "viewer") is True
        assert has_permission(["editor"], "moderator") is False
        assert has_permission(["moderator", "editor"], "editor") is True

    def test_permission_based_access(self):
        """Test permission-based access control."""

        def has_permission(user_permissions, required_permissions):
            return all(p in user_permissions for p in required_permissions)

        user_perms = {"read", "write", "delete"}
        assert has_permission(user_perms, ["read"]) is True
        assert has_permission(user_perms, ["read", "write"]) is True
        assert has_permission(user_perms, ["admin"]) is False

    def test_resource_ownership_check(self):
        """Test resource ownership check."""

        def can_access_resource(user_id, resource, is_admin=False):
            if is_admin:
                return True
            if resource.get("owner_id") == user_id:
                return True
            if user_id in resource.get("shared_with", []):
                return True
            if resource.get("is_public"):
                return True
            return False

        resource = {"owner_id": "user1", "shared_with": ["user2"]}
        assert can_access_resource("user1", resource) is True  # Owner
        assert can_access_resource("user2", resource) is True  # Shared
        assert can_access_resource("user3", resource) is False  # No access
        assert can_access_resource("user3", resource, is_admin=True) is True

    def test_scope_based_access(self):
        """Test OAuth-style scope-based access."""

        def has_scope(token_scopes, required_scope):
            # Check for exact match or wildcard
            if required_scope in token_scopes:
                return True
            # Check for wildcard scopes (e.g., "user:*" matches "user:read")
            scope_parts = required_scope.split(":")
            if len(scope_parts) >= 2:
                wildcard = f"{scope_parts[0]}:*"
                if wildcard in token_scopes:
                    return True
            return False

        scopes = {"user:read", "posts:*"}
        assert has_scope(scopes, "user:read") is True
        assert has_scope(scopes, "user:write") is False
        assert has_scope(scopes, "posts:read") is True  # Wildcard match
        assert has_scope(scopes, "posts:write") is True  # Wildcard match

    def test_time_based_access(self):
        """Test time-based access control."""

        def is_access_allowed(access_window, current_time=None):
            if current_time is None:
                current_time = datetime.now()
            if (
                access_window.get("start")
                and current_time < access_window["start"]
            ):
                return False
            if access_window.get("end") and current_time > access_window["end"]:
                return False
            return True

        now = datetime.now()
        # Valid window
        window1 = {
            "start": now - timedelta(hours=1),
            "end": now + timedelta(hours=1),
        }
        assert is_access_allowed(window1, now) is True

        # Expired window
        window2 = {
            "start": now - timedelta(hours=2),
            "end": now - timedelta(hours=1),
        }
        assert is_access_allowed(window2, now) is False


class TestInputSanitization:
    """Tests for input sanitization patterns."""

    def test_sanitize_html(self):
        """Test HTML sanitization."""

        def sanitize_html(text):
            # Remove script tags
            text = re.sub(
                r"<script[^>]*>.*?</script>",
                "",
                text,
                flags=re.DOTALL | re.IGNORECASE,
            )
            # Remove event handlers
            text = re.sub(
                r"\s*on\w+\s*=\s*[\"'][^\"']*[\"']",
                "",
                text,
                flags=re.IGNORECASE,
            )
            # Remove javascript: URLs
            text = re.sub(r"javascript:", "", text, flags=re.IGNORECASE)
            return text

        assert sanitize_html("<script>alert('xss')</script>") == ""
        assert (
            sanitize_html('<a onclick="alert(1)">click</a>') == "<a>click</a>"
        )
        assert (
            sanitize_html('<a href="javascript:alert(1)">')
            == '<a href="alert(1)">'
        )

    def test_sanitize_sql_input(self):
        """Test SQL input sanitization."""

        def sanitize_sql_string(value):
            # Escape single quotes by doubling them
            return value.replace("'", "''")

        assert sanitize_sql_string("O'Brien") == "O''Brien"
        assert sanitize_sql_string("normal") == "normal"
        assert (
            sanitize_sql_string("'; DROP TABLE users; --")
            == "''; DROP TABLE users; --"
        )

    def test_sanitize_filename(self):
        """Test filename sanitization."""

        def sanitize_filename(filename):
            # Remove path separators
            filename = filename.replace("/", "_").replace("\\", "_")
            # Remove null bytes
            filename = filename.replace("\x00", "")
            # Remove other dangerous characters
            filename = re.sub(r"[<>:\"|?*]", "", filename)
            # Limit length
            if len(filename) > 255:
                name, ext = (
                    filename.rsplit(".", 1)
                    if "." in filename
                    else (filename, "")
                )
                filename = (
                    name[: 255 - len(ext) - 1] + "." + ext
                    if ext
                    else name[:255]
                )
            return filename

        assert sanitize_filename("../../../etc/passwd") == ".._.._.._etc_passwd"
        assert sanitize_filename("file<script>.txt") == "filescript.txt"
        assert sanitize_filename("normal_file.txt") == "normal_file.txt"
        assert sanitize_filename("/etc/passwd") == "_etc_passwd"

    def test_sanitize_url(self):
        """Test URL sanitization."""

        def sanitize_url(url):
            # Only allow http and https
            if not url.startswith(("http://", "https://")):
                return None
            # Block javascript protocol
            if "javascript:" in url.lower():
                return None
            # Block data URLs
            if url.lower().startswith("data:"):
                return None
            return url

        assert sanitize_url("https://example.com") == "https://example.com"
        assert sanitize_url("javascript:alert(1)") is None
        assert sanitize_url("data:text/html,<script>alert(1)</script>") is None

    def test_sanitize_email(self):
        """Test email sanitization."""

        def sanitize_email(email):
            # Lowercase
            email = email.lower().strip()
            # Remove potentially dangerous characters
            email = re.sub(r"[<>'\";]", "", email)
            # Validate basic format
            if not re.match(
                r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", email
            ):
                return None
            return email

        assert sanitize_email("User@Example.com") == "user@example.com"
        # After removing <>, becomes userscript@example.com which is valid
        assert (
            sanitize_email("user<script>@example.com")
            == "userscript@example.com"
        )
        # Invalid characters remain after sanitization
        assert (
            sanitize_email("user@exam ple.com") is None
        )  # Space makes it invalid


class TestSessionManagement:
    """Tests for session management patterns."""

    def test_create_session(self):
        """Test session creation."""

        def create_session(user_id, expires_in=3600):
            return {
                "session_id": secrets.token_hex(32),
                "user_id": user_id,
                "created_at": time.time(),
                "expires_at": time.time() + expires_in,
                "is_active": True,
            }

        session = create_session("user123")
        assert len(session["session_id"]) == 64
        assert session["user_id"] == "user123"
        assert session["is_active"] is True
        assert session["expires_at"] > session["created_at"]

    def test_validate_session(self):
        """Test session validation."""

        def validate_session(session):
            if not session:
                return False
            if not session.get("is_active"):
                return False
            if time.time() > session.get("expires_at", 0):
                return False
            return True

        valid_session = {
            "session_id": "abc",
            "is_active": True,
            "expires_at": time.time() + 3600,
        }
        expired_session = {
            "session_id": "abc",
            "is_active": True,
            "expires_at": time.time() - 100,
        }
        inactive_session = {
            "session_id": "abc",
            "is_active": False,
            "expires_at": time.time() + 3600,
        }

        assert validate_session(valid_session) is True
        assert validate_session(expired_session) is False
        assert validate_session(inactive_session) is False
        assert validate_session(None) is False

    def test_refresh_session(self):
        """Test session refresh."""

        def refresh_session(session, extension=3600):
            if not session or not session.get("is_active"):
                return None
            return {
                **session,
                "expires_at": time.time() + extension,
                "refreshed_at": time.time(),
            }

        session = {
            "session_id": "abc",
            "is_active": True,
            "expires_at": time.time() + 100,
        }
        refreshed = refresh_session(session)
        assert refreshed["expires_at"] > session["expires_at"]
        assert "refreshed_at" in refreshed

    def test_session_fixation_prevention(self):
        """Test session fixation prevention by regenerating ID."""

        def regenerate_session_id(session):
            return {
                **session,
                "session_id": secrets.token_hex(32),
                "regenerated_at": time.time(),
            }

        session = {"session_id": "old_id", "user_id": "user123"}
        new_session = regenerate_session_id(session)
        assert new_session["session_id"] != "old_id"
        assert new_session["user_id"] == "user123"

    def test_concurrent_session_limit(self):
        """Test limiting concurrent sessions."""

        def get_sessions_to_invalidate(sessions, max_sessions=5):
            if len(sessions) <= max_sessions:
                return []
            # Sort by created_at, invalidate oldest
            sorted_sessions = sorted(sessions, key=lambda s: s["created_at"])
            return sorted_sessions[: len(sessions) - max_sessions]

        sessions = [{"session_id": f"s{i}", "created_at": i} for i in range(7)]
        to_invalidate = get_sessions_to_invalidate(sessions, max_sessions=5)
        assert len(to_invalidate) == 2
        assert to_invalidate[0]["session_id"] == "s0"
        assert to_invalidate[1]["session_id"] == "s1"


class TestRateLimiting:
    """Tests for rate limiting security patterns."""

    def test_login_attempt_tracking(self):
        """Test tracking login attempts."""

        class LoginAttemptTracker:
            def __init__(self, max_attempts=5, lockout_duration=300):
                self.attempts = {}
                self.max_attempts = max_attempts
                self.lockout_duration = lockout_duration

            def record_attempt(self, identifier, success=False):
                now = time.time()
                if identifier not in self.attempts:
                    self.attempts[identifier] = {
                        "count": 0,
                        "first_attempt": now,
                        "locked_until": 0,
                    }

                data = self.attempts[identifier]
                if success:
                    data["count"] = 0
                    return True

                data["count"] += 1
                if data["count"] >= self.max_attempts:
                    data["locked_until"] = now + self.lockout_duration
                return True

            def is_locked(self, identifier):
                data = self.attempts.get(identifier)
                if not data:
                    return False
                return time.time() < data.get("locked_until", 0)

        tracker = LoginAttemptTracker(max_attempts=3)
        tracker.record_attempt("user1", success=False)
        tracker.record_attempt("user1", success=False)
        assert tracker.is_locked("user1") is False
        tracker.record_attempt("user1", success=False)
        assert tracker.is_locked("user1") is True

    def test_progressive_delay(self):
        """Test progressive delay for failed attempts."""

        def calculate_delay(attempt_count, base_delay=1, max_delay=60):
            delay = base_delay * (2 ** (attempt_count - 1))
            return min(delay, max_delay)

        assert calculate_delay(1) == 1
        assert calculate_delay(2) == 2
        assert calculate_delay(3) == 4
        assert calculate_delay(10) == 60  # Capped at max

    def test_ip_based_blocking(self):
        """Test IP-based blocking."""

        class IPBlocker:
            def __init__(self):
                self.blocked_ips = {}

            def block(self, ip, duration=3600, reason=""):
                self.blocked_ips[ip] = {
                    "blocked_at": time.time(),
                    "expires_at": time.time() + duration,
                    "reason": reason,
                }

            def is_blocked(self, ip):
                if ip not in self.blocked_ips:
                    return False
                return time.time() < self.blocked_ips[ip]["expires_at"]

        blocker = IPBlocker()
        blocker.block(
            "192.168.1.1", duration=3600, reason="Too many failed logins"
        )
        assert blocker.is_blocked("192.168.1.1") is True
        assert blocker.is_blocked("192.168.1.2") is False


class TestCSRFProtection:
    """Tests for CSRF protection patterns."""

    def test_generate_csrf_token(self):
        """Test CSRF token generation."""

        def generate_csrf_token(session_id, secret):
            message = f"{session_id}|{int(time.time())}"
            signature = hmac.new(
                secret.encode(), message.encode(), hashlib.sha256
            ).hexdigest()
            token_data = f"{message}|{signature}"
            return base64.urlsafe_b64encode(token_data.encode()).decode()

        token = generate_csrf_token("session123", "secret")
        assert len(token) > 0

    def test_validate_csrf_token(self):
        """Test CSRF token validation."""

        def generate_csrf_token(session_id, secret):
            timestamp = int(time.time())
            message = f"{session_id}|{timestamp}"
            signature = hmac.new(
                secret.encode(), message.encode(), hashlib.sha256
            ).hexdigest()
            token_data = f"{message}|{signature}"
            return base64.urlsafe_b64encode(token_data.encode()).decode()

        def validate_csrf_token(token, session_id, secret, max_age=3600):
            try:
                decoded = base64.urlsafe_b64decode(token.encode()).decode()
                parts = decoded.split("|")
                if len(parts) != 3:
                    return False
                token_session, timestamp, signature = parts
                if token_session != session_id:
                    return False
                if int(time.time()) - int(timestamp) > max_age:
                    return False
                message = f"{token_session}|{timestamp}"
                expected_sig = hmac.new(
                    secret.encode(), message.encode(), hashlib.sha256
                ).hexdigest()
                return hmac.compare_digest(signature, expected_sig)
            except Exception:
                return False

        secret = "my_secret"
        token = generate_csrf_token("session123", secret)
        assert validate_csrf_token(token, "session123", secret) is True
        assert validate_csrf_token(token, "other_session", secret) is False
        assert validate_csrf_token("invalid", "session123", secret) is False

    def test_double_submit_cookie_validation(self):
        """Test double submit cookie validation."""

        def validate_double_submit(cookie_token, header_token):
            if not cookie_token or not header_token:
                return False
            return hmac.compare_digest(cookie_token, header_token)

        assert validate_double_submit("token123", "token123") is True
        assert validate_double_submit("token123", "token456") is False
        assert validate_double_submit("", "token123") is False


class TestEncryption:
    """Tests for encryption utility patterns."""

    def test_derive_key_from_password(self):
        """Test key derivation from password."""

        def derive_key(password, salt, iterations=100000):
            # Simple PBKDF2-like derivation
            key = password.encode()
            salt_bytes = salt.encode()
            for _ in range(iterations):
                key = hashlib.sha256(key + salt_bytes).digest()
            return key.hex()

        key1 = derive_key("password", "salt1", iterations=1000)
        key2 = derive_key("password", "salt1", iterations=1000)
        key3 = derive_key("password", "salt2", iterations=1000)

        assert key1 == key2  # Same input = same key
        assert key1 != key3  # Different salt = different key

    def test_create_secure_hash(self):
        """Test creating secure hash."""

        def secure_hash(data, algorithm="sha256"):
            if algorithm == "sha256":
                return hashlib.sha256(data.encode()).hexdigest()
            if algorithm == "sha512":
                return hashlib.sha512(data.encode()).hexdigest()
            return ""

        hash256 = secure_hash("test", "sha256")
        hash512 = secure_hash("test", "sha512")
        assert len(hash256) == 64
        assert len(hash512) == 128

    def test_constant_time_compare(self):
        """Test constant-time string comparison."""

        def secure_compare(a, b):
            return hmac.compare_digest(a, b)

        assert secure_compare("secret", "secret") is True
        assert secure_compare("secret", "other") is False

    def test_mask_sensitive_data(self):
        """Test masking sensitive data for logs."""

        def mask_sensitive(data, visible_chars=4):
            if not data or len(data) <= visible_chars:
                return "*" * len(data) if data else ""
            return data[:visible_chars] + "*" * (len(data) - visible_chars)

        assert mask_sensitive("1234567890123456") == "1234************"
        assert mask_sensitive("abc") == "***"
        assert mask_sensitive("") == ""

    def test_generate_recovery_codes(self):
        """Test generating account recovery codes."""

        def generate_recovery_codes(count=10, length=8):
            codes = []
            for _ in range(count):
                code = secrets.token_hex(length // 2).upper()
                # Format as XXXX-XXXX
                formatted = f"{code[:4]}-{code[4:]}"
                codes.append(formatted)
            return codes

        codes = generate_recovery_codes(10)
        assert len(codes) == 10
        assert all("-" in code for code in codes)
        assert len(set(codes)) == 10  # All unique


class TestAPIKeySecurity:
    """Tests for API key security patterns."""

    def test_generate_api_key(self):
        """Test API key generation."""

        def generate_api_key(prefix="sk"):
            key = secrets.token_urlsafe(32)
            return f"{prefix}_{key}"

        key = generate_api_key("sk")
        assert key.startswith("sk_")
        assert len(key) > 35

    def test_hash_api_key(self):
        """Test API key hashing for storage."""

        def hash_api_key(key):
            return hashlib.sha256(key.encode()).hexdigest()

        key = "sk_abcdef123456"
        hashed = hash_api_key(key)
        assert len(hashed) == 64
        assert hashed != key

    def test_validate_api_key_format(self):
        """Test API key format validation."""

        def validate_api_key_format(key, expected_prefix="sk"):
            if not key:
                return False
            if not key.startswith(f"{expected_prefix}_"):
                return False
            # Check minimum length
            if len(key) < 20:
                return False
            return True

        assert validate_api_key_format("sk_abcdef123456789012345") is True
        assert validate_api_key_format("pk_abcdef123456789012345") is False
        assert validate_api_key_format("sk_short") is False
        assert validate_api_key_format("") is False

    def test_api_key_rotation_check(self):
        """Test checking if API key needs rotation."""

        def should_rotate_key(key_metadata, max_age_days=90):
            created_at = key_metadata.get("created_at")
            if not created_at:
                return True
            age_days = (time.time() - created_at) / (24 * 3600)
            return age_days > max_age_days

        # Old key - should rotate
        old_key = {"created_at": time.time() - (100 * 24 * 3600)}
        assert should_rotate_key(old_key) is True

        # New key - no rotation needed
        new_key = {"created_at": time.time()}
        assert should_rotate_key(new_key) is False

    def test_api_key_scoping(self):
        """Test API key scope validation."""

        def validate_key_scope(key_scopes, required_scope):
            if "*" in key_scopes:
                return True
            return required_scope in key_scopes

        key_scopes = {"read", "write"}
        assert validate_key_scope(key_scopes, "read") is True
        assert validate_key_scope(key_scopes, "delete") is False
        assert validate_key_scope({"*"}, "anything") is True


class TestAuditLogging:
    """Tests for security audit logging patterns."""

    def test_create_audit_entry(self):
        """Test creating audit log entry."""

        def create_audit_entry(action, user_id, resource, details=None):
            return {
                "timestamp": time.time(),
                "action": action,
                "user_id": user_id,
                "resource": resource,
                "details": details or {},
                "ip_address": None,  # Would be set from request
            }

        entry = create_audit_entry(
            "login", "user123", "auth", {"success": True}
        )
        assert entry["action"] == "login"
        assert entry["user_id"] == "user123"
        assert entry["details"]["success"] is True

    def test_redact_sensitive_fields(self):
        """Test redacting sensitive fields in audit logs."""

        def redact_sensitive(data, sensitive_fields=None):
            if sensitive_fields is None:
                sensitive_fields = {"password", "token", "api_key", "secret"}
            result = {}
            for key, value in data.items():
                if key.lower() in sensitive_fields:
                    result[key] = "[REDACTED]"
                elif isinstance(value, dict):
                    result[key] = redact_sensitive(value, sensitive_fields)
                else:
                    result[key] = value
            return result

        data = {
            "username": "john",
            "password": "secret123",
            "nested": {"api_key": "abc123"},
        }
        redacted = redact_sensitive(data)
        assert redacted["username"] == "john"
        assert redacted["password"] == "[REDACTED]"
        assert redacted["nested"]["api_key"] == "[REDACTED]"

    def test_classify_security_event(self):
        """Test classifying security events by severity."""

        def classify_event(event_type):
            critical = {"breach", "data_leak", "privilege_escalation"}
            high = {
                "failed_login_threshold",
                "api_key_compromised",
                "admin_action",
            }
            medium = {"password_reset", "permission_change", "session_expired"}
            low = {"login", "logout", "settings_change"}

            if event_type in critical:
                return "critical"
            if event_type in high:
                return "high"
            if event_type in medium:
                return "medium"
            if event_type in low:
                return "low"
            return "info"

        assert classify_event("breach") == "critical"
        assert classify_event("failed_login_threshold") == "high"
        assert classify_event("password_reset") == "medium"
        assert classify_event("login") == "low"

    def test_format_audit_log(self):
        """Test formatting audit log for storage."""

        def format_audit_log(entry):
            return (
                f"[{entry['timestamp']}] "
                f"{entry['action'].upper()} "
                f"user={entry['user_id']} "
                f"resource={entry['resource']} "
                f"details={entry['details']}"
            )

        entry = {
            "timestamp": 1704067200,
            "action": "login",
            "user_id": "user123",
            "resource": "auth",
            "details": {"success": True},
        }
        log = format_audit_log(entry)
        assert "LOGIN" in log
        assert "user=user123" in log
