"""
Log Redaction Utilities for AI Data Protection.

This module provides utilities for redacting sensitive information from logs,
implementing requirement AIP-003 from:
    lightwave://schemas/governance/security/ai_data_protection

Philosophy:
    - AI systems proliferate private data through logs
    - Prompts, responses, and debug logs all contain sensitive information
    - Redact or tokenize sensitive values before logging

Usage:
    import logging
    from lightwave.security.log_redaction import RedactingFormatter, install_redacting_handler

    # Option 1: Use the redacting formatter
    handler = logging.StreamHandler()
    handler.setFormatter(RedactingFormatter())
    logger.addHandler(handler)

    # Option 2: Install globally
    install_redacting_handler()

    # Now logs will automatically redact sensitive patterns
    logger.info(f"User email: user@example.com")  # Logs: "User email: [EMAIL]"
"""

from __future__ import annotations

import logging
import re
from typing import Any, Pattern

# =============================================================================
# Redaction Patterns
# =============================================================================

# These patterns identify sensitive data that should be redacted from logs
REDACTION_PATTERNS: dict[str, tuple[Pattern[str], str]] = {
    # Email addresses
    "email": (
        re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"),
        "[EMAIL]",
    ),
    # Phone numbers (various formats)
    "phone": (
        re.compile(r"\b(?:\+?1[-.\s]?)?(?:\(?[0-9]{3}\)?[-.\s]?)?[0-9]{3}[-.\s]?[0-9]{4}\b"),
        "[PHONE]",
    ),
    # Credit card numbers (basic pattern)
    "credit_card": (
        re.compile(r"\b(?:\d{4}[-\s]?){3}\d{4}\b"),
        "[CARD]",
    ),
    # SSN (US Social Security Number)
    "ssn": (
        re.compile(r"\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b"),
        "[SSN]",
    ),
    # IP addresses (IPv4)
    "ip_address": (
        re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b"),
        "[IP]",
    ),
    # API keys (common patterns)
    "api_key": (
        re.compile(r"\b(?:sk-|pk-|api[_-]?key[_-]?)[A-Za-z0-9]{20,}\b", re.IGNORECASE),
        "[API_KEY]",
    ),
    # Bearer tokens
    "bearer_token": (
        re.compile(r"\bBearer\s+[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\b"),
        "[BEARER_TOKEN]",
    ),
    # JWT tokens (standalone)
    "jwt": (
        re.compile(r"\beyJ[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\b"),
        "[JWT]",
    ),
    # AWS keys
    "aws_key": (
        re.compile(r"\b(?:AKIA|ASIA)[A-Z0-9]{16}\b"),
        "[AWS_KEY]",
    ),
    # Passwords in URLs or query strings
    "password_param": (
        re.compile(r"(?:password|passwd|pwd|secret)=[^&\s]+", re.IGNORECASE),
        "[PASSWORD_PARAM]",
    ),
    # Fernet keys (base64 encoded 32 bytes)
    "fernet_key": (
        re.compile(r"\b[A-Za-z0-9_-]{43}=\b"),
        "[ENCRYPTION_KEY]",
    ),
}

# Additional patterns for AI-specific content
AI_REDACTION_PATTERNS: dict[str, tuple[Pattern[str], str]] = {
    # Long base64 strings (potential embeddings or encrypted data)
    "base64_blob": (
        re.compile(r"\b[A-Za-z0-9+/]{100,}={0,2}\b"),
        "[BASE64_DATA]",
    ),
    # Conversation content markers (common in AI logs)
    "user_message": (
        re.compile(r'(?:user[_\s]?message|human[_\s]?input)\s*[=:]\s*["\'](.{50,})["\']', re.IGNORECASE),
        "[USER_MESSAGE]",
    ),
    # System prompts
    "system_prompt": (
        re.compile(r'(?:system[_\s]?prompt|instructions)\s*[=:]\s*["\'](.{50,})["\']', re.IGNORECASE),
        "[SYSTEM_PROMPT]",
    ),
}


# =============================================================================
# Redaction Functions
# =============================================================================


def redact_text(
    text: str,
    include_ai_patterns: bool = True,
    additional_patterns: dict[str, tuple[Pattern[str], str]] | None = None,
) -> str:
    """
    Redact sensitive information from text.

    Args:
        text: The text to redact
        include_ai_patterns: Whether to include AI-specific patterns
        additional_patterns: Additional patterns to apply

    Returns:
        Text with sensitive information redacted
    """
    if not text:
        return text

    result = text

    # Apply standard patterns
    for _name, (pattern, replacement) in REDACTION_PATTERNS.items():
        result = pattern.sub(replacement, result)

    # Apply AI-specific patterns
    if include_ai_patterns:
        for _name, (pattern, replacement) in AI_REDACTION_PATTERNS.items():
            result = pattern.sub(replacement, result)

    # Apply additional patterns
    if additional_patterns:
        for _name, (pattern, replacement) in additional_patterns.items():
            result = pattern.sub(replacement, result)

    return result


def redact_dict(
    data: dict[str, Any],
    sensitive_keys: set[str] | None = None,
    include_ai_patterns: bool = True,
) -> dict[str, Any]:
    """
    Redact sensitive information from a dictionary.

    Recursively processes nested dictionaries and lists.

    Args:
        data: The dictionary to redact
        sensitive_keys: Set of key names to fully redact
        include_ai_patterns: Whether to include AI-specific patterns

    Returns:
        Dictionary with sensitive information redacted
    """
    if sensitive_keys is None:
        sensitive_keys = {
            "password",
            "secret",
            "token",
            "api_key",
            "apikey",
            "access_token",
            "refresh_token",
            "private_key",
            "secret_key",
            "authorization",
            "auth",
            "credential",
            "ssn",
            "credit_card",
            "card_number",
        }

    result = {}
    for key, value in data.items():
        key_lower = key.lower()

        # Check if this key should be fully redacted
        if any(sensitive in key_lower for sensitive in sensitive_keys):
            result[key] = "[REDACTED]"
        elif isinstance(value, dict):
            result[key] = redact_dict(value, sensitive_keys, include_ai_patterns)
        elif isinstance(value, list):
            result[key] = [
                redact_dict(item, sensitive_keys, include_ai_patterns)
                if isinstance(item, dict)
                else redact_text(str(item), include_ai_patterns)
                if isinstance(item, str)
                else item
                for item in value
            ]
        elif isinstance(value, str):
            result[key] = redact_text(value, include_ai_patterns)
        else:
            result[key] = value

    return result


# =============================================================================
# Logging Integration
# =============================================================================


class RedactingFormatter(logging.Formatter):
    """
    A logging formatter that automatically redacts sensitive information.

    Usage:
        handler = logging.StreamHandler()
        handler.setFormatter(RedactingFormatter(
            fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        ))
        logger.addHandler(handler)
    """

    def __init__(
        self,
        fmt: str | None = None,
        datefmt: str | None = None,
        include_ai_patterns: bool = True,
        additional_patterns: dict[str, tuple[Pattern[str], str]] | None = None,
    ) -> None:
        """
        Initialize the redacting formatter.

        Args:
            fmt: Log format string
            datefmt: Date format string
            include_ai_patterns: Whether to redact AI-specific patterns
            additional_patterns: Additional patterns to apply
        """
        super().__init__(fmt, datefmt)
        self.include_ai_patterns = include_ai_patterns
        self.additional_patterns = additional_patterns or {}

    def format(self, record: logging.LogRecord) -> str:
        """Format the log record with redaction applied."""
        # Format first using parent
        formatted = super().format(record)

        # Then redact
        return redact_text(
            formatted,
            include_ai_patterns=self.include_ai_patterns,
            additional_patterns=self.additional_patterns,
        )


class RedactingFilter(logging.Filter):
    """
    A logging filter that redacts sensitive information from log records.

    This is useful when you want to use existing formatters but still
    redact sensitive data.

    Usage:
        logger.addFilter(RedactingFilter())
    """

    def __init__(
        self,
        include_ai_patterns: bool = True,
        additional_patterns: dict[str, tuple[Pattern[str], str]] | None = None,
    ) -> None:
        """
        Initialize the redacting filter.

        Args:
            include_ai_patterns: Whether to redact AI-specific patterns
            additional_patterns: Additional patterns to apply
        """
        super().__init__()
        self.include_ai_patterns = include_ai_patterns
        self.additional_patterns = additional_patterns or {}

    def filter(self, record: logging.LogRecord) -> bool:
        """Filter the record, redacting sensitive information."""
        # Redact the message
        if isinstance(record.msg, str):
            record.msg = redact_text(
                record.msg,
                include_ai_patterns=self.include_ai_patterns,
                additional_patterns=self.additional_patterns,
            )

        # Redact arguments if they're strings
        if record.args:
            if isinstance(record.args, dict):
                record.args = {
                    k: redact_text(str(v), self.include_ai_patterns) if isinstance(v, str) else v
                    for k, v in record.args.items()
                }
            elif isinstance(record.args, tuple):
                record.args = tuple(
                    redact_text(str(arg), self.include_ai_patterns) if isinstance(arg, str) else arg
                    for arg in record.args
                )

        return True


# =============================================================================
# Global Installation
# =============================================================================


def install_redacting_handler(
    logger_name: str | None = None,
    level: int = logging.DEBUG,
    fmt: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    include_ai_patterns: bool = True,
) -> logging.Handler:
    """
    Install a redacting log handler.

    Args:
        logger_name: Name of logger to configure (None for root)
        level: Logging level
        fmt: Log format string
        include_ai_patterns: Whether to include AI-specific patterns

    Returns:
        The configured handler
    """
    logger = logging.getLogger(logger_name)

    handler = logging.StreamHandler()
    handler.setLevel(level)
    handler.setFormatter(RedactingFormatter(fmt=fmt, include_ai_patterns=include_ai_patterns))

    logger.addHandler(handler)
    return handler


def install_redacting_filter(
    logger_name: str | None = None,
    include_ai_patterns: bool = True,
) -> RedactingFilter:
    """
    Install a redacting filter on a logger.

    This is useful when you want to keep existing handlers/formatters
    but add redaction.

    Args:
        logger_name: Name of logger to configure (None for root)
        include_ai_patterns: Whether to include AI-specific patterns

    Returns:
        The configured filter
    """
    logger = logging.getLogger(logger_name)
    redacting_filter = RedactingFilter(include_ai_patterns=include_ai_patterns)
    logger.addFilter(redacting_filter)
    return redacting_filter
