#!/usr/bin/env python3

# -- nercone-modern --------------------------------------------- #
# logging.py on nercone-modern                                    #
# Made by DiamondGotCat, Licensed under MIT License               #
# Copyright (c) 2025 DiamondGotCat                                #
# ---------------------------------------------- DiamondGotCat -- #

ModernLoggingLevels = ["DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"]
MAX_LOG_LEVEL_WIDTH = max(len(level) for level in ModernLoggingLevels)
LEVEL_ALIASES = {
    "D": "DEBUG",
    "DEBUG": "DEBUG",
    "I": "INFO",
    "INFO": "INFO",
    "INFORMATION": "INFO",
    "W": "WARN",
    "WARN": "WARN",
    "WARNING": "WARN",
    "E": "ERROR",
    "ERROR": "ERROR",
    "C": "CRITICAL",
    "CRITICAL": "CRITICAL"
}

_last_process = None
_last_level = None
_max_proc_width = 0

def normalize_level(level: str) -> str:
    level = level.strip().upper()
    return LEVEL_ALIASES.get(level, level)

def is_higher_priority(level_a: str, level_b: str) -> bool:
    a = normalize_level(level_a)
    b = normalize_level(level_b)
    try:
        return ModernLoggingLevels.index(a) >= ModernLoggingLevels.index(b)
    except ValueError:
        raise ValueError(f"Unknown log level: {level_a} or {level_b}")

class ModernLogging:
    def __init__(self, process_name: str, display_level: str = "INFO"):
        self.process_name = process_name
        self.display_level = display_level

        global _max_proc_width
        _max_proc_width = max(_max_proc_width, len(process_name))

    def log(self, message="", level="INFO"):
        if not is_higher_priority(level, self.display_level):
            return

        global _last_process, _last_level
        level_text = normalize_level(level.strip().upper())
        show_proc = (self.process_name != _last_process)
        show_level = show_proc or (level_text != _last_level)

        if level_text == "DEBUG":
            color = 'gray'
        elif level_text == "INFO":
            color = 'blue'
        elif level_text == "WARN":
            color = 'yellow'
        elif level_text == "ERROR":
            color = 'red'
        elif level_text == "CRITICAL":
            color = 'red'
        else:
            color = 'blue'

        print(self._make(message, level_text, color, show_proc, show_level))

        _last_process = self.process_name
        _last_level = level_text

    def prompt(self, message="", level="INFO") -> str:
        if not is_higher_priority(level, self.display_level):
            return

        global _last_process, _last_level
        level_text = normalize_level(level.strip().upper())
        show_proc = (self.process_name != _last_process)
        show_level = show_proc or (level_text != _last_level)

        if level_text == "DEBUG":
            color = 'gray'
        elif level_text == "INFO":
            color = 'blue'
        elif level_text == "WARN":
            color = 'yellow'
        elif level_text == "ERROR":
            color = 'red'
        elif level_text == "CRITICAL":
            color = 'red'
        else:
            color = 'blue'

        print(self._make(message, level_text, color, show_proc, show_level), end="")

        _last_process = self.process_name
        _last_level = level_text
        return input()

    def _make(self, message, level_text, color, show_proc, show_level):
        global _max_proc_width
        level_width = max(MAX_LOG_LEVEL_WIDTH, len(level_text))

        proc_part = self.process_name if show_proc else ""
        proc_part = proc_part.ljust(_max_proc_width) if proc_part else " " * _max_proc_width

        if show_level:
            level_part = f"{self._color(color)}{level_text.ljust(level_width)} |{self._color('reset')}"
        else:
            level_part = (" " * level_width) + f"{self._color(color)} |{self._color('reset')}"

        return f"{proc_part} {level_part} {str(message)}"

    def _color(self, color_name):
        if color_name == "cyan":
            return self._color_by_code(36)
        elif color_name == "magenta":
            return self._color_by_code(35)
        elif color_name == "yellow":
            return self._color_by_code(33)
        elif color_name == "green":
            return self._color_by_code(32)
        elif color_name == "red":
            return self._color_by_code(31)
        elif color_name == "blue":
            return self._color_by_code(34)
        elif color_name == "white":
            return self._color_by_code(37)
        elif color_name == "black":
            return self._color_by_code(30)
        elif color_name in ("gray", "grey"):
            return self._color_by_code(90)
        elif color_name == "reset":
            return self._color_by_code(0)
        else:
            return ""

    def _color_by_code(self, color_code):
        return f"\033[{color_code}m"
