import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { invoke } from "@tauri-apps/api/core";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { WebviewWindow } from "@tauri-apps/api/webviewWindow";
import { revealItemInDir } from "@tauri-apps/plugin-opener";
import { ask, message } from "@tauri-apps/plugin-dialog";
import { Button, Divider, InputNumber, Layout, Menu, Space, theme, Tooltip } from "antd";
import {
  countPendingTasks,
  listPendingTasks,
  markTaskDone,
  updateTaskSiteUrl,
  updateTaskTags,
  type Task,
} from "./lib/tasks";
import {
  EVT_RECORDING_FAILED,
  EVT_RECORDING_FINISHED,
  EVT_RECORD_SHORTCUT,
  EVT_SCREENSHOT_FAILED,
  EVT_SCREENSHOT_FINISHED,
  EVT_TASK_PAGE_READY,
  EVT_TASK_PAGE_TIMEOUT,
  EVT_TASK_SITE_URL_FOUND,
  EVT_TASK_TAGS_EXTRACTED,
  EVT_TASKS_IMPORTED,
  type RecordingFailedPayload,
  type RecordingFinishedPayload,
  type RecordShortcutPayload,
  type RecordState,
  type ScreenshotFailedPayload,
  type ScreenshotFinishedPayload,
  type TaskAssets,
  type TaskPageReadyPayload,
  type TaskPageTimeoutPayload,
  type TaskSiteUrlFoundPayload,
  type TaskTagsExtractedPayload,
} from "./types/ipc";
import { capturePage } from "./lib/capture";
import {
  cancelRecording,
  getRecordState,
  startRecording,
  stopRecording,
  subscribeRecordState,
} from "./lib/recorder";
import "./App.css";
import {
  CheckCircleOutlined,
  CloseOutlined,
  FileImageOutlined,
  FolderOpenOutlined,
  LeftOutlined,
  LinkOutlined,
  LoadingOutlined,
  MinusOutlined,
  OrderedListOutlined,
  PictureOutlined,
  PlayCircleFilled,
  PlusOutlined,
  RedoOutlined,
  StopOutlined,
  VideoCameraOutlined,
} from "@ant-design/icons";
import { asyncSleep } from "./lib/utils";

const { Header, Content, Sider } = Layout;

/** showStatus 支持的三类语义，对应底部状态栏色块颜色 + 原生对话框 kind */
type StatusKind = "success" | "error" | "warning";
const STATUS_COLOR: Record<StatusKind, string> = {
  success: "#52c41a",
  error: "#ff4d4f",
  warning: "#faad14",
};
const STATUS_DIALOG_KIND: Record<StatusKind, "info" | "warning" | "error"> = {
  success: "info",
  error: "error",
  warning: "warning",
};

/**
 * 与 Rust 端常量保持一致（src-tauri/src/lib.rs）：
 *   LEFT_PANEL_WIDTH = 180
 *   TOOLBAR_HEIGHT   = 48
 *
 * 导出以避免 noUnusedLocals 误报；后续如有组件需要可直接 import。
 */

/** 工具栏右侧的尺寸预设（统一横屏：宽 × 高） */
const SIZE_PRESETS = [
  { label: "1280×720", w: 1280, h: 720 },
  { label: "720x1280", w: 720, h: 1280 },
  { label: "1920×1080", w: 1920, h: 1080 },
  { label: "1024×768", w: 1024, h: 768 }
];



/** 点条目：把 url 加载到子 webview，并把 task_id 一并下发（用于截图命名 + 自动截图回调） */
async function loadInWebview(taskId: string, url: string) {
  try {
    await invoke("clean_webview");
    await asyncSleep(200);
  } catch (e) {

  }
  try {
    await invoke("navigate_webview", { taskId, url });
  } catch (e) {
    console.error("webview 加载失败:", e);
  }
}


function App() {
  // 当前激活的任务 id（仅用于左栏视觉高亮）
  const [activeId, setActiveId] = useState<string | null>(null);
  const activeIdRef = useRef(activeId);
  activeIdRef.current = activeId;
  const activeTaskRef = useRef<Task | undefined>(undefined);

  useLayoutEffect(() => {
    activeTaskRef.current = activeId ? tasks.find(({ id }) => id == activeId) : undefined;
  }, [activeId]);

  // 待处理任务（status=0）的真实列表，来自 sqlite
  const [tasks, setTasks] = useState<Task[]>([]);

  const [contentSize, setContentSize] = useState<{ w: number; h: number }>({ w: 1280, h: 720 });
  const contentSizeRef = useRef(contentSize);
  const [customMode, setCustomMode] = useState(false);
  const isCustomSize = useCallback(() => {
    return !SIZE_PRESETS.find(({ w, h }) => w == contentSize.w && h == contentSize.h);
  }, [contentSize])
  // 录制状态机（由 lib/recorder.ts 内部单例维护，这里订阅以驱动按钮）
  const [recordState, setRecordState] = useState<RecordState>(getRecordState());

  // 当前任务的产物文件信息（截图 / 录制 mp4 是否存在 + 路径），驱动右侧三个按钮的 disabled
  const [assets, setAssets] = useState<TaskAssets | null>(null);

  /**
   * landing 流转门控：true 表示已加载到最终 (Site) URL，可截图 / 录制。
   * - handleSelectTask 触发 navigate 时强制 false
   * - 监听 EVT_TASK_PAGE_READY 后变 true
   * - 监听 EVT_TASK_PAGE_TIMEOUT 时保持 false，弹窗让用户「重试 / 取消」
   */
  const [pageReady, setPageReady] = useState(false);


  const [isWorking, setIsWorking] = useState(false);

  const [isLoading, setIsLoading] = useState(false);

  // activeId 的最新值快照，供 listen 闭包中读取（避免在每个 effect 上加 activeId 依赖
  // 导致 listen 频繁重订）


  // 底部状态栏文案与左侧色块；showStatus 同时弹原生对话框 + 写状态栏
  const [statusColor, setStatusColor] = useState("#888");
  const [statusText, setStatusText] = useState("请选择任务");

  /**
   * 统一的状态提示通道：
   *   1) 调 @tauri-apps/plugin-dialog 的 message() 弹一个原生对话框
   *   2) 同步把 statusText / statusColor 更新到底部状态栏
   * 对话框为非阻塞触发（fire-and-forget），失败仅打日志兜底
   */
  const showStatus = useCallback(
    (kind: StatusKind, title: string, description?: string, dlg?: boolean) => {
      setStatusColor(STATUS_COLOR[kind]);
      setStatusText(title);
      dlg !== false && void message(description ? `${title}\n${description}` : title, {
        title: "提示",
        kind: STATUS_DIALOG_KIND[kind],
      }).catch((e) => console.error("显示对话框失败:", e));
    },
    [],
  );

  /**
   * 重新拉取 sqlite 中 status=0 的任务列表。
   * 调用场景：
   *   - 首次挂载
   *   - 监听到 EVT_TASKS_IMPORTED（导入窗口写入完）
   *   - "完成" 按钮把当前任务标记为已完成后
   */
  const reloadTasks = useCallback(async (): Promise<Task[]> => {
    try {
      const list = await listPendingTasks();
      setTasks(list);

      if (!activeId && list.length > 0) {
        await asyncSleep(500);
        handleSelectTask({ key: list[0].id });
      }

      return list;
    } catch (e) {
      console.error("加载任务列表失败:", e);
      showStatus("error", "加载任务失败", String(e));
      return [];
    }
  }, [showStatus]);

  /** 启动「批量导入 URL」窗口（独立 WebviewWindow，label = "import"） */
  const openImportWindow = useCallback(async () => {
    try {
      // 若已存在直接 setFocus，避免重复创建
      const existing = await WebviewWindow.getByLabel("import");
      if (existing) {
        await existing.setFocus();
        return;
      }
      const w = new WebviewWindow("import", {
        url: "import.html",
        title: "导入 URL",
        width: 560,
        height: 480,
        resizable: true,
        // 主窗口是 decorations: false，这里给原生标题栏方便用户拖动/关闭
        decorations: true,
      });
      w.once("tauri://error", (e) => {
        console.error("导入窗口创建失败:", e);
      });
    } catch (e) {
      console.error("openImportWindow 失败:", e);
      showStatus("error", "无法打开导入窗口", String(e));
    }
  }, [showStatus]);

  const handleSelectTask = useCallback(({ key }: { key: string }) => {
    const t = tasks.find((v) => v.id == key);
    if (!t) {
      return;
    }
    showStatus("success", "任务开始", "任务开始", false);
    setIsWorking(true);
    setActiveId(t.id);
    // 新一轮 landing 流转开始：先把按钮门控锁住，等 Rust 端发 task-page-ready 再放开
    setPageReady(false);
    void loadInWebview(t.id, t.url);
  }, [tasks, showStatus, setIsWorking, setActiveId, setPageReady]);
  const handleStopTask = useCallback(() => {
    setActiveId(null);
    setIsLoading(false);
    setIsWorking(false);
    invoke("clean_webview");
  }, []);
  /** 重新查询指定任务的产物文件信息（截图 + mp4） */
  const refreshAssets = useCallback(async (taskId: string | null) => {
    if (!taskId) {
      setAssets(null);
      return;
    }
    try {
      const a = await invoke<TaskAssets>("query_task_assets", { taskId });
      setAssets(a);
    } catch (e) {
      console.error("query_task_assets 失败:", e);
      setAssets(null);
    }
  }, []);

  // activeId 变化时刷新 assets（切换任务后右侧按钮要按新任务的文件存在性变 enabled/disabled）
  useEffect(() => {
    void refreshAssets(activeId);
  }, [activeId, refreshAssets]);

  // 首次挂载：从 sqlite 加载待处理任务；若为空则拉起「批量导入 URL」窗口。
  // 依赖项故意只放 reloadTasks / openImportWindow（都用 useCallback 稳定引用），
  // 避免 tasks 变化导致再次拉起导入窗口。
  useEffect(() => {
    (async () => {
      const pending = await countPendingTasks().catch(() => 0);
      const list = await reloadTasks();
      if (pending === 0 && list.length === 0) {
        await asyncSleep(500);
        openImportWindow();
      }
    })();

  }, []);

  // 监听导入窗口发出的 tasks-imported 事件，刷新主列表
  useEffect(() => {
    let unlisten: UnlistenFn | null = null;
    let disposed = false;
    (async () => {
      const u = await listen<{ count: number }>(EVT_TASKS_IMPORTED, (e) => {
        showStatus("success", "导入完成", `已写入 ${e.payload.count} 条任务`);
        void reloadTasks();
      });
      if (disposed) u();
      else unlisten = u;
    })();
    return () => {
      disposed = true;
      unlisten?.();
    };
  }, [showStatus, reloadTasks]);

  /**
   * 监听 landing 三个事件：
   *   - task-tags-extracted：中间页抓到标签 → 写库 + reload
   *   - task-page-ready：跳到最终 URL 完成 → 解锁按钮 + 状态栏提示
   *   - task-page-timeout：未找到 Visit Site → ask() 弹「重试 / 取消」
   * 全部按 activeIdRef 过滤，避免给已切走的旧任务弹无效提示。
   */
  useEffect(() => {
    let unlistenTags: UnlistenFn | null = null;
    let unlistenSite: UnlistenFn | null = null;
    let unlistenReady: UnlistenFn | null = null;
    let unlistenTimeout: UnlistenFn | null = null;
    let unlistenPageStarted: UnlistenFn | null = null;
    let unlistenPageLoaded: UnlistenFn | null = null;
    let disposed = false;

    (async () => {
      const u1 = await listen<TaskTagsExtractedPayload>(
        EVT_TASK_TAGS_EXTRACTED,
        async (e) => {
          const { taskId, tags } = e.payload;
          setAssets((assets) => {
            if (assets?.task_id !== taskId) {
              return assets;
            }
            return { ...assets, tags };
          });
          try {
            await updateTaskTags(taskId, tags);
            // await reloadTasks();
            showStatus("success", "已提取标签", tags || "(空)", false);
          } catch (err) {
            console.error("写入 tags 失败:", err);
            showStatus("error", "写入标签失败", String(err));
          }
        },
      );
      const u2 = await listen<TaskSiteUrlFoundPayload>(
        EVT_TASK_SITE_URL_FOUND,
        async (e) => {
          const { taskId, url } = e.payload;
          if (!url) return;
          setAssets((assets) => {
            if (assets?.task_id !== taskId) {
              return assets;
            }
            return { ...assets, site_url: url };
          });
          try {
            await updateTaskSiteUrl(taskId, url);
            // await reloadTasks();
          } catch (err) {
            console.error("写入 site_url 失败:", err);
          }
        },
      );
      const u3 = await listen<TaskPageReadyPayload>(EVT_TASK_PAGE_READY, (e) => {
        const { taskId } = e.payload;
        // 只对当前激活任务生效，避免切走后还把按钮放开
        if (activeIdRef.current !== taskId) return;
        setPageReady(true);
        setIsWorking(false);
        showStatus("success", "页面就绪", "已加载到最终 URL，可截图 / 录制", false);
      });
      const u4 = await listen<TaskPageTimeoutPayload>(
        EVT_TASK_PAGE_TIMEOUT,
        async (e) => {
          const { taskId, url, reason } = e.payload;
          if (activeIdRef.current !== taskId) return;
          // 弹原生 ask 对话框：确认 = 重试；取消 = 保持禁用
          const retry = await ask(`${reason}\n\n是否刷新重试？`, {
            title: "未找到 网站 链接",
            kind: "warning",
            okLabel: "重试",
            cancelLabel: "取消",
          }).catch(() => false);
          if (retry) {
            // 重新 navigate：Rust 端会重置 page_stage=Initial 并重新轮询
            setPageReady(false);
            await loadInWebview(taskId, url);
          } else {
            setActiveId(null);

            setIsLoading(false);
            setIsWorking(false);
            // 用户取消：保持按钮禁用，写状态栏告知
            showStatus("warning", "已取消任务", "重新点击任务可继续尝试");
          }
        },
      );

      const u5 = await listen("page_started", () => {
        setIsLoading(true);
      })
      const u6 = await listen("page_loaded", () => {
        setIsLoading(false);
      });

      if (disposed) {
        u1();
        u2();
        u3();
        u4();
        u5();
        u6();
      } else {
        unlistenTags = u1;
        unlistenSite = u2;
        unlistenReady = u3;
        unlistenTimeout = u4;
        unlistenPageStarted = u5;
        unlistenPageLoaded = u6;
      }
    })();

    return () => {
      disposed = true;
      unlistenTags?.();
      unlistenSite?.();
      unlistenReady?.();
      unlistenTimeout?.();
      unlistenPageStarted?.();
      unlistenPageLoaded?.();
    };
  }, [showStatus, setAssets]);

  /** 工具栏 "完成" 按钮：把当前激活任务标记为已完成（status=1）→ 刷新 → 清空选中 */
  const handleCompleteTask = useCallback(async () => {
    if (!activeId) return;
    try {
      await markTaskDone(activeId);
      showStatus("success", "任务已完成", `任务 ${activeId} 已从列表移除`, false);
      setActiveId(null);

      setIsLoading(false);
      setIsWorking(false);
      await asyncSleep(500);
      const list = await reloadTasks();
      // 若已无任务，自动再拉起一次导入窗口（用户可继续粘贴）
      if (list.length === 0) {
        await openImportWindow();
      }

    } catch (e) {
      showStatus("error", "标记完成失败", String(e));
    }
  }, [activeId, reloadTasks, openImportWindow, showStatus]);

  /** 手动截图：交给 Rust 命令，结果通过事件回推（统一与自动截图的提示路径） */
  const handleManualCapture = useCallback(async () => {
    // 必须已加载到最终页才能截图，否则截到的是中间页
    if (!activeId || !pageReady) return;
    try {
      await capturePage(activeId);
    } catch (e) {
      // Rust 侧失败时已经 emit 过 screenshot-failed，这里仅打日志兜底
      console.error("手动截图失败:", e);
    }
  }, [activeId, pageReady]);

  // 订阅 Rust 端的截图事件，统一走 showStatus（原生 dialog + 状态栏）
  useEffect(() => {
    let unlistenFinished: UnlistenFn | null = null;
    let unlistenFailed: UnlistenFn | null = null;
    let disposed = false;

    (async () => {
      const u1 = await listen<ScreenshotFinishedPayload>(EVT_SCREENSHOT_FINISHED, (e) => {
        const { taskId, path, auto } = e.payload;
        showStatus(
          "success",
          auto ? "自动截图完成" : "截图完成",
          `任务 ${taskId} → ${path}`, false
        );
        // 当前选中即此任务时刷新 assets，让预览图按钮立即变可点
        if (activeIdRef.current === taskId) {
          void refreshAssets(taskId);
        }
      });
      const u2 = await listen<ScreenshotFailedPayload>(EVT_SCREENSHOT_FAILED, (e) => {
        const { taskId, auto, error } = e.payload;
        showStatus(
          "error",
          auto ? "自动截图失败" : "截图失败",
          `任务 ${taskId}：${error}`,
        );
      });

      if (disposed) {
        u1();
        u2();
      } else {
        unlistenFinished = u1;
        unlistenFailed = u2;
      }
    })();

    return () => {
      disposed = true;
      unlistenFinished?.();
      unlistenFailed?.();
    };
  }, [showStatus, refreshAssets]);

  // 订阅录制状态机变化，驱动按钮 UI 切换
  useEffect(() => {
    return subscribeRecordState(setRecordState);
  }, []);

  // 订阅 Rust 端的录制事件（finalize 转码完成 / 失败），统一走 showStatus。
  // 与截图事件订阅采用同一套 disposed 模式，避免 StrictMode 双调用的清理竞态。
  useEffect(() => {
    let unlistenFinished: UnlistenFn | null = null;
    let unlistenFailed: UnlistenFn | null = null;
    let disposed = false;

    (async () => {
      const u1 = await listen<RecordingFinishedPayload>(EVT_RECORDING_FINISHED, async (e) => {
        const { taskId, path } = e.payload;
        showStatus("success", "录制完成", `任务 ${taskId} → ${path}`, false);
        if (activeIdRef.current === taskId) {
          await refreshAssets(taskId);
          await asyncSleep(100);
          handleCompleteTask();
        }
      });
      const u2 = await listen<RecordingFailedPayload>(EVT_RECORDING_FAILED, (e) => {
        const { taskId, error } = e.payload;
        showStatus("error", "录制失败", `任务 ${taskId}：${error}`);
      });

      if (disposed) {
        u1();
        u2();
      } else {
        unlistenFinished = u1;
        unlistenFailed = u2;
      }
    })();

    return () => {
      disposed = true;
      unlistenFinished?.();
      unlistenFailed?.();
    };
  }, [showStatus, refreshAssets]);

  // 组件卸载兜底：若正在录制 / 暂停 / 转码中，主动取消，避免悬挂 stream
  useEffect(() => {
    return () => {
      void cancelRecording();
    };
  }, []);

  /** 开始按钮：仅在 idle + 已选任务 + 页面已就绪 时可点；Rust 端 spawn ffmpeg 子进程录屏 */
  const handleStartRecord = useCallback(async () => {
    if (recordState !== "idle" || !activeId || !pageReady) return;
    try {
      await startRecording(activeId, contentSize.w, contentSize.h);
    } catch (e) {
      showStatus("error", "开始录制失败", String(e));
    }
  }, [recordState, activeId, pageReady, contentSize, showStatus]);

  /** 停止按钮：触发 ffmpeg 优雅退出 + 落盘 */
  const handleStopRecord = useCallback(async () => {
    if (recordState !== "recording") return;
    try {
      await stopRecording();
      // 成功时由 EVT_RECORDING_FINISHED 事件统一提示；这里不重复弹
    } catch (e) {
      // 本地异常（如内部状态不一致）兜底提示；ffmpeg 失败由 EVT_RECORDING_FAILED 处理
      showStatus("error", "停止录制失败", String(e));
    }
  }, [recordState, showStatus]);

  /** 全局快捷键派发：F9 = 开始，F11 = 停止 */
  const handleShortcut = useCallback(
    async (action: "start" | "stop") => {
      try {
        if (action === "start") {
          if (recordState !== "idle") return;
          if (!activeId) {
            showStatus("warning", "无法开始录制", "请先在左栏选择一个任务");
            return;
          }
          if (!pageReady) {
            showStatus("warning", "无法开始录制", "等待最终页加载完成后再试");
            return;
          }
          await startRecording(activeId, contentSize.w, contentSize.h);

        } else if (action === "stop") {
          if (recordState === "recording") {
            await stopRecording();
          }
        }
      } catch (e) {
        showStatus("error", "快捷键操作失败", String(e));
      }
    },
    [recordState, activeId, pageReady, contentSize, showStatus],
  );

  // 订阅 Rust 端的全局快捷键事件 (F9 / F11)
  useEffect(() => {
    let unlisten: UnlistenFn | null = null;
    let disposed = false;
    (async () => {
      const u = await listen<RecordShortcutPayload>(EVT_RECORD_SHORTCUT, (e) => {
        void handleShortcut(e.payload.action);
      });
      if (disposed) u();
      else unlisten = u;
    })();
    return () => {
      disposed = true;
      unlisten?.();
    };
  }, [handleShortcut]);

  /** 打开当前任务产物所在的文件夹：优先 reveal mp4 → png → 兜底打开 screenshots 目录 */
  const handleOpenFolder = useCallback(async () => {
    if (!assets) return;
    const target = assets.recording_exists
      ? assets.recording_path
      : assets.screenshot_exists
        ? assets.screenshot_path
        : assets.screenshots_dir;
    try {
      await revealItemInDir(target);
    } catch (e) {
      showStatus("error", "打开文件夹失败", String(e));
    }
  }, [assets, showStatus]);

  /** 在新 WebviewWindow 中预览截图 */
  const handlePreviewImage = useCallback(async () => {
    if (!assets?.screenshot_exists) return;
    try {
      await invoke("open_preview_window", {
        path: assets.screenshot_path,
        kind: "image",
      });
    } catch (e) {
      showStatus("error", "图片预览失败", String(e));
    }
  }, [assets, showStatus]);

  /** 在新 WebviewWindow 中预览录制 mp4 */
  const handlePreviewVideo = useCallback(async () => {
    if (!assets?.recording_exists) return;
    try {
      await invoke("open_preview_window", {
        path: assets.recording_path,
        kind: "video",
      });
    } catch (e) {
      showStatus("error", "视频预览失败", String(e));
    }
  }, [assets, showStatus]);

  // 开始按钮的图标 / tooltip
  const startConfig = (() => {
    if (recordState === "stopping") {
      return { icon: <LoadingOutlined />, tip: "正在停止…" };
    }
    if (recordState === "recording") {
      // 视觉上保持 Play 图标，但 disabled
      return { icon: <PlayCircleFilled />, tip: "录制中…（F11 停止）" };
    }
    if (!activeId) {
      return { icon: <PlayCircleFilled />, tip: "请先选择任务" };
    }
    if (!pageReady) {
      return { icon: <LoadingOutlined />, tip: "等待最终 URL 加载完成…" };
    }
    return { icon: <PlayCircleFilled />, tip: "开始录制（F9）" };
  })();

  // 开始按钮仅在 idle + 已选任务 + 最终页已就绪时可点
  const startDisabled = recordState !== "idle" || !activeId || !pageReady;
  // 停止按钮仅在 recording 时可点
  const stopDisabled = recordState !== "recording";

  const [collapsed, setCollapsed] = useState(false);

  const applyWorkAreaSize = useCallback(async (w?: number, h?: number) => {
    if (w && h) {
      setContentSize({ w, h });
      contentSizeRef.current = { w, h };
    } else {
      w = contentSize.w;
      h = contentSize.h;
    }
    try {
      // 先调主窗口，避免子 webview 越界
      await invoke("set_window_size", {
        width: w + (collapsed ? 30 : 180) + 4,
        height: h + 48 + 24 + 4,
        contentX: (collapsed ? 30 : 180) + 2,
        contentY: 50,
        contentWidth: w,
        contentHeight: h
      });
    } catch (e) {
      console.error("调整尺寸失败 ", e);
    }
  }, [contentSize, setContentSize]);

  useEffect(() => {
    applyWorkAreaSize();
  }, []);

  // 录制状态机（RecordState 类型已抽到 src/types/ipc.ts），录制功能下一阶段接入
  const handleCollaspsed = useCallback(async (c: boolean) => {
    setCollapsed(c);
    try {
      // 先调主窗口，避免子 webview 越界
      await invoke("set_window_size", {
        width: contentSize.w + (collapsed ? 30 : 180),
        height: contentSize.h + 60,
        contentWidth: contentSize.w,
        contentHeight: contentSize.h
      });
    } catch (e) {
      console.error("调整尺寸失败:", e);
    }
  }, [setCollapsed]);
  const {
    token: { colorBgContainer },
  } = theme.useToken();

  // 当前窗口句柄，用于自定义标题栏的最小化 / 关闭
  const appWindow = useMemo(() => getCurrentWindow(), []);

  const handleAppClose = useCallback(async () => {
    try {
      await appWindow.close();
    } catch (e) {
      console.error("appWindow.close 失败:", e);
    }
  }, [appWindow]);

  const handleAppMinimize = useCallback(async () => {
    try {
      await appWindow.minimize();
    } catch (e) {
      console.error("appWindow.minimize 失败:", e);
    }
  }, [appWindow]);

  const handleBack = useCallback(() => {
    invoke("webview_history_action", { action: "back" });
  }, []);

  const handleRefresh = useCallback(() => {
    invoke("webview_history_action", { action: "reload" });
  }, []);
  return (
    <Layout className="m-0 p-0 h-full overflow-hidden">
      {/* data-tauri-drag-region：Tauri 2 原生支持，webview 层会自动 hook mousedown
       *   触发系统级拖窗，比手写 startDragging 更稳定（Windows 尤其如此）。 */}
      <Header
        className="app-title h-8 select-none"
        style={{ paddingLeft: 8, paddingRight: 8 }}
        data-tauri-drag-region
      >
        <div className="flex h-full flex-row items-center" data-tauri-drag-region>
          <Space size={4}>
            <Button
              className="app-close"
              icon={<CloseOutlined />}
              // size="large"
              type="text"
              data-tauri-drag-region="no-drag"
              onClick={handleAppClose}
            />
            <Button
              className="app-minimize"
              icon={<MinusOutlined />}
              // size="large"
              type="text"
              data-tauri-drag-region="no-drag"
              onClick={handleAppMinimize}
            />
          </Space>
          <div className="w-8" />
          <Button
            icon={<LeftOutlined />}
            data-tauri-drag-region="no-drag"
            onClick={handleBack}
          />
          <Button
            icon={<RedoOutlined />}

            data-tauri-drag-region="no-drag"
            onClick={handleRefresh}
          />
          <Divider orientation="vertical" />
          <Tooltip
            title={
              !activeId
                ? "请先选择任务"
                : !pageReady
                  ? "等待最终 URL 加载完成…"
                  : "截图整页（覆盖之前的自动截图）"
            }
            placement="right"
          >
            <Button
              icon={<PictureOutlined />}
              disabled={!activeId || !pageReady}
              onClick={handleManualCapture}
            />
          </Tooltip>
          <Divider orientation="vertical" />
          {/* 开始按钮：仅 idle + 已选任务时可点 */}
          <Tooltip title={startConfig.tip} placement="right">
            <Button

              icon={startConfig.icon}
              disabled={startDisabled}
              onClick={() => void handleStartRecord()}
            />
          </Tooltip>
          {/* 停止按钮：仅 recording 时可用 */}
          <Tooltip
            title={
              stopDisabled
                ? "无进行中的录制"
                : "停止录制并落盘 mp4（F11）"
            }
            placement="right"
          >
            <Button

              icon={<StopOutlined />}
              disabled={stopDisabled}
              onClick={() => void handleStopRecord()}
            />
          </Tooltip>

          <Divider orientation="vertical" />
          {/* 完成按钮：把当前任务在 sqlite 标记为 status=1，从主列表移除 */}
          <Tooltip
            title={activeId ? "标记当前任务为已完成" : "请先选择任务"}
            placement="right"
          >
            <Button
              type="primary"
              icon={<CheckCircleOutlined />}
              disabled={!activeId || !assets?.recording_exists || !assets.screenshot_exists}
              onClick={() => void handleCompleteTask()}
            >完成</Button>
          </Tooltip>
          {/* 打开文件夹：reveal mp4 → png → 兜底 screenshots 目录 */}
          <Tooltip
            title={
              !activeId
                ? "请先选择任务"
                : assets?.recording_exists
                  ? "在文件管理器中显示录制视频"
                  : assets?.screenshot_exists
                    ? "在文件管理器中显示截图"
                    : "打开截图目录"
            }
            placement="right"
          >
            <Button

              icon={<FolderOpenOutlined />}
              disabled={!assets}
              onClick={() => void handleOpenFolder()}
            />
          </Tooltip>
          {/* 预览截图：仅当 png 存在 */}
          <Tooltip
            title={
              !assets?.screenshot_exists
                ? "暂无截图可预览"
                : "在新窗口预览截图"
            }
            placement="right"
          >
            <Button
              icon={<FileImageOutlined />}
              disabled={!assets?.screenshot_exists}
              onClick={() => void handlePreviewImage()}
            />
          </Tooltip>
          {/* 预览视频：仅当 mp4 存在 */}
          <Tooltip
            title={
              !assets?.recording_exists
                ? "暂无录制视频可预览"
                : "在新窗口预览录制视频"
            }
            placement="right"
          >
            <Button
              icon={<VideoCameraOutlined />}
              disabled={!assets?.recording_exists}
              onClick={() => void handlePreviewVideo()}
            />
          </Tooltip>
          {isWorking && <Tooltip title="停止当前任务" placement="right">
            <Button icon={<CloseOutlined color="red" style={{ color: 'red' }} />} color="red" onClick={handleStopTask} />
          </Tooltip>}
          <div className="flex-1">

          </div>
          {!customMode ? (
            <>
              {SIZE_PRESETS.map((p) => (
                <Button
                  key={p.label}
                  type={contentSize.w == p.w && contentSize.h == p.h ? "primary" : undefined}

                  onClick={contentSize.w != p.w || contentSize.h != p.h ? () => applyWorkAreaSize(p.w, p.h) as any : undefined}
                >
                  {p.label}
                </Button>
              ))}
              {isCustomSize() ?
                <Button type="primary" onClick={() => setCustomMode(true)}>
                  自定义（{contentSize.w}x{contentSize.h}）
                </Button> : <Button onClick={() => setCustomMode(true)}>
                  自定义
                </Button>}
            </>
          ) : (
            <>
              <InputNumber
                min={200}
                max={3840}
                value={contentSize.w}
                onChange={(v) => setContentSize({ w: v || 0, h: contentSize.h })}
                style={{ width: 80 }}
              />
              <span className="text-gray-7 select-none">×</span>
              <InputNumber

                min={200}
                max={2160}
                value={contentSize.h}
                onChange={(v) => setContentSize({ w: contentSize.w, h: v || 0 })}
                style={{ width: 80 }}
              />
              <Button
                type="primary"
                onClick={() => {
                  setCustomMode(false);
                  applyWorkAreaSize();

                }}
              >
                应用
              </Button>
              <Button onClick={() => {
                setContentSize(contentSizeRef.current)
                setCustomMode(false);
              }}>
                取消
              </Button>
            </>
          )}
        </div>
      </Header>
      <Layout>
        <Sider className="select-none" collapsible collapsed={collapsed} onCollapse={(c) => handleCollaspsed(c)} width={180} style={{ background: colorBgContainer }}>
          <div className="h-full w-full flex-row">
            <div className="flex justify-between items-center text-md pl-3 font-bold text-white"><OrderedListOutlined />{collapsed ? '' : '所有任务'}<Button icon={<PlusOutlined />} onClick={openImportWindow} /> </div>
            <div className="flex-1 overflow-y-auto">
              <Menu
                mode="vertical"
                defaultSelectedKeys={[activeId || '']}
                selectedKeys={[activeId || '']}
                style={{ borderInlineEnd: 0, height: '100%' }}

                onClick={isWorking ? undefined : handleSelectTask}

              >
                {tasks.map((t) => <Menu.Item icon={isWorking && t.id == activeId ? <LoadingOutlined /> : <LinkOutlined />} key={t.id}>
                  {t.id}
                </Menu.Item>
                )}
              </Menu>
            </div>
          </div>
        </Sider>
        <Layout className="flex flex-row">
          <Content className="w-full m-0 p-1 flex-1" />
          <div className="w-full h-6 flex items-center px-2 text-gray-3">

            <div className="flex-1">任务：{activeId} {isLoading && <LoadingOutlined />}</div>
            <div className="flex-1 truncate">{assets?.site_url || activeTaskRef.current?.url}</div>
            <div className="flex-1 truncate">{assets?.tags}</div>
            <div className="flex-1 text-right">{statusText}</div>
            <div className="bg-gray-6 rounded-full w-3 h-3 m-2" style={{ backgroundColor: isWorking ? '#ff0' : statusColor }} />
          </div>
        </Layout>
      </Layout>
    </Layout >
  );
}




export default App;