import os
import sys
import threading
import time
from datetime import datetime

# Style Constants
StyleFontBold = 1
StyleFontItalic = 3
StyleFontComment = 2  # Dim
StyleFgBlack = 30
StyleFgRed = 31
StyleFgYellow = 33
StyleFgWhite = 37
StyleBgMagenta = 45
StyleBgRed = 41

class Logger:
    """
    A synchronized logger providing ANSI-colored console output and daily file rotation.
    Ported from Golang for high-performance synchronous logging.
    """

    def __init__(self, tag: str, debug_mode: bool):
        """
        Initialize a new Logger instance.
        
        :param tag: A string displayed in log messages (auto-padded to 7 characters).
        :param debug_mode: If False, .debug() calls will be ignored.
        """
        # Format tag to 7 characters
        self.tag = f"[{tag.ljust(7)}]"
        self.en_debug = debug_mode
        self.write_file_enable = False
        self.file_handle = None
        self.path = ""
        self.object_name = ""
        
        # Styles
        self.info_style = []
        self.warn_style = []
        self.error_style = []
        self.debug_style = []
        self.panic_style = []
        self.fatal_style = []

    def _get_time(self):
        return f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] "

    def _apply_style(self, text: str, styles: list):
        if not styles:
            return text
        style_str = ";".join(map(str, styles))
        return f"\033[{style_str}m{text}\033[0m"

    def set_default_style(self):
        """Applies a standard set of ANSI colors to all log levels."""
        self.info_style = [StyleFgWhite]
        self.warn_style = [StyleFgYellow]
        self.error_style = [StyleFgRed]
        self.debug_style = [StyleFontItalic, StyleFgWhite]
        self.panic_style = [StyleFontBold, StyleFgBlack, StyleBgMagenta]
        self.fatal_style = [StyleFontBold, StyleFgBlack, StyleBgRed]

    def set_write_files_enable(self, path: str, file_name: str):
        """
        Enables logging to a physical file in addition to the console.
        
        :param path: Directory where logs will be stored.
        :param file_name: The base prefix for the log file.
        """
        self.path = path
        self.object_name = file_name
        if not os.path.exists(path):
            os.makedirs(path)
        
        self._rotate_file()
        self.write_file_enable = True

    def _rotate_file(self):
        if self.file_handle:
            self.file_handle.close()
        
        date_str = datetime.now().strftime("%Y%m%d")
        file_name = f"{self.object_name}_{date_str}.log"
        full_path = os.path.join(self.path, file_name)
        self.file_handle = open(full_path, "a", encoding="utf-8")

    def change_file_routine(self, hour: int, minute: int):
        """
        Starts a background thread to automatically rotate the log file at a specific time.
        
        :param hour: Hour of the day (0-23) to rotate.
        :param minute: Minute of the hour (0-59) to rotate.
        """
        if not self.write_file_enable:
            raise Exception("Set write files enable first")

        def routine():
            while True:
                now = datetime.now()
                if now.hour == hour and now.minute == minute:
                    self._rotate_file()
                    time.sleep(61) # Avoid double rotation in same minute
                time.sleep(30)

        thread = threading.Thread(target=routine, daemon=True)
        thread.start()

    def _log(self, level_key, msg, styles, is_fatal=False, is_panic=False):
        full_msg = f"{self._get_time()}{level_key}{self.tag}: {msg}"
        
        if self.write_file_enable and self.file_handle:
            self.file_handle.write(full_msg + "\n")
            self.file_handle.flush()

        styled_msg = self._apply_style(full_msg, styles)
        print(styled_msg)

        if is_panic:
            raise Exception(full_msg)
        if is_fatal:
            sys.exit(1)

    def info(self, *args):
        """Logs an informational message."""
        self._log("[INFO] ", " ".join(map(str, args)), self.info_style)

    def warn(self, *args):
        """Logs a warning message."""
        self._log("[WARN] ", " ".join(map(str, args)), self.warn_style)

    def error(self, *args):
        """Logs an error message."""
        self._log("[ERROR]", " ".join(map(str, args)), self.error_style)

    def debug(self, *args):
        """Logs a debug message if debug_mode is enabled."""
        if self.en_debug:
            self._log("[DEBUG]", " ".join(map(str, args)), self.debug_style)

    def panic(self, *args):
        """Logs a message and raises a Python Exception."""
        self._log("[PANIC]", " ".join(map(str, args)), self.panic_style, is_panic=True)

    def fatal(self, *args):
        """Logs a message and immediately terminates the program with exit code 1."""
        self._log("[FATAL]", " ".join(map(str, args)), self.fatal_style, is_fatal=True)