import sys
import time
import shutil
import threading
from .colors import ConsoleColors


class ProgressBar:
    STYLES = {
        "default": ("█", "░"),
        "blocks": ("▓", "░"),
        "arrows": ("▶", "─"),
        "dots": ("●", "○"),
        "lines": ("━", "─"),
        "squares": ("■", "□"),
    }
    
    def __init__(self, total, description="", width=40, style="default", 
                 color_done=None, color_remaining=None, show_percentage=True,
                 show_count=True, show_eta=True):
        self.total = total
        self.description = description
        self.width = width
        self.current = 0
        self.start_time = None
        self.show_percentage = show_percentage
        self.show_count = show_count
        self.show_eta = show_eta
        self.color_done = color_done or ConsoleColors.CYAN
        self.color_remaining = color_remaining or ConsoleColors.GRAY
        if style in self.STYLES:
            self.char_done, self.char_remaining = self.STYLES[style]
        else:
            self.char_done, self.char_remaining = self.STYLES["default"]
    
    def __enter__(self):
        self.start_time = time.time()
        self._draw()
        return self
    
    def __exit__(self, *args):
        self._draw()
        print()
    
    def update(self, amount=1):
        self.current = min(self.current + amount, self.total)
        self._draw()
    
    def set(self, value):
        self.current = min(value, self.total)
        self._draw()
    
    def _format_time(self, seconds):
        if seconds < 60:
            return f"{seconds:.0f}s"
        elif seconds < 3600:
            return f"{seconds/60:.0f}m"
        else:
            return f"{seconds/3600:.1f}h"
    
    def _draw(self):
        if self.total <= 0:
            return
        
        progress = self.current / self.total
        filled = int(self.width * progress)
        empty = self.width - filled
        
        bar = f"{self.color_done}{self.char_done * filled}{ConsoleColors.RESET}"
        bar += f"{self.color_remaining}{self.char_remaining * empty}{ConsoleColors.RESET}"
        
        parts = []
        if self.description:
            parts.append(self.description)
        parts.append(f"[{bar}]")
        
        if self.show_percentage:
            parts.append(f"{progress * 100:.1f}%")
        
        if self.show_count:
            parts.append(f"{self.current}/{self.total}")
        
        if self.show_eta and self.start_time and self.current > 0:
            elapsed = time.time() - self.start_time
            eta = (elapsed / self.current) * (self.total - self.current)
            parts.append(f"ETA: {self._format_time(eta)}")
        
        line = " ".join(parts)
        sys.stdout.write(f"\r{line}")
        sys.stdout.flush()


class Spinner:
    FRAMES = {
        "dots": ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
        "line": ["-", "\\", "|", "/"],
        "dots2": ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"],
        "arc": ["◜", "◠", "◝", "◞", "◡", "◟"],
        "bounce": ["⠁", "⠂", "⠄", "⠂"],
        "circle": ["◐", "◓", "◑", "◒"],
        "square": ["◰", "◳", "◲", "◱"],
        "star": ["✶", "✸", "✹", "✺", "✹", "✷"],
        "arrows": ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"],
        "clock": ["🕐", "🕑", "🕒", "🕓", "🕔", "🕕", "🕖", "🕗", "🕘", "🕙", "🕚", "🕛"],
    }
    
    def __init__(self, message="Loading...", style="dots", color=None, interval=0.1):
        self.message = message
        self.color = color or ConsoleColors.CYAN
        self.interval = interval
        self.frames = self.FRAMES.get(style, self.FRAMES["dots"])
        self._stop_event = threading.Event()
        self._thread = None
    
    def __enter__(self):
        self.start()
        return self
    
    def __exit__(self, *args):
        self.stop()
    
    def start(self):
        self._stop_event.clear()
        self._thread = threading.Thread(target=self._spin, daemon=True)
        self._thread.start()
    
    def stop(self, final_message=None):
        self._stop_event.set()
        if self._thread:
            self._thread.join()
        sys.stdout.write("\r" + " " * (len(self.message) + 10) + "\r")
        if final_message:
            print(final_message)
        sys.stdout.flush()
    
    def _spin(self):
        idx = 0
        while not self._stop_event.is_set():
            frame = self.frames[idx % len(self.frames)]
            sys.stdout.write(f"\r{self.color}{frame}{ConsoleColors.RESET} {self.message}")
            sys.stdout.flush()
            idx += 1
            time.sleep(self.interval)


class Table:
    def __init__(self, headers, style="default", header_color=None, row_color=None,
                 border_color=None, padding=1):
        self.headers = headers
        self.rows = []
        self.style = style
        self.header_color = header_color or ConsoleColors.CYAN
        self.row_color = row_color or ConsoleColors.WHITE
        self.border_color = border_color or ConsoleColors.GRAY
        self.padding = padding
        self._set_style()
    
    def _set_style(self):
        styles = {
            "default": {"h": "─", "v": "│", "tl": "┌", "tr": "┐", "bl": "└", "br": "┘", 
                       "ml": "├", "mr": "┤", "mt": "┬", "mb": "┴", "mm": "┼"},
            "rounded": {"h": "─", "v": "│", "tl": "╭", "tr": "╮", "bl": "╰", "br": "╯",
                       "ml": "├", "mr": "┤", "mt": "┬", "mb": "┴", "mm": "┼"},
            "double": {"h": "═", "v": "║", "tl": "╔", "tr": "╗", "bl": "╚", "br": "╝",
                      "ml": "╠", "mr": "╣", "mt": "╦", "mb": "╩", "mm": "╬"},
            "simple": {"h": "-", "v": "|", "tl": "+", "tr": "+", "bl": "+", "br": "+",
                      "ml": "+", "mr": "+", "mt": "+", "mb": "+", "mm": "+"},
        }
        self.chars = styles.get(self.style, styles["default"])
    
    def add_row(self, row):
        self.rows.append(row)
        return self
    
    def add_rows(self, rows):
        self.rows.extend(rows)
        return self
    
    def _get_col_widths(self):
        widths = [len(str(h)) for h in self.headers]
        for row in self.rows:
            for i, cell in enumerate(row):
                if i < len(widths):
                    widths[i] = max(widths[i], len(str(cell)))
        return [w + self.padding * 2 for w in widths]
    
    def _make_line(self, left, mid, right, widths):
        parts = [self.chars["h"] * w for w in widths]
        return f"{left}{mid.join(parts)}{right}"
    
    def _make_row(self, cells, widths, color):
        parts = []
        for i, cell in enumerate(cells):
            w = widths[i] - self.padding * 2
            content = str(cell).center(w)
            parts.append(" " * self.padding + content + " " * self.padding)
        v = self.chars["v"]
        return f"{self.border_color}{v}{ConsoleColors.RESET}{color}" + \
               f"{ConsoleColors.RESET}{self.border_color}{v}{ConsoleColors.RESET}{color}".join(parts) + \
               f"{ConsoleColors.RESET}{self.border_color}{v}{ConsoleColors.RESET}"
    
    def print(self):
        widths = self._get_col_widths()
        bc = self.border_color
        
        print(f"{bc}{self._make_line(self.chars['tl'], self.chars['mt'], self.chars['tr'], widths)}{ConsoleColors.RESET}")
        print(self._make_row(self.headers, widths, self.header_color))
        print(f"{bc}{self._make_line(self.chars['ml'], self.chars['mm'], self.chars['mr'], widths)}{ConsoleColors.RESET}")
        
        for row in self.rows:
            extended_row = list(row) + [""] * (len(self.headers) - len(row))
            print(self._make_row(extended_row, widths, self.row_color))
        
        print(f"{bc}{self._make_line(self.chars['bl'], self.chars['mb'], self.chars['br'], widths)}{ConsoleColors.RESET}")
    
    def to_string(self):
        import io
        old_stdout = sys.stdout
        sys.stdout = io.StringIO()
        self.print()
        result = sys.stdout.getvalue()
        sys.stdout = old_stdout
        return result


class Timer:
    def __init__(self, name="Timer", color=None, logger=None):
        self.name = name
        self.color = color or ConsoleColors.PURPLE
        self.logger = logger
        self.start_time = None
        self.end_time = None
    
    def __enter__(self):
        self.start_time = time.perf_counter()
        return self
    
    def __exit__(self, *args):
        self.end_time = time.perf_counter()
        elapsed = self.end_time - self.start_time
        msg = f"{self.color}⏱ {self.name}: {self._format_time(elapsed)}{ConsoleColors.RESET}"
        if self.logger:
            self.logger.info(msg)
        else:
            print(msg)
    
    def _format_time(self, seconds):
        if seconds < 0.001:
            return f"{seconds * 1000000:.2f}μs"
        elif seconds < 1:
            return f"{seconds * 1000:.2f}ms"
        elif seconds < 60:
            return f"{seconds:.3f}s"
        else:
            mins = int(seconds // 60)
            secs = seconds % 60
            return f"{mins}m {secs:.2f}s"
    
    @property
    def elapsed(self):
        if self.start_time is None:
            return 0
        end = self.end_time or time.perf_counter()
        return end - self.start_time


class Panel:
    def __init__(self, content, title="", style="default", color=None, padding=1, width=None):
        self.content = content
        self.title = title
        self.style = style
        self.color = color or ConsoleColors.CYAN
        self.padding = padding
        self.width = width
        self._set_style()
    
    def _set_style(self):
        styles = {
            "default": {"h": "─", "v": "│", "tl": "┌", "tr": "┐", "bl": "└", "br": "┘"},
            "rounded": {"h": "─", "v": "│", "tl": "╭", "tr": "╮", "bl": "╰", "br": "╯"},
            "double": {"h": "═", "v": "║", "tl": "╔", "tr": "╗", "bl": "╚", "br": "╝"},
            "heavy": {"h": "━", "v": "┃", "tl": "┏", "tr": "┓", "bl": "┗", "br": "┛"},
        }
        self.chars = styles.get(self.style, styles["default"])
    
    def print(self):
        lines = self.content.split("\n") if isinstance(self.content, str) else self.content
        max_len = max(len(line) for line in lines) if lines else 0
        max_len = max(max_len, len(self.title))
        width = self.width or (max_len + self.padding * 2 + 2)
        inner_width = width - 2
        
        c = self.color
        ch = self.chars
        
        if self.title:
            title_display = f" {self.title} "
            padding_left = (inner_width - len(title_display)) // 2
            padding_right = inner_width - len(title_display) - padding_left
            top = f"{c}{ch['tl']}{ch['h'] * padding_left}{title_display}{ch['h'] * padding_right}{ch['tr']}{ConsoleColors.RESET}"
        else:
            top = f"{c}{ch['tl']}{ch['h'] * inner_width}{ch['tr']}{ConsoleColors.RESET}"
        
        print(top)
        
        for _ in range(self.padding):
            print(f"{c}{ch['v']}{' ' * inner_width}{ch['v']}{ConsoleColors.RESET}")
        
        for line in lines:
            text_padding = inner_width - len(line) - self.padding * 2
            left_pad = self.padding
            right_pad = text_padding + self.padding
            print(f"{c}{ch['v']}{ConsoleColors.RESET}{' ' * left_pad}{line}{' ' * right_pad}{c}{ch['v']}{ConsoleColors.RESET}")
        
        for _ in range(self.padding):
            print(f"{c}{ch['v']}{' ' * inner_width}{ch['v']}{ConsoleColors.RESET}")
        
        bottom = f"{c}{ch['bl']}{ch['h'] * inner_width}{ch['br']}{ConsoleColors.RESET}"
        print(bottom)
