"use client"
import { useRef, useState } from "react"
import { type NodeProps } from "@xyflow/react"
import {
  Link2, Upload, Download, Scissors, Image as ImageIcon,
  Mic, Languages, FileEdit, Sparkles, Film, FileVideo, Loader2, Plus,
} from "lucide-react"
import { NodeShell, type NodeStatus, type NodeKind } from "./node-shell"
import { type Job, frameUrl, videoUrl } from "@/lib/api"

export interface NodeData {
  job: Job | null
  submitting: boolean
  analyzing: boolean
  selectedFrames: Set<number>
  onSubmitUrl: (url: string) => void
  onUploadFile: (file: File) => void
  onAnalyze: () => void
  onToggleFrame: (idx: number) => void
  onExpandFrame: (idx: number) => void
  onAddManualFrame: (t: number) => void
  onOpenVideoLightbox: () => void
}

/* ---- 状态映射工具 ---- */
function inputStatus(job: Job | null): NodeStatus {
  if (!job) return "pending"
  return "done"
}
function downloadStatus(job: Job | null): NodeStatus {
  if (!job) return "pending"
  if (job.status === "failed" && job.progress < 30) return "failed"
  if (job.status === "downloading") return "running"
  if (job.video_url) return "done"
  return "pending"
}
function splitStatus(job: Job | null): NodeStatus {
  if (!job || !job.video_url) return "pending"
  if (job.status === "failed" && job.progress >= 20 && job.progress < 50) return "failed"
  if (job.status === "splitting") return "running"
  if (["frames_extracted", "transcribing", "transcribed"].includes(job.status)) return "done"
  return "pending"
}
function keyframeStatus(job: Job | null): NodeStatus {
  if (!job) return "pending"
  if (job.status === "failed" && job.progress >= 50 && job.progress < 70) return "failed"
  if (job.frames.length === 0 && job.status === "splitting") return "running"
  if (job.frames.length > 0) return "done"
  return "pending"
}
function asrStatus(job: Job | null): NodeStatus {
  if (!job) return "pending"
  if (job.status === "transcribing") return "running"
  if (job.transcript.length > 0) return "done"
  if (job.status === "failed" && job.progress >= 70) return "failed"
  return "pending"
}

/* ============================================================
   1. InputNode — TK 链接 / 上传
   ============================================================ */
export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | any) {
  const d: NodeData = data
  const [url, setUrl] = useState("")
  const [videoT, setVideoT] = useState(0)
  const [addingFrame, setAddingFrame] = useState(false)
  const [videoExpanded, setVideoExpanded] = useState(false)
  const fileRef = useRef<HTMLInputElement>(null)
  const videoRef = useRef<HTMLVideoElement>(null)
  const job = d.job

  // 是否已下载 → 显示视频 + 解析按钮
  const hasVideo = !!job?.video_url
  const isDownloading = job?.status === "downloading" || job?.status === "created"
  const isAnalyzing = !!job && ["splitting", "frames_extracted", "transcribing"].includes(job.status)
  const isDone = job?.status === "transcribed"
  const hasFrames = (job?.frames.length ?? 0) > 0
  const inputLocked = isDownloading || d.submitting

  return (
    <div className="relative" style={{ width: 320 }}>
      {/* 视频缩略图浮于节点上方 — 跟关键帧缩略图同尺寸（小），点击稍微放大可选帧 */}
      {hasVideo && job && !videoExpanded && (
        <div className="absolute left-0 right-0 flex justify-center" style={{ bottom: "calc(100% + 12px)" }}>
          <button
            type="button"
            onClick={(e) => { e.stopPropagation(); setVideoExpanded(true) }}
            title="点击展开 · 可拖时间轴选帧"
            className="group relative rounded-md overflow-hidden border border-white/30 shadow-lg hover:-translate-y-0.5 transition"
            style={{ width: 80 }}
          >
            <video
              src={videoUrl(job.id)}
              muted
              loop
              playsInline
              preload="metadata"
              className="block w-full bg-black"
              style={{ aspectRatio: `${job.width}/${job.height}` }}
              onMouseEnter={(e) => {
                const v = e.target as HTMLVideoElement
                v.play().catch(() => {})
              }}
              onMouseLeave={(e) => {
                const v = e.target as HTMLVideoElement
                v.pause()
                v.currentTime = 0
              }}
            />
            <div className="absolute bottom-0.5 right-0.5 bg-black/70 text-white text-[9px] font-mono px-1 py-0.5 rounded">
              {job.duration.toFixed(1)}s
            </div>
          </button>
        </div>
      )}

      {/* 展开态 — 稍微放大（360 宽），含 controls + 加帧按钮，不全屏 */}
      {hasVideo && job && videoExpanded && (
        <div
          className="absolute left-0 right-0 flex justify-center"
          style={{ bottom: "calc(100% + 12px)" }}
        >
          <div
            onClick={(e) => e.stopPropagation()}
            className="relative rounded-xl overflow-hidden border border-white/25 shadow-2xl bg-black"
            style={{ width: 360, animation: "drawer-in 0.18s cubic-bezier(0.32, 0.72, 0, 1)" }}
          >
            <video
              ref={videoRef}
              src={videoUrl(job.id)}
              controls
              autoPlay
              playsInline
              preload="auto"
              onTimeUpdate={(e) => setVideoT((e.target as HTMLVideoElement).currentTime)}
              className="block w-full bg-black"
              style={{ aspectRatio: `${job.width}/${job.height}`, maxHeight: "60vh" }}
            />
            <div className="flex items-center justify-between px-3 py-2 bg-black/70 backdrop-blur-md gap-2">
              <button
                type="button"
                disabled={addingFrame}
                onClick={async (e) => {
                  e.stopPropagation()
                  const t = videoRef.current?.currentTime ?? videoT
                  setAddingFrame(true)
                  try { await d.onAddManualFrame(t) } finally { setAddingFrame(false) }
                }}
                className="flex-1 text-[11.5px] py-1.5 rounded-md bg-emerald-500 hover:bg-emerald-400 text-white disabled:opacity-50 inline-flex items-center justify-center gap-1.5 font-medium"
              >
                {addingFrame ? <Loader2 className="h-3 w-3 animate-spin" /> : <Plus className="h-3 w-3" />}
                + 把 {videoT.toFixed(1)}s 加为关键帧
              </button>
              <button
                type="button"
                onClick={(e) => { e.stopPropagation(); setVideoExpanded(false) }}
                className="px-2.5 py-1.5 text-[11px] rounded-md bg-white/10 hover:bg-white/20 text-white"
              >
                收起
              </button>
            </div>
          </div>
        </div>
      )}

    <NodeShell
      type="input" status={inputStatus(job)}
      icon={<Link2 className="h-4 w-4" />}
      title="输入 · Input"
      subtitle={isDownloading ? "STEP 1 · 下载中" : hasVideo ? "STEP 1 · 视频就绪" : "STEP 1"}
      width={320}
      selected={selected}
      hasTarget={false}
    >
      {/* 未下载：URL + 上传入口 */}
      {!hasVideo && (
        <>
          <input
            value={url}
            onChange={(e) => setUrl(e.target.value)}
            placeholder="粘贴 TikTok 链接"
            disabled={inputLocked}
            className="w-full text-[12px] px-2.5 py-2 rounded-md bg-white/60 dark:bg-black/40 border border-black/10 dark:border-white/10 outline-none text-[var(--text-strong)] placeholder:text-[var(--text-faint)] focus:ring-2 focus:ring-[var(--ring)] disabled:opacity-40"
          />
          <div className="mt-2 flex gap-1.5">
            <button
              type="button"
              disabled={inputLocked || !url.trim()}
              onClick={() => d.onSubmitUrl(url.trim())}
              className="flex-1 text-[11.5px] py-1.5 rounded-md bg-black text-white dark:bg-white dark:text-black hover:opacity-90 disabled:opacity-30 disabled:cursor-not-allowed flex items-center justify-center gap-1"
            >
              {(d.submitting || isDownloading) ? <Loader2 className="h-3 w-3 animate-spin" /> : null}
              {isDownloading ? "下载中…" : "提交链接"}
            </button>
            <button
              type="button"
              disabled={inputLocked}
              onClick={() => fileRef.current?.click()}
              className="text-[11.5px] px-2.5 py-1.5 rounded-md bg-white/60 dark:bg-white/[0.06] border border-black/10 dark:border-white/15 hover:bg-white/80 dark:hover:bg-white/[0.12] inline-flex items-center gap-1 disabled:opacity-30"
            >
              <Upload className="h-3 w-3" /> 上传
            </button>
            <input
              ref={fileRef}
              type="file"
              accept="video/mp4,video/quicktime,video/webm,video/x-matroska,.mp4,.mov,.webm,.mkv,.m4v"
              className="hidden"
              onChange={(e) => {
                const f = e.target.files?.[0]
                if (f) d.onUploadFile(f)
                e.target.value = ""
              }}
            />
          </div>
        </>
      )}

      {/* 已下载：仅元数据（视频缩略图浮在节点上方，点击进 lightbox） */}
      {hasVideo && job && (
        <>
          <div className="rounded-md bg-black/30 border border-black/10 dark:border-white/10 px-3 py-2 flex items-center justify-between text-[10.5px] font-mono">
            <span className="text-[var(--text-strong)]">{job.width}×{job.height} · {job.duration.toFixed(1)}s</span>
            <span className="text-[var(--text-faint)]">{job.url.startsWith("upload://") ? "📎 上传" : "🔗 链接"}</span>
          </div>

          <button
            type="button"
            disabled={isAnalyzing || d.analyzing}
            onClick={d.onAnalyze}
            className={`mt-2 w-full text-[14px] py-3 rounded-md bg-gradient-to-r from-indigo-500 to-violet-500 text-white hover:opacity-95 disabled:opacity-40 disabled:cursor-not-allowed flex items-center justify-center gap-2 font-semibold shadow-lg shadow-violet-500/30 ${
              !isAnalyzing && !d.analyzing && !isDone && !hasFrames ? "animate-[pulse_2s_ease-in-out_infinite] ring-2 ring-violet-400/40 ring-offset-2 ring-offset-transparent" : ""
            }`}
          >
            {(isAnalyzing || d.analyzing) ? (
              <><Loader2 className="h-4 w-4 animate-spin" /> 解析中…</>
            ) : isDone || hasFrames ? (
              "重新解析"
            ) : (
              <>▶ 点这里开始解析</>
            )}
          </button>
        </>
      )}
    </NodeShell>
    </div>
  )
}

/* ============================================================
   2. DownloadNode
   ============================================================ */
export function DownloadNode({ data, selected }: any) {
  const d: NodeData = data
  const st = downloadStatus(d.job)
  return (
    <NodeShell
      type="process" status={st}
      icon={<Download className="h-4 w-4" />}
      title="下载 · Download"
      subtitle="STEP 2 · yt-dlp"
      selected={selected}
    >
      <div className="text-[11.5px] text-[var(--text-soft)] leading-relaxed">
        {d.job?.url.startsWith("upload://") ? "本地上传 · 跳过下载" : "TikTok / yt-dlp 兼容站点"}
      </div>
      {d.job && st === "done" && (
        <div className="mt-2 grid grid-cols-2 gap-2 text-[10.5px] font-mono text-[var(--text-faint)]">
          <div>分辨率<br /><span className="text-[var(--text-strong)] text-[12px]">{d.job.width}×{d.job.height}</span></div>
          <div>时长<br /><span className="text-[var(--text-strong)] text-[12px]">{d.job.duration.toFixed(1)}s</span></div>
        </div>
      )}
    </NodeShell>
  )
}

/* ============================================================
   3. SplitNode
   ============================================================ */
export function SplitNode({ data, selected }: any) {
  const d: NodeData = data
  return (
    <NodeShell
      type="process" status={splitStatus(d.job)}
      icon={<Scissors className="h-4 w-4" />}
      title="拆分 · Split"
      subtitle="STEP 3 · ffmpeg"
      selected={selected}
    >
      <div className="grid grid-cols-2 gap-2 text-[11px]">
        <div className="rounded-md bg-white/40 dark:bg-white/[0.04] border border-black/5 dark:border-white/5 px-2 py-1.5">
          <div className="text-[10px] uppercase tracking-widest text-[var(--text-faint)]">视频流</div>
          <div className="text-[var(--text-strong)] mt-0.5">→ 关键帧</div>
        </div>
        <div className="rounded-md bg-white/40 dark:bg-white/[0.04] border border-black/5 dark:border-white/5 px-2 py-1.5">
          <div className="text-[10px] uppercase tracking-widest text-[var(--text-faint)]">音频流</div>
          <div className="text-[var(--text-strong)] mt-0.5">→ ASR</div>
        </div>
      </div>
    </NodeShell>
  )
}

/* ============================================================
   4. KeyframeNode — 缩略图横排浮在节点上方，点击展开 lightbox
   ============================================================ */
const KEYFRAME_WIDTH = 360
const THUMB_W = 64
const THUMB_GAP = 6

export function KeyframeNode({ data, selected }: any) {
  const d: NodeData = data
  const st = keyframeStatus(d.job)
  const frames = d.job?.frames ?? []
  const jobId = d.job?.id

  return (
    <div className="relative" style={{ width: KEYFRAME_WIDTH }}>
      {/* 缩略图浮条（节点上方，最多 5 个一行，多行向上扩展） */}
      {frames.length > 0 && jobId && (
        <div
          className="absolute left-0 right-0 grid grid-cols-5 gap-1.5"
          style={{ bottom: "calc(100% + 12px)" }}
        >
          {frames.map((f) => {
            const isSel = d.selectedFrames.has(f.index)
            return (
              <button
                key={f.index}
                onClick={(e) => { e.stopPropagation(); d.onExpandFrame(f.index) }}
                title={`第 ${f.index + 1} 张 · ${f.timestamp.toFixed(1)}s · 点击放大`}
                className={`group relative rounded-md border transition shadow-lg hover:-translate-y-0.5 aspect-video ${
                  isSel
                    ? "border-emerald-400 ring-2 ring-emerald-400/60"
                    : "border-white/30 dark:border-white/20"
                }`}
              >
                <img
                  src={frameUrl(jobId, f.index)}
                  alt={`frame ${f.index}`}
                  className="absolute inset-0 w-full h-full object-cover rounded-md"
                />
                {isSel && (
                  <div className="absolute inset-0 bg-emerald-400/15 rounded-md pointer-events-none" />
                )}
                {/* 时间戳 */}
                <div className="absolute bottom-0 right-0 bg-black/70 text-white text-[8.5px] font-mono px-1 py-0.5 leading-none rounded-bl rounded-br-md">
                  {f.timestamp.toFixed(1)}s
                </div>

                {/* Hover 静态大图预览（关键帧是给下游生图垫图的素材，不需要视频） */}
                <div
                  className="pointer-events-none absolute opacity-0 group-hover:opacity-100 scale-95 group-hover:scale-100 transition-all duration-200 z-[60]"
                  style={{
                    bottom: "calc(100% + 10px)",
                    left: "50%",
                    transform: "translateX(-50%)",
                    transformOrigin: "bottom center",
                  }}
                >
                  <div className="rounded-2xl overflow-hidden border border-white/25 bg-black" style={{ boxShadow: "0 40px 100px -20px rgba(0,0,0,0.85), 0 0 0 1px rgba(255,255,255,0.06)" }}>
                    <img
                      src={frameUrl(jobId, f.index)}
                      alt={`preview ${f.index}`}
                      className="block"
                      style={{
                        width: KEYFRAME_WIDTH * 2,
                        maxWidth: "min(720px, 80vw)",
                        height: "auto",
                        maxHeight: "82vh",
                        objectFit: "contain",
                      }}
                    />
                    <div className="flex items-center justify-between px-3 py-2 bg-black/70 backdrop-blur-md">
                      <span className="text-white text-[12.5px] font-medium">分镜 {f.index + 1}</span>
                      <span className="text-white/60 text-[11px] font-mono">{f.timestamp.toFixed(2)}s · 静态帧</span>
                    </div>
                  </div>
                </div>
              </button>
            )
          })}
        </div>
      )}

      <NodeShell
        type="process" status={st}
        icon={<ImageIcon className="h-4 w-4" />}
        title="关键帧 · Keyframes"
        subtitle={`STEP 2 · ffmpeg · ${frames.length ? `${d.selectedFrames.size}/${frames.length} 选用` : "等待抽取"}`}
        width={KEYFRAME_WIDTH}
        selected={selected}
      >
        {frames.length > 0 ? (
          <div className="text-[11.5px] leading-relaxed text-[var(--text-soft)]">
            自动 <span className="text-[var(--text-strong)] font-medium">{frames.length}</span> 张 · 选中 {d.selectedFrames.size} 张
            <br />
            <span className="text-[10.5px] text-[var(--text-faint)]">
              上方缩略图点击放大 · 在 Input 节点拖时间轴可手动加帧
            </span>
          </div>
        ) : (
          <div className="text-[11.5px] text-[var(--text-faint)] py-1">
            等待解析（默认 5 张）
          </div>
        )}
      </NodeShell>
    </div>
  )
}

/* ============================================================
   5. ASRNode — Gemini 转录
   ============================================================ */
export function ASRNode({ data, selected }: any) {
  const d: NodeData = data
  return (
    <NodeShell
      type="ai" status={asrStatus(d.job)}
      icon={<Mic className="h-4 w-4" />}
      title="转录 · ASR"
      subtitle="STEP 5 · Gemini"
      selected={selected}
    >
      <div className="text-[11.5px] text-[var(--text-soft)]">
        Gemini 2.5 · 英文带时间戳分段
      </div>
      {d.job && d.job.transcript.length > 0 && (
        <div className="mt-2 max-h-24 overflow-y-auto text-[11px] space-y-1 text-[var(--text-strong)]">
          {d.job.transcript.slice(0, 3).map((s) => (
            <div key={s.index} className="leading-snug">
              <span className="text-[var(--text-faint)] font-mono text-[10px] mr-1">
                {s.start.toFixed(1)}s
              </span>
              {s.en.slice(0, 60)}
              {s.en.length > 60 && "…"}
            </div>
          ))}
          {d.job.transcript.length > 3 && (
            <div className="text-[var(--text-faint)] text-[10px]">还有 {d.job.transcript.length - 3} 段…</div>
          )}
        </div>
      )}
    </NodeShell>
  )
}

/* ============================================================
   6. TranslateNode
   ============================================================ */
export function TranslateNode({ data, selected }: any) {
  const d: NodeData = data
  const hasZh = d.job?.transcript.some((s) => s.zh) ?? false
  const st: NodeStatus = !d.job ? "pending" :
    d.job.status === "transcribing" ? "running" :
    hasZh ? "done" :
    d.job.status === "failed" ? "failed" : "pending"
  return (
    <NodeShell
      type="ai" status={st}
      icon={<Languages className="h-4 w-4" />}
      title="翻译 · Translate"
      subtitle="STEP 6 · EN → ZH"
      selected={selected}
    >
      <div className="text-[11.5px] text-[var(--text-soft)]">
        中文翻译 · 段落级 · 实时输出
      </div>
      {hasZh && d.job && (
        <div className="mt-2 max-h-24 overflow-y-auto text-[11px] space-y-1 text-[var(--text-strong)]">
          {d.job.transcript.slice(0, 3).map((s) => (
            <div key={s.index} className="leading-snug">{s.zh.slice(0, 30)}{s.zh.length > 30 && "…"}</div>
          ))}
        </div>
      )}
    </NodeShell>
  )
}

/* ============================================================
   7. RewriteNode (placeholder)
   ============================================================ */
export function RewriteNode({ selected }: any) {
  return (
    <NodeShell
      type="ai" status="pending"
      icon={<FileEdit className="h-4 w-4" />}
      title="文案改写 · Rewrite"
      subtitle="STEP 7 · 接产品信息"
      selected={selected}
    >
      <textarea
        placeholder="粘贴 SKG 产品信息 / 关键卖点（占位，未接通）"
        rows={3}
        disabled
        className="w-full text-[11.5px] px-2.5 py-2 rounded-md bg-white/30 dark:bg-white/[0.03] border border-dashed border-black/15 dark:border-white/10 placeholder:text-[var(--text-faint)] text-[var(--text-strong)] resize-none opacity-70"
      />
      <div className="mt-1.5 text-[10px] text-[var(--text-faint)]">下一冲刺接入</div>
    </NodeShell>
  )
}

/* ============================================================
   8. ImageGenNode (placeholder)
   ============================================================ */
export function ImageGenNode({ selected }: any) {
  return (
    <NodeShell
      type="ai" status="pending"
      icon={<Sparkles className="h-4 w-4" />}
      title="生图 · Image Gen"
      subtitle="STEP 8 · nano-banana / GPT"
      selected={selected}
    >
      <div className="grid grid-cols-2 gap-1.5">
        <div className="rounded-md border border-dashed border-black/15 dark:border-white/10 px-2 py-1.5 text-[10.5px] text-[var(--text-faint)]">
          nano-banana-pro<br /><span className="text-[var(--text-strong)] text-[11px]">Gemini 3 Image</span>
        </div>
        <div className="rounded-md border border-dashed border-black/15 dark:border-white/10 px-2 py-1.5 text-[10.5px] text-[var(--text-faint)]">
          GPT Image<br /><span className="text-[var(--text-strong)] text-[11px]">OpenAI</span>
        </div>
      </div>
    </NodeShell>
  )
}

/* ============================================================
   9. VideoGenNode (placeholder)
   ============================================================ */
export function VideoGenNode({ selected }: any) {
  return (
    <NodeShell
      type="ai" status="pending"
      icon={<Film className="h-4 w-4" />}
      title="生视频 · Video Gen"
      subtitle="STEP 9 · 多家可切"
      selected={selected}
    >
      <div className="grid grid-cols-3 gap-1.5 text-[10.5px]">
        {["Seedance", "Kling", "Veo 3"].map((m) => (
          <div key={m} className="rounded-md border border-dashed border-black/15 dark:border-white/10 px-2 py-1.5 text-center text-[var(--text-faint)]">
            <span className="text-[var(--text-strong)] text-[11px]">{m}</span>
          </div>
        ))}
      </div>
    </NodeShell>
  )
}

/* ============================================================
   10. ComposeNode (placeholder)
   ============================================================ */
export function ComposeNode({ selected }: any) {
  return (
    <NodeShell
      type="output" status="pending"
      icon={<FileVideo className="h-4 w-4" />}
      title="合成成品 · Compose"
      subtitle="STEP 10 · ffmpeg + TTS"
      selected={selected}
      hasSource={false}
    >
      <div className="text-[11.5px] text-[var(--text-soft)] leading-relaxed">
        视频片段 + 字幕 / TTS<br />→ 最终 mp4 输出
      </div>
    </NodeShell>
  )
}
