import datetime
import shutil
import traceback
from .colors import ConsoleColors, Theme
from .formatters import GradientFormatter, TimestampFormatter, LevelFormatter
from .handlers import ConsoleHandler


class Logger:
    def __init__(self, name=None, level="DEBUG", theme=None, handlers=None):
        self.name = name
        self.level = level
        self.theme = theme
        self.handlers = handlers or []
        self._console_handler = ConsoleHandler(level=level)
        self.timestamp_formatter = TimestampFormatter()
        self.level_formatter = LevelFormatter()
        self.gradient_formatter = GradientFormatter()
        self.context = None
        self._apply_theme()
    
    def _apply_theme(self):
        if self.theme is None:
            return
        if hasattr(Theme, self.theme) if isinstance(self.theme, str) else False:
            self.theme = getattr(Theme, self.theme)
    
    def add_handler(self, handler):
        self.handlers.append(handler)
        return self
    
    def remove_handler(self, handler):
        if handler in self.handlers:
            self.handlers.remove(handler)
        return self
    
    def set_level(self, level):
        self.level = level
        self._console_handler.level = level
        return self
    
    def set_context(self, context):
        self.context = context
        return self
    
    def clear_context(self):
        self.context = None
        return self
    
    def _timestamp(self):
        return f"{ConsoleColors.GRAY}[-({datetime.datetime.now().strftime('%H:%M:%S')})-]{ConsoleColors.RESET}"
    
    def _format_message(self, level, message):
        ts = self._timestamp()
        level_fmt = self.level_formatter.format(level)
        ctx = f"[{self.context}] " if self.context else ""
        return f"{ts} {level_fmt}{ConsoleColors.GRAY}»{ConsoleColors.RESET}  {ctx}{message}"
    
    def _log(self, level, message, error=None):
        formatted = self._format_message(level, message)
        record = {
            "timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "level": level,
            "message": message,
            "formatted_message": formatted,
            "error": error,
            "context": self.context,
        }
        print(formatted)
        if error:
            err_type = type(error).__name__
            print(f"            {ConsoleColors.RED}└──> [{err_type}] {error}{ConsoleColors.RESET}")
        for handler in self.handlers:
            try:
                handler.emit(record)
            except Exception:
                pass
    
    def debug(self, message):
        self._log("DEBUG", message)
    
    def info(self, message):
        self._log("INFO", message)
    
    def success(self, message):
        self._log("SUCCESS", message)
    
    def warning(self, message):
        self._log("WARNING", message)
    
    def error(self, message, error=None):
        self._log("ERROR", message, error)
    
    def critical(self, message):
        self._log("CRITICAL", message)
    
    def exception(self, message, exc_info=True):
        self._log("ERROR", message)
        if exc_info:
            tb = traceback.format_exc()
            for line in tb.strip().split("\n"):
                print(f"            {ConsoleColors.RED}{line}{ConsoleColors.RESET}")
    
    def section(self, title, start_rgb=(189, 147, 249), end_rgb=(139, 233, 253)):
        width = shutil.get_terminal_size(fallback=(100, 24)).columns
        h_padding = max(1, int(len(title) * (1/6)))
        box_width = len(title) + (h_padding * 2)
        padded_title = f"{title}".center(box_width)
        padding_left = " " * ((width - box_width) // 2)
        print()
        print(padding_left + self._gradient_bg_fmt(padded_title, start_rgb, end_rgb, bold=True))
        print()
    
    def gradient_print(self, text, start_rgb, end_rgb):
        print(self._gradient_fmt(text, start_rgb, end_rgb))
    
    def _gradient_fmt(self, text, start_rgb, end_rgb):
        r1, g1, b1 = start_rgb
        r2, g2, b2 = end_rgb
        length = len(text)
        if length == 0:
            return ""
        result = ""
        for i, char in enumerate(text):
            if length > 1:
                r = int(r1 + (r2 - r1) * i / (length - 1))
                g = int(g1 + (g2 - g1) * i / (length - 1))
                b = int(b1 + (b2 - b1) * i / (length - 1))
            else:
                r, g, b = r1, g1, b1
            result += f"\033[38;2;{r};{g};{b}m{char}"
        return result + ConsoleColors.RESET
    
    def _gradient_bg_fmt(self, text, start_rgb, end_rgb, bold=False):
        r1, g1, b1 = start_rgb
        r2, g2, b2 = end_rgb
        length = len(text)
        if length == 0:
            return ""
        result = ""
        fg_style = "\033[38;2;0;0;0m"
        if bold:
            fg_style += ConsoleColors.BOLD
        for i, char in enumerate(text):
            if length > 1:
                r = int(r1 + (r2 - r1) * i / (length - 1))
                g = int(g1 + (g2 - g1) * i / (length - 1))
                b = int(b1 + (b2 - b1) * i / (length - 1))
            else:
                r, g, b = r1, g1, b1
            result += f"\033[48;2;{r};{g};{b}m{fg_style}{char}"
        return result + ConsoleColors.RESET
    
    def rule(self, title="", style="-", color=None):
        width = shutil.get_terminal_size(fallback=(100, 24)).columns
        color = color or ConsoleColors.GRAY
        if title:
            padding = (width - len(title) - 2) // 2
            line = f"{style * padding} {title} {style * padding}"
            if len(line) < width:
                line += style
        else:
            line = style * width
        print(f"{color}{line}{ConsoleColors.RESET}")
    
    def blank(self, count=1):
        for _ in range(count):
            print()
    
    def divider(self, char="─", color=None):
        width = shutil.get_terminal_size(fallback=(100, 24)).columns
        color = color or ConsoleColors.GRAY
        print(f"{color}{char * width}{ConsoleColors.RESET}")
