from re import I
import sys
import time

from datetime import datetime
from typing import Optional, Any, Dict
from sunhpc.util import getTerminalWidth

class Colors:
    """终端颜色代码"""
    # 基本颜色
    BLACK = '\033[30m'
    RED = '\033[31m'
    GREEN = '\033[32m'
    YELLOW = '\033[33m'
    BLUE = '\033[34m'
    MAGENTA = '\033[35m'
    CYAN = '\033[36m'
    WHITE = '\033[37m'
    
    # 亮色
    BRIGHT_BLACK = '\033[90m'
    BRIGHT_RED = '\033[91m'
    BRIGHT_GREEN = '\033[92m'
    BRIGHT_YELLOW = '\033[93m'
    BRIGHT_BLUE = '\033[94m'
    BRIGHT_MAGENTA = '\033[95m'
    BRIGHT_CYAN = '\033[96m'
    BRIGHT_WHITE = '\033[97m'
    
    # 背景色
    BG_RED = '\033[41m'
    BG_GREEN = '\033[42m'
    BG_YELLOW = '\033[43m'
    BG_BLUE = '\033[44m'
    BG_MAGENTA = '\033[45m'
    BG_CYAN = '\033[46m'
    BG_WHITE = '\033[47m'
    
    # 样式
    BOLD = '\033[1m'
    DIM = '\033[2m'
    ITALIC = '\033[3m'
    UNDERLINE = '\033[4m'
    BLINK = '\033[5m'
    REVERSE = '\033[7m'
    HIDDEN = '\033[8m'
    
    # 重置
    RESET = '\033[0m'

class LogLevel:
    """日志级别定义"""
    DEBUG = 0
    INFO = 1
    OK = 2
    WARN = 3
    ERROR = 4
    CRITICAL = 5
    
    _names = {
        DEBUG: "DEBUG",
        INFO: "INFO",
        OK: "OK",
        WARN: "WARN",
        ERROR: "ERROR",
        CRITICAL: "CRITICAL"
    }
    
    _prefixes = {
        DEBUG: "[d]",
        INFO: "[i]",
        OK: "[+]",
        WARN: "[-]",
        ERROR: "[e]",
        CRITICAL: "[!]"
    }
    
    _colors = {
        DEBUG: Colors.BRIGHT_BLACK,
        INFO: Colors.BRIGHT_BLUE,
        OK: Colors.BRIGHT_GREEN,
        WARN: Colors.BRIGHT_YELLOW,
        ERROR: Colors.BRIGHT_RED,
        CRITICAL: Colors.BG_RED + Colors.BRIGHT_WHITE + Colors.BOLD
    }
    
    @classmethod
    def get_name(cls, level: int) -> str:
        return cls._names.get(level, "UNKNOWN")
    
    @classmethod
    def get_prefix(cls, level: int) -> str:
        return cls._prefixes.get(level, "[?]")
    
    @classmethod
    def get_color(cls, level: int) -> str:
        return cls._colors.get(level, Colors.WHITE)

    def __str__(self):
        return self.get_name(self.level)

class Logs:
    """日志类"""
    
    def __init__(self, 
                 name: str = "",
                 level: int = LogLevel.INFO,
                 show_time: bool = True,
                 show_level: bool = False,
                 show_caller: bool = False,
                 show_function: bool = False,
                 time_format: str = "%H:%M:%S",
                 output_file: Optional[str] = None,
                 color_enabled: bool = True):
        """
        初始化日志对象
        
        :param name: 日志名称（通常为模块名）
        :param level: 最低日志级别
        :param show_time: 是否显示时间
        :param show_level: 是否显示日志级别名称
        :param time_format: 时间格式
        :param color_enabled: 是否启用颜色
        """
        self.name            = name
        self.level           = level
        self.reduce          = 20
        self.show_time       = show_time
        self.show_level      = show_level
        self.output_file     = output_file
        self.show_caller     = show_caller
        self.show_function   = show_function
        self.time_format     = time_format
        self.color_enabled   = color_enabled and self._supports_color()

        self._file_handler   = None
        if self.output_file:
            try:
                self._file_handler = open(self.output_file, 'a', encoding='utf-8')
            except Exception:
                print(f"Failed to open log file {self.output_file}: {e}", file=sys.stderr)

    def __del__(self):
        if self._file_handler:
            self._file_handler.close()

    def _get_caller_info(self, stack_level: int = 3) -> Dict[str, str]:
        """
        Get caller information from the stack.
        
        Args:
            stack_level: How many levels to go up the stack (2 = direct caller)
            
        Returns:
            Dictionary with file, line, function information
        """
        try:
            # Get the current stack frame
            frame = sys._getframe(stack_level)
            
            # Get caller information
            filename = frame.f_code.co_filename
            line_number = frame.f_lineno
            function_name = frame.f_code.co_name
            
            # Get relative path (remove current working directory)
            try:
                cwd = os.getcwd()
                if filename.startswith(cwd):
                    filename = os.path.relpath(filename, cwd)
            except Exception:
                pass
            
            return {
                'file': filename,
                'line': str(line_number),
                'function': function_name,
                'full_path': filename
            }
        except Exception:
            return {
                'file': 'unknown',
                'line': '?',
                'function': 'unknown',
                'full_path': 'unknown'
            }


    def _supports_color(self) -> bool:
        """检测终端是否支持颜色"""
        return (
            hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
        ) or 'ANSICON' in sys.environ or 'PYCHARM_HOSTED' in sys.environ
    
    def _format_message(self, 
            msg: str, 
            level: int, 
            same_color: Optional[str] = None,
            caller_info: Optional[Dict[str, str]] = None) -> str:
        """
        格式化日志消息
        
        :param msg: 日志消息
        :param level: 日志级别
        :param same_color: 统一颜色（如果指定，则忽略级别颜色）
        :param caller_info: 调用者信息（文件、行号、函数名、完整路径，可选）
        :return: 格式化后的消息
        """
        parts = []
        
        # 时间戳
        if self.show_time:
            timestamp = datetime.now().strftime(self.time_format)
            if self.color_enabled:
                parts.append(f"{Colors.BRIGHT_BLACK}{timestamp}{Colors.RESET}")
            else:
                parts.append(timestamp)

        # 日志名称
        if self.name:
            if self.color_enabled:
                parts.append(f"{Colors.BRIGHT_CYAN}[{self.name}]{Colors.RESET}")
            else:
                parts.append(f"[{self.name}]")
        
        # 日志级别名称
        if self.show_level:
            level_name = LogLevel.get_name(level)
            if self.color_enabled:
                color = same_color if same_color else LogLevel.get_color(level)
                parts.append(f"{color}{level_name:7}{Colors.RESET}")
            else:
                parts.append(f"{level_name:7}")

        # 调用者信息
        if self.show_caller and caller_info:
            parts.append(f"{caller_info['file']}:{caller_info['line']}")

        # 日志级别前缀
        prefix = LogLevel.get_prefix(level)
        if self.color_enabled:
            color = same_color if same_color else LogLevel.get_color(level)
            prefix_colored = f"{color}{prefix}{Colors.RESET}"
            parts.append(prefix_colored)
        else:
            parts.append(prefix)
        
        # 消息内容
        if self.color_enabled and same_color:
            parts.append(f"{same_color}{msg}{Colors.RESET}")
        elif self.color_enabled:
            color = LogLevel.get_color(level)
            parts.append(f"{color}{msg}{Colors.RESET}")
        else:
            parts.append(msg)
        
        return " ".join(parts)
    
    def _split_words(self, msg: str) -> list:
        """将消息按宽度拆分成多个行"""
        try:
            terminal_width = (getTerminalWidth() - 30)
        except:
            terminal_width = 80

        if type(msg) in [type([]), type(())]:
            msg = ' '.join(msg)

        msg = ' '.join(msg.strip('\n').splitlines())
        if len(msg) <= terminal_width:
            return [msg]

        words           = msg.split()
        result          = []
        current_line    = ''
        for word in words:
            if current_line:
                test_line = current_line + " " + word
            else:
                test_line = word

            if len(test_line) <= terminal_width:
                current_line = test_line
            else:
                if current_line:
                    result.append(current_line)

                if len(word) > terminal_width:
                    for i in range(0, len(word), terminal_width):
                        result.append(word[i:i+terminal_width])
                    current_line = ''
                else:
                    current_line = word

        if current_line:
            result.append(current_line)

        return result

    def _log(self, level: int, msg: str, same_color: Optional[str] = None):
        """内部日志方法"""
        if level < self.level:
            return

        # Get caller information (skip _log and the calling method)
        caller_info  = self._get_caller_info()
        intput_lines = self._split_words(msg)
        output_lines = []
        for line in intput_lines:
            output_lines.append(self._format_message(line, level, same_color, caller_info))

        # 写入文件
        if self._file_handler:
            try:
                self._file_handler.write(formatted_msg + '\n')
                self._file_handler.flush()
            except Exception:
                pass

        # 错误级别输出到 stderr
        if level >= LogLevel.ERROR:
            for line in output_lines:
                if line.strip():
                    print (line, file=sys.stderr)
            #print(formatted_msg, file=sys.stderr)
        else:
            for line in output_lines:
                if line.strip():
                    print (line)
    
    def set_level(self, level: int):
        """设置日志级别"""
        self.level = level

    def set_output_file(self, filename: str) -> None:
        '''Set or change the output file.'''
        if self._file_handler:
            self._file_handler.close()
        try:
            self._file_handler = open(filename, 'a', encoding='utf-8')
            self.output_file = filename
        except Exception:
            pass

    def debug(self, msg: str, same_color: Optional[str] = None):
        """调试日志 [d]"""
        self._log(LogLevel.DEBUG, msg, same_color)
    
    def info(self, msg: str, same_color: Optional[str] = None):
        """信息日志 [i]"""
        self._log(LogLevel.INFO, msg, same_color)
    
    def ok(self, msg: str, same_color: Optional[str] = None):
        """成功日志 [+]"""
        self._log(LogLevel.OK, msg, same_color)
    
    def warn(self, msg: str, same_color: Optional[str] = None):
        """警告日志 [-]"""
        self._log(LogLevel.WARN, msg, same_color)
    
    def error(self, msg: str, same_color: Optional[str] = None):
        """错误日志 [e]"""
        self._log(LogLevel.ERROR, msg, same_color)
    
    def critical(self, msg: str, same_color: Optional[str] = None):
        """严重错误日志 [!]"""
        self._log(LogLevel.CRITICAL, msg, same_color)
    
    def __call__(self, msg: str, level: str = "info", same_color: Optional[str] = None):
        """
        直接调用方法，支持字符串级别
        
        :param msg: 日志消息
        :param level: 日志级别 (debug, info, ok, warn, error, critical)
        :param same_color: 统一颜色
        """
        level_map = {
            "debug": LogLevel.DEBUG,
            "info": LogLevel.INFO,
            "ok": LogLevel.OK,
            "warn": LogLevel.WARN,
            "warning": LogLevel.WARN,
            "error": LogLevel.ERROR,
            "critical": LogLevel.CRITICAL,
            "err": LogLevel.ERROR,
        }
        
        level_value = level_map.get(level.lower(), LogLevel.INFO)
        
        # 转换颜色字符串到颜色代码（如果提供）
        color_code = None
        if same_color and self.color_enabled:
            color_code = self._get_color_code(same_color)
        
        self._log(level_value, msg, color_code)
    
    def _get_color_code(self, color_name: str) -> str:
        """根据颜色名称获取颜色代码"""
        color_map = {
            "black": Colors.BLACK,
            "red": Colors.RED,
            "green": Colors.GREEN,
            "yellow": Colors.YELLOW,
            "blue": Colors.BLUE,
            "magenta": Colors.MAGENTA,
            "cyan": Colors.CYAN,
            "white": Colors.WHITE,
            "bright_black": Colors.BRIGHT_BLACK,
            "bright_red": Colors.BRIGHT_RED,
            "bright_green": Colors.BRIGHT_GREEN,
            "bright_yellow": Colors.BRIGHT_YELLOW,
            "bright_blue": Colors.BRIGHT_BLUE,
            "bright_magenta": Colors.BRIGHT_MAGENTA,
            "bright_cyan": Colors.BRIGHT_CYAN,
            "bright_white": Colors.BRIGHT_WHITE,
        }
        return color_map.get(color_name.lower(), Colors.WHITE)
    
    def line(self, char: str = "-", length: Optional[int] = None):
        """打印分隔线"""
        if length is None:
            try:
                import shutil
                length = shutil.get_terminal_size().columns
            except:
                length = 80
        
        line_str = char * min(length, 100)  # 限制最大长度
        if self.color_enabled:
            print(f"{Colors.BRIGHT_BLACK}{line_str}{Colors.RESET}")
        else:
            print(line_str)
    
    def blank(self):
        """打印空行"""
        print()


# 创建默认日志实例
default_logger = Logs()

# 为了方便使用，提供模块级别的函数
def debug(msg: str):
    default_logger.debug(msg)

def info(msg: str):
    default_logger.info(msg)

def ok(msg: str):
    default_logger.ok(msg)

def warn(msg: str):
    default_logger.warn(msg)

def error(msg: str):
    default_logger.error(msg)

def critical(msg: str):
    default_logger.critical(msg)


# 使用示例
if __name__ == "__main__":
    # 示例1：基本使用
    print("=" * 60)
    print("示例1:基本使用")
    print("=" * 60)
    
    log = Logs(name="Test", show_time=True, show_level=True)
    log.ok("操作成功完成")
    log.info("正在加载配置...")
    log.warn("配置文件未找到，使用默认配置")
    log.error("连接数据库失败")
    log.debug("这是调试信息")  # 默认 INFO 级别，不会显示
    
    print("\n")
    
    # 示例2：使用 __call__ 方法（self.log('msg', 'err')）
    print("=" * 60)
    print("示例2:使用 __call__ 方法")
    print("=" * 60)
    
    log2 = Logs(name="MyApp", show_time=False)
    log2("这是一条普通信息", "info")
    log2("这是一条警告", "warn")
    log2("这是一条错误", "err")
    log2("这是一条成功消息", "ok")
    log2("这是调试信息", "debug")
    
    print("\n")
    
    # 示例3：固定颜色
    print("=" * 60)
    print("示例3：固定颜色输出")
    print("=" * 60)
    
    log3 = Logs(name="ColorTest")
    log3("这条消息是红色的", "error", "red")
    log3("这条消息是绿色的", "info", "green")
    log3("这条消息是黄色的", "warn", "yellow")
    log3("这条消息是蓝色的", "ok", "blue")
    
    print("\n")
    
    # 示例4：不使用颜色
    print("=" * 60)
    print("示例4：禁用颜色")
    print("=" * 60)
    
    log4 = Logs(name="NoColor", color_enabled=False)
    log4.ok("没有颜色的成功消息")
    log4.error("没有颜色的错误消息")
    
    print("\n")
    
    # 示例5：分隔线
    print("=" * 60)
    print("示例5: 工具方法")
    print("=" * 60)
    
    log5 = Logs(name="Tool")
    log5.line("=")
    log5.ok("功能演示")
    log5.line("-")
    log5.blank()
    log5.info("完成")
    log5.line("=")