"""Security audit trail helpers."""

import logging
from typing import Any, Dict, Optional

from django.http import HttpRequest

from .models import SecurityLog

logger = logging.getLogger(__name__)

SENSITIVE_DETAIL_KEYS = {
    "password",
    "pass",
    "passwd",
    "secret",
    "token",
    "private_key",
    "privatekey",
    "pem",
    "certificate",
    "certificate_pem",
    "signed_nonce",
    "nonce",
    "payload",
    "request_body",
    "raw_request",
    "authorization",
    "cookie",
}

MAX_STRING_LENGTH = 500
MAX_LIST_ITEMS = 20


def _looks_sensitive_key(key: str) -> bool:
    lowered = key.lower()
    return any(pattern in lowered for pattern in SENSITIVE_DETAIL_KEYS)


def _sanitize_value(value: Any) -> Any:
    if isinstance(value, dict):
        cleaned: Dict[str, Any] = {}
        for raw_key, raw_value in value.items():
            key = str(raw_key)
            if _looks_sensitive_key(key):
                cleaned[key] = "[REDACTED]"
            else:
                cleaned[key] = _sanitize_value(raw_value)
        return cleaned

    if isinstance(value, list):
        return [_sanitize_value(item) for item in value[:MAX_LIST_ITEMS]]

    if isinstance(value, tuple):
        return [_sanitize_value(item) for item in value[:MAX_LIST_ITEMS]]

    if isinstance(value, str):
        candidate = value.strip()
        if "-----BEGIN" in candidate and "-----END" in candidate:
            return "[REDACTED]"
        if len(candidate) > MAX_STRING_LENGTH:
            return f"{candidate[:MAX_STRING_LENGTH]}...[TRUNCATED]"
        return candidate

    if isinstance(value, (bool, int, float)) or value is None:
        return value

    return str(value)[:MAX_STRING_LENGTH]


def extract_request_ip(request: Optional[HttpRequest]) -> Optional[str]:
    """Extract client IP address from request headers in a proxy-safe way."""
    if not request:
        return None

    x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
    if x_forwarded_for:
        return x_forwarded_for.split(",")[0].strip()

    return request.META.get("REMOTE_ADDR")


def log_security_event(
    event_type: str,
    details: Optional[Dict[str, Any]] = None,
    request: Optional[HttpRequest] = None,
    user: Any = None,
    ip_address: Optional[str] = None,
) -> None:
    """
    Safely persist security event to DB without breaking auth flow.

    Any logging error is intentionally swallowed after internal logging,
    because audit logging must never interrupt authentication process.
    """
    safe_details = _sanitize_value(details or {})

    try:
        SecurityLog.objects.create(
            event_type=event_type,
            user=user,
            ip_address=ip_address or extract_request_ip(request),
            details=safe_details,
        )
    except Exception as exc:
        logger.exception("Failed to save SecurityLog event '%s': %s", event_type, exc)
