"""
Security utilities for payload-guard.
Handles sensitive field detection and removal.
"""

from typing import Any, Optional


# Default sensitive field names
DEFAULT_SENSITIVE_FIELDS: list[str] = [
    "password",
    "password_hash",
    "password_reset_token",
    "pwd",
    "token",
    "access_token",
    "refresh_token",
    "auth_token",
    "secret",
    "api_key",
    "private_key",
    "encryption_key",
    "authorization",
    "auth",
    "session_id",
    "ssn",
    "credit_card",
    "cvv",
    "card_number",
]


def is_sensitive(field: str, sensitive_fields: Optional[list[str]] = None) -> bool:
    """
    Check if a field name is sensitive.

    Args:
        field: Field name to check
        sensitive_fields: Additional sensitive field names

    Returns:
        True if field is sensitive
    """
    all_sensitive = set(DEFAULT_SENSITIVE_FIELDS)
    if sensitive_fields:
        all_sensitive.update(sensitive_fields)

    field_lower = field.lower()
    return field_lower in all_sensitive or any(
        sensitive in field_lower for sensitive in all_sensitive
    )


def remove_sensitive(
    data: Any, sensitive_fields: Optional[list[str]] = None
) -> Any:
    """
    Remove sensitive fields from data recursively.

    Args:
        data: Data to sanitize
        sensitive_fields: Additional sensitive field names

    Returns:
        Sanitized data with sensitive fields removed
    """
    if data is None:
        return data

    if isinstance(data, dict):
        result = {}
        for key, value in data.items():
            if not is_sensitive(key, sensitive_fields):
                result[key] = remove_sensitive(value, sensitive_fields)
        return result

    if isinstance(data, list):
        return [remove_sensitive(item, sensitive_fields) for item in data]

    return data


def mask_value(value: str, visible_chars: int = 2, mask_char: str = "*") -> str:
    """
    Mask a sensitive value, showing only first few characters.

    Args:
        value: Value to mask
        visible_chars: Number of characters to show at start
        mask_char: Character to use for masking

    Returns:
        Masked string
    """
    if not value or not isinstance(value, str):
        return value

    if len(value) <= visible_chars:
        return mask_char * len(value)

    return value[:visible_chars] + mask_char * (len(value) - visible_chars)


def redact_sensitive(
    data: Any,
    sensitive_fields: Optional[list[str]] = None,
    mask: bool = False,
    visible_chars: int = 2,
) -> Any:
    """
    Redact sensitive fields from data (either remove or mask).

    Args:
        data: Data to redact
        sensitive_fields: Additional sensitive field names
        mask: If True, mask values instead of removing
        visible_chars: Number of visible characters when masking

    Returns:
        Redacted data
    """
    if data is None:
        return data

    if isinstance(data, dict):
        result = {}
        for key, value in data.items():
            if is_sensitive(key, sensitive_fields):
                if mask and isinstance(value, str):
                    result[key] = mask_value(value, visible_chars)
                # If not masking, skip the field entirely
            else:
                result[key] = redact_sensitive(value, sensitive_fields, mask, visible_chars)
        return result

    if isinstance(data, list):
        return [redact_sensitive(item, sensitive_fields, mask, visible_chars) for item in data]

    return data
