import logging
import logging.handlers as handlers
import sys
from typing import Dict, Optional
from contextlib import contextmanager
import colorama
from colorama import Back, Fore, Style

from popsynth.utils.configuration import popsynth_config
from popsynth.utils.package_data import get_path_of_log_dir, get_path_of_log_file

colorama.deinit()
colorama.init(strip=False)
## set up the console logging


class ColoredFormatter(logging.Formatter):
    """
    Colored log formatter.
    """
    def __init__(self,
                 *args,
                 colors: Optional[Dict[str, str]] = None,
                 **kwargs) -> None:
        """Initialize the formatter with specified format strings."""

        super().__init__(*args, **kwargs)

        self.colors = colors if colors else {}

    def format(self, record) -> str:
        """Format the specified record as text."""

        record.color = self.colors.get(record.levelname, "")
        record.reset = Style.RESET_ALL

        return super().format(record)


class MyFilter(object):
    def __init__(self, level):
        self.__level = level

    def filter(self, logRecord):
        return logRecord.levelno != self.__level


# now create the developer handler that rotates every day and keeps
# 10 days worth of backup
popsynth_dev_log_handler = handlers.TimedRotatingFileHandler(
    get_path_of_log_file("dev.log"), when="D", interval=1, backupCount=10)

# lots of info written out

_dev_formatter = logging.Formatter(
    "%(asctime)s | %(name)s | %(levelname)s| %(funcName)s | %(lineno)d | %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

popsynth_dev_log_handler.setFormatter(_dev_formatter)
popsynth_dev_log_handler.setLevel(logging.DEBUG)

# now set up the usr log which will save the info

popsynth_usr_log_handler = handlers.TimedRotatingFileHandler(
    get_path_of_log_file("usr.log"), when="D", interval=1, backupCount=10)

popsynth_usr_log_handler.setLevel(popsynth_config["logging"]["file"]["level"])

# lots of info written out
_usr_formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s",
                                   datefmt="%Y-%m-%d %H:%M:%S")

popsynth_usr_log_handler.setFormatter(_usr_formatter)

# now set up the console logger

_console_formatter = ColoredFormatter(
    "{color} {levelname:8} {reset}| {color} {message} {reset}",
    style="{",
    datefmt="%Y-%m-%d %H:%M:%S",
    colors={
        "DEBUG": Fore.CYAN,
        "INFO": Fore.GREEN + Style.BRIGHT,
        "WARNING": Fore.YELLOW + Style.DIM,
        "ERROR": Fore.RED + Style.BRIGHT,
        "CRITICAL": Fore.RED + Back.WHITE + Style.BRIGHT,
    },
)

popsynth_console_log_handler = logging.StreamHandler(sys.stdout)
popsynth_console_log_handler.setFormatter(_console_formatter)
popsynth_console_log_handler.setLevel(
    popsynth_config["logging"]["console"]["level"])

warning_filter = MyFilter(logging.WARNING)



class LoggingState(object):

    def __init__(self, popsynth_usr_log_handler, popsynth_console_log_handler,
       
                 ):
        """
        A container to store the stat of the logs
        """

        # attach the log handlers

        self.popsynth_usr_log_handler = popsynth_usr_log_handler
        self.popsynth_console_log_handler = popsynth_console_log_handler


        # store their current states

        self.popsynth_usr_log_handler_state = popsynth_usr_log_handler.level
        self.popsynth_console_log_handler_state = popsynth_console_log_handler.level

    def _store_state(self):

        self.popsynth_usr_log_handler_state = popsynth_usr_log_handler.level
        self.popsynth_console_log_handler_state = popsynth_console_log_handler.level


    def restore_last_state(self):

        self.popsynth_usr_log_handler.setLevel(
            self.popsynth_usr_log_handler_state)
        self.popsynth_console_log_handler.setLevel(
            self.popsynth_console_log_handler_state)

    def silence_logs(self):

        # store the state
        self._store_state()

        # silence the logs

        self.popsynth_usr_log_handler.setLevel(
            logging.CRITICAL)
        self.popsynth_console_log_handler.setLevel(
            logging.CRITICAL)


    def loud_logs(self):

        # store the state
        self._store_state()

        # silence the logs

        self.popsynth_usr_log_handler.setLevel(
            logging.INFO)
        self.popsynth_console_log_handler.setLevel(
            logging.INFO)

    def debug_logs(self):

        # store the state
        self._store_state()

        # silence the logs
        self.popsynth_console_log_handler.setLevel(
            logging.DEBUG)



_log_state = LoggingState(popsynth_usr_log_handler, popsynth_console_log_handler,
                          )




def silence_warnings():
    """
    supress warning messages in console and file usr logs
    """

    popsynth_usr_log_handler.addFilter(warning_filter)
    popsynth_console_log_handler.addFilter(warning_filter)


def activate_warnings():
    """
    supress warning messages in console and file usr logs
    """

    popsynth_usr_log_handler.removeFilter(warning_filter)
    popsynth_console_log_handler.removeFilter(warning_filter)


def update_logging_level(level):

    popsynth_console_log_handler.setLevel(level)


def silence_logs():
    """
    Turn off all logging
    """

    # handle dev logs independently
    popsynth_dev_log_handler.setLevel(logging.CRITICAL)


    _log_state.silence_logs()


def show_progress_bars():

    popsynth_config.show_progress = True


def silence_progress_bars():

    popsynth_config.show_progress = False



    
def quiet_mode():
    """
    turn off all logging and progress bars
    """

    silence_progress_bars()

    # save state and silence
    silence_logs()


def loud_mode():
    """
    turn on all progress bars and logging
    """

    show_progress_bars()

    # save state and make loud
    _log_state.loud_logs()


def activate_logs():
    """
    re-activate silenced logs
    """

    # handle dev logs independently
    popsynth_dev_log_handler.setLevel(logging.DEBUG)


    _log_state.restore_last_state()


def debug_mode():
    """
    activate debug in the console
    """

    # store state and switch console to debug
    _log_state.debug_logs()

    


    
@contextmanager
def silence_console_log():

    current_console_logging_level = popsynth_console_log_handler.level
    current_usr_logging_level = popsynth_usr_log_handler.level

    popsynth_console_log_handler.setLevel(logging.ERROR)
    popsynth_usr_log_handler.setLevel(logging.ERROR)

    try:
        yield

    finally:

        popsynth_console_log_handler.setLevel(current_console_logging_level)
        popsynth_usr_log_handler.setLevel(current_usr_logging_level)



def setup_logger(name):

    # A logger with name name will be created
    # and then add it to the print stream
    log = logging.getLogger(name)

    # this must be set to allow debug messages through
    log.setLevel(logging.DEBUG)

    # add the handlers

    if popsynth_config["logging"]["debug"]:
        log.addHandler(popsynth_dev_log_handler)

    if popsynth_config["logging"]["console"]["on"]:

        log.addHandler(popsynth_console_log_handler)

    if popsynth_config["logging"]["file"]["on"]:
        log.addHandler(popsynth_usr_log_handler)

    # we do not want to duplicate teh messages in the parents
    log.propagate = False

    return log
