"""Logger initialization utilities for configuring logging system."""

import logging
from typing import Any

from .config import LogConfig
from .handlers import get_handlers_data

__all__ = (
    "log_initializer",
    "celery_logger_handler",
    "get_gunicorn_logconfig_dict",
)

# Logger names for third-party libraries
_CELERY_LOGGERS = ("celery", "celery.task", "celery.worker", "celery.beat")
_UVICORN_LOGGERS = ("uvicorn", "uvicorn.error")
_UVICORN_ACCESS_LOGGERS = ("uvicorn.access",)
_GUNICORN_LOGGERS = ("gunicorn", "gunicorn.error")
_GUNICORN_ACCESS_LOGGERS = ("gunicorn.access",)
_OPENSEARCH_LOGGERS = ("opensearch", "opensearchpy")


def _configure_library_loggers(
    logger_names: tuple[str, ...],
    handlers: list[logging.Handler],
    log_level: int,
) -> None:
    """Configure multiple library loggers with the provided handlers.

    Args:
        logger_names: Tuple of logger names to configure.
        handlers: List of handlers to attach to each logger.
        log_level: The log level to set for each logger.
    """
    for name in logger_names:
        logger = logging.getLogger(name)
        logger.setLevel(log_level)
        # Clear existing handlers to prevent duplicate logging
        logger.handlers.clear()
        for handler in handlers:
            logger.addHandler(handler)
        # Don't propagate to root to avoid duplicate logs
        logger.propagate = False


def _disable_access_loggers(logger_names: tuple[str, ...]) -> None:
    """Disable access loggers to suppress HTTP request logging.

    Sets the log level to CRITICAL+1 (effectively disabled) and clears
    handlers to prevent any output from access loggers.

    Args:
        logger_names: Tuple of access logger names to disable.
    """
    for name in logger_names:
        logger = logging.getLogger(name)
        # Set to a level higher than CRITICAL to effectively disable
        logger.setLevel(logging.CRITICAL + 1)
        logger.handlers.clear()
        logger.propagate = False


def log_initializer(config: LogConfig) -> None:
    """Initialize the logging system based on the provided configuration.

    This function sets up the logging system by creating and attaching handlers
    to the root logger. Optionally configures loggers for third-party libraries
    (Celery, Uvicorn, OpenSearch) based on the configuration.

    When JSON logging is enabled (default), all configured loggers will
    output single-line JSON logs suitable for cloud logging platforms.

    Args:
        config: The LogConfig object containing the logging configuration.

    Example:
        >>> config = LogConfig()
        >>> config.set_log_print(True) \
        ...       .set_celery_mode(True) \
        ...       .set_uvicorn_mode(True) \
        ...       .set_opensearch_mode(True)
        >>> log_initializer(config)
    """
    handlers, log_level = get_handlers_data(config=config)

    # Configure root logger
    root_logger = logging.getLogger()
    root_logger.setLevel(log_level)  # Set root level to allow logs through
    # Clear existing handlers to prevent duplicate logging on re-init
    root_logger.handlers.clear()
    for handler in handlers:
        root_logger.addHandler(handler)

    # Configure Celery loggers if enabled (default: True for backward compat)
    if config.get("celery_mode", True):
        _configure_library_loggers(_CELERY_LOGGERS, handlers, log_level)

    # Configure Uvicorn loggers if enabled
    if config.get("uvicorn_mode", True):
        _configure_library_loggers(_UVICORN_LOGGERS, handlers, log_level)
        # Configure access loggers only if access_log_mode is enabled (default: False)
        if config.get("access_log_mode", False):
            _configure_library_loggers(_UVICORN_ACCESS_LOGGERS, handlers, log_level)
        else:
            _disable_access_loggers(_UVICORN_ACCESS_LOGGERS)

    # Configure Gunicorn loggers if enabled
    if config.get("gunicorn_mode", True):
        _configure_library_loggers(_GUNICORN_LOGGERS, handlers, log_level)
        # Configure access loggers only if access_log_mode is enabled (default: False)
        if config.get("access_log_mode", False):
            _configure_library_loggers(_GUNICORN_ACCESS_LOGGERS, handlers, log_level)
        else:
            _disable_access_loggers(_GUNICORN_ACCESS_LOGGERS)

    # Configure OpenSearch loggers if enabled
    if config.get("opensearch_mode", True):
        _configure_library_loggers(_OPENSEARCH_LOGGERS, handlers, log_level)


def celery_logger_handler(
    config: LogConfig,
    logger: Any,
    propagate: bool,
) -> None:
    """Configure a Celery logger with handlers from the provided configuration.

    This function is intended to be used within a Celery application to set up
    logging for its workers via Celery signals. It applies the same handlers
    configured for the main application, including JSON formatting.

    Args:
        config: The LogConfig object containing the logging configuration.
        logger: The Celery logger instance to configure (typically from signals).
        propagate: Whether the logger should propagate messages to its parent.

    Example:
        >>> from celery.signals import setup_logging
        >>> @setup_logging.connect
        ... def configure_logging(logger, **kwargs):
        ...     celery_logger_handler(config, logger, propagate=False)
    """
    celery_mode = config.get("celery_mode", True)
    if celery_mode:
        handlers, log_level = get_handlers_data(config=config)
        logger.setLevel(log_level)
        logger.propagate = propagate
        # Clear existing handlers to avoid duplicates
        if hasattr(logger, "handlers"):
            logger.handlers.clear()
        for handler in handlers:
            logger.addHandler(handler)


def get_gunicorn_logconfig_dict(
    log_level: str = "INFO",
    access_log: bool = False,
) -> dict[str, Any]:
    """Get a logging dict config for gunicorn to use JSON formatting from startup.

    This function returns a logging configuration dictionary that can be used
    with gunicorn's `logconfig_dict` option. It ensures gunicorn's startup logs
    are formatted as JSON and sent to stdout (not stderr), preventing cloud
    platforms like Railway from marking them as errors.

    Args:
        log_level: The log level for gunicorn loggers (default: "INFO").
        access_log: Whether to enable access logging (default: False).

    Returns:
        A logging dict config for use with gunicorn's logconfig_dict.

    Example:
        In your gunicorn.conf.py:

        >>> from dtpyfw.log.initializer import get_gunicorn_logconfig_dict
        >>>
        >>> # Use JSON logging from startup
        >>> logconfig_dict = get_gunicorn_logconfig_dict(
        ...     log_level="INFO",
        ...     access_log=False,
        ... )
    """
    access_level = log_level if access_log else "CRITICAL"

    return {
        "version": 1,
        "disable_existing_loggers": False,
        "filters": {
            "graceful_shutdown": {
                "()": "dtpyfw.log.handlers.GracefulShutdownFilter",
            },
        },
        "formatters": {
            "json": {
                "()": "dtpyfw.log.formatter.StructuredJsonFormatter",
            },
        },
        "handlers": {
            "console": {
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stdout",  # stdout, not stderr
                "formatter": "json",
                "filters": ["graceful_shutdown"],
                "level": log_level,
            },
        },
        "loggers": {
            "gunicorn": {
                "level": log_level,
                "handlers": ["console"],
                "propagate": False,
            },
            "gunicorn.error": {
                "level": log_level,
                "handlers": ["console"],
                "propagate": False,
            },
            "gunicorn.access": {
                "level": access_level,
                "handlers": ["console"],
                "propagate": False,
            },
            "uvicorn": {
                "level": log_level,
                "handlers": ["console"],
                "propagate": False,
            },
            "uvicorn.error": {
                "level": log_level,
                "handlers": ["console"],
                "propagate": False,
            },
        },
        "root": {
            "level": log_level,
            "handlers": ["console"],
        },
    }
