import logging
import os
from typing import Optional

from ddtrace.internal.utils.formats import asbool


DD_LOG_FORMAT = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)d] {}- %(message)s".format(
    "[dd.service=%(dd.service)s dd.env=%(dd.env)s dd.version=%(dd.version)s"
    " dd.trace_id=%(dd.trace_id)s dd.span_id=%(dd.span_id)s] "
)

DEFAULT_FILE_SIZE_BYTES = 15 << 20  # 15 MB


def configure_ddtrace_logger():
    # type: () -> None
    """Configures ddtrace log levels and file paths.

    Customization is possible with the environment variables:
        ``DD_TRACE_DEBUG``, ``DD_TRACE_LOG_FILE_LEVEL``, and ``DD_TRACE_LOG_FILE``

    By default, when none of the settings have been changed, ddtrace loggers
        inherit from the root logger in the logging module and no logs are written to a file.

    When DD_TRACE_DEBUG has been enabled:
        - Logs are propagated up so that they appear in the application logs if a file path wasn't provided
        - Logs are routed to a file when DD_TRACE_LOG_FILE is specified, using the log level in DD_TRACE_LOG_FILE_LEVEL.
        - Child loggers inherit from the parent ddtrace logger

    Note(s):
        1) The ddtrace-run logs under commands/ddtrace_run do not follow DD_TRACE_LOG_FILE if DD_TRACE_DEBUG is enabled.
            This is because ddtrace-run calls ``logging.basicConfig()`` when DD_TRACE_DEBUG is enabled, so
            this configuration is not applied.
        2) Python 2: If the application is using DD_TRACE_DEBUG=true, logging will need to be configured,
            ie: ``logging.basicConfig()``.

    """
    ddtrace_logger = logging.getLogger("ddtrace")
    if asbool(os.environ.get("DD_TRACE_LOG_STREAM_HANDLER", "true")):
        ddtrace_logger.addHandler(logging.StreamHandler())

    _configure_ddtrace_debug_logger(ddtrace_logger)
    _configure_ddtrace_file_logger(ddtrace_logger)


def _configure_ddtrace_debug_logger(logger):
    if asbool(os.environ.get("DD_TRACE_DEBUG", "false")):
        logger.setLevel(logging.DEBUG)
        logger.debug("debug mode has been enabled for the ddtrace logger")


def _configure_ddtrace_file_logger(logger):
    log_file_level = os.environ.get("DD_TRACE_LOG_FILE_LEVEL", "DEBUG").upper()
    try:
        file_log_level_value = getattr(logging, log_file_level)
    except AttributeError:
        raise ValueError(
            "DD_TRACE_LOG_FILE_LEVEL is invalid. Log level must be CRITICAL/ERROR/WARNING/INFO/DEBUG.",
            log_file_level,
        )
    max_file_bytes = int(os.environ.get("DD_TRACE_LOG_FILE_SIZE_BYTES", DEFAULT_FILE_SIZE_BYTES))
    log_path = os.environ.get("DD_TRACE_LOG_FILE")
    _add_file_handler(logger=logger, log_path=log_path, log_level=file_log_level_value, max_file_bytes=max_file_bytes)


def _add_file_handler(
    logger: logging.Logger,
    log_path: str,
    log_level: int,
    handler_name: Optional[str] = None,
    max_file_bytes: int = DEFAULT_FILE_SIZE_BYTES,
):
    ddtrace_file_handler = None
    if log_path is not None:
        log_path = os.path.abspath(log_path)
        num_backup = 1
        from logging.handlers import RotatingFileHandler

        ddtrace_file_handler = RotatingFileHandler(
            filename=log_path, mode="a", maxBytes=max_file_bytes, backupCount=num_backup
        )
        log_format = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)d] - %(message)s"
        log_formatter = logging.Formatter(log_format)
        ddtrace_file_handler.setLevel(log_level)
        ddtrace_file_handler.setFormatter(log_formatter)
        if handler_name:
            ddtrace_file_handler.set_name(handler_name)
        logger.addHandler(ddtrace_file_handler)
        logger.debug("ddtrace logs will be routed to %s", log_path)
    return ddtrace_file_handler


def _configure_log_injection():
    """
    Ensures that logging is patched before we inject trace information into logs.
    """
    from ddtrace import patch

    patch(logging=True)
    ddtrace_logger = logging.getLogger("ddtrace")
    for handler in ddtrace_logger.handlers:
        handler.setFormatter(logging.Formatter(DD_LOG_FORMAT))
