import re
import tkinter as tk
from tkinter import messagebox
import time

from .ai import AIAssist
from .terminal import TerminalSession


class TerminalGUI:
    ai_assist: AIAssist
    terminal: TerminalSession

    def __init__(self, ai_assist):
        self.ai_assist = ai_assist

        self.root = tk.Tk()
        self.root.title("Goosh")
        self.root.geometry("800x600")

        self.last_shift_time = None
        self.shift_threshold = 0.3

        self.text = self.create_terminal_widget()
        self.setup_keybindings()
        self.setup_color_tags()
        self.root.update_idletasks()
        self.root.deiconify()
        self.root.update()

        cols, rows = self.calculate_terminal_size()
        self.terminal = TerminalSession(cols, rows)

        self.root.bind("<Configure>", self.on_resize)
        self.last_resize_time = 0

    def create_terminal_widget(self) -> tk.Text:
        text = tk.Text(
            self.root,
            width=80,
            height=24,
            font=("Monaco", 12),
            bg="#000000",
            fg="#FFFFFF",
            insertwidth=8,
            insertbackground="#808080",
            # blockcursor=True,
            wrap=tk.NONE,
            state=tk.NORMAL,
        )
        text.pack(fill=tk.BOTH, expand=True)
        return text

    def setup_color_tags(self):
        """Set up ANSI color tags"""
        basic_colors = {
            "black": "#000000",
            "red": "#CD0000",
            "green": "#00CD00",
            "yellow": "#CDCD00",
            "blue": "#0000EE",
            "magenta": "#CD00CD",
            "cyan": "#00CDCD",
            "white": "#E5E5E5",
            "brightblack": "#7F7F7F",
            "brightred": "#FF0000",
            "brightgreen": "#00FF00",
            "brightyellow": "#FFFF00",
            "brightblue": "#5C5CFF",
            "brightmagenta": "#FF00FF",
            "brightcyan": "#00FFFF",
            "brightwhite": "#FFFFFF",
        }

        for name, color in basic_colors.items():
            self.text.tag_config(f"fg_{name}", foreground=color)
            self.text.tag_config(f"bg_{name}", background=color)

        self.text.tag_config("default", foreground="#FFFFFF", background="#000000")

    def setup_keybindings(self):
        self.text.bind("<KeyPress-Shift_L>", self.on_shift_press)
        self.text.bind("<KeyPress-Shift_R>", self.on_shift_press)

        self.text.bind("<Command-c>", self.on_copy)
        self.text.bind("<Shift-Control-c>", self.on_copy)

        self.text.bind("<Command-v>", self.on_paste)
        self.text.bind("<Shift-Control-v>", self.on_paste)

        self.text.bind("<Control-c>", self.on_ctrl_c)

        self.text.bind("<Key>", self.on_key)

    def on_shift_press(self, event):
        now = time.time()
        if self.last_shift_time and (now - self.last_shift_time) < self.shift_threshold:
            self.show_ai_dialog()
            self.last_shift_time = None
        else:
            self.last_shift_time = now
        return "break"

    def on_key(self, event):
        if event.keysym in [
            "Shift_L",
            "Shift_R",
            "Control_L",
            "Control_R",
            "Command_L",
            "Command_R",
            "Alt_L",
            "Alt_R",
            "Meta_L",
            "Meta_R",
        ]:
            return

        self.terminal.handle_key_for_buffer(event)

        if event.char:
            self.terminal.send_keys(event.char)
        else:
            self.terminal.send_key(event.keysym)

        self.terminal.invalidate()

        return "break"

    def on_copy(self, event):
        if self.text.tag_ranges("sel"):
            selected = self.text.get("sel.first", "sel.last")
            self.root.clipboard_clear()
            self.root.clipboard_append(selected)
        return "break"

    def on_paste(self, event):
        clipboard = self.root.clipboard_get()
        if clipboard:
            self.terminal.send_keys(clipboard)
        return "break"

    def on_ctrl_c(self, event):
        self.terminal.send_keys("\x03")
        return "break"

    def show_ai_dialog(self):
        dialog = tk.Toplevel(self.root)
        dialog.title("AI Assist")
        dialog.geometry("500x200")
        dialog.transient(self.root)
        dialog.grab_set()

        current_cmd = self.terminal.get_current_command()
        if current_cmd:
            tk.Label(
                dialog,
                text=f"Current: {current_cmd.replace('{{CURSOR}}', '|')}",
                font=("Monaco", 10),
                fg="#666666",
            ).pack(pady=(10, 5))

        tk.Label(dialog, text="What would you like?").pack(pady=(5, 5))

        entry = tk.Text(dialog, height=4, width=60, font=("Monaco", 11))
        entry.pack(padx=10, pady=10)
        entry.focus_set()

        button_frame = tk.Frame(dialog)
        button_frame.pack(pady=5)

        def on_submit():
            user_request = entry.get("1.0", tk.END).strip()
            dialog.destroy()
            if user_request:
                self.handle_ai_request(user_request)

        def on_cancel():
            dialog.destroy()

        tk.Button(button_frame, text="Go", command=on_submit, width=10).pack(
            side=tk.LEFT, padx=5
        )
        tk.Button(button_frame, text="Cancel", command=on_cancel, width=10).pack(
            side=tk.LEFT, padx=5
        )

        entry.bind("<Return>", lambda e: on_submit() if not (e.state & 0x1) else None)
        entry.bind("<Escape>", lambda e: on_cancel())

    def handle_ai_request(self, user_request: str):
        """Send request to AI and type response"""
        self.root.config(cursor="watch")
        self.root.update()

        screen = self.terminal.get_screen_text()
        current_cmd = self.terminal.get_current_command()
        current_process = self.terminal.get_foreground_process()

        command = self.ai_assist.get_command(
            screen, current_cmd, user_request, current_process
        )

        if command:
            self.type_command(command)

        self.root.config(cursor="")

    def type_command(self, command: str):
        if self.terminal.command_buffer:
            self.terminal.clear_current_command()

        pattern = r"(\{[^}]*\})"
        parts = re.split(pattern, command)

        for part in parts:
            if not part:
                continue

            if part.startswith("{") and part.endswith("}"):
                key = part[1:-1]
                if key == "{":
                    self.terminal.send_keys("{")
                else:
                    self.terminal.send_key(key)
            else:
                self.terminal.send_keys(part)

        self.terminal.invalidate()

    def render_terminal(self):
        screen_data = self.terminal.get_screen_with_colors()

        self.text.config(state=tk.NORMAL)
        self.text.delete("1.0", tk.END)

        for row, line in enumerate(screen_data, start=1):
            for col, char_info in enumerate(line):
                char = char_info["char"]
                char_pos = f"{row}.{col}"

                self.text.insert(char_pos, char)

                for tag in self.get_color_tag(char_info):
                    self.text.tag_add(tag, char_pos)

            self.text.insert(tk.END, "\n")

        cursor_line = self.terminal.screen.cursor.y + 1
        cursor_col = self.terminal.screen.cursor.x
        self.text.mark_set("insert", f"{cursor_line}.{cursor_col}")
        self.text.see(tk.END)
        self.text.focus_set()

    def get_color_tag(self, char_info):
        fg = char_info["fg"]
        bg = char_info["bg"]
        bold = char_info.get("bold", False)

        tags = []

        for color, prefix, attr in [(fg, "fg", "foreground"), (bg, "bg", "background")]:
            if color != "default":
                if isinstance(color, str):
                    tags.append(f"{prefix}_{color}")
                elif isinstance(color, int):
                    tag_name = f"{prefix}_256_{color}"
                    if tag_name not in self.text.tag_names():
                        rgb = self._map_256_color(color)
                        self.text.tag_config(tag_name, **{attr: rgb})
                    tags.append(tag_name)

        if bold:
            if "bold" not in self.text.tag_names():
                self.text.tag_config("bold", font=("Monaco", 12, "bold"))
            tags.append("bold")

        return tags

    def _map_256_color(self, color_num: int) -> str:
        """Map 256-color palette to RGB hex"""
        if color_num < 16:
            basic = [
                "#000000",
                "#CD0000",
                "#00CD00",
                "#CDCD00",
                "#0000EE",
                "#CD00CD",
                "#00CDCD",
                "#E5E5E5",
                "#7F7F7F",
                "#FF0000",
                "#00FF00",
                "#FFFF00",
                "#5C5CFF",
                "#FF00FF",
                "#00FFFF",
                "#FFFFFF",
            ]
            return basic[color_num]

        elif color_num < 232:
            color_num -= 16
            r = (color_num // 36) * 51
            g = ((color_num % 36) // 6) * 51
            b = (color_num % 6) * 51
            return f"#{r:02x}{g:02x}{b:02x}"
        else:
            gray = 8 + (color_num - 232) * 10
            return f"#{gray:02x}{gray:02x}{gray:02x}"

    def update(self):
        self.root.update()

    def is_running(self) -> bool:
        return self.root.winfo_exists()

    def calculate_terminal_size(self):
        width = self.text.winfo_width()
        height = self.text.winfo_height()

        char_width = 8
        char_height = 17

        cols = max(20, width // char_width)
        rows = max(5, height // char_height)

        return cols, rows

    def on_resize(self, event):
        now = time.time()

        if now - self.last_resize_time < 0.1:
            return

        self.last_resize_time = now

        cols, rows = self.calculate_terminal_size()
        self.terminal.resize(cols, rows)
