"""Structured logging configuration using structlog."""

import logging
import sys
from typing import Any

import structlog
from structlog.types import EventDict, Processor

from src.core.config import settings

# Sensitive field names that should be redacted
SENSITIVE_FIELDS = frozenset({
    "content",
    "resolved_content",
    "answer",
    "answer_text",
    "api_key",
    "password",
    "secret",
    "token",
    "authorization",
    "key_hash",
    "file_content",
    "query_answer",
})


def filter_sensitive_data(
    logger: logging.Logger, method_name: str, event_dict: EventDict
) -> EventDict:
    """Filter sensitive data from log entries.

    Replaces values of sensitive fields with [REDACTED].

    Args:
        logger: The logger instance.
        method_name: The logging method name.
        event_dict: The event dictionary to filter.

    Returns:
        Filtered event dictionary.
    """
    for key in list(event_dict.keys()):
        if key.lower() in SENSITIVE_FIELDS:
            event_dict[key] = "[REDACTED]"
        elif isinstance(event_dict[key], dict):
            # Recursively filter nested dicts
            event_dict[key] = _filter_dict(event_dict[key])
    return event_dict


def _filter_dict(d: dict[str, Any]) -> dict[str, Any]:
    """Recursively filter sensitive data from a dictionary.

    Args:
        d: Dictionary to filter.

    Returns:
        Filtered dictionary.
    """
    result = {}
    for key, value in d.items():
        if key.lower() in SENSITIVE_FIELDS:
            result[key] = "[REDACTED]"
        elif isinstance(value, dict):
            result[key] = _filter_dict(value)
        else:
            result[key] = value
    return result


def configure_logging() -> None:
    """Configure structured logging for the application.

    Sets up structlog with:
    - JSON format in production (LOG_LEVEL != DEBUG)
    - Pretty console format in development (LOG_LEVEL == DEBUG)
    - Sensitive data filtering
    - Standard processors for timestamp, level, event
    """
    # Determine if we're in debug mode
    is_debug = settings.log_level.upper() == "DEBUG"

    # Common processors for both modes
    shared_processors: list[Processor] = [
        structlog.contextvars.merge_contextvars,
        structlog.stdlib.add_log_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.UnicodeDecoder(),
        filter_sensitive_data,
    ]

    if is_debug:
        # Pretty console output for development
        processors: list[Processor] = [
            *shared_processors,
            structlog.dev.ConsoleRenderer(colors=True),
        ]
    else:
        # JSON output for production
        processors = [
            *shared_processors,
            structlog.processors.format_exc_info,
            structlog.processors.JSONRenderer(),
        ]

    # Configure structlog
    structlog.configure(
        processors=processors,
        wrapper_class=structlog.stdlib.BoundLogger,
        context_class=dict,
        logger_factory=structlog.stdlib.LoggerFactory(),
        cache_logger_on_first_use=True,
    )

    # Configure standard logging to work with structlog
    log_level = getattr(logging, settings.log_level.upper(), logging.INFO)

    # Configure root logger
    logging.basicConfig(
        format="%(message)s",
        stream=sys.stdout,
        level=log_level,
    )

    # Set level for uvicorn loggers
    logging.getLogger("uvicorn").setLevel(log_level)
    logging.getLogger("uvicorn.access").setLevel(log_level)
    logging.getLogger("uvicorn.error").setLevel(log_level)


def get_logger(name: str | None = None) -> structlog.stdlib.BoundLogger:
    """Get a configured structlog logger.

    Args:
        name: Optional logger name.

    Returns:
        Configured structlog BoundLogger instance.
    """
    return structlog.get_logger(name)
