"""
cortex/logging_utils.py — Logging utilities for Cortex.

Provides a RedactSecretsFilter that masks API keys and bearer tokens
in log output, preventing accidental credential leaks.

Usage:
    from cortex.logging_utils import install_redaction_filter
    install_redaction_filter()  # applies to root logger

Or manually:
    import logging
    from cortex.logging_utils import RedactSecretsFilter
    logging.getLogger().addFilter(RedactSecretsFilter())
"""
from __future__ import annotations

import logging
import re
from typing import List, Pattern


# Patterns to detect and redact (compiled for performance).
# Each tuple: (compiled regex, replacement template).
_REDACTION_PATTERNS: List[tuple[Pattern[str], str]] = [
    # Anthropic-style keys: sk-ant-... (must come before generic sk-)
    (re.compile(r"\bsk-ant-[A-Za-z0-9_-]{8,}\b"), "sk-ant-*****"),
    # OpenAI-style keys: sk-..., sk-proj-...
    (re.compile(r"\bsk-[A-Za-z0-9_-]{8,}\b"), "sk-*****"),
    # Personal access tokens: pa-...
    (re.compile(r"\bpa-[A-Za-z0-9_-]{8,}\b"), "pa-*****"),
    # Generic key- prefix tokens
    (re.compile(r"\bkey-[A-Za-z0-9_-]{8,}\b"), "key-*****"),
    # CORTEX_API_KEY=<value> in env dumps / log messages
    (re.compile(r"CORTEX_API_KEY=[^\s]+"), "CORTEX_API_KEY=*****"),
    # Bearer tokens in auth headers
    (re.compile(r"Bearer\s+[A-Za-z0-9_.\-/+=]{8,}"), "Bearer *****"),
    # xai- prefix keys (X.AI / Grok)
    (re.compile(r"\bxai-[A-Za-z0-9_-]{8,}\b"), "xai-*****"),
]


class RedactSecretsFilter(logging.Filter):
    """Logging filter that redacts API keys and bearer tokens from log records.

    Applies to the ``msg`` field after formatting. Handles both pre-formatted
    strings and %-style format args.
    """

    def filter(self, record: logging.LogRecord) -> bool:
        """Always returns True (never suppresses records), but modifies the message."""
        # Format the message with args first if they exist
        if record.args:
            try:
                formatted = record.getMessage()
                record.msg = self._redact(formatted)
                record.args = None  # already formatted
            except (TypeError, ValueError):
                # If formatting fails, redact the raw msg string
                if isinstance(record.msg, str):
                    record.msg = self._redact(record.msg)
        elif isinstance(record.msg, str):
            record.msg = self._redact(record.msg)
        return True

    @staticmethod
    def _redact(text: str) -> str:
        """Apply all redaction patterns to *text*."""
        for pattern, replacement in _REDACTION_PATTERNS:
            text = pattern.sub(replacement, text)
        return text


def install_redaction_filter(logger_name: str = "") -> RedactSecretsFilter:
    """Install RedactSecretsFilter on the specified logger (root by default).

    Idempotent: will not add a second filter if one is already installed.

    Args:
        logger_name: Logger name. Empty string = root logger.

    Returns:
        The installed (or existing) RedactSecretsFilter instance.
    """
    target_logger = logging.getLogger(logger_name)

    # Check if already installed
    for f in target_logger.filters:
        if isinstance(f, RedactSecretsFilter):
            return f

    filt = RedactSecretsFilter()
    target_logger.addFilter(filt)
    return filt
