"""
SMF Structured Logging.

Provides consistent, structured logging throughout the SMF framework.
Uses stderr by default to avoid interfering with MCP stdio transport.
"""

from __future__ import annotations

import json
import logging as stdlib_logging
import sys
from contextvars import ContextVar
from datetime import UTC, datetime
from typing import Any

# Context variable for request-scoped logging context
_log_context: ContextVar[dict[str, Any]] = ContextVar("log_context", default={})


class StructuredFormatter(stdlib_logging.Formatter):
    """JSON-structured log formatter for machine-readable output."""

    def format(self, record: stdlib_logging.LogRecord) -> str:
        """Format log record as JSON."""
        log_entry: dict[str, Any] = {
            "timestamp": datetime.now(UTC).isoformat(),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getMessage(),
        }

        # Add file/line info for debugging
        if record.pathname and record.lineno:
            log_entry["location"] = f"{record.pathname}:{record.lineno}"

        # Add context from ContextVar
        context = _log_context.get()
        if context:
            log_entry["context"] = context

        # Add extra fields from record (if set via SMFLogger)
        if hasattr(record, "extra_fields") and record.extra_fields:
            log_entry["extra"] = record.extra_fields

        # Add exception info if present
        if record.exc_info:
            log_entry["exception"] = self.formatException(record.exc_info)

        return json.dumps(log_entry, default=str)


class StandardFormatter(stdlib_logging.Formatter):
    """Human-readable formatter for development/debugging."""

    FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"

    def __init__(self) -> None:
        super().__init__(self.FORMAT, datefmt="%Y-%m-%d %H:%M:%S")


class SMFLogger:
    """SMF-specific logger with structured output support.

    Wraps the standard library logger with convenience methods
    and support for structured logging.

    Args:
        name: Logger name (will be prefixed with 'smf.')
        structured: If True, output JSON-formatted logs.
        level: Logging level (default: INFO).
    """

    def __init__(
        self,
        name: str,
        *,
        structured: bool = False,
        level: int = stdlib_logging.INFO,
    ) -> None:
        self._logger = stdlib_logging.getLogger(name)
        self._logger.setLevel(level)
        self._structured = structured

        # Only add handler if none exist
        if not self._logger.handlers:
            handler = stdlib_logging.StreamHandler(sys.stderr)
            handler.setLevel(level)
            if structured:
                handler.setFormatter(StructuredFormatter())
            else:
                handler.setFormatter(StandardFormatter())
            self._logger.addHandler(handler)
            # Don't propagate to root logger
            self._logger.propagate = False

    def _log(
        self,
        level: int,
        message: str,
        **kwargs: Any,
    ) -> None:
        """Internal logging method with extra fields support."""
        # Create a LogRecord with extra fields
        record = self._logger.makeRecord(
            self._logger.name,
            level,
            "(unknown file)",
            0,
            message,
            (),
            None,
        )
        # Attach extra fields for the formatter
        record.extra_fields = kwargs if kwargs else None  # type: ignore[attr-defined]
        self._logger.handle(record)

    def debug(self, message: str, **kwargs: Any) -> None:
        """Log a debug message."""
        self._log(stdlib_logging.DEBUG, message, **kwargs)

    def info(self, message: str, **kwargs: Any) -> None:
        """Log an info message."""
        self._log(stdlib_logging.INFO, message, **kwargs)

    def warning(self, message: str, **kwargs: Any) -> None:
        """Log a warning message."""
        self._log(stdlib_logging.WARNING, message, **kwargs)

    def error(self, message: str, **kwargs: Any) -> None:
        """Log an error message."""
        self._log(stdlib_logging.ERROR, message, **kwargs)

    def critical(self, message: str, **kwargs: Any) -> None:
        """Log a critical message."""
        self._log(stdlib_logging.CRITICAL, message, **kwargs)

    def exception(self, message: str, **kwargs: Any) -> None:
        """Log an exception with traceback."""
        self._logger.exception(message, extra={"extra_fields": kwargs})

    def set_level(self, level: int) -> None:
        """Set the logging level."""
        self._logger.setLevel(level)
        for handler in self._logger.handlers:
            handler.setLevel(level)


# Logger cache to avoid creating multiple loggers for the same name
_loggers: dict[str, SMFLogger] = {}


def get_logger(
    name: str,
    structured: bool = False,
    level: int = stdlib_logging.INFO,
) -> SMFLogger:
    """Get or create an SMF logger.

    Args:
        name: Logger name (will be prefixed with 'smf.')
        structured: If True, output JSON-formatted logs.
        level: Logging level.

    Returns:
        SMFLogger instance.

    Example:
        >>> logger = get_logger("elasticsearch")
        >>> logger.info("Connected to cluster", cluster="production")
    """
    full_name = f"smf.{name}" if not name.startswith("smf.") else name
    cache_key = f"{full_name}:{structured}:{level}"

    if cache_key not in _loggers:
        _loggers[cache_key] = SMFLogger(full_name, structured=structured, level=level)

    return _loggers[cache_key]


def set_log_context(**kwargs: Any) -> None:
    """Set context variables for the current request/operation.

    Context variables are included in all log messages within
    the current async context.

    Args:
        **kwargs: Key-value pairs to add to the context.

    Example:
        >>> set_log_context(request_id="abc123", user="admin")
        >>> logger.info("Processing request")  # Includes context
    """
    current = _log_context.get().copy()
    current.update(kwargs)
    _log_context.set(current)


def clear_log_context() -> None:
    """Clear the logging context."""
    _log_context.set({})


def configure_logging(
    *,
    structured: bool = False,
    level: int = stdlib_logging.INFO,
) -> None:
    """Configure global logging settings for SMF.

    Args:
        structured: If True, use JSON-formatted output.
        level: Global logging level.
    """
    root_smf_logger = stdlib_logging.getLogger("smf")
    root_smf_logger.setLevel(level)

    # Remove existing handlers
    for handler in root_smf_logger.handlers[:]:
        root_smf_logger.removeHandler(handler)

    # Add new handler
    handler = stdlib_logging.StreamHandler(sys.stderr)
    handler.setLevel(level)
    if structured:
        handler.setFormatter(StructuredFormatter())
    else:
        handler.setFormatter(StandardFormatter())
    root_smf_logger.addHandler(handler)
    root_smf_logger.propagate = False


# Log level constants for convenience
DEBUG = stdlib_logging.DEBUG
INFO = stdlib_logging.INFO
WARNING = stdlib_logging.WARNING
ERROR = stdlib_logging.ERROR
CRITICAL = stdlib_logging.CRITICAL


__all__ = [
    "CRITICAL",
    "DEBUG",
    "ERROR",
    "INFO",
    "WARNING",
    "SMFLogger",
    "StandardFormatter",
    "StructuredFormatter",
    "clear_log_context",
    "configure_logging",
    "get_logger",
    "set_log_context",
]
