"""SSH MCP Server GUI 配置面板 - 现代化深色主题"""
import json
import os
import sys
import threading
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from typing import Optional

from .config import ConfigManager, ServerConfig
from .ssh_manager import SSHManager

# ─── 配色方案 (Catppuccin Mocha 风格) ──────────────────────
C = {
    "bg":         "#1e1e2e",
    "surface0":   "#313244",
    "surface1":   "#45475a",
    "surface2":   "#585b70",
    "overlay0":   "#6c7086",
    "text":       "#cdd6f4",
    "subtext":    "#a6adc8",
    "blue":       "#89b4fa",
    "green":      "#a6e3a1",
    "red":        "#f38ba8",
    "yellow":     "#f9e2af",
    "mauve":      "#cba6f7",
    "teal":       "#94e2d5",
    "mantle":     "#181825",
    "crust":      "#11111b",
}

FONT       = ("Microsoft YaHei UI", 9)
FONT_BOLD  = ("Microsoft YaHei UI", 9, "bold")
FONT_TITLE = ("Microsoft YaHei UI", 14, "bold")
FONT_MONO  = ("Consolas", 10)
FONT_SM    = ("Microsoft YaHei UI", 8)


def _btn(parent, text, command, bg_color=None, fg_color=None):
    """创建深色主题按钮（使用原生 tk.Button，最可靠）"""
    bg = bg_color or C["blue"]
    fg = fg_color or C["crust"]
    # 计算 hover 色（稍亮）
    r, g, b = int(bg[1:3], 16), int(bg[3:5], 16), int(bg[5:7], 16)
    hover = f"#{min(255,r+25):02x}{min(255,g+25):02x}{min(255,b+25):02x}"

    btn = tk.Button(
        parent, text=text, command=command,
        bg=bg, fg=fg, activebackground=hover, activeforeground=fg,
        relief=tk.FLAT, cursor="hand2", bd=0, highlightthickness=0,
        font=FONT_BOLD, padx=12, pady=4,
    )
    btn.bind("<Enter>", lambda e: btn.config(bg=hover))
    btn.bind("<Leave>", lambda e: btn.config(bg=bg))
    return btn


def _entry(parent, var, w=30, show=None):
    """创建深色输入框"""
    e = tk.Entry(parent, textvariable=var, width=w, bg=C["surface0"], fg=C["text"],
                 insertbackground=C["blue"], relief=tk.FLAT, font=FONT_MONO,
                 highlightbackground=C["surface1"], highlightcolor=C["blue"],
                 highlightthickness=1, bd=5)
    if show:
        e.config(show=show)
    return e


def _label(parent, text, font=FONT, fg=None):
    return tk.Label(parent, text=text, bg=parent["bg"], fg=fg or C["text"], font=font)


class ServerDialog(tk.Toplevel):
    """添加/编辑服务器 - 深色卡片式对话框"""

    def __init__(self, parent, title="添加服务器", server: Optional[ServerConfig] = None):
        super().__init__(parent)
        self.title(title)
        self.resizable(False, False)
        self.result: Optional[dict] = None
        self.configure(bg=C["bg"])
        self.grab_set()
        self.transient(parent)

        # ── Header ──
        hdr = tk.Frame(self, bg=C["bg"])
        hdr.pack(fill=tk.X, padx=24, pady=(20, 0))
        _label(hdr, title, FONT_TITLE, C["text"]).pack(anchor=tk.W)
        _label(hdr, "SSH 连接配置", FONT_SM, C["overlay0"]).pack(anchor=tk.W, pady=(2, 0))

        # ── Card ──
        card = tk.Frame(self, bg=C["surface0"])
        card.pack(fill=tk.BOTH, padx=20, pady=16)
        inner = tk.Frame(card, bg=C["surface0"])
        inner.pack(padx=20, pady=20)

        r = 0

        # -- 基本信息区 --
        _label(inner, "连接信息", FONT_SM, C["blue"]).grid(
            row=r, column=0, columnspan=3, sticky=tk.W, pady=(0, 8)); r += 1

        for label_text, attr, default in [
            ("名称",   "name",     ""),
            ("主机",   "host",     ""),
            ("端口",   "port",     "22"),
            ("用户名", "username", ""),
        ]:
            _label(inner, label_text, FONT, C["subtext"]).grid(row=r, column=0, sticky=tk.W, pady=4, padx=(0, 16))
            var = tk.StringVar(value=str(getattr(server, attr, default)) if server else default)
            setattr(self, f"{attr}_var", var)
            w = 10 if attr == "port" else 32
            _entry(inner, var, w=w).grid(row=r, column=1, columnspan=2, sticky=tk.W, pady=4)
            r += 1

        # -- 分隔线 --
        tk.Frame(inner, bg=C["surface1"], height=1).grid(
            row=r, column=0, columnspan=3, sticky=tk.EW, pady=14); r += 1

        # -- 认证区 --
        _label(inner, "认证方式", FONT_SM, C["blue"]).grid(
            row=r, column=0, columnspan=3, sticky=tk.W, pady=(0, 8)); r += 1

        self.auth_type_var = tk.StringVar(value=server.auth_type if server else "password")
        af = tk.Frame(inner, bg=C["surface0"])
        af.grid(row=r, column=0, columnspan=3, sticky=tk.W, pady=4); r += 1

        for txt, val in [("密码认证", "password"), ("密钥认证", "key")]:
            tk.Radiobutton(
                af, text=txt, variable=self.auth_type_var, value=val,
                command=self._on_auth_change, bg=C["surface0"], fg=C["text"],
                selectcolor=C["surface1"], activebackground=C["surface0"],
                activeforeground=C["text"], font=FONT, cursor="hand2",
                indicatoron=True,
            ).pack(side=tk.LEFT, padx=(0, 18))

        # Password
        _label(inner, "密码", FONT, C["subtext"]).grid(row=r, column=0, sticky=tk.W, pady=4, padx=(0, 16))
        self.password_var = tk.StringVar(value=server.password if server else "")
        self.pw_frame = tk.Frame(inner, bg=C["surface0"])
        self.pw_frame.grid(row=r, column=1, columnspan=2, sticky=tk.W, pady=4); r += 1
        self.password_entry = _entry(self.pw_frame, self.password_var, w=26, show="*")
        self.password_entry.pack(side=tk.LEFT)
        self._pw_show = False
        self.pw_toggle = tk.Label(self.pw_frame, text="[显示]", bg=C["surface0"],
                                   fg=C["blue"], font=FONT_SM, cursor="hand2")
        self.pw_toggle.pack(side=tk.LEFT, padx=(6, 0))
        self.pw_toggle.bind("<Button-1>", self._toggle_pw)

        # Key File
        _label(inner, "密钥文件", FONT, C["subtext"]).grid(row=r, column=0, sticky=tk.W, pady=4, padx=(0, 16))
        self.key_path_var = tk.StringVar(value=server.key_path if server else "")
        kf = tk.Frame(inner, bg=C["surface0"])
        kf.grid(row=r, column=1, columnspan=2, sticky=tk.W, pady=4); r += 1
        self.key_entry = _entry(kf, self.key_path_var, w=22)
        self.key_entry.pack(side=tk.LEFT)
        self.key_btn = tk.Button(kf, text="...", command=self._browse_key,
                                  bg=C["surface1"], fg=C["text"], relief=tk.FLAT,
                                  font=FONT_BOLD, width=3, cursor="hand2")
        self.key_btn.pack(side=tk.LEFT, padx=(4, 0))

        # Key Passphrase
        _label(inner, "密钥密码", FONT, C["subtext"]).grid(row=r, column=0, sticky=tk.W, pady=4, padx=(0, 16))
        self.key_passphrase_var = tk.StringVar(value=server.key_passphrase if server else "")
        self.kp_entry = _entry(inner, self.key_passphrase_var, w=32, show="*")
        self.kp_entry.grid(row=r, column=1, columnspan=2, sticky=tk.W, pady=4); r += 1

        self._on_auth_change()

        # ── 底部按钮 ──
        btn_bar = tk.Frame(self, bg=C["bg"])
        btn_bar.pack(fill=tk.X, padx=20, pady=(0, 20))

        _btn(btn_bar, "测试连接", self._test_connection, C["teal"]).pack(side=tk.LEFT, padx=(0, 8))
        _btn(btn_bar, "保存", self._save, C["green"]).pack(side=tk.LEFT, padx=(0, 8))
        _btn(btn_bar, "取消", self.destroy, C["surface2"], C["text"]).pack(side=tk.LEFT)

        # 居中
        self.update_idletasks()
        pw, ph = parent.winfo_width(), parent.winfo_height()
        px, py = parent.winfo_x(), parent.winfo_y()
        w, h = self.winfo_width(), self.winfo_height()
        self.geometry(f"+{px + (pw - w) // 2}+{py + (ph - h) // 2}")

    def _toggle_pw(self, _event=None):
        self._pw_show = not self._pw_show
        self.password_entry.config(show="" if self._pw_show else "*")
        self.pw_toggle.config(text="[隐藏]" if self._pw_show else "[显示]")

    def _on_auth_change(self):
        is_pw = self.auth_type_var.get() == "password"
        st_pw  = "normal"   if is_pw else "disabled"
        st_key = "disabled" if is_pw else "normal"
        self.password_entry.config(state=st_pw)
        self.key_entry.config(state=st_key)
        self.key_btn.config(state=st_key)
        self.kp_entry.config(state=st_key)

    def _browse_key(self):
        path = filedialog.askopenfilename(
            title="选择密钥文件",
            initialdir=os.path.expanduser("~/.ssh"),
            filetypes=[("所有文件", "*.*"), ("PEM 文件", "*.pem")],
        )
        if path:
            self.key_path_var.set(path)

    def _test_connection(self):
        data = self._collect()
        if not data:
            return

        import paramiko
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        connect_kw = {
            "hostname": data["host"],
            "port": data["port"],
            "username": data["username"],
            "timeout": 10,
        }
        if data["auth_type"] == "password":
            connect_kw["password"] = data["password"]
        else:
            key_path = os.path.expanduser(data["key_path"])
            connect_kw["key_filename"] = key_path
            if data["key_passphrase"]:
                connect_kw["passphrase"] = data["key_passphrase"]

        try:
            client.connect(**connect_kw)
            messagebox.showinfo("测试成功",
                f"已成功连接到 {data['host']}:{data['port']}")
            client.close()
        except Exception as e:
            messagebox.showerror("连接失败", f"{type(e).__name__}: {e}")

    def _collect(self) -> Optional[dict]:
        name     = self.name_var.get().strip()
        host     = self.host_var.get().strip()
        username = self.username_var.get().strip()
        if not name or not host or not username:
            messagebox.showwarning("验证失败", "名称、主机和用户名为必填项")
            return None
        try:
            port = int(self.port_var.get().strip())
        except ValueError:
            messagebox.showwarning("验证失败", "端口必须为数字")
            return None
        at = self.auth_type_var.get()
        return {
            "name": name, "host": host, "port": port, "username": username,
            "auth_type": at,
            "password":       self.password_var.get()           if at == "password" else "",
            "key_path":       self.key_path_var.get().strip()   if at == "key"      else "",
            "key_passphrase": self.key_passphrase_var.get()     if at == "key"      else "",
        }

    def _save(self):
        data = self._collect()
        if data:
            self.result = data
            self.destroy()


class MainWindow:
    """主窗口 - 现代化深色主题"""

    def __init__(self, config_manager: ConfigManager, ssh_manager: SSHManager,
                 on_close_callback=None):
        self.config_manager = config_manager
        self.ssh_manager = ssh_manager
        self.on_close_callback = on_close_callback

        self.root = tk.Tk()
        self.root.title("SSH MCP Server")
        self.root.geometry("780x520")
        self.root.minsize(660, 420)
        self.root.configure(bg=C["bg"])

        self._setup_styles()
        self._build_ui()
        self._refresh_server_list()
        self._build_context_menu()

        self.root.protocol("WM_DELETE_WINDOW", self._on_close)

    # ── Styles ──

    def _setup_styles(self):
        s = ttk.Style()
        s.theme_use("clam")

        s.configure("Treeview",
                     background=C["surface0"], foreground=C["text"], fieldbackground=C["surface0"],
                     borderwidth=0, font=FONT, rowheight=30)
        s.configure("Treeview.Heading",
                     background=C["surface1"], foreground=C["subtext"],
                     borderwidth=0, font=FONT_BOLD, relief=tk.FLAT)
        s.map("Treeview",
              background=[("selected", "#3d5a99")],
              foreground=[("selected", "#ffffff")])
        s.map("Treeview.Heading",
              background=[("active", C["surface2"])])

        s.configure("Vertical.TScrollbar",
                     background=C["surface1"], troughcolor=C["surface0"],
                     borderwidth=0, arrowsize=0)

    # ── UI Build ──

    def _build_ui(self):
        # ── Title bar ──
        title_bar = tk.Frame(self.root, bg=C["mantle"])
        title_bar.pack(fill=tk.X)

        title_inner = tk.Frame(title_bar, bg=C["mantle"])
        title_inner.pack(fill=tk.X, padx=20, pady=14)

        _label(title_inner, "SSH MCP 服务器", FONT_TITLE, C["text"]).pack(side=tk.LEFT)

        # 状态指示灯
        status_f = tk.Frame(title_inner, bg=C["mantle"])
        status_f.pack(side=tk.RIGHT)

        self.mcp_dot = tk.Canvas(status_f, width=10, height=10, bg=C["mantle"], highlightthickness=0)
        self.mcp_dot.pack(side=tk.LEFT, padx=(0, 4))
        self.mcp_dot.create_oval(1, 1, 9, 9, fill=C["overlay0"], outline="")
        _label(status_f, "MCP", FONT_SM, C["overlay0"]).pack(side=tk.LEFT, padx=(0, 16))

        self.ssh_dot = tk.Canvas(status_f, width=10, height=10, bg=C["mantle"], highlightthickness=0)
        self.ssh_dot.pack(side=tk.LEFT, padx=(0, 4))
        self.ssh_dot.create_oval(1, 1, 9, 9, fill=C["overlay0"], outline="")
        self.ssh_status_text = _label(status_f, "未连接", FONT_SM, C["overlay0"])
        self.ssh_status_text.pack(side=tk.LEFT)

        # 当前选中服务器显示
        active_f = tk.Frame(title_inner, bg=C["mantle"])
        active_f.pack(side=tk.RIGHT, padx=(0, 20))
        _label(active_f, "当前选中:", FONT_SM, C["overlay0"]).pack(side=tk.LEFT)
        self.active_label = _label(active_f, "无", FONT_SM, C["yellow"])
        self.active_label.pack(side=tk.LEFT, padx=(4, 0))

        # ── Toolbar ──
        tb = tk.Frame(self.root, bg=C["bg"])
        tb.pack(fill=tk.X, padx=16, pady=(14, 8))

        _btn(tb, "+ 添加",  self._add_server,    C["blue"]).pack(side=tk.LEFT, padx=(0, 6))
        _btn(tb, "编辑",    self._edit_server,   C["mauve"]).pack(side=tk.LEFT, padx=(0, 6))
        _btn(tb, "删除",    self._delete_server, C["red"]).pack(side=tk.LEFT, padx=(0, 6))
        _btn(tb, "✔ 选中",  self._set_active,    C["green"]).pack(side=tk.LEFT, padx=(0, 6))

        # 右侧
        _btn(tb, "刷新", self._refresh_server_list, C["surface2"], C["text"]).pack(side=tk.RIGHT)
        _btn(tb, "安装到 IDE ▾", self._show_install_menu, C["yellow"]).pack(side=tk.RIGHT, padx=(0, 6))
        _btn(tb, "测试连接", self._test_selected, C["teal"]).pack(side=tk.RIGHT, padx=(0, 6))

        # ── Server list ──
        list_wrap = tk.Frame(self.root, bg=C["surface0"])
        list_wrap.pack(fill=tk.BOTH, expand=True, padx=16, pady=(0, 6))

        columns = ("name", "host", "port", "username", "auth")
        self.tree = ttk.Treeview(list_wrap, columns=columns, show="headings", selectmode="browse")

        for col, txt, w in [
            ("name", "名称", 140), ("host", "主机", 200), ("port", "端口", 60),
            ("username", "用户名", 110), ("auth", "认证", 90),
        ]:
            self.tree.heading(col, text=txt, anchor=tk.W)
            self.tree.column(col, width=w, anchor=tk.W)

        vsb = ttk.Scrollbar(list_wrap, orient=tk.VERTICAL, command=self.tree.yview)
        self.tree.configure(yscrollcommand=vsb.set)
        self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        vsb.pack(side=tk.RIGHT, fill=tk.Y)

        self.tree.bind("<Double-1>", lambda e: self._edit_server())

        # 交替行色
        self.tree.tag_configure("even", background=C["surface0"])
        self.tree.tag_configure("odd",  background=C["mantle"])

        # ── 底部信息栏 ──
        footer = tk.Frame(self.root, bg=C["crust"])
        footer.pack(fill=tk.X)
        fi = tk.Frame(footer, bg=C["crust"])
        fi.pack(fill=tk.X, padx=20, pady=6)

        from .config import CONFIG_FILE
        _label(fi, f"配置文件: {CONFIG_FILE}", FONT_SM, C["overlay0"]).pack(side=tk.LEFT)

        self.server_count_label = _label(fi, "共 0 台服务器", FONT_SM, C["overlay0"])
        self.server_count_label.pack(side=tk.RIGHT)

    # ── 右键菜单 ──

    def _build_context_menu(self):
        self.ctx_menu = tk.Menu(self.root, tearoff=0,
                                 bg=C["surface0"], fg=C["text"],
                                 activebackground=C["surface1"], activeforeground=C["blue"],
                                 font=FONT, bd=0, relief=tk.FLAT)
        self.ctx_menu.add_command(label="  ✔ 选中",      command=self._set_active)
        self.ctx_menu.add_command(label="  编辑",        command=self._edit_server)
        self.ctx_menu.add_command(label="  测试连接",    command=self._test_selected)
        self.ctx_menu.add_separator()
        self.ctx_menu.add_command(label="  删除",        command=self._delete_server)

        self.tree.bind("<Button-3>", self._show_ctx)

    def _show_ctx(self, event):
        iid = self.tree.identify_row(event.y)
        if iid:
            self.tree.selection_set(iid)
            self.ctx_menu.post(event.x_root, event.y_root)

    # ── Data ──

    def _refresh_server_list(self):
        self.config_manager.load()
        active_id = self.config_manager.get_active_server_id()
        for item in self.tree.get_children():
            self.tree.delete(item)
        for i, srv in enumerate(self.config_manager.servers):
            auth = "密码" if srv.auth_type == "password" else "密钥"
            name_display = srv.name
            if srv.id == active_id:
                name_display = "✔ " + srv.name
            tag = "even" if i % 2 == 0 else "odd"
            self.tree.insert("", tk.END, iid=srv.id,
                             values=(name_display, srv.host, srv.port, srv.username, auth),
                             tags=(tag,))
        cnt = len(self.config_manager.servers)
        self.server_count_label.config(text=f"共 {cnt} 台服务器")
        # 更新标题栏当前选中显示
        active_srv = self.config_manager.get_active_server()
        if active_srv:
            self.active_label.config(text=active_srv.name, fg=C["green"])
        else:
            self.active_label.config(text="无", fg=C["yellow"])

    def _selected_id(self) -> Optional[str]:
        sel = self.tree.selection()
        if not sel:
            messagebox.showinfo("提示", "请先选择一个服务器")
            return None
        return sel[0]

    # ── Actions ──

    def _set_active(self):
        sid = self._selected_id()
        if not sid:
            return
        srv = self.config_manager.get_server(sid)
        if not srv:
            return
        self.config_manager.set_active_server(sid)
        self._refresh_server_list()
        messagebox.showinfo("已选中", f"已将 '{srv.name}' 设为当前活动服务器。\nMCP 调用时将自动连接此服务器。")

    def _add_server(self):
        dlg = ServerDialog(self.root, "添加服务器")
        self.root.wait_window(dlg)
        if dlg.result:
            self.config_manager.add_server(ServerConfig(**dlg.result))
            self._refresh_server_list()

    def _edit_server(self):
        sid = self._selected_id()
        if not sid:
            return
        srv = self.config_manager.get_server(sid)
        if not srv:
            return
        dlg = ServerDialog(self.root, "编辑服务器", srv)
        self.root.wait_window(dlg)
        if dlg.result:
            self.config_manager.update_server(sid, **dlg.result)
            self._refresh_server_list()

    def _delete_server(self):
        sid = self._selected_id()
        if not sid:
            return
        srv = self.config_manager.get_server(sid)
        if not srv:
            return
        if messagebox.askyesno("确认删除", f"确定要删除服务器 '{srv.name}' 吗？"):
            self.config_manager.delete_server(sid)
            self._refresh_server_list()

    def _test_selected(self):
        sid = self._selected_id()
        if not sid:
            return
        srv = self.config_manager.get_server(sid)
        if not srv:
            return

        import paramiko
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        connect_kw = {
            "hostname": srv.host,
            "port": srv.port,
            "username": srv.username,
            "timeout": 10,
        }
        if srv.auth_type == "password":
            connect_kw["password"] = srv.password
        else:
            key_path = os.path.expanduser(srv.key_path)
            connect_kw["key_filename"] = key_path
            if srv.key_passphrase:
                connect_kw["passphrase"] = srv.key_passphrase

        try:
            client.connect(**connect_kw)
            messagebox.showinfo("测试成功",
                f"已成功连接到 {srv.name} ({srv.host}:{srv.port})")
            client.close()
        except Exception as e:
            messagebox.showerror("连接失败", f"{type(e).__name__}: {e}")

    # ── 安装到 IDE ──

    def _show_install_menu(self):
        from .cli import IDE_CONFIGS
        menu = tk.Menu(self.root, tearoff=0,
                       bg=C["surface0"], fg=C["text"],
                       activebackground=C["surface1"], activeforeground=C["yellow"],
                       font=FONT, bd=0, relief=tk.FLAT)
        for ide_name in IDE_CONFIGS:
            menu.add_command(label=f"  {ide_name}", command=lambda n=ide_name: self._install_to_ide(n))
        menu.add_separator()
        menu.add_command(label="  全部安装", command=self._install_all)
        menu.add_command(label="  复制配置到剪贴板", command=self._copy_mcp_config)
        try:
            menu.tk_popup(self.root.winfo_pointerx(), self.root.winfo_pointery())
        finally:
            menu.grab_release()

    def _install_to_ide(self, ide_name: str):
        from .cli import IDE_CONFIGS, install_to_ide, get_mcp_command
        config_path = IDE_CONFIGS[ide_name]

        # GUI 中增加覆盖确认
        if os.path.exists(config_path):
            try:
                with open(config_path, "r", encoding="utf-8") as f:
                    existing = json.load(f)
                if "ssh" in existing.get("mcpServers", {}):
                    if not messagebox.askyesno("已存在",
                            f"{ide_name} 中已配置了 ssh MCP Server。\n是否覆盖？"):
                        return
            except (json.JSONDecodeError, OSError):
                pass

        if install_to_ide(ide_name):
            command = get_mcp_command()
            messagebox.showinfo("安装成功",
                f"已安装到 {ide_name}！\n\n"
                f"配置文件: {config_path}\n"
                f"命令: {command}\n\n"
                f"重启 {ide_name} 即可使用 SSH MCP 工具。")
        else:
            messagebox.showerror("安装失败", f"安装到 {ide_name} 失败，请检查权限。")

    def _install_all(self):
        from .cli import IDE_CONFIGS, install_to_ide
        success = []
        for ide_name in IDE_CONFIGS:
            if install_to_ide(ide_name):
                success.append(ide_name)
        messagebox.showinfo("安装完成",
            f"已安装到 {len(success)} 个 IDE：\n" + "\n".join(f"  ✔ {n}" for n in success))

    def _copy_mcp_config(self):
        from .cli import get_mcp_command
        command = get_mcp_command()
        config = {"mcpServers": {"ssh": {"command": command}}}
        text = json.dumps(config, indent=2, ensure_ascii=False)
        self.root.clipboard_clear()
        self.root.clipboard_append(text)
        self.root.update()
        messagebox.showinfo("已复制", "MCP 配置已复制到剪贴板：\n\n" + text)

    # ── Status ──

    def set_mcp_status(self, running: bool):
        self.mcp_dot.delete("all")
        color = C["green"] if running else C["overlay0"]
        self.mcp_dot.create_oval(1, 1, 9, 9, fill=color, outline="")

    def set_ssh_status(self, name: Optional[str] = None):
        self.ssh_dot.delete("all")
        if name:
            self.ssh_dot.create_oval(1, 1, 9, 9, fill=C["green"], outline="")
            self.ssh_status_text.config(text=name, fg=C["green"])
        else:
            self.ssh_dot.create_oval(1, 1, 9, 9, fill=C["overlay0"], outline="")
            self.ssh_status_text.config(text="未连接", fg=C["overlay0"])

    def _on_close(self):
        if self.on_close_callback:
            self.on_close_callback()
        self.root.destroy()

    def run(self):
        self.root.mainloop()
