"""Log sanitization for removing sensitive information from logs."""

import json
import re
from typing import Any, Dict

import structlog

logger = structlog.get_logger(__name__)


class LogSanitizer:
    """Class for cleaning logs from sensitive information."""

    def __init__(self) -> None:
        """Initialize LogSanitizer with sensitive fields and patterns."""
        self.sensitive_fields = {
            "password",
            "passwd",
            "pwd",
            "secret",
            "token",
            "key",
            "auth_token",
            "guac_token",
            "jwt_token",
            "access_token",
            "refresh_token",
            "api_key",
            "private_key",
            "encryption_key",
            "session_id",
            "cookie",
            "authorization",
            "credential",
            "credentials",
            "global_credentials",
            "machine_credentials",
            "ssh_password",
            "ssh_username",
            "credential_hash",
            "password_hash",
            "password_salt",
            "encrypted_password",
        }

        self.sensitive_patterns = [
            r'password["\']?\s*[:=]\s*["\']?([^"\'\\s]+)["\']?',
            r'token["\']?\s*[:=]\s*["\']?([^"\'\\s]+)["\']?',
            r'key["\']?\s*[:=]\s*["\']?([^"\'\\s]+)["\']?',
            r'secret["\']?\s*[:=]\s*["\']?([^"\'\\s]+)["\']?',
            r'authorization["\']?\s*[:=]\s*["\']?([^"\'\\s]+)["\']?',
        ]

        self.jwt_pattern = re.compile(
            r"\b[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b"
        )
        self.api_key_pattern = re.compile(r"\b[A-Za-z0-9]{32,}\b")

    def mask_sensitive_value(self, value: str, mask_char: str = "*") -> str:
        """
        Mask sensitive value.

        Args:
            value: Value to mask.
            mask_char: Character to use for masking.

        Returns:
            Masked value.
        """
        if not value or len(value) <= 4:
            return mask_char * 4

        if len(value) <= 8:
            return value[:2] + mask_char * (len(value) - 4) + value[-2:]

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

    def sanitize_string(self, text: str) -> str:
        """
        Clean string from sensitive information.

        Args:
            text: Text to clean.

        Returns:
            Cleaned text.
        """
        if not isinstance(text, str):
            return text

        sanitized = text

        sanitized = self.jwt_pattern.sub(
            lambda m: self.mask_sensitive_value(m.group(0)), sanitized
        )

        sanitized = self.api_key_pattern.sub(
            lambda m: self.mask_sensitive_value(m.group(0)), sanitized
        )

        for pattern in self.sensitive_patterns:
            sanitized = re.sub(
                pattern,
                lambda m: m.group(0).replace(
                    m.group(1), self.mask_sensitive_value(m.group(1))
                ),
                sanitized,
                flags=re.IGNORECASE,
            )

        return sanitized

    def sanitize_dict(
        self, data: Dict[str, Any], max_depth: int = 10
    ) -> Dict[str, Any]:
        """
        Recursively clean dictionary from sensitive information.

        Args:
            data: Dictionary to clean.
            max_depth: Maximum recursion depth.

        Returns:
            Cleaned dictionary.
        """
        if max_depth <= 0:
            return {"error": "max_depth_exceeded"}

        if not isinstance(data, dict):
            return data

        sanitized = {}

        for key, value in data.items():
            key_lower = key.lower()
            is_sensitive_key = any(
                sensitive_field in key_lower
                for sensitive_field in self.sensitive_fields
            )

            if is_sensitive_key:
                if isinstance(value, str):
                    sanitized[key] = self.mask_sensitive_value(value)
                elif isinstance(value, (dict, list)):
                    sanitized[key] = self.sanitize_value(value, max_depth - 1)
                else:
                    sanitized[key] = "[MASKED]"
            else:
                sanitized[key] = self.sanitize_value(value, max_depth - 1)

        return sanitized

    def sanitize_value(self, value: Any, max_depth: int = 10) -> Any:
        """
        Clean value of any type.

        Args:
            value: Value to clean.
            max_depth: Maximum recursion depth.

        Returns:
            Cleaned value.
        """
        if max_depth <= 0:
            return "[max_depth_exceeded]"

        if isinstance(value, str):
            return self.sanitize_string(value)

        if isinstance(value, dict):
            return self.sanitize_dict(value, max_depth)

        if isinstance(value, list):
            return [
                self.sanitize_value(item, max_depth - 1) for item in value
            ]

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

        return self.sanitize_string(str(value))

    def sanitize_log_event(
        self, event_dict: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Clean log event from sensitive information.

        Args:
            event_dict: Log event dictionary.

        Returns:
            Cleaned event dictionary.
        """
        try:
            sanitized_event = event_dict.copy()
            sanitized_event = self.sanitize_dict(sanitized_event)

            special_fields = ["request_body", "response_body", "headers"]
            for field in special_fields:
                if field in sanitized_event:
                    sanitized_event[field] = self.sanitize_value(
                        sanitized_event[field]
                    )

            return sanitized_event

        except Exception as e:
            logger.error("Error sanitizing log event", error=str(e))
            return {
                "error": "sanitization_failed",
                "original_error": str(e),
            }

    def sanitize_json_string(self, json_string: str) -> str:
        """
        Clean JSON string from sensitive information.

        Args:
            json_string: JSON string to clean.

        Returns:
            Cleaned JSON string.
        """
        try:
            data = json.loads(json_string)
            sanitized_data = self.sanitize_value(data)
            return json.dumps(sanitized_data, ensure_ascii=False)

        except json.JSONDecodeError:
            return self.sanitize_string(json_string)
        except Exception as e:
            logger.error("Error sanitizing JSON string", error=str(e))
            return json_string

    def is_sensitive_field(self, field_name: str) -> bool:
        """
        Check if field is sensitive.

        Args:
            field_name: Field name.

        Returns:
            True if field is sensitive.
        """
        field_lower = field_name.lower()
        return any(
            sensitive_field in field_lower
            for sensitive_field in self.sensitive_fields
        )

    def get_sanitization_stats(self) -> Dict[str, Any]:
        """
        Get sanitization statistics.

        Returns:
            Sanitization statistics dictionary.
        """
        return {
            "sensitive_fields_count": len(self.sensitive_fields),
            "sensitive_patterns_count": len(self.sensitive_patterns),
            "sensitive_fields": list(self.sensitive_fields),
            "jwt_pattern_active": bool(self.jwt_pattern),
            "api_key_pattern_active": bool(self.api_key_pattern),
        }


log_sanitizer = LogSanitizer()


def sanitize_log_processor(
    logger: Any, name: str, event_dict: Dict[str, Any]
) -> Dict[str, Any]:
    """
    Processor for structlog for automatic log sanitization.

    Args:
        logger: Logger instance.
        name: Logger name.
        event_dict: Event dictionary.

    Returns:
        Sanitized event dictionary.
    """
    return log_sanitizer.sanitize_log_event(event_dict)
