import { useCallback, useEffect, useRef, useState } from "react";
import { invoke } from "@tauri-apps/api/core";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import { openUrl, revealItemInDir } from "@tauri-apps/plugin-opener";
import { Button, Divider, InputNumber, notification, Tooltip } from "antd";
import { mockTasks, type Task } from "./mocks/tasks";
import {
  EVT_RECORDING_FAILED,
  EVT_RECORDING_FINISHED,
  EVT_RECORD_SHORTCUT,
  EVT_SCREENSHOT_FAILED,
  EVT_SCREENSHOT_FINISHED,
  type RecordingFailedPayload,
  type RecordingFinishedPayload,
  type RecordShortcutPayload,
  type RecordState,
  type ScreenshotFailedPayload,
  type ScreenshotFinishedPayload,
  type TaskAssets,
} from "./types/ipc";
import { capturePage } from "./lib/capture";
import {
  cancelRecording,
  getRecordState,
  startRecording,
  stopRecording,
  subscribeRecordState,
} from "./lib/recorder";
import "./App.css";
import {
  FileImageOutlined,
  FolderOpenOutlined,
  LoadingOutlined,
  PictureOutlined,
  PlayCircleFilled,
  StopOutlined,
  VideoCameraOutlined,
} from "@ant-design/icons";

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

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


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

/** 点条目右侧图标：调系统浏览器打开 */
async function openInBrowser(url: string) {
  try {
    await openUrl(url);
  } catch (e) {
    console.error("打开系统浏览器失败:", e);
  }
}

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

  const [contentSize, setContentSize] = useState<{ w: number; h: number }>({ w: 1280, h: 720 });
  const [customMode, setCustomMode] = useState(false);

  // 录制状态机（由 lib/recorder.ts 内部单例维护，这里订阅以驱动按钮）
  const [recordState, setRecordState] = useState<RecordState>(getRecordState());

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

  // activeId 的最新值快照，供 listen 闭包中读取（避免在每个 effect 上加 activeId 依赖
  // 导致 listen 频繁重订）
  const activeIdRef = useRef<string | null>(activeId);
  useEffect(() => {
    activeIdRef.current = activeId;
  }, [activeId]);

  // antd 6 notification 必须用 hook + contextHolder 才能正确取到主题
  const [notifyApi, notifyContext] = notification.useNotification();

  function handleSelectTask(t: Task) {
    setActiveId(t.id);
    void loadInWebview(t.id, t.url);
  }

  /** 重新查询指定任务的产物文件信息（截图 + 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]);

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

  // 订阅 Rust 端的截图事件，统一用 notification 提示
  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;
        notifyApi.success({
          message: auto ? "自动截图完成" : "截图完成",
          description: `任务 ${taskId} → ${path}`,
          duration: 4,
        });
        // 当前选中即此任务时刷新 assets，让预览图按钮立即变可点
        if (activeIdRef.current === taskId) {
          void refreshAssets(taskId);
        }
      });
      const u2 = await listen<ScreenshotFailedPayload>(EVT_SCREENSHOT_FAILED, (e) => {
        const { taskId, auto, error } = e.payload;
        notifyApi.error({
          message: auto ? "自动截图失败" : "截图失败",
          description: `任务 ${taskId}：${error}`,
          duration: 6,
        });
      });

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

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

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

  // 订阅 Rust 端的录制事件（finalize 转码完成 / 失败），统一 notification 提示。
  // 与截图事件订阅采用同一套 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, (e) => {
        const { taskId, path } = e.payload;
        notifyApi.success({
          message: "录制完成",
          description: `任务 ${taskId} → ${path}`,
          duration: 5,
        });
        if (activeIdRef.current === taskId) {
          void refreshAssets(taskId);
        }
      });
      const u2 = await listen<RecordingFailedPayload>(EVT_RECORDING_FAILED, (e) => {
        const { taskId, error } = e.payload;
        notifyApi.error({
          message: "录制失败",
          description: `任务 ${taskId}：${error}`,
          duration: 6,
        });
      });

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

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

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

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

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

  /** 全局快捷键派发：F9 = 开始，F11 = 停止 */
  const handleShortcut = useCallback(
    async (action: "start" | "stop") => {
      try {
        if (action === "start") {
          if (recordState !== "idle") return;
          if (!activeId) {
            notifyApi.warning({
              message: "无法开始录制",
              description: "请先在左栏选择一个任务",
              duration: 4,
            });
            return;
          }
          await startRecording(activeId);
        } else if (action === "stop") {
          if (recordState === "recording") {
            await stopRecording();
          }
        }
      } catch (e) {
        notifyApi.error({
          message: "快捷键操作失败",
          description: String(e),
          duration: 6,
        });
      }
    },
    [recordState, activeId, notifyApi],
  );

  // 订阅 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) {
      notifyApi.error({
        message: "打开文件夹失败",
        description: String(e),
        duration: 6,
      });
    }
  }, [assets, notifyApi]);

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

  /** 在新 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) {
      notifyApi.error({
        message: "视频预览失败",
        description: String(e),
        duration: 6,
      });
    }
  }, [assets, notifyApi]);

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

  // 开始按钮仅在 idle 且已选任务时可点；其它状态 disabled
  const startDisabled = recordState !== "idle" || !activeId;
  // 停止按钮仅在 recording 时可点
  const stopDisabled = recordState !== "recording";

  const applyWorkAreaSize = useCallback(async (w: number, h: number) => {
    try {
      // 先调主窗口，避免子 webview 越界
      await invoke("set_window_size", {
        width: w + 180,
        height: h + 46,
        contentWidth: w,
        contentHeight: h
      });
      setContentSize({ w, h })
    } catch (e) {
      console.error("调整尺寸失败:", e);
    }
  }, [setContentSize]);

  // 首次挂载时按默认 contentSize 调整窗口；依赖项故意为空（仅初始化用）。
  useEffect(() => {
    applyWorkAreaSize(contentSize.w, contentSize.h);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // 录制状态机（RecordState 类型已抽到 src/types/ipc.ts），录制功能下一阶段接入

  return (
    <div className="flex h-screen w-screen overflow-hidden select-none">
      {/* antd notification 的渲染容器 —— 必须挂在树中 */}
      {notifyContext}
      {/* ===== 左栏：任务列表（180px） ===== */}
      <aside className="w-[180px] shrink-0 border-r border-gray-4 bg-gray-2 h-full flex flex-col">
        <div className="px-3 py-2 text-xs text-gray-7 border-b border-gray-4 select-none">
          任务列表
        </div>
        <div className="flex-1 overflow-y-auto">
          <ul>
            {mockTasks.map((t) => {
              const active = activeId === t.id;
              return (
                <li
                  key={t.id}
                  className={
                    "flex items-center justify-between px-3 py-2 text-sm cursor-pointer transition-colors " +
                    (active
                      ? "bg-primary-1 text-primary-7"
                      : "hover:bg-gray-3 text-gray-10")
                  }
                  onClick={() => handleSelectTask(t)}
                >
                  <span className="truncate">任务 {t.id}</span>
                  <button
                    type="button"
                    title="在系统浏览器打开"
                    className="ml-2 inline-flex items-center justify-center w-6 h-6 rounded-sm hover:bg-gray-4 text-gray-7 hover:text-primary-6"
                    onClick={(e) => {
                      e.stopPropagation();
                      void openInBrowser(t.url);
                    }}
                  >
                    {/* 外链图标（lucide external-link 同款 path，不引入新依赖） */}
                    <svg
                      width="14"
                      height="14"
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      strokeWidth="2"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                    >
                      <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
                      <polyline points="15 3 21 3 21 9" />
                      <line x1="10" y1="14" x2="21" y2="3" />
                    </svg>
                  </button>
                </li>
              );
            })}
          </ul>
        </div>
      </aside>

      {/* ===== 右侧主区 ===== */}
      <main className="flex-1 flex flex-col min-w-0">
        {/* 工具栏（48px） */}
        <header className="h-12 shrink-0 flex items-center justify-between gap-2 px-3 border-b border-gray-4 bg-gray-1">
          {/* 左侧：操作按钮区 */}
          <div className="flex items-center gap-2">
            <Tooltip title={activeId ? "截图整页（覆盖之前的自动截图）" : "请先选择任务"} placement="top">
              <Button
                size="small"
                icon={<PictureOutlined />}
                disabled={!activeId}
                onClick={() => void handleManualCapture()}
              />
            </Tooltip>
            <Divider type="vertical" />
            {/* 开始按钮：仅 idle + 已选任务时可点 */}
            <Tooltip title={startConfig.tip} placement="top">
              <Button
                size="small"
                icon={startConfig.icon}
                disabled={startDisabled}
                onClick={() => void handleStartRecord()}
              />
            </Tooltip>
            {/* 停止按钮：仅 recording 时可用 */}
            <Tooltip
              title={
                stopDisabled
                  ? "无进行中的录制"
                  : "停止录制并落盘 mp4（F11）"
              }
              placement="top"
            >
              <Button
                size="small"
                icon={<StopOutlined />}
                disabled={stopDisabled}
                onClick={() => void handleStopRecord()}
              />
            </Tooltip>
            <Divider type="vertical" />
            {/* 打开文件夹：reveal mp4 → png → 兜底 screenshots 目录 */}
            <Tooltip
              title={
                !activeId
                  ? "请先选择任务"
                  : assets?.recording_exists
                    ? "在文件管理器中显示录制视频"
                    : assets?.screenshot_exists
                      ? "在文件管理器中显示截图"
                      : "打开截图目录"
              }
              placement="top"
            >
              <Button
                size="small"
                icon={<FolderOpenOutlined />}
                disabled={!assets}
                onClick={() => void handleOpenFolder()}
              />
            </Tooltip>
            {/* 预览截图：仅当 png 存在 */}
            <Tooltip
              title={
                !assets?.screenshot_exists
                  ? "暂无截图可预览"
                  : "在新窗口预览截图"
              }
              placement="top"
            >
              <Button
                size="small"
                icon={<FileImageOutlined />}
                disabled={!assets?.screenshot_exists}
                onClick={() => void handlePreviewImage()}
              />
            </Tooltip>
            {/* 预览视频：仅当 mp4 存在 */}
            <Tooltip
              title={
                !assets?.recording_exists
                  ? "暂无录制视频可预览"
                  : "在新窗口预览录制视频"
              }
              placement="top"
            >
              <Button
                size="small"
                icon={<VideoCameraOutlined />}
                disabled={!assets?.recording_exists}
                onClick={() => void handlePreviewVideo()}
              />
            </Tooltip>
          </div>

          {/* 右侧：尺寸预设 / 自定义 */}
          <div className="flex items-center gap-2">
            {!customMode ? (
              <>
                {SIZE_PRESETS.map((p) => (
                  <Button
                    key={p.label}
                    size="small"
                    disabled={contentSize.w == p.w && contentSize.h == p.h}
                    onClick={() => applyWorkAreaSize(p.w, p.h)}
                  >
                    {p.label}
                  </Button>
                ))}
                <Button size="small" onClick={() => setCustomMode(true)}>
                  自定义
                </Button>
              </>
            ) : (
              <>
                <InputNumber
                  size="small"
                  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
                  size="small"
                  min={200}
                  max={2160}
                  value={contentSize.h}
                  onChange={(v) => setContentSize({ w: contentSize.w, h: v || 0 })}
                  style={{ width: 80 }}
                />
                <Button
                  size="small"
                  type="primary"
                  onClick={() => applyWorkAreaSize(contentSize.w, contentSize.h)}
                >
                  应用
                </Button>
                <Button size="small" onClick={() => setCustomMode(false)}>
                  取消
                </Button>
              </>
            )}
          </div>
        </header>

        {/* 工作区占位：实际由 Rust child webview 覆盖在此区域之上 */}
        <section className="flex-1 bg-gray-7" />
        <section className="h-10 bg-gray-5 broder border-gray-7" />
      </main>
    </div>
  );
}

export default App;
