import logging
from logging import Logger
from pathlib import Path
from typing import Any, cast

from rich.logging import RichHandler

from bmde.config.schema import Settings
from bmde.core.types import DATE_FORMAT, LogLevel, get_default_log_path

# ---- extend the logging module with TRACE
TRACE_LEVEL_NUM = 1
logging.addLevelName(TRACE_LEVEL_NUM, "TRACE")
MESSAGE_FORMAT = "%(asctime)s | %(name)s | %(message)s"


class ExtendedLogger(logging.Logger):
    def trace(self: Logger, message: str, *args: Any, **kwargs: Any) -> None:
        self._log(TRACE_LEVEL_NUM, message, args, **kwargs)


# Tell the logging system to use your new class
logging.setLoggerClass(ExtendedLogger)


class SecretsFilter(logging.Filter):
    def __init__(self, secrets: list[str | None] | None):
        super().__init__()
        self.secrets: list[str | None] = secrets or []

    def filter(self, record: logging.LogRecord) -> bool:
        if not self.secrets:
            return True

        if isinstance(record.msg, str):
            for secret in self.secrets:
                if secret and secret in record.msg:
                    record.msg = record.msg.replace(secret, "*****")

        """
        if record.args:
            if isinstance(record.args, tuple):
                new_args = []
                modified = False
                for arg in record.args:
                    temp_arg = arg
                    for secret in self.secrets:
                        if secret and secret in temp_arg:
                            temp_arg = temp_arg.replace(secret, "*****")
                            modified = True
                    new_args.append(temp_arg)
                if modified:
                    record.args = tuple(new_args)
        """
        return True


def setup_logging(
    level: int | None | None,
    log_file: str | Path | None = None,
    secrets: list[str | None] | None = None,
) -> None:
    # Default level is INFO
    if level is None:
        level = logging.INFO

    handlers: list[logging.Handler] = []
    secrets_filter = SecretsFilter(secrets)

    # ---- console (Rich)
    console = RichHandler(
        rich_tracebacks=True,
        markup=True,
        show_time=False,
        show_level=True,
        show_path=False,
    )
    common_formatter = logging.Formatter(MESSAGE_FORMAT, DATE_FORMAT)
    console.setLevel(level)
    console.setFormatter(common_formatter)
    console.addFilter(secrets_filter)
    handlers.append(console)

    if log_file:
        path = Path(log_file)
        path.parent.mkdir(parents=True, exist_ok=True)
        file_handler = logging.FileHandler(path, encoding="utf-8")
        file_handler.setLevel(level)
        file_handler.setFormatter(common_formatter)
        file_handler.addFilter(secrets_filter)
        handlers.append(file_handler)

    logging.basicConfig(
        level=level,  # root captures everything
        handlers=handlers,
        format=MESSAGE_FORMAT,
        force=True,
    )


def obfuscate_text(text: str | None) -> str:
    if text is None:
        return str(text)
    else:
        return "*****"


def get_logger(name: str) -> ExtendedLogger:
    """Return a logger with trace() method available."""
    return cast(ExtendedLogger, logging.getLogger(name))


def process_log_flags(
    very_verbose: bool, verbose: bool, quiet: bool, very_quiet: bool
) -> tuple[LogLevel | None, bool]:
    more_than_one_flag = False
    flag_counter = 0
    for flag in (very_verbose, verbose, quiet, very_quiet):
        if flag:
            flag_counter += 1
    if flag_counter > 1:
        more_than_one_flag = True

    if very_verbose:
        return LogLevel.TRACE, more_than_one_flag
    elif verbose:
        return LogLevel.DEBUG, more_than_one_flag
    elif quiet:
        return LogLevel.WARNING, more_than_one_flag
    elif very_quiet:
        return LogLevel.QUIET, more_than_one_flag
    else:
        return None, more_than_one_flag


def configure_logging_from_settings(settings: Settings) -> None:
    if settings.logging.file is None:
        settings.logging.file = get_default_log_path()

    if settings.logging.level is None:
        settings.logging.level = LogLevel.get_default_log_level()

    # Always obfuscate all passwords
    words_to_obfuscate = [settings.git.vpn.password, settings.git.ssh.password]
    # Optionally but by default true obfuscate other sensible data.
    if settings.logging.hide_sensibles:
        words_to_obfuscate += [
            settings.git.git.name,
            settings.git.git.email,
            settings.git.ssh.host,
            settings.git.ssh.username,
            settings.git.vpn.host,
            str(settings.git.vpn.port),
            settings.git.vpn.username,
            settings.git.vpn.cert,
            settings.git.vpn.realm,
            settings.git.vpn.test_dns,
            settings.git.vpn.test_ip,
        ]

    setup_logging(
        level=settings.logging.level.to_logging_level(),
        log_file=settings.logging.file,
        secrets=words_to_obfuscate,
    )  # Preventive creation of log for logging the loading of settings
