"""SwiftyLoggerMixin — Structured logging mixin for Django classes.

Provides a ``logger`` property that returns a bound ``structlog`` logger
with automatic module/class naming and optional context binding.
"""

import structlog
from functools import lru_cache
from django.conf import settings
from swifty.utils.mapper import getpath


@lru_cache(maxsize=128)
def _get_logger(name: str) -> structlog.BoundLogger:
    """Get or create a cached structlog logger for the given name.

    Args:
        name: The logger name (typically ``module.ClassName``).

    Returns:
        A bound structlog logger instance.
    """
    return structlog.getLogger(name, exc_info=settings.DEBUG)


class SwiftyLoggerMixin:
    """Mixin class providing structured logging capabilities.

    Classes using this mixin get a ``logger`` property that automatically
    creates a logger named after the module and class.

    Example::

        class MyService(SwiftyLoggerMixin):
            def process(self):
                self.logger.info("Processing started", item_count=42)
    """

    logging_context: dict | None = None

    @property
    def logger(self) -> structlog.BoundLogger:
        """Get a cached, bound logger instance for this class.

        The logger is named ``{module}.{ClassName}`` and optionally
        bound with ``logging_context`` if set.

        Returns:
            A structlog.BoundLogger instance.
        """
        name = f"{self.__module__}.{self.__class__.__name__}"
        _logger = _get_logger(name)
        if self.logging_context:
            _logger = _logger.bind(**(self.logging_context or {}))
        return _logger

    def init_context(self) -> dict:
        """Initialize and return the logging context.

        Returns:
            The current logging context dict, or empty dict.
        """
        return self.logging_context or {}

    @property
    def context(self) -> dict:
        """Get the current bound logging context.

        Returns:
            Dict of currently bound context variables.
        """
        return (
            getpath(self.logger.bind(), "_context._dict") or self.logging_context or {}
        )

    def set_extra_context(self, extra_context: dict) -> None:
        """Bind extra context variables to the logger.

        Args:
            extra_context: Dict of key-value pairs to bind.
        """
        self.logger.bind(**extra_context)


def get_swifty_logger(name: str = __name__) -> structlog.BoundLogger:
    """Get a standalone Swifty logger instance.

    Args:
        name: Logger name. Defaults to the current module.

    Returns:
        A structlog.BoundLogger instance.
    """
    return _get_logger(name)


swifty_logger = get_swifty_logger()
