import sys
import threading
import tkinter as tk
from dataclasses import dataclass, field
from tkinter import ttk
from typing import Callable


@dataclass
class ToolUpdateState:
    name: str
    status: str = "pending"  # pending, installing, done, failed


@dataclass
class UpdateDialogState:
    tools: list[ToolUpdateState] = field(default_factory=list)
    cancelled: bool = False
    completed: bool = False


class UpdateDialog:
    """Tkinter dialog showing update progress for multiple tools."""

    def __init__(
        self,
        tools: list[str],
        install_func: Callable[[str], bool],
    ) -> None:
        self.install_func = install_func
        self.state = UpdateDialogState(
            tools=[ToolUpdateState(name=name) for name in tools]
        )

        self.root = tk.Tk()
        self.root.title("Updating Tools")
        self.root.resizable(False, False)

        # Center window on screen
        window_width = 300
        window_height = 60 + len(tools) * 28 + 50
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        x = (screen_width - window_width) // 2
        y = (screen_height - window_height) // 2
        self.root.geometry(f"{window_width}x{window_height}+{x}+{y}")

        # Main frame
        frame = ttk.Frame(self.root, padding=20)
        frame.pack(fill=tk.BOTH, expand=True)

        # Tool list frame
        self.tool_labels: dict[str, ttk.Label] = {}
        for tool_state in self.state.tools:
            label = ttk.Label(frame, text=self._format_tool_line(tool_state))
            label.pack(anchor=tk.W, pady=2)
            self.tool_labels[tool_state.name] = label

        # Button frame
        btn_frame = ttk.Frame(frame)
        btn_frame.pack(fill=tk.X, pady=(15, 0))

        self.action_btn = ttk.Button(btn_frame, text="Cancel", command=self._on_cancel)
        self.action_btn.pack(side=tk.RIGHT)

        # Handle window close
        self.root.protocol("WM_DELETE_WINDOW", self._on_cancel)

    def _format_tool_line(self, tool_state: ToolUpdateState) -> str:
        """Format a tool line with status indicator."""
        indicators = {
            "pending": "   ",
            "installing": " \u22ef ",  # ⋯
            "done": " \u2713 ",  # ✓
            "failed": " \u2717 ",  # ✗
        }
        indicator = indicators.get(tool_state.status, "   ")
        suffix = " (installing...)" if tool_state.status == "installing" else ""
        return f"{indicator}{tool_state.name}{suffix}"

    def _update_tool_display(self, tool_name: str) -> None:
        """Update the display for a specific tool."""
        for tool_state in self.state.tools:
            if tool_state.name == tool_name:
                label = self.tool_labels.get(tool_name)
                if label:
                    label.config(text=self._format_tool_line(tool_state))
                break

    def _on_complete(self) -> None:
        """Called when all updates are done."""
        self.state.completed = True
        self.action_btn.config(text="Close", command=self._on_close)

    def _on_cancel(self) -> None:
        """Handle cancel button or window close during updates."""
        self.state.cancelled = True
        self.root.destroy()

    def _on_close(self) -> None:
        """Handle close button after updates complete."""
        self.root.destroy()

    def _run_updates(self) -> None:
        """Run updates in background thread."""
        for tool_state in self.state.tools:
            if self.state.cancelled:
                break

            # Mark as installing
            tool_state.status = "installing"
            self.root.after(0, lambda n=tool_state.name: self._update_tool_display(n))

            # Run install
            success = self.install_func(tool_state.name)

            # Update status
            tool_state.status = "done" if success else "failed"
            self.root.after(0, lambda n=tool_state.name: self._update_tool_display(n))

        # Signal completion
        self.root.after(0, self._on_complete)

    def run(self) -> None:
        """Show dialog and run updates."""
        # Start updates in background
        thread = threading.Thread(target=self._run_updates, daemon=True)
        thread.start()

        self.root.mainloop()


def show_update_dialog(
    tools: list[str],
    install_func: Callable[[str], bool],
) -> None:
    """Show update dialog and run updates. Blocks until closed."""
    dialog = UpdateDialog(tools, install_func)
    dialog.run()


def main() -> None:
    """CLI entry point for subprocess invocation."""
    from tool_tray.config import load_config
    from tool_tray.manifest import Manifest, fetch_manifest
    from tool_tray.updater import install_tool

    tools = sys.argv[1:]
    if not tools:
        print("Usage: python -m tool_tray.update_dialog tool1 tool2 ...")
        sys.exit(1)

    config = load_config()
    if not config:
        print("No config found")
        sys.exit(1)

    token = config.get("token", "")
    repos = config.get("repos", [])

    # Build name -> (repo, manifest) mapping
    tool_info: dict[str, tuple[str, Manifest]] = {}
    for repo in repos:
        manifest = fetch_manifest(repo, token)
        if manifest:
            tool_info[manifest.name] = (repo, manifest)

    def install_by_name(name: str) -> bool:
        info = tool_info.get(name)
        if not info:
            return False
        repo, manifest = info
        return install_tool(repo, manifest, token)

    # Filter to valid tools
    valid_tools = [t for t in tools if t in tool_info]
    if not valid_tools:
        print("No valid tools to update")
        sys.exit(1)

    show_update_dialog(valid_tools, install_by_name)


if __name__ == "__main__":
    main()
