#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
蓝卡云维护平台 - SSLink 图形化获取工具
Windows GUI (Python tkinter 实现)
"""

import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import threading
import requests
import json
import time
import re
import sys
import os

# ─── 配置 ────────────────────────────────────────────
BASE_URL = "http://web.bluecardservice.com/service"
USERNAME = "lhj"
PASSWORD = "1"
VERIFY = "1"
REMOTE_PEOPLE = "windows_client"
MAX_RETRIES = 3
SLEEP_BETWEEN_RETRIES = 3
CONNECT_WAIT = 20
POLL_INTERVAL = 1.5
REQUEST_TIMEOUT = 15


class SSLinkApp:
    def __init__(self, root):
        self.root = root
        self.root.title("蓝卡云维护平台 - SSLink 获取工具")
        self.root.geometry("900x680")
        self.root.minsize(800, 600)

        # 状态变量
        self.session = requests.Session()
        self.logged_in = False
        self.devices = []
        self.selected_device = None
        self.server_addr = ""
        self.current_sslink = ""
        self.busy = False

        # 设置样式
        self.setup_styles()

        # 构建UI
        self.build_ui()

        # 程序启动时自动登录
        self.root.after(500, self.auto_login)

    def setup_styles(self):
        """配置 tkinter 样式"""
        style = ttk.Style()
        style.theme_use("vista" if "vista" in style.theme_names() else "clam")

        # 自定义样式
        style.configure("Search.TButton", padding=6)
        style.configure("Copy.TButton", padding=6, font=("微软雅黑", 10))
        style.configure("Header.TLabel", font=("微软雅黑", 11, "bold"))
        style.configure("Status.TLabel", font=("微软雅黑", 9))
        style.configure("Link.TLabel", font=("Consolas", 10), foreground="#0066cc")
        style.configure("Red.TLabel", foreground="red")
        style.configure("Green.TLabel", foreground="green")
        style.configure("Orange.TLabel", foreground="#cc6600")

    def build_ui(self):
        """构建主界面"""
        # ─── 顶部：搜索区域 ────────────────────────────
        top_frame = ttk.Frame(self.root, padding=(10, 8, 10, 5))
        top_frame.pack(fill=tk.X)

        ttk.Label(top_frame, text="搜索设备:", style="Header.TLabel").pack(side=tk.LEFT, padx=(0, 8))

        self.search_entry = ttk.Entry(top_frame, font=("微软雅黑", 11), width=35)
        self.search_entry.pack(side=tk.LEFT, padx=(0, 8), fill=tk.X, expand=True)
        self.search_entry.bind("<Return>", lambda e: self.on_search())

        self.search_btn = ttk.Button(top_frame, text="🔍 查询", style="Search.TButton",
                                     command=self.on_search)
        self.search_btn.pack(side=tk.LEFT, padx=(0, 4))

        self.refresh_btn = ttk.Button(top_frame, text="🔄 刷新列表", style="Search.TButton",
                                      command=self.on_refresh_selected)
        self.refresh_btn.pack(side=tk.LEFT)

        # ─── 中间：设备列表 ────────────────────────────
        list_frame = ttk.Frame(self.root, padding=(10, 0, 10, 5))
        list_frame.pack(fill=tk.BOTH, expand=True)

        # 列表标题
        title_frame = ttk.Frame(list_frame)
        title_frame.pack(fill=tk.X, pady=(0, 4))
        ttk.Label(title_frame, text="设备列表（双击选中）:", style="Header.TLabel").pack(side=tk.LEFT)

        self.list_status_label = ttk.Label(title_frame, text="", style="Status.TLabel")
        self.list_status_label.pack(side=tk.RIGHT)

        # 表格
        columns = ("序号", "设备号", "名称", "状态", "远程状态", "代理端口", "设备IP")
        self.tree = ttk.Treeview(list_frame, columns=columns, show="headings",
                                 height=10, selectmode="browse")

        # 定义列
        col_widths = [50, 140, 180, 60, 80, 80, 130]
        col_anchors = [tk.CENTER, tk.CENTER, tk.W, tk.CENTER, tk.CENTER, tk.CENTER, tk.CENTER]
        for i, (col, w, anchor) in enumerate(zip(columns, col_widths, col_anchors)):
            self.tree.heading(col, text=col, command=lambda c=col: self.sort_treeview(c))
            self.tree.column(col, width=w, minwidth=40, anchor=anchor)

        # 滚动条
        vsb = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.tree.yview)
        hsb = ttk.Scrollbar(list_frame, orient=tk.HORIZONTAL, command=self.tree.xview)
        self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)

        self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        vsb.pack(side=tk.RIGHT, fill=tk.Y)
        hsb.pack(side=tk.BOTTOM, fill=tk.X)

        list_frame.pack_propagate(False)

        # 绑定双击事件
        self.tree.bind("<Double-1>", self.on_device_select)
        self.tree.bind("<Return>", self.on_device_select)

        # ─── 底部：信息面板 ────────────────────────────
        bottom_frame = ttk.Frame(self.root, padding=(10, 5, 10, 8))
        bottom_frame.pack(fill=tk.X)

        # 设备信息区域
        info_frame = ttk.LabelFrame(bottom_frame, text="选中设备信息", padding=8)
        info_frame.pack(fill=tk.X, pady=(0, 6))

        info_grid = ttk.Frame(info_frame)
        info_grid.pack(fill=tk.X)

        # 信息行1
        row1 = ttk.Frame(info_grid)
        row1.pack(fill=tk.X, pady=1)
        ttk.Label(row1, text="设备号:", width=8, anchor=tk.E).pack(side=tk.LEFT, padx=(0, 4))
        self.info_sn = ttk.Label(row1, text="-", font=("Consolas", 10, "bold"))
        self.info_sn.pack(side=tk.LEFT, padx=(0, 20))
        ttk.Label(row1, text="名称:", width=8, anchor=tk.E).pack(side=tk.LEFT, padx=(0, 4))
        self.info_name = ttk.Label(row1, text="-")
        self.info_name.pack(side=tk.LEFT, padx=(0, 20))
        ttk.Label(row1, text="状态:", width=8, anchor=tk.E).pack(side=tk.LEFT, padx=(0, 4))
        self.info_state = ttk.Label(row1, text="-")
        self.info_state.pack(side=tk.LEFT)

        # 信息行2
        row2 = ttk.Frame(info_grid)
        row2.pack(fill=tk.X, pady=1)
        ttk.Label(row2, text="设备IP:", width=8, anchor=tk.E).pack(side=tk.LEFT, padx=(0, 4))
        self.info_ip = ttk.Label(row2, text="-")
        self.info_ip.pack(side=tk.LEFT, padx=(0, 20))
        ttk.Label(row2, text="代理端口:", width=8, anchor=tk.E).pack(side=tk.LEFT, padx=(0, 4))
        self.info_port = ttk.Label(row2, text="-")
        self.info_port.pack(side=tk.LEFT, padx=(0, 20))
        ttk.Label(row2, text="远程状态:", width=8, anchor=tk.E).pack(side=tk.LEFT, padx=(0, 4))
        self.info_remote = ttk.Label(row2, text="-")
        self.info_remote.pack(side=tk.LEFT)

        # 操作按钮
        btn_frame = ttk.Frame(info_frame)
        btn_frame.pack(fill=tk.X, pady=(6, 0))
        self.connect_btn = ttk.Button(btn_frame, text="🔗 建立远程连接", command=self.on_connect,
                                      state=tk.DISABLED)
        self.connect_btn.pack(side=tk.LEFT, padx=(0, 6))
        self.auto_btn = ttk.Button(btn_frame, text="⚡ 一键获取 SSLink", command=self.on_auto_get,
                                   state=tk.DISABLED)
        self.auto_btn.pack(side=tk.LEFT)

        # SSLink 显示区域
        sslink_frame = ttk.LabelFrame(bottom_frame, text="SSLink 连接串", padding=8)
        sslink_frame.pack(fill=tk.X, pady=(0, 6))

        sslink_row = ttk.Frame(sslink_frame)
        sslink_row.pack(fill=tk.X)

        self.sslink_entry = ttk.Entry(sslink_row, font=("Consolas", 10),
                                      state="readonly")
        self.sslink_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 6))

        self.copy_btn = ttk.Button(sslink_row, text="📋 复制", style="Copy.TButton",
                                   command=self.on_copy, state=tk.DISABLED)
        self.copy_btn.pack(side=tk.RIGHT)

        # ─── 状态栏 ────────────────────────────────────
        status_frame = ttk.Frame(self.root, relief=tk.SUNKEN, padding=(8, 3))
        status_frame.pack(fill=tk.X, side=tk.BOTTOM)

        self.status_label = ttk.Label(status_frame, text="就绪", style="Status.TLabel")
        self.status_label.pack(side=tk.LEFT)

        self.progress_bar = ttk.Progressbar(status_frame, mode="indeterminate",
                                            length=150)
        self.progress_bar.pack(side=tk.RIGHT, padx=(10, 0))

    # ═══════════════════════════════════════════════════
    # 辅助方法
    # ═══════════════════════════════════════════════════

    def set_status(self, text, color="black"):
        """设置状态栏文字"""
        self.status_label.config(text=text, foreground=color)
        self.root.update_idletasks()

    def start_progress(self):
        """启动进度条动画"""
        self.progress_bar.start(10)
        self.busy = True

    def stop_progress(self):
        """停止进度条动画"""
        self.progress_bar.stop()
        self.busy = False

    def show_error(self, title, msg):
        """显示错误对话框"""
        self.root.after(0, lambda: messagebox.showerror(title, msg))

    def show_info(self, title, msg):
        """显示信息对话框"""
        self.root.after(0, lambda: messagebox.showinfo(title, msg))

    def run_in_thread(self, target, args=()):
        """在后台线程中运行任务"""
        if self.busy:
            self.show_info("提示", "正在处理中，请稍候...")
            return
        t = threading.Thread(target=target, args=args, daemon=True)
        t.start()

    def sort_treeview(self, col):
        """按列排序"""
        items = [(self.tree.set(item, col), item) for item in self.tree.get_children("")]
        items.sort(key=lambda x: x[0])
        for index, (val, item) in enumerate(items):
            self.tree.move(item, "", index)

    # ═══════════════════════════════════════════════════
    # API 调用
    # ═══════════════════════════════════════════════════

    def auto_login(self):
        """程序启动时自动登录"""
        self.run_in_thread(self._do_login)

    def _do_login(self):
        """执行登录（后台线程）"""
        self.start_progress()
        self.set_status("正在登录平台...", "blue")

        try:
            resp = self.session.post(
                f"{BASE_URL}/login2.do",
                data={"username": USERNAME, "password1": PASSWORD, "verify": VERIFY},
                headers={"Content-Type": "application/x-www-form-urlencoded"},
                timeout=REQUEST_TIMEOUT
            )
            data = resp.json()
            if data.get("status") == "success":
                self.logged_in = True
                self.set_status("登录成功 ✅ 请搜索设备", "green")
            else:
                self.set_status(f"登录失败: {data.get('datas', '未知错误')}", "red")
                self.show_error("登录失败", f"登录失败: {data.get('datas', '未知错误')}")
        except requests.exceptions.Timeout:
            self.set_status("登录超时，请检查网络连接", "red")
            self.show_error("网络错误", "登录请求超时，请检查网络连接")
        except requests.exceptions.ConnectionError:
            self.set_status("无法连接服务器，请检查网络", "red")
            self.show_error("网络错误", "无法连接到服务器，请检查网络")
        except Exception as e:
            self.set_status(f"登录异常: {str(e)}", "red")
            self.show_error("登录异常", str(e))
        finally:
            self.stop_progress()

    def ensure_login(self):
        """确保已登录，未登录则自动登录"""
        if not self.logged_in:
            self.set_status("未登录，正在自动登录...", "blue")
            self._do_login()
            # 简单等待登录完成
            for _ in range(20):
                if self.logged_in:
                    break
                time.sleep(0.5)
            if not self.logged_in:
                raise Exception("登录失败，请重试")

    def on_search(self):
        """搜索按钮回调"""
        keyword = self.search_entry.get().strip()
        if not keyword:
            self.show_info("提示", "请输入设备号或关键字")
            return
        self.run_in_thread(self._do_search, (keyword,))

    def _do_search(self, keyword):
        """执行搜索（后台线程）"""
        self.start_progress()
        self.set_status(f"正在搜索 '{keyword}'...", "blue")

        try:
            self.ensure_login()

            # 先访问 devices.jsp 建立会话上下文
            try:
                self.session.get(
                    f"{BASE_URL}/device/devices.jsp",
                    timeout=REQUEST_TIMEOUT
                )
            except Exception:
                pass

            # 搜索设备
            resp = self.session.post(
                f"{BASE_URL}/device/pageLoad",
                data={
                    "deviceSnAll": keyword,
                    "deviceNameAll": "-1",
                    "deviceStateAll": "-1",
                    "page": "1",
                    "rows": "50"
                },
                headers={
                    "Content-Type": "application/x-www-form-urlencoded",
                    "X-Requested-With": "XMLHttpRequest"
                },
                timeout=REQUEST_TIMEOUT
            )
            data = resp.json()
            rows = data.get("rows", [])

            # 清空表格
            for item in self.tree.get_children():
                self.tree.delete(item)

            if not rows:
                self.set_status(f"未找到匹配 '{keyword}' 的设备", "orange")
                self.show_info("搜索结果", f"未找到匹配 '{keyword}' 的设备")
                self.devices = []
                self.clear_info_panel()
                return

            self.devices = rows
            for i, dev in enumerate(rows):
                state = "在线" if dev.get("state") == 1 else "离线"
                remote = dev.get("remote", 0)
                port = dev.get("port8080", 0)
                remote_str = "已远程" if remote == 1 else "未远程"
                if remote == 1 and dev.get("operator_name"):
                    remote_str += f"({dev['operator_name']})"
                self.tree.insert("", tk.END, values=(
                    i + 1,
                    dev.get("device_sn", ""),
                    dev.get("name", ""),
                    state,
                    remote_str,
                    port if port else "-",
                    dev.get("device_ip", "")
                ))

            self.list_status_label.config(text=f"共 {len(rows)} 个匹配设备")
            self.set_status(f"搜索完成，找到 {len(rows)} 个设备", "green")

        except requests.exceptions.Timeout:
            self.set_status("搜索请求超时", "red")
            self.show_error("网络错误", "搜索请求超时")
        except requests.exceptions.ConnectionError:
            self.set_status("无法连接服务器", "red")
            self.show_error("网络错误", "无法连接服务器")
        except json.JSONDecodeError:
            self.set_status("解析返回数据失败", "red")
            self.show_error("数据错误", "服务器返回数据格式异常")
        except Exception as e:
            self.set_status(f"搜索异常: {str(e)}", "red")
            self.show_error("搜索异常", str(e))
        finally:
            self.stop_progress()

    def on_device_select(self, event=None):
        """双击/回车选中设备"""
        selected = self.tree.selection()
        if not selected:
            return
        item = selected[0]
        values = self.tree.item(item, "values")
        if not values or len(values) < 2:
            return

        idx = int(values[0]) - 1
        if 0 <= idx < len(self.devices):
            self.select_device_by_index(idx)

    def select_device_by_index(self, idx):
        """按索引选中设备"""
        if idx < 0 or idx >= len(self.devices):
            return
        dev = self.devices[idx]
        self.selected_device = dev

        # 更新信息面板
        state = dev.get("state", 0)
        remote = dev.get("remote", 0)
        port = dev.get("port8080", 0)
        oper = dev.get("operator_name", "")

        self.info_sn.config(text=dev.get("device_sn", "-"))
        self.info_name.config(text=dev.get("name", "-"))

        if state == 1:
            self.info_state.config(text="在线", foreground="green")
        else:
            self.info_state.config(text="离线", foreground="red")

        self.info_ip.config(text=dev.get("device_ip", "-"))
        self.info_port.config(text=str(port) if port else "0")

        if remote == 1:
            text = "已远程"
            if oper:
                text += f" ({oper})"
            self.info_remote.config(text=text, foreground="green")
        else:
            self.info_remote.config(text="未远程", foreground="#cc6600")

        # 启用按钮
        self.connect_btn.config(state=tk.NORMAL)
        self.auto_btn.config(state=tk.NORMAL)

        self.set_status(f"已选中: {dev.get('device_sn')} - {dev.get('name')}", "blue")

    def clear_info_panel(self):
        """清空信息面板"""
        self.selected_device = None
        self.info_sn.config(text="-")
        self.info_name.config(text="-")
        self.info_state.config(text="-")
        self.info_ip.config(text="-")
        self.info_port.config(text="-")
        self.info_remote.config(text="-")
        self.connect_btn.config(state=tk.DISABLED)
        self.auto_btn.config(state=tk.DISABLED)

    def on_refresh_selected(self):
        """刷新当前选中设备状态"""
        if not self.selected_device:
            self.show_info("提示", "请先在列表中选中一个设备")
            return
        dev_sn = self.selected_device.get("device_sn")
        self.run_in_thread(self._do_refresh, (dev_sn,))

    def _do_refresh(self, dev_sn):
        """刷新指定设备信息"""
        self.start_progress()
        self.set_status(f"正在刷新设备 {dev_sn}...", "blue")

        try:
            self.ensure_login()
            resp = self.session.post(
                f"{BASE_URL}/device/pageLoad",
                data={
                    "deviceSnAll": dev_sn,
                    "deviceNameAll": "-1",
                    "deviceStateAll": "-1",
                    "page": "1",
                    "rows": "10"
                },
                headers={
                    "Content-Type": "application/x-www-form-urlencoded",
                    "X-Requested-With": "XMLHttpRequest"
                },
                timeout=REQUEST_TIMEOUT
            )
            data = resp.json()
            rows = data.get("rows", [])
            if rows:
                # 更新设备列表中的记录
                for i, dev in enumerate(self.devices):
                    if dev.get("device_sn") == dev_sn:
                        self.devices[i] = rows[0]
                        # 更新表格行
                        for item in self.tree.get_children():
                            vals = self.tree.item(item, "values")
                            if len(vals) >= 2 and vals[1] == dev_sn:
                                state = "在线" if rows[0].get("state") == 1 else "离线"
                                remote = rows[0].get("remote", 0)
                                port = rows[0].get("port8080", 0)
                                remote_str = "已远程" if remote == 1 else "未远程"
                                if remote == 1 and rows[0].get("operator_name"):
                                    remote_str += f"({rows[0]['operator_name']})"
                                self.tree.item(item, values=(
                                    i + 1, dev_sn, rows[0].get("name", ""),
                                    state, remote_str,
                                    port if port else "-",
                                    rows[0].get("device_ip", "")
                                ))
                                break
                        break

                # 更新选中信息
                self.select_device_by_index(self.get_device_index(dev_sn))
                self.set_status(f"设备 {dev_sn} 已刷新", "green")
            else:
                self.set_status(f"未找到设备 {dev_sn}", "orange")

        except Exception as e:
            self.set_status(f"刷新失败: {str(e)}", "red")
        finally:
            self.stop_progress()

    def get_device_index(self, dev_sn):
        """获取设备在列表中的索引"""
        for i, dev in enumerate(self.devices):
            if dev.get("device_sn") == dev_sn:
                return i
        return -1

    def on_connect(self):
        """建立远程连接"""
        if not self.selected_device:
            self.show_info("提示", "请先选中一个设备")
            return
        dev = self.selected_device
        if dev.get("state") != 1:
            self.show_info("提示", "设备当前为离线状态，无法建立远程连接")
            return
        self.run_in_thread(self._do_connect)

    def _do_connect(self):
        """执行远程连接（后台线程）"""
        dev = self.selected_device
        if not dev:
            return
        dev_sn = dev.get("device_sn", "")
        remote = dev.get("remote", 0)
        port = dev.get("port8080", 0)

        # 如果已经远程且有端口，直接获取SSLink
        if remote == 1 and port and port != "0" and port != 0:
            self.set_status("设备已处于远程连接状态，无需重复连接", "green")
            self._get_sslink(dev)
            return

        self.start_progress()
        self.set_status(f"正在建立远程连接 {dev_sn}...", "blue")

        try:
            self.ensure_login()

            # 发起远程连接请求
            retries = 0
            connected = False
            while retries < MAX_RETRIES and not connected:
                retries += 1
                self.set_status(f"发送远程连接请求 ({retries}/{MAX_RETRIES})...", "blue")

                try:
                    resp = self.session.post(
                        f"{BASE_URL}/device/deviceLink",
                        data={"deviceSn": dev_sn, "remote_people": REMOTE_PEOPLE},
                        headers={
                            "Content-Type": "application/x-www-form-urlencoded",
                            "X-Requested-With": "XMLHttpRequest"
                        },
                        timeout=REQUEST_TIMEOUT
                    )
                    result = resp.json()
                    if result.get("state") == "success":
                        connected = True
                        self.set_status(f"连接请求已发送，等待端口分配...", "blue")
                    else:
                        if retries < MAX_RETRIES:
                            self.set_status(f"连接请求失败，{SLEEP_BETWEEN_RETRIES}秒后重试...", "orange")
                            time.sleep(SLEEP_BETWEEN_RETRIES)
                        else:
                            msg = result.get("datas", "未知错误")
                            self.set_status(f"连接失败: {msg}", "red")
                            self.show_error("连接失败", f"远程连接失败: {msg}")
                            return
                except Exception as e:
                    if retries < MAX_RETRIES:
                        self.set_status(f"请求异常，{SLEEP_BETWEEN_RETRIES}秒后重试...", "orange")
                        time.sleep(SLEEP_BETWEEN_RETRIES)
                    else:
                        self.set_status(f"连接请求异常: {str(e)}", "red")
                        self.show_error("连接异常", str(e))
                        return

            if not connected:
                self.set_status("远程连接失败，已达最大重试次数", "red")
                return

            # 轮询等待端口分配
            self.set_status(f"等待连接建立 (最长{CONNECT_WAIT}秒)...", "blue")
            for w in range(CONNECT_WAIT):
                time.sleep(POLL_INTERVAL)
                self.set_status(f"轮询设备状态... ({w+1}/{CONNECT_WAIT})", "blue")

                try:
                    poll_resp = self.session.post(
                        f"{BASE_URL}/device/pageLoad",
                        data={
                            "deviceSnAll": dev_sn,
                            "deviceNameAll": "-1",
                            "deviceStateAll": "-1",
                            "page": "1",
                            "rows": "10"
                        },
                        headers={
                            "Content-Type": "application/x-www-form-urlencoded",
                            "X-Requested-With": "XMLHttpRequest"
                        },
                        timeout=8
                    )
                    poll_data = poll_resp.json()
                    poll_rows = poll_data.get("rows", [])
                    if poll_rows:
                        row = poll_rows[0]
                        poll_port = row.get("port8080", 0)
                        poll_remote = row.get("remote", 0)
                        poll_oper = row.get("operator_name", "")

                        # 更新选中的设备信息
                        idx = self.get_device_index(dev_sn)
                        if idx >= 0:
                            self.devices[idx] = row

                        if poll_remote == 1 and poll_port and poll_port != 0 and poll_port != "0":
                            # 连接已建立
                            self.selected_device = row
                            self.select_device_by_index(self.get_device_index(dev_sn))
                            self.set_status(f"连接已建立 ✅ 端口: {poll_port}", "green")
                            self._get_sslink(row)
                            return
                except Exception:
                    pass

            # 超时后检查是否有端口
            self.set_status("连接等待超时，检查当前端口...", "orange")
            try:
                poll_resp = self.session.post(
                    f"{BASE_URL}/device/pageLoad",
                    data={
                        "deviceSnAll": dev_sn,
                        "deviceNameAll": "-1",
                        "deviceStateAll": "-1",
                        "page": "1",
                        "rows": "10"
                    },
                    headers={
                        "Content-Type": "application/x-www-form-urlencoded",
                        "X-Requested-With": "XMLHttpRequest"
                    },
                    timeout=8
                )
                poll_data = poll_resp.json()
                poll_rows = poll_data.get("rows", [])
                if poll_rows:
                    row = poll_rows[0]
                    poll_port = row.get("port8080", 0)
                    if poll_port and poll_port != 0 and poll_port != "0":
                        self.selected_device = row
                        self.select_device_by_index(self.get_device_index(dev_sn))
                        self._get_sslink(row)
                        return
            except Exception:
                pass

            self.set_status("连接等待超时，请稍后重试或手动刷新", "orange")
            self.show_info("连接超时", "连接等待超时，请稍后点击「刷新列表」检查状态")

        except Exception as e:
            self.set_status(f"连接异常: {str(e)}", "red")
            self.show_error("连接异常", str(e))
        finally:
            self.stop_progress()

    def on_auto_get(self):
        """一键获取 SSLink"""
        if not self.selected_device:
            self.show_info("提示", "请先选中一个设备")
            return
        self.run_in_thread(self._do_auto_get)

    def _do_auto_get(self):
        """一键获取：自动连接并输出SSLink"""
        dev = self.selected_device
        if not dev:
            return

        dev_sn = dev.get("device_sn", "")
        state = dev.get("state", 0)
        remote = dev.get("remote", 0)
        port = dev.get("port8080", 0)

        if state != 1:
            self.set_status("设备离线，无法获取 SSLink", "red")
            self.show_error("设备离线", f"设备 {dev_sn} 当前为离线状态，无法获取 SSLink")
            return

        if remote == 1 and port and port != 0 and port != "0":
            # 已经连接且有端口，直接获取
            self._get_sslink(dev)
        else:
            # 需要先连接
            self._do_connect()

    def _get_server_addr(self):
        """获取服务器地址"""
        self.set_status("正在获取服务器地址...", "blue")
        try:
            resp = self.session.post(
                f"{BASE_URL}/device/getConfig",
                headers={"Content-Type": "application/x-www-form-urlencoded"},
                timeout=REQUEST_TIMEOUT
            )
            addr = resp.text.strip()
            # 验证是否为有效的IP地址
            ip_pattern = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"
            if re.match(ip_pattern, addr):
                self.server_addr = addr
                return addr
            else:
                self.set_status(f"警告: 服务器地址格式异常 ({addr})", "orange")
                self.server_addr = addr
                return addr
        except Exception as e:
            self.set_status(f"获取服务器地址失败: {str(e)}", "red")
            raise

    def _get_sslink(self, dev):
        """构建并显示 SSLink，并在 Windows 上通过协议关联自动打开"""
        self.start_progress()
        try:
            dev_sn = dev.get("device_sn", "")
            port = dev.get("port8080", 0)

            if not port or port == 0 or port == "0":
                self.set_status(f"端口为0，无法生成 SSLink", "red")
                self.show_error("端口无效", f"设备 {dev_sn} 的代理端口为0，连接可能尚未完全建立")
                return

            # 获取服务器地址（如果尚未获取）
            if not self.server_addr:
                self._get_server_addr()

            # 构建 SSLink
            self.current_sslink = (
                f"sslink:SocksCap64 -i{self.server_addr} "
                f"-p{port} -t6 -u -s123456 -cchacha20-ietf-poly1305 --quit"
            )

            # 更新界面
            self.root.after(0, self._update_sslink_display)

            self.set_status(f"SSLink 已就绪 ✅ 端口: {port}", "green")

            # 在 Windows 上通过系统协议关联打开 sslink
            if sys.platform == 'win32':
                self._open_sslink_protocol()

        except Exception as e:
            self.set_status(f"获取 SSLink 失败: {str(e)}", "red")
            self.show_error("获取失败", str(e))
        finally:
            self.stop_progress()

    def _open_sslink_protocol(self):
        """尝试通过系统关联的 sslink 协议打开 SSLink（仅 Windows）"""
        try:
            self.set_status("正在调用 SocksCap64 打开 SSLink...", "blue")
            os.startfile(self.current_sslink)
        except OSError:
            self.set_status("未检测到 sslink 协议关联，请安装 SocksCap64", "orange")
            self.show_error("协议未关联",
                "系统未检测到 sslink 协议关联程序。\n\n"
                "请确保已安装并运行 SocksCap64，\n"
                "SSLink 链接已复制到剪贴板，可手动粘贴使用。")
        except Exception as e:
            self.set_status(f"调用协议关联失败: {str(e)}", "orange")
            self.show_error("打开失败",
                f"尝试打开 sslink 协议时出错: {str(e)}\n\n"
                f"SSLink 链接已复制到剪贴板，可手动使用。")

    def _update_sslink_display(self):
        """更新 SSLink 显示（主线程）"""
        self.sslink_entry.config(state="normal")
        self.sslink_entry.delete(0, tk.END)
        self.sslink_entry.insert(0, self.current_sslink)
        self.sslink_entry.config(state="readonly")
        self.copy_btn.config(state=tk.NORMAL)

    def on_copy(self):
        """复制 SSLink 到剪贴板"""
        if self.current_sslink:
            self.root.clipboard_clear()
            self.root.clipboard_append(self.current_sslink)
            self.set_status("SSLink 已复制到剪贴板 📋", "green")
            self.root.after(2000, lambda: self.set_status("就绪") if self.status_label.cget("text") == "SSLink 已复制到剪贴板 📋" else None)


def main():
    root = tk.Tk()
    app = SSLinkApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()
