import { useEffect, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { open } from "@tauri-apps/plugin-dialog";
import { check as checkUpdate } from "@tauri-apps/plugin-updater";
import { relaunch } from "@tauri-apps/plugin-process";
import { getVersion } from "@tauri-apps/api/app";
import {
  detectEditors, getSettings, getSpaces, searchEditorPath, updateSettings, getWslDistros,
  getProjects, syncBlueprintRules, checkMcpHealth,
  type EditorCandidate, type SyncResult, type McpHealthResult,
} from "../../lib/commands";
// GitHub OAuth 配置已移至侧边栏「应用设置」
import { useUIStore } from "../../store/ui";
import { Dialog } from "../ui/Dialog";
import { GiteaInstancesSection } from "../dashboard/GiteaPanel";

// 编辑器条目数据结构（存为 JSON 保存到 settings 表）
interface EditorEntry {
  id: string;       // 唯一 id
  name: string;     // 显示名
  path: string;     // 完整可执行路径或命令名
}

function loadEditors(raw: string | undefined): EditorEntry[] {
  try { return raw ? JSON.parse(raw) : []; } catch { return []; }
}

const inputCls = "w-full border border-gray-200 rounded-lg px-3 py-2 text-sm font-mono outline-none focus:ring-2 focus:ring-blue-200 focus:border-blue-400";

/* ── 编辑器表单弹窗 ─────────────────────────────────────────────────────────── */
function EditorFormModal({ entry, onClose, onSave }: {
  entry: EditorEntry | null;   // null = 新增
  onClose: () => void;
  onSave: (e: EditorEntry) => void;
}) {
  const showToast = useUIStore((s) => s.showToast);
  const [name, setName] = useState(entry?.name ?? "");
  const [path, setPath] = useState(entry?.path ?? "");
  const [searching, setSearching] = useState(false);

  const { data: detected, isLoading: detecting } = useQuery({
    queryKey: ["editors"],
    queryFn: detectEditors,
    staleTime: 60000,
  });

  const pickExe = async () => {
    const selected = await open({
      multiple: false,
      filters: [{ name: "可执行文件", extensions: ["exe", "cmd", "bat"] }],
    });
    if (selected) setPath(selected as string);
  };

  const autoSearch = async () => {
    if (!name.trim()) { showToast("❌ 请先填写名称或命令关键词"); return; }
    setSearching(true);
    try {
      // 把名称当命令名搜一下
      const keyword = name.trim().toLowerCase().replace(/\s+/g, "");
      const found = await searchEditorPath(keyword);
      if (found) {
        setPath(found);
        showToast(`已找到：${found}`);
      } else {
        showToast("未找到，请手动填写路径");
      }
    } finally {
      setSearching(false);
    }
  };

  const fillFromDetected = (c: EditorCandidate) => {
    setName(c.name);
    setPath(c.path ?? c.cmd);
  };

  const handleSave = () => {
    if (!name.trim()) { showToast("❌ 名称不能为空"); return; }
    if (!path.trim()) { showToast("❌ 路径不能为空"); return; }
    onSave({
      id: entry?.id ?? `editor-${Date.now()}`,
      name: name.trim(),
      path: path.trim(),
    });
  };

  return (
    <Dialog title={entry ? "编辑编辑器" : "新增编辑器"} onClose={onClose} size="md">
      <div className="px-6 py-4 space-y-4">

        {/* 检测到的编辑器快速填充 */}
        <div>
          <p className="text-xs font-medium text-gray-500 mb-2">系统检测到的编辑器（点击快速填充）</p>
          {detecting ? (
            <p className="text-xs text-gray-400">检测中…</p>
          ) : (
            <div className="flex flex-wrap gap-2">
              {(detected ?? []).map((c) => (
                <button
                  key={c.cmd}
                  onClick={() => fillFromDetected(c)}
                  className={`px-3 py-1.5 rounded-lg text-xs border transition-colors ${
                    c.found
                      ? "border-green-200 bg-green-50 text-green-800 hover:bg-green-100"
                      : "border-gray-100 text-gray-300 cursor-not-allowed"
                  }`}
                  disabled={!c.found}
                  title={c.path ?? "未检测到"}
                >
                  {c.found ? "✓ " : ""}{c.name}
                </button>
              ))}
            </div>
          )}
        </div>

        <hr className="border-gray-100" />

        {/* 名称 */}
        <div>
          <label className="block text-xs font-medium text-gray-600 mb-1">显示名称</label>
          <input
            className="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-blue-200"
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="Cursor / VS Code / …"
          />
        </div>

        {/* 路径 */}
        <div>
          <label className="block text-xs font-medium text-gray-600 mb-1">可执行路径或命令名</label>
          <div className="flex gap-2">
            <input
              className={`${inputCls} flex-1`}
              value={path}
              onChange={(e) => setPath(e.target.value)}
              placeholder="cursor  或  C:\...\Cursor.exe"
            />
            <button
              onClick={pickExe}
              className="px-3 py-2 rounded-lg border border-gray-200 text-xs text-gray-600 hover:bg-gray-50 shrink-0"
              title="浏览文件"
            >
              📁 浏览
            </button>
          </div>

          {/* 搜索按钮 */}
          <button
            onClick={autoSearch}
            disabled={searching}
            className="mt-2 flex items-center gap-1.5 text-xs text-blue-600 hover:text-blue-800 disabled:opacity-50"
          >
            <span>{searching ? "🔍 搜索中…" : "🔍 自动搜索路径"}</span>
            <span className="text-gray-400">（根据名称在系统中查找）</span>
          </button>
        </div>

        <div className="flex justify-end gap-3 pt-2">
          <button onClick={onClose} className="px-4 py-2 rounded-lg text-sm border border-gray-200 text-gray-600 hover:bg-gray-50">
            取消
          </button>
          <button onClick={handleSave} className="px-4 py-2 rounded-lg text-sm bg-blue-600 text-white hover:bg-blue-700">
            保存
          </button>
        </div>
      </div>
    </Dialog>
  );
}

/* ── 主设置页 ───────────────────────────────────────────────────────────────── */
export function SettingsPage() {
  const showToast = useUIStore((s) => s.showToast);
  const qc = useQueryClient();

  const { data: spaces = [] } = useQuery({ queryKey: ["spaces"], queryFn: getSpaces });
  const spaceId = spaces.find((s) => s.is_current)?.id;

  const { data: settings } = useQuery({
    queryKey: ["settings", spaceId],
    queryFn: () => getSettings(spaceId),
    enabled: spaces.length > 0,
  });

  const [editors, setEditors] = useState<EditorEntry[]>([]);
  const [activeIds, setActiveIds] = useState<string[]>([]);  // 多选
  const [wslDistro, setWslDistro] = useState("Ubuntu");
  const [wslOpenMode, setWslOpenMode] = useState("wsl_internal");
  const [wslDistros, setWslDistros] = useState<string[]>([]);
  const [detectingWsl, setDetectingWsl] = useState(false);
  const [wslError, setWslError] = useState("");
  const [formTarget, setFormTarget] = useState<EditorEntry | null | undefined>(undefined);
  const [mcpPort, setMcpPort] = useState("27190");
  const [mcpHealth, setMcpHealth] = useState<McpHealthResult | null>(null);
  const [mcpChecking, setMcpChecking] = useState(false);

  useEffect(() => {
    if (settings) {
      setEditors(loadEditors(settings.editors));
      try {
        setActiveIds(settings.active_editors ? JSON.parse(settings.active_editors) : []);
      } catch {
        // 兼容旧的单选字段
        const old = settings.active_editor;
        setActiveIds(old ? [old] : []);
      }
      setWslDistro(settings.wsl_distro ?? "Ubuntu");
      setWslOpenMode(settings.wsl_open_mode ?? "wsl_internal");
      setMcpPort(settings.mcp_port ?? "27190");
      // 设置加载后自动检查一次 MCP 健康状态
      pingMcp();
    }
  }, [settings]);

  const saveAll = async (nextEditors: EditorEntry[], nextActiveIds: string[]) => {
    await updateSettings({
      editors: JSON.stringify(nextEditors),
      active_editors: JSON.stringify(nextActiveIds),
      wsl_distro: wslDistro,
    }, spaceId);
    qc.invalidateQueries({ queryKey: ["settings", spaceId] });
    showToast("设置已保存 ✓");
  };

  const handleSaveEditor = (entry: EditorEntry) => {
    const next = formTarget
      ? editors.map((e) => (e.id === entry.id ? entry : e))
      : [...editors, entry];
    // 新增时自动勾选
    const nextActive = formTarget ? activeIds : [...new Set([...activeIds, entry.id])];
    setEditors(next);
    setActiveIds(nextActive);
    setFormTarget(undefined);
    saveAll(next, nextActive);
  };

  const handleDelete = (id: string) => {
    const next = editors.filter((e) => e.id !== id);
    const nextActive = activeIds.filter((a) => a !== id);
    setEditors(next);
    setActiveIds(nextActive);
    saveAll(next, nextActive);
    showToast("已删除");
  };

  const toggleActive = (id: string) => {
    const next = activeIds.includes(id)
      ? activeIds.filter((a) => a !== id)
      : [...activeIds, id];
    setActiveIds(next);
    saveAll(editors, next);
  };

  const saveWSL = async () => {
    await updateSettings({ wsl_distro: wslDistro, wsl_open_mode: wslOpenMode }, spaceId);
    qc.invalidateQueries({ queryKey: ["settings", spaceId] });
    showToast("已保存 ✓");
  };

  const pingMcp = async () => {
    setMcpChecking(true);
    try {
      const result = await checkMcpHealth();
      setMcpHealth(result);
    } catch {
      setMcpHealth({ ok: false, port: parseInt(mcpPort, 10), error: "检测失败" });
    } finally {
      setMcpChecking(false);
    }
  };

  const saveMcp = async () => {
    const port = parseInt(mcpPort, 10);
    if (isNaN(port) || port < 1024 || port > 65535) {
      showToast("❌ 端口范围：1024 ~ 65535");
      return;
    }
    await updateSettings({ mcp_port: mcpPort });
    qc.invalidateQueries({ queryKey: ["settings", spaceId] });
    showToast("已保存，所有产品组成员配置已更新 ✓");
    pingMcp();
  };

  const detectWsl = async () => {
    setDetectingWsl(true);
    setWslError("");
    try {
      const list = await getWslDistros();
      setWslDistros(list);
      if (list.length > 0 && !list.includes(wslDistro)) {
        setWslDistro(list[0]);
      }
    } catch (e) {
      setWslError(String(e));
      setWslDistros([]);
    } finally {
      setDetectingWsl(false);
    }
  };

  return (
    <div className="max-w-xl mx-auto px-6 py-8 space-y-8 pb-12">
      <h1 className="text-lg font-semibold text-gray-800">设置</h1>

      {/* ── 编辑器管理 ── */}
      <section className="bg-white rounded-xl border border-gray-100 shadow-sm p-5 space-y-4">
        <div className="flex items-center justify-between">
          <h2 className="text-sm font-semibold text-gray-700">编辑器</h2>
          <button
            onClick={() => setFormTarget(null)}
            className="px-3 py-1.5 rounded-lg text-xs bg-blue-600 text-white hover:bg-blue-700"
          >
            + 新增编辑器
          </button>
        </div>

        {editors.length === 0 ? (
          <div className="text-center py-8 text-gray-400 text-sm border border-dashed border-gray-200 rounded-lg">
            <p className="text-2xl mb-2">🖥️</p>
            <p>还没有配置编辑器</p>
            <p className="text-xs mt-1">点击「新增编辑器」添加</p>
          </div>
        ) : (
          <div className="space-y-2">
            {editors.map((e) => {
              const checked = activeIds.includes(e.id);
              return (
                <div
                  key={e.id}
                  className={`flex items-center gap-3 p-3 rounded-lg border transition-colors ${
                    checked ? "border-blue-300 bg-blue-50" : "border-gray-100 bg-gray-50 hover:bg-gray-100"
                  }`}
                >
                  {/* 多选 checkbox */}
                  <input
                    type="checkbox"
                    checked={checked}
                    onChange={() => toggleActive(e.id)}
                    className="w-4 h-4 rounded accent-blue-600 shrink-0 cursor-pointer"
                  />

                  {/* 信息 */}
                  <div className="flex-1 min-w-0 cursor-pointer" onClick={() => toggleActive(e.id)}>
                    <div className="flex items-center gap-2">
                      <span className="text-sm font-medium text-gray-800">{e.name}</span>
                      {checked && (
                        <span className="px-1.5 py-0.5 rounded text-xs bg-blue-100 text-blue-700">已启用</span>
                      )}
                    </div>
                    <div className="text-xs font-mono text-gray-400 truncate" title={e.path}>
                      {e.path}
                    </div>
                  </div>

                  {/* 操作 */}
                  <div className="flex gap-1 shrink-0">
                    <button
                      onClick={() => setFormTarget(e)}
                      className="px-2 py-1 rounded text-xs border border-gray-200 text-gray-500 hover:bg-white"
                    >
                      编辑
                    </button>
                    <button
                      onClick={() => handleDelete(e.id)}
                      className="px-2 py-1 rounded text-xs border border-red-200 text-red-500 hover:bg-red-50"
                    >
                      删除
                    </button>
                  </div>
                </div>
              );
            })}
          </div>
        )}

        <p className="text-xs text-gray-400">
          勾选的编辑器会在项目卡片上各显示一个按钮，可同时启用多个。
        </p>
      </section>

      {/* ── WSL ── */}
      <section className="bg-white rounded-xl border border-gray-100 shadow-sm p-5 space-y-4">
        <h2 className="text-sm font-semibold text-gray-700">WSL 配置</h2>

        {/* 检测按钮 */}
        <div className="flex items-center gap-3">
          <button
            onClick={detectWsl}
            disabled={detectingWsl}
            className="px-3 py-1.5 rounded-lg text-xs border border-gray-200 text-gray-600 hover:bg-gray-50 disabled:opacity-50"
          >
            {detectingWsl ? "检测中…" : "检测已安装的 WSL 发行版"}
          </button>
          {wslDistros.length > 0 && (
            <span className="text-xs text-green-600">✓ 检测到 {wslDistros.length} 个发行版</span>
          )}
          {wslError && (
            <span className="text-xs text-red-500">{wslError}</span>
          )}
        </div>

        {/* 发行版选择 */}
        <div>
          <label className="block text-xs font-medium text-gray-500 mb-1">当前使用的发行版</label>
          {wslDistros.length > 0 ? (
            <select
              className={inputCls}
              value={wslDistro}
              onChange={(e) => setWslDistro(e.target.value)}
            >
              {wslDistros.map((d) => (
                <option key={d} value={d}>{d}</option>
              ))}
            </select>
          ) : (
            <input
              className={inputCls}
              value={wslDistro}
              onChange={(e) => setWslDistro(e.target.value)}
              placeholder="Ubuntu"
            />
          )}
          <p className="text-xs text-gray-400 mt-1">
            路径前缀：
            <code className="font-mono bg-gray-50 px-1 rounded">
              \\wsl.localhost\{wslDistro}\
            </code>
          </p>
        </div>

        {/* 编辑器启动方式 */}
        <div>
          <label className="block text-xs font-medium text-gray-500 mb-2">编辑器启动方式</label>
          <div className="space-y-2">
            {([
              {
                value: "wsl_internal",
                label: "从 WSL 内部启动（推荐）",
                desc: `wsl -d ${wslDistro} -- <editor> <linux-path>`,
              },
              {
                value: "remote_flag",
                label: "Remote WSL 参数",
                desc: `<editor> --remote wsl+${wslDistro} <linux-path>`,
              },
              {
                value: "unc_path",
                label: "Windows UNC 路径（不走 Remote）",
                desc: `<editor> \\\\wsl.localhost\\${wslDistro}\\<path>`,
              },
            ] as const).map(({ value, label, desc }) => (
              <label
                key={value}
                className={`flex items-start gap-3 p-3 rounded-lg border cursor-pointer transition-colors ${
                  wslOpenMode === value
                    ? "border-blue-300 bg-blue-50"
                    : "border-gray-100 bg-gray-50 hover:bg-gray-100"
                }`}
              >
                <input
                  type="radio"
                  name="wslOpenMode"
                  value={value}
                  checked={wslOpenMode === value}
                  onChange={() => setWslOpenMode(value)}
                  className="mt-0.5 accent-blue-600 shrink-0"
                />
                <div className="min-w-0">
                  <div className="text-sm font-medium text-gray-800">{label}</div>
                  <code className="text-xs font-mono text-gray-400 break-all">{desc}</code>
                </div>
              </label>
            ))}
          </div>
        </div>

        <div className="flex justify-end">
          <button
            onClick={saveWSL}
            className="px-4 py-2 rounded-lg text-sm bg-blue-600 text-white hover:bg-blue-700"
          >
            保存
          </button>
        </div>
      </section>

      {/* ── MCP 服务 ── */}
      <section className="bg-white rounded-xl border border-gray-100 shadow-sm p-5 space-y-4">
        <div className="flex items-center gap-2">
          <h2 className="text-sm font-semibold text-gray-700">MCP 服务</h2>
          {mcpChecking ? (
            <span className="text-xs text-gray-400">检测中…</span>
          ) : mcpHealth ? (
            mcpHealth.ok ? (
              <span className="flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs bg-green-50 text-green-600 border border-green-200">
                <span className="w-1.5 h-1.5 rounded-full bg-green-500 inline-block" />
                运行中 {mcpHealth.latency_ms}ms
              </span>
            ) : (
              <span className="flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs bg-red-50 text-red-600 border border-red-200" title={mcpHealth.error}>
                <span className="w-1.5 h-1.5 rounded-full bg-red-500 inline-block" />
                端口未响应
              </span>
            )
          ) : null}
          <button onClick={pingMcp} className="text-xs text-gray-400 hover:text-gray-600">↻</button>
        </div>
        <p className="text-xs text-gray-400">
          产品组 MCP Server，为组内项目的 Claude 提供跨项目上下文能力。配置好产品组成员后，每个项目的
          {" "}<code className="font-mono bg-gray-50 px-1 rounded">.claude/settings.json</code>{" "}
          会自动注入对应的 MCP 连接。
        </p>
        <div>
          <label className="block text-xs font-medium text-gray-500 mb-1">监听端口</label>
          <div className="flex gap-2 items-center">
            <input
              type="number"
              className="w-28 border border-gray-200 rounded-lg px-3 py-2 text-sm font-mono outline-none focus:ring-2 focus:ring-blue-200 focus:border-blue-400"
              value={mcpPort}
              onChange={(e) => setMcpPort(e.target.value)}
              min={1024}
              max={65535}
            />
            <button
              onClick={saveMcp}
              className="px-4 py-2 rounded-lg text-sm bg-blue-600 text-white hover:bg-blue-700"
            >
              保存
            </button>
          </div>
          <p className="text-xs text-gray-400 mt-1.5">
            产品组 MCP 地址：
            <code className="font-mono bg-gray-50 px-1 rounded">
              http://localhost:{mcpPort}/mcp/group/&lt;group_id&gt;
            </code>
          </p>
        </div>
      </section>

      {/* 编辑器表单弹窗 */}
      {formTarget !== undefined && (
        <EditorFormModal
          entry={formTarget}
          onClose={() => setFormTarget(undefined)}
          onSave={handleSaveEditor}
        />
      )}

      <BatchSyncSection />
      <GiteaInstancesSection />
      <UpdateSection />
    </div>
  );
}

/* ── 蓝图规则批量同步区块 ────────────────────────────────────────────────────── */

interface SyncRow {
  name: string;
  path: string;
  status: "pending" | "ok" | "error";
  result?: SyncResult;
  error?: string;
}

function BatchSyncSection() {
  const [rows, setRows] = useState<SyncRow[]>([]);
  const [running, setRunning] = useState(false);
  const [done, setDone] = useState(false);

  const handleSync = async () => {
    setRunning(true);
    setDone(false);

    let projects: Awaited<ReturnType<typeof getProjects>> = [];
    try {
      projects = await getProjects(undefined, undefined);
    } catch {
      setRunning(false);
      return;
    }

    // 只处理有 win_path 的项目
    const targets = projects
      .filter((p) => p.win_path)
      .map((p) => ({ name: p.name, path: p.win_path! }));

    if (targets.length === 0) {
      setRows([]);
      setRunning(false);
      setDone(true);
      return;
    }

    const initial: SyncRow[] = targets.map((t) => ({ ...t, status: "pending" }));
    setRows(initial);

    const results = [...initial];
    for (let i = 0; i < results.length; i++) {
      try {
        const result = await syncBlueprintRules(results[i].path);
        results[i] = { ...results[i], status: "ok", result };
      } catch (e) {
        results[i] = { ...results[i], status: "error", error: String(e) };
      }
      setRows([...results]);
    }

    setRunning(false);
    setDone(true);
  };

  return (
    <section className="bg-white rounded-xl border border-gray-200 shadow-sm px-6 py-5">
      <div className="flex items-center justify-between mb-4">
        <div>
          <h2 className="text-sm font-semibold text-gray-700">蓝图规则同步</h2>
          <p className="text-xs text-gray-400 mt-0.5">
            将最新 CONVENTIONS.md 同步至所有已注册项目
          </p>
        </div>
        <button
          onClick={handleSync}
          disabled={running}
          className="px-4 py-1.5 rounded-lg border border-gray-200 text-sm text-gray-600 hover:bg-gray-50 disabled:opacity-50 transition-colors"
        >
          {running ? "同步中…" : "同步所有项目"}
        </button>
      </div>

      {rows.length > 0 && (
        <div className="space-y-1.5 max-h-60 overflow-y-auto">
          {rows.map((row) => (
            <div
              key={row.path}
              className={`flex items-start gap-2 px-3 py-2 rounded-lg text-xs ${
                row.status === "ok"
                  ? "bg-green-50 text-green-800"
                  : row.status === "error"
                  ? "bg-red-50 text-red-700"
                  : "bg-gray-50 text-gray-400"
              }`}
            >
              <span className="shrink-0 mt-0.5">
                {row.status === "ok" ? "✓" : row.status === "error" ? "✗" : "…"}
              </span>
              <div className="min-w-0">
                <span className="font-medium">{row.name}</span>
                {row.status === "ok" && row.result && (
                  <span className="ml-2 opacity-70">{row.result.message}</span>
                )}
                {row.status === "error" && (
                  <span className="ml-2 font-mono break-all">{row.error}</span>
                )}
              </div>
            </div>
          ))}
        </div>
      )}

      {done && rows.length === 0 && (
        <p className="text-xs text-gray-400">没有找到包含 win_path 的项目。</p>
      )}
    </section>
  );
}

/* ── 检查更新区块 ─────────────────────────────────────────────────────────────── */

type UpdateState =
  | { status: "idle" }
  | { status: "checking" }
  | { status: "latest" }
  | { status: "available"; version: string; notes: string; downloadAndInstall: () => Promise<void> }
  | { status: "downloading"; progress: number }
  | { status: "error"; message: string };

function UpdateSection() {
  const [appVersion, setAppVersion] = useState<string>("");
  const [state, setState] = useState<UpdateState>({ status: "idle" });

  useEffect(() => {
    getVersion().then(setAppVersion).catch(() => {});
  }, []);

  const handleCheck = async () => {
    setState({ status: "checking" });
    try {
      const update = await checkUpdate();
      if (!update?.available) {
        setState({ status: "latest" });
        return;
      }
      setState({
        status: "available",
        version: update.version,
        notes: update.body ?? "",
        downloadAndInstall: async () => {
          let downloaded = 0;
          let total = 0;
          await update.downloadAndInstall((event) => {
            if (event.event === "Started") {
              total = event.data.contentLength ?? 0;
              setState({ status: "downloading", progress: 0 });
            } else if (event.event === "Progress") {
              downloaded += event.data.chunkLength;
              setState({ status: "downloading", progress: total > 0 ? Math.round((downloaded / total) * 100) : 0 });
            } else if (event.event === "Finished") {
              setState({ status: "idle" });
            }
          });
          await relaunch();
        },
      });
    } catch (e) {
      setState({ status: "error", message: String(e) });
    }
  };

  return (
    <section className="bg-white rounded-xl border border-gray-200 shadow-sm px-6 py-5">
      <h2 className="text-sm font-semibold text-gray-700 mb-4">关于 & 更新</h2>
      <div className="flex items-center justify-between">
        <div>
          <p className="text-sm text-gray-700">炼境</p>
          <p className="text-xs text-gray-400 mt-0.5">当前版本 v{appVersion}</p>
        </div>
        <div className="flex items-center gap-3">
          {state.status === "available" && (
            <span className="text-xs text-blue-600 bg-blue-50 border border-blue-200 px-2 py-0.5 rounded-full">
              v{state.version} 可用
            </span>
          )}
          {state.status === "latest" && (
            <span className="text-xs text-green-600">已是最新版本</span>
          )}
          {state.status === "downloading" && (
            <span className="text-xs text-blue-600">下载中 {state.progress}%</span>
          )}
          {state.status === "available" ? (
            <button
              onClick={state.downloadAndInstall}
              className="px-4 py-1.5 rounded-lg bg-blue-600 text-white text-sm hover:bg-blue-700 transition-colors"
            >
              立即更新
            </button>
          ) : (
            <button
              onClick={handleCheck}
              disabled={state.status === "checking" || state.status === "downloading"}
              className="px-4 py-1.5 rounded-lg border border-gray-200 text-sm text-gray-600 hover:bg-gray-50 disabled:opacity-50 transition-colors"
            >
              {state.status === "checking" ? "检查中…" : "检查更新"}
            </button>
          )}
        </div>
      </div>

      {state.status === "error" && (
        <div className="mt-3 px-3 py-2.5 bg-red-50 border border-red-200 rounded-lg">
          <div className="flex items-start justify-between gap-2">
            <p className="text-xs text-red-600 break-all font-mono flex-1 select-text">
              {state.message}
            </p>
            <button
              onClick={() => {
                navigator.clipboard.writeText(state.message);
              }}
              className="text-xs text-red-400 hover:text-red-600 shrink-0 transition-colors"
            >
              复制
            </button>
          </div>
        </div>
      )}
    </section>
  );
}
