import { useEffect, useMemo, useRef, useState } from "react";
import { useParams, useNavigate } from "react-router";
import {
  Check,
  Download,
  Edit2,
  Film,
  Loader2,
  Pause,
  Play,
  SkipBack,
  SkipForward,
  Trash2,
} from "lucide-react";
import {
  useAssemblyTask,
  useDeleteVideoTask,
  useEpisodes,
  useStartAssembly,
  useStoryboards,
  useVideoTasks,
} from "../../hooks/useAi";
import type { AiTask, Episode, Storyboard } from "../../lib/api/ai";

const FRAME_RATE = 24;
const FRAME_STEP = 1 / FRAME_RATE;
const CONTROL_SURFACE = "border border-slate-200/80 bg-white shadow-[0_1px_2px_rgba(15,23,42,0.04)]";

function fmt(secs: number) {
  const safe = Number.isFinite(secs) ? Math.max(0, secs) : 0;
  const m = Math.floor(safe / 60);
  const s = Math.floor(safe % 60);
  return `${m}:${String(s).padStart(2, "0")}`;
}

function timeAgo(dateStr: string) {
  const diff = Date.now() - new Date(dateStr).getTime();
  const h = Math.floor(diff / 3600000);
  if (h < 1) return "刚刚";
  if (h < 24) return `${h}小时前`;
  return `${Math.floor(h / 24)}天前`;
}

function statusLabel(status?: string | null) {
  if (status === "succeeded") return "已完成";
  if (status === "failed") return "失败";
  if (status === "running" || status === "submitted" || status === "pending") return "生成中";
  return "未生成";
}

export function VideoGeneration() {
  const { projectId } = useParams<{ projectId: string }>();
  const navigate = useNavigate();
  const pid = projectId ?? "";

  const { data: episodes = [], isLoading: epLoading } = useEpisodes(pid);
  const { data: allTasks = [] } = useVideoTasks(pid);
  const [activeEpId, setActiveEpId] = useState("");

  useEffect(() => {
    if (episodes.length > 0 && !activeEpId) {
      setActiveEpId(episodes[0].id);
    }
  }, [episodes, activeEpId]);

  const { data: storyboards = [] } = useStoryboards(pid, activeEpId);
  const { data: assemblyTask } = useAssemblyTask(pid, activeEpId);
  const startAssembly = useStartAssembly(pid, activeEpId);
  const deleteTask = useDeleteVideoTask(pid);

  const epTasks = useMemo(
    () => allTasks.filter((task) => task.episodeId === activeEpId),
    [allTasks, activeEpId]
  );

  const getTaskForSb = (sb: Storyboard): AiTask | undefined => {
    const byId = epTasks.filter((task) => task.storyboardId === sb.id);
    return byId.find((task) => task.status === "succeeded")
      ?? byId.find((task) => task.status === "running" || task.status === "submitted" || task.status === "pending")
      ?? byId[0];
  };

  const orderedShots = storyboards
    .map((sb) => ({ sb, task: getTaskForSb(sb) }))
    .filter(({ task }) => task?.status === "succeeded" && task.resultVideoUrl);
  const orderedShotTaskIds = orderedShots.map(({ task }) => task!.id);

  const totalShots = storyboards.length;
  const doneShots = orderedShots.length;
  const pendingShots = epTasks.filter((task) =>
    task.status === "pending" || task.status === "submitted" || task.status === "running"
  ).length;
  const totalDuration = storyboards.reduce((sum, sb) => sum + (sb.durationSeconds || 0), 0);

  const videoRef = useRef<HTMLVideoElement>(null);
  const [playing, setPlaying] = useState(false);
  const [playerMode, setPlayerMode] = useState<"assembly" | "shot">("assembly");
  const [currentShotIdx, setCurrentShotIdx] = useState(0);
  const [currentTime, setCurrentTime] = useState(0);
  const [mediaDuration, setMediaDuration] = useState(0);

  const hasAssembly = assemblyTask?.status === "succeeded" && !!assemblyTask.downloadUrl;
  const currentVideoUrl = playerMode === "assembly" && hasAssembly
    ? assemblyTask!.downloadUrl!
    : orderedShots[currentShotIdx]?.task?.resultVideoUrl ?? null;
  const currentShot = playerMode === "shot" ? orderedShots[currentShotIdx] : null;
  const timelineDuration = mediaDuration || (playerMode === "assembly" ? totalDuration : currentShot?.sb.durationSeconds ?? 0) || 1;
  const timelineProgress = Math.min(100, Math.max(0, (currentTime / timelineDuration) * 100));
  const currentFrame = Math.max(0, Math.round(currentTime * FRAME_RATE));

  const shotRanges = useMemo(() => {
    let cursor = 0;
    return orderedShots.map(({ sb, task }, index) => {
      const duration = sb.durationSeconds || task?.videoDuration || 1;
      const range = { index, sb, task, start: cursor, end: cursor + duration, duration };
      cursor += duration;
      return range;
    });
  }, [orderedShots]);

  const activeTimelineShot = shotRanges.find((shot) => currentTime >= shot.start && currentTime < shot.end) ?? shotRanges[0];

  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;
    video.pause();
    video.load();
    setPlaying(false);
    setCurrentTime(0);
    setMediaDuration(0);
  }, [currentVideoUrl]);

  const syncMediaState = () => {
    const video = videoRef.current;
    if (!video) return;
    setCurrentTime(video.currentTime || 0);
    if (Number.isFinite(video.duration)) {
      setMediaDuration(video.duration || 0);
    }
  };

  const seekTo = (seconds: number) => {
    const video = videoRef.current;
    if (!video || !currentVideoUrl) return;
    const next = Math.min(Math.max(seconds, 0), timelineDuration);
    video.currentTime = next;
    setCurrentTime(next);
  };

  const stepFrame = (direction: -1 | 1) => {
    const video = videoRef.current;
    if (!video || !currentVideoUrl) return;
    video.pause();
    setPlaying(false);
    seekTo(video.currentTime + direction * FRAME_STEP);
  };

  const togglePlay = async () => {
    const video = videoRef.current;
    if (!video || !currentVideoUrl) return;
    if (video.paused) {
      await video.play();
      setPlaying(true);
    } else {
      video.pause();
      setPlaying(false);
    }
  };

  const selectShot = (idx: number) => {
    setCurrentShotIdx(idx);
    setPlayerMode("shot");
  };

  const seekAssemblyShot = (idx: number) => {
    if (hasAssembly) {
      setPlayerMode("assembly");
      setTimeout(() => seekTo(shotRanges[idx]?.start ?? 0), 0);
    } else {
      selectShot(idx);
    }
  };

  const handleDelete = async (taskId: string) => {
    if (!confirm("确定删除此视频？")) return;
    await deleteTask.mutateAsync(taskId);
  };

  const handleStartAssembly = () => {
    startAssembly.mutate(orderedShotTaskIds);
  };

  const epShotCount = (ep: Episode) => ep.storyboardCount ?? 0;
  const epDoneCount = (ep: Episode) =>
    allTasks.filter((task) => task.episodeId === ep.id && task.status === "succeeded").length;

  return (
    <div className="flex h-full overflow-hidden bg-[#f7f8fb] text-[#111827]">
      <aside className="flex w-[190px] flex-shrink-0 flex-col border-r border-[#e5e8f0] bg-[#fbfcff]">
        <div className="flex h-14 flex-shrink-0 items-center border-b border-[#e5e8f0] px-4">
          <span className="text-sm font-medium text-[#111827]">选集</span>
        </div>
        <div className="flex-1 overflow-y-auto py-2">
          {epLoading ? (
            <div className="flex justify-center pt-6">
              <Loader2 className="h-4 w-4 animate-spin text-[#98a2b3]" />
            </div>
          ) : episodes.length === 0 ? (
            <p className="px-3 pt-6 text-center text-xs text-[#98a2b3]">暂无分集</p>
          ) : (
            episodes.map((ep) => {
              const total = epShotCount(ep);
              const done = epDoneCount(ep);
              const isActive = ep.id === activeEpId;
              return (
                <button
                  key={ep.id}
                  onClick={() => {
                    setActiveEpId(ep.id);
                    setPlayerMode("assembly");
                    setCurrentShotIdx(0);
                  }}
                  className={`w-full border-b border-[#edf0f5] px-4 py-3 text-left transition ${
                    isActive ? "bg-[#f4f0ff]" : "hover:bg-white"
                  }`}
                >
                  <div className="mb-1 flex items-center justify-between">
                    <span className={`text-sm font-semibold ${isActive ? "text-[#6147e8]" : "text-[#111827]"}`}>
                      第{ep.episodeNumber}集
                    </span>
                    {isActive && <Check className="h-3.5 w-3.5 text-[#7657ff]" />}
                  </div>
                  <div className="flex items-center justify-between text-[11px]">
                    <span className="text-[#475467]">{total}个分镜</span>
                    <span className={done > 0 ? "text-emerald-600" : "text-[#667085]"}>
                      {done}/{total}完成
                    </span>
                  </div>
                </button>
              );
            })
          )}
        </div>
      </aside>

      <main className="min-w-0 flex-1 overflow-y-auto">
        <div className="space-y-5 p-5">
          <section className="overflow-hidden rounded-lg border border-[#e4e8f0] bg-white shadow-[0_8px_24px_rgba(15,23,42,0.04)]">
            <div className="flex items-start justify-between gap-4 border-b border-[#edf0f5] px-5 py-4">
              <div>
                <div className="flex items-center gap-2">
                  <h2 className="text-lg font-semibold tracking-tight text-[#111827]">整集剪辑台</h2>
                  <span className="rounded-md bg-[#eef4ff] px-2 py-1 text-[11px] font-medium text-[#1c64ff]">
                    {playerMode === "assembly" ? "整集时间轴" : "单镜预览"}
                  </span>
                </div>
                <p className="mt-1 text-xs text-[#667085]">
                  合成后可拖动时间轴定位画面，也可以用帧按钮按 1/24 秒逐帧检查。
                </p>
              </div>
              <div className="flex flex-wrap items-center justify-end gap-2">
                {hasAssembly && (
                  <button
                    onClick={() => setPlayerMode("assembly")}
                    className={`inline-flex items-center gap-1.5 rounded-md px-3 py-2 text-xs font-medium transition ${
                      playerMode === "assembly"
                        ? "bg-[#7657ff] text-white shadow-[0_8px_18px_rgba(118,87,255,0.22)]"
                        : `${CONTROL_SURFACE} text-[#111827] hover:border-[#c7d2e5]`
                    }`}
                  >
                    <Film className="h-3.5 w-3.5" />
                    整集
                  </button>
                )}
                {(assemblyTask?.status === "running" || assemblyTask?.status === "pending") && (
                  <span className="inline-flex items-center gap-1.5 rounded-md bg-[#fff7ed] px-3 py-2 text-xs text-orange-600">
                    <Loader2 className="h-3.5 w-3.5 animate-spin" />
                    合成中
                  </span>
                )}
                {hasAssembly && (
                  <a
                    href={assemblyTask!.downloadUrl!}
                    target="_blank"
                    rel="noreferrer"
                    className="inline-flex items-center gap-1.5 rounded-md bg-emerald-600 px-3 py-2 text-xs font-medium text-white transition hover:bg-emerald-700"
                  >
                    <Download className="h-3.5 w-3.5" />
                    下载整集
                  </a>
                )}
                {assemblyTask?.status !== "running" && assemblyTask?.status !== "pending" && (
                  <button
                    onClick={handleStartAssembly}
                    disabled={startAssembly.isPending || doneShots === 0}
                    className={`inline-flex items-center gap-1.5 rounded-md px-3 py-2 text-xs font-medium text-[#111827] transition hover:border-[#c7d2e5] disabled:opacity-50 ${CONTROL_SURFACE}`}
                  >
                    {startAssembly.isPending ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Film className="h-3.5 w-3.5" />}
                    {hasAssembly ? "重新合成" : "合成整集"}
                  </button>
                )}
              </div>
            </div>

            <div className="grid gap-5 p-5 xl:grid-cols-[minmax(520px,1fr)_360px]">
              <div className="min-w-0">
                <div className="relative overflow-hidden rounded-lg bg-[#0b111b] shadow-[0_18px_45px_rgba(15,23,42,0.18)]" style={{ aspectRatio: "16 / 9" }}>
                  {currentVideoUrl ? (
                    <>
                      <div className="absolute left-3 top-3 z-10 rounded-md bg-black/55 px-3 py-2 text-white backdrop-blur-sm">
                        <div className="text-[10px] leading-none text-white/65">
                          {playerMode === "assembly" ? "整集合成" : "分镜预览"}
                        </div>
                        <div className="mt-1 text-xs font-semibold">{playerMode === "shot" ? `分镜 ${String(currentShot?.sb.sequenceNum ?? 1).padStart(3, "0")}` : "完整成片"}</div>
                      </div>
                      <div className="absolute right-3 top-3 z-10 rounded-md bg-black/55 px-3 py-2 font-mono text-xs text-white backdrop-blur-sm">
                        {fmt(currentTime)} / {fmt(timelineDuration)}
                      </div>
                      <video
                        ref={videoRef}
                        src={currentVideoUrl}
                        className="h-full w-full object-contain"
                        onLoadedMetadata={syncMediaState}
                        onTimeUpdate={syncMediaState}
                        onPlay={() => setPlaying(true)}
                        onPause={() => setPlaying(false)}
                        onEnded={() => {
                          setPlaying(false);
                          if (playerMode === "shot" && currentShotIdx < orderedShots.length - 1) {
                            setCurrentShotIdx((idx) => idx + 1);
                          }
                        }}
                      />
                      <button
                        onClick={togglePlay}
                        className="absolute inset-0 flex items-center justify-center"
                        aria-label={playing ? "暂停" : "播放"}
                      >
                        <span className={`flex h-16 w-16 items-center justify-center rounded-full bg-white/18 text-white backdrop-blur-sm transition hover:bg-white/28 ${playing ? "opacity-0 hover:opacity-100" : ""}`}>
                          {playing ? <Pause className="h-8 w-8" /> : <Play className="ml-1 h-8 w-8" />}
                        </span>
                      </button>
                    </>
                  ) : (
                    <div className="flex h-full w-full flex-col items-center justify-center gap-3">
                      <Film className="h-12 w-12 text-white/20" />
                      <p className="text-sm text-white/55">
                        {assemblyTask?.status === "running" || assemblyTask?.status === "pending"
                          ? "整集视频合成中..."
                          : doneShots === 0
                            ? "暂无已完成的分镜视频"
                            : "整集视频尚未合成，可先查看单镜或点击合成整集"}
                      </p>
                    </div>
                  )}
                </div>

                <div className="mt-4 rounded-lg border border-[#e4e8f0] bg-[#fbfcff] p-4">
                  <div className="mb-3 flex items-center justify-between gap-3">
                    <div className="flex items-center gap-2">
                      <button
                        onClick={() => stepFrame(-1)}
                        disabled={!currentVideoUrl}
                        className={`inline-flex h-8 w-8 items-center justify-center rounded-md text-[#475467] transition hover:text-[#7657ff] disabled:opacity-40 ${CONTROL_SURFACE}`}
                        title="上一帧"
                      >
                        <SkipBack className="h-4 w-4" />
                      </button>
                      <button
                        onClick={togglePlay}
                        disabled={!currentVideoUrl}
                        className="inline-flex h-9 w-9 items-center justify-center rounded-full bg-[#7657ff] text-white shadow-[0_8px_18px_rgba(118,87,255,0.24)] transition hover:bg-[#6747f4] disabled:opacity-40"
                        title={playing ? "暂停" : "播放"}
                      >
                        {playing ? <Pause className="h-4 w-4" /> : <Play className="ml-0.5 h-4 w-4" />}
                      </button>
                      <button
                        onClick={() => stepFrame(1)}
                        disabled={!currentVideoUrl}
                        className={`inline-flex h-8 w-8 items-center justify-center rounded-md text-[#475467] transition hover:text-[#7657ff] disabled:opacity-40 ${CONTROL_SURFACE}`}
                        title="下一帧"
                      >
                        <SkipForward className="h-4 w-4" />
                      </button>
                    </div>
                    <div className="flex items-center gap-3 text-xs text-[#667085]">
                      <span className="font-mono">Frame {currentFrame}</span>
                      <span className="font-mono">{fmt(currentTime)} / {fmt(timelineDuration)}</span>
                    </div>
                  </div>

                  <div className="relative">
                    <div className="absolute inset-x-0 top-1/2 h-2 -translate-y-1/2 rounded-full bg-[#e8edf6]" />
                    <div
                      className="absolute left-0 top-1/2 h-2 -translate-y-1/2 rounded-full bg-[#7657ff]"
                      style={{ width: `${timelineProgress}%` }}
                    />
                    <input
                      type="range"
                      min={0}
                      max={Math.max(0.01, timelineDuration)}
                      step={FRAME_STEP}
                      value={Math.min(currentTime, timelineDuration)}
                      onChange={(event) => seekTo(Number(event.target.value))}
                      disabled={!currentVideoUrl}
                      className="relative z-10 h-8 w-full cursor-pointer appearance-none bg-transparent accent-[#7657ff] disabled:cursor-not-allowed disabled:opacity-50"
                      aria-label="视频时间轴"
                    />
                  </div>

                  {shotRanges.length > 0 && (
                    <div className="mt-4">
                      <div className="mb-2 flex items-center justify-between text-[11px] text-[#667085]">
                        <span>分镜轨道</span>
                        <span>{playerMode === "assembly" ? "拖动查看整集画面" : "单镜逐帧预览"}</span>
                      </div>
                      <div className="flex h-12 overflow-hidden rounded-md border border-[#dfe5f2] bg-white">
                        {shotRanges.map((shot) => {
                          const width = `${Math.max(7, (shot.duration / Math.max(totalDuration || 1, 1)) * 100)}%`;
                          const active = playerMode === "assembly"
                            ? activeTimelineShot?.sb.id === shot.sb.id
                            : currentShot?.sb.id === shot.sb.id;
                          return (
                            <button
                              key={shot.sb.id}
                              onClick={() => seekAssemblyShot(shot.index)}
                              className={`relative min-w-[54px] border-r border-white px-2 text-left transition last:border-r-0 ${
                                active ? "bg-[#7657ff] text-white" : "bg-[#eef4ff] text-[#344054] hover:bg-[#e1e9ff]"
                              }`}
                              style={{ width }}
                            >
                              <span className="block pt-2 text-[10px] font-semibold tabular-nums">
                                {String(shot.sb.sequenceNum).padStart(3, "0")}
                              </span>
                              <span className={`mt-1 block text-[10px] ${active ? "text-white/75" : "text-[#667085]"}`}>{shot.duration}s</span>
                            </button>
                          );
                        })}
                      </div>
                    </div>
                  )}
                </div>
              </div>

              <aside className="min-w-0 rounded-lg border border-[#e4e8f0] bg-[#fbfcff] p-4">
                <div className="mb-3 flex items-center justify-between">
                  <h3 className="text-sm font-semibold text-[#111827]">镜头索引</h3>
                  <span className="text-xs text-[#667085]">共 {orderedShots.length} 个</span>
                </div>
                <div className="max-h-[520px] space-y-2 overflow-y-auto pr-1">
                  {orderedShots.length === 0 ? (
                    <div className="rounded-md border border-dashed border-[#d9dee8] bg-white px-4 py-8 text-center text-xs text-[#98a2b3]">
                      暂无可预览分镜
                    </div>
                  ) : (
                    orderedShots.map(({ sb, task }, index) => {
                      const active = playerMode === "shot" && currentShotIdx === index;
                      return (
                        <button
                          key={sb.id}
                          onClick={() => selectShot(index)}
                          className={`flex w-full gap-3 rounded-md border p-2 text-left transition ${
                            active ? "border-[#7657ff] bg-[#f4f0ff]" : "border-[#e4e8f0] bg-white hover:border-[#cfd7e6]"
                          }`}
                        >
                          <div className="relative h-14 w-24 flex-shrink-0 overflow-hidden rounded bg-black">
                            <video src={task!.resultVideoUrl!} muted className="h-full w-full object-cover" />
                            <span className="absolute bottom-1 left-1 rounded bg-black/65 px-1.5 py-0.5 text-[10px] text-white">
                              {String(sb.sequenceNum).padStart(3, "0")}
                            </span>
                          </div>
                          <div className="min-w-0 flex-1">
                            <p className="line-clamp-2 text-xs font-medium leading-5 text-[#111827]">
                              {sb.shortDescription || sb.detailedDescription || `分镜 ${sb.sequenceNum}`}
                            </p>
                            <p className="mt-1 text-[11px] text-[#667085]">{sb.durationSeconds || task?.videoDuration || 0}s · {timeAgo(task!.createdAt)}</p>
                          </div>
                        </button>
                      );
                    })
                  )}
                </div>
              </aside>
            </div>
          </section>

          <section className="grid grid-cols-2 gap-4 xl:grid-cols-4">
            {[
              { label: "总分镜数", value: totalShots, color: "text-[#111827]" },
              { label: "已完成", value: doneShots, color: "text-emerald-600" },
              { label: "生成中", value: pendingShots, color: "text-orange-500" },
              { label: "总时长", value: fmt(totalDuration), color: "text-[#111827]" },
            ].map((stat) => (
              <div key={stat.label} className="rounded-lg border border-[#e4e8f0] bg-white p-5 shadow-[0_8px_24px_rgba(15,23,42,0.04)]">
                <div className={`mb-1 text-3xl font-bold tabular-nums ${stat.color}`}>{stat.value}</div>
                <div className="text-xs text-[#667085]">{stat.label}</div>
              </div>
            ))}
          </section>

          <section className="rounded-lg border border-[#e4e8f0] bg-white p-5 shadow-[0_8px_24px_rgba(15,23,42,0.04)]">
            <div className="mb-4 flex items-center justify-between">
              <h3 className="text-base font-semibold text-[#111827]">分镜管理</h3>
              <span className="text-xs text-[#667085]">{doneShots}/{totalShots} 已完成</span>
            </div>
            {storyboards.length === 0 ? (
              <div className="py-12 text-center text-sm text-[#98a2b3]">
                {!activeEpId ? "请从左侧选择集数" : "该集暂无分镜"}
              </div>
            ) : (
              <div className="grid grid-cols-1 gap-4 md:grid-cols-2 2xl:grid-cols-3">
                {storyboards.map((sb) => {
                  const task = getTaskForSb(sb);
                  const hasVideo = task?.status === "succeeded" && task.resultVideoUrl;
                  const isPending = task?.status === "pending" || task?.status === "submitted" || task?.status === "running";
                  return (
                    <div key={sb.id} className="overflow-hidden rounded-lg border border-[#e4e8f0] bg-white">
                      <button
                        className="relative block w-full bg-black"
                        style={{ aspectRatio: "16 / 9" }}
                        onClick={() => {
                          if (!hasVideo) return;
                          const idx = orderedShots.findIndex((shot) => shot.sb.id === sb.id);
                          if (idx >= 0) selectShot(idx);
                          window.scrollTo({ top: 0, behavior: "smooth" });
                        }}
                      >
                        {hasVideo ? (
                          <video src={task!.resultVideoUrl!} muted className="h-full w-full object-cover" />
                        ) : isPending ? (
                          <div className="flex h-full w-full flex-col items-center justify-center gap-2">
                            <Loader2 className="h-6 w-6 animate-spin text-white/45" />
                            <span className="text-xs text-white/45">生成中</span>
                          </div>
                        ) : (
                          <div className="flex h-full w-full items-center justify-center">
                            <Film className="h-8 w-8 text-white/20" />
                          </div>
                        )}
                        <div className="absolute left-2 top-2 rounded bg-black/60 px-2 py-1 text-[11px] font-medium text-white backdrop-blur-sm">
                          分镜 {String(sb.sequenceNum).padStart(3, "0")}
                        </div>
                        <div className="absolute right-2 top-2 rounded bg-black/60 px-2 py-1 text-[11px] text-white backdrop-blur-sm">
                          {sb.durationSeconds || task?.videoDuration || 0}s
                        </div>
                        <div className={`absolute bottom-2 right-2 h-2 w-2 rounded-full ${
                          hasVideo ? "bg-emerald-400" : isPending ? "animate-pulse bg-orange-400" : "bg-slate-400"
                        }`} />
                        {hasVideo && (
                          <div className="absolute inset-0 flex items-center justify-center bg-black/0 opacity-0 transition hover:bg-black/20 hover:opacity-100">
                            <Play className="h-8 w-8 text-white drop-shadow" />
                          </div>
                        )}
                      </button>
                      <div className="flex items-center justify-between gap-3 px-3 py-3">
                        <div className="min-w-0">
                          <p className="truncate text-xs font-medium text-[#111827]">{sb.shortDescription || `分镜 ${sb.sequenceNum}`}</p>
                          <p className="mt-0.5 text-[11px] text-[#667085]">{task ? `${statusLabel(task.status)} · ${timeAgo(task.createdAt)}` : "未生成"}</p>
                        </div>
                        <div className="flex flex-shrink-0 items-center gap-2">
                          <button
                            onClick={() => navigate(`/project/${pid}/storyboard/${activeEpId}`)}
                            className="inline-flex items-center gap-1 text-[11px] text-[#7657ff] hover:underline"
                          >
                            <Edit2 className="h-3 w-3" />
                            编辑
                          </button>
                          {hasVideo && (
                            <a
                              href={task!.resultVideoUrl!}
                              target="_blank"
                              rel="noreferrer"
                              className="inline-flex items-center gap-1 text-[11px] text-[#111827] hover:text-[#7657ff]"
                            >
                              <Download className="h-3 w-3" />
                              下载
                            </a>
                          )}
                          {task && (
                            <button
                              onClick={() => handleDelete(task.id)}
                              disabled={deleteTask.isPending}
                              className="text-red-500 transition hover:text-red-700 disabled:opacity-40"
                              title="删除视频"
                            >
                              <Trash2 className="h-3.5 w-3.5" />
                            </button>
                          )}
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>
            )}
          </section>
        </div>
      </main>
    </div>
  );
}
