from __future__ import annotations

import sys
from logging import DEBUG
from logging import Formatter
from logging import getLogger
from logging import Handler
from logging import Logger
from logging import LogRecord
from logging import NOTSET
from logging import StreamHandler
from logging.handlers import QueueHandler
from logging.handlers import QueueListener
from queue import SimpleQueue as Queue
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .app import Quart  # noqa

default_handler = StreamHandler(sys.stderr)
default_handler.setFormatter(
    Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s")
)


class LocalQueueHandler(QueueHandler):
    """Custom QueueHandler that skips record preparation.

    There is no need to prepare records that go into a local, in-process queue,
    we can skip that process and minimise the cost of logging further.
    """

    def prepare(self, record: LogRecord) -> LogRecord:
        return record


def _setup_logging_queue(*handlers: Handler) -> QueueHandler:
    """Create a new LocalQueueHandler and start an associated QueueListener."""
    queue: Queue = Queue()
    queue_handler = LocalQueueHandler(queue)

    serving_listener = QueueListener(queue, *handlers, respect_handler_level=True)
    serving_listener.start()

    return queue_handler


def has_level_handler(logger: Logger) -> bool:
    """Check if the logger already has a handler"""
    level = logger.getEffectiveLevel()
    current = logger

    while current:
        if any(handler.level <= level for handler in current.handlers):
            return True

        if not current.propagate:
            break

        current = current.parent

    return False


def create_logger(app: Quart) -> Logger:
    """Create a logger for the app based on the app settings.

    This creates a logger named quart.app that has a log level based
    on the app configuration.
    """
    logger = getLogger(app.name)

    if app.debug and logger.level == NOTSET:
        logger.setLevel(DEBUG)

    if not has_level_handler(logger):
        queue_handler = _setup_logging_queue(default_handler)
        logger.addHandler(queue_handler)

    return logger
