"""Utility functions and classes for the maybankforme application."""

import logging
import os
import sys
from typing import Any

import structlog

__all__ = ["DockerSafeLogger", "configure_logging", "get_logger"]


def configure_logging() -> None:
    """Configure structlog with JSON output for containers and console for development.

    Configuration is based on environment variables:
    - LOG_LEVEL: Set logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
    - LOG_FORMAT: Set output format ('json' for JSON, 'console' for human-readable)
    - IN_CONTAINER: Automatically detected or can be set to 'true' to force JSON

    The logger is configured with the following processors:
    - Add log level
    - Add timestamp (ISO format)
    - Add logger name
    - Add process ID
    - Add thread ID
    - Add caller info (function, file, line number)
    - Stack info unwinder for exceptions
    - Format exceptions
    - JSON or console renderer based on environment
    """
    # Determine log level
    log_level_str = os.getenv("LOG_LEVEL", "INFO").upper()
    log_level = getattr(logging, log_level_str, logging.INFO)

    # Determine output format - default to JSON in containers
    log_format = os.getenv("LOG_FORMAT", "").lower()
    in_container = os.getenv("IN_CONTAINER", "false").lower() == "true"

    # Auto-detect container environment if not explicitly set
    if not log_format:
        # Check common container environment indicators
        in_docker = (
            os.path.exists("/.dockerenv")
            or os.path.exists("/run/.containerenv")
            or in_container
        )
        log_format = "json" if in_docker else "console"

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

    # Common processors for all configurations
    common_processors: list[Any] = [
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.UnicodeDecoder(),
        structlog.processors.CallsiteParameterAdder(
            [
                structlog.processors.CallsiteParameter.FUNC_NAME,
                structlog.processors.CallsiteParameter.LINENO,
                structlog.processors.CallsiteParameter.MODULE,
            ]
        ),
    ]

    # Choose renderer based on format
    renderer: structlog.processors.JSONRenderer | structlog.dev.ConsoleRenderer
    if log_format == "json":
        renderer = structlog.processors.JSONRenderer()
    else:
        renderer = structlog.dev.ConsoleRenderer(
            colors=sys.stdout.isatty(),
            exception_formatter=structlog.dev.plain_traceback,
        )

    # Configure structlog
    structlog.configure(
        processors=[
            structlog.contextvars.merge_contextvars,
            structlog.stdlib.filter_by_level,
            *common_processors,
            structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        wrapper_class=structlog.stdlib.BoundLogger,
        context_class=dict,
        logger_factory=structlog.stdlib.LoggerFactory(),
        cache_logger_on_first_use=True,
    )

    # Configure formatter for stdlib integration
    # foreign_pre_chain is used for standard library loggers (e.g., uvicorn, httpx)
    formatter = structlog.stdlib.ProcessorFormatter(
        processors=[
            structlog.stdlib.ProcessorFormatter.remove_processors_meta,
            renderer,
        ],
        foreign_pre_chain=[
            structlog.stdlib.add_log_level,
            structlog.stdlib.ExtraAdder(),
        ],
    )

    # Update root logger handler
    handler = logging.StreamHandler(sys.stdout)
    handler.setFormatter(formatter)
    root_logger = logging.getLogger()
    root_logger.handlers.clear()
    root_logger.addHandler(handler)
    root_logger.setLevel(log_level)


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

    Args:
        name: Logger name, typically __name__ of the calling module

    Returns:
        Configured structlog BoundLogger instance

    Examples:
        >>> log = get_logger(__name__)
        >>> log.info("processing_started", file_count=5)
        >>> log.error(
        ...     "processing_failed",
        ...     error="File not found",
        ...     file_path="/data/file.pdf"
        ... )
    """
    return structlog.get_logger(name)


class DockerSafeLogger:
    """Legacy logger class for backward compatibility.

    This class maintains compatibility with existing code while using
    structlog underneath. New code should use get_logger() directly.

    Deprecated: Use get_logger(__name__) instead for new code.
    """

    def __init__(self) -> None:
        """Initialize the logger with structlog configuration."""
        # Configure logging on first use
        if not structlog.is_configured():
            configure_logging()

        self.logger: Any = get_logger(self.__class__.__name__)

    def __getattr__(self, name: str) -> Any:
        """Delegate logger methods to the underlying structlog logger.

        Args:
            name: Name of the attribute/method to access

        Returns:
            The requested attribute from the logger instance
        """
        return getattr(self.logger, name)
