"use client";

import { useMemo, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import Topbar from "@/components/Topbar";
import Icon from "@/components/Icon";

/* ============================================================
   Data
   ============================================================ */

interface Product {
  id: string;
  name: string;
  cat: string;
  price: number;
  imgs: number;
  points: string[];
  tags: string[];
  thumb: string;
}

const PRODUCTS: Product[] = [
  { id: "mask",    name: "透真玻尿酸补水面膜",       cat: "美妆个护", price: 39.9,  imgs: 3, points: ["透明质酸 + B5", "30g 大容量精华", "0 香精 0 酒精"], tags: ["熬夜党", "敏感肌"],   thumb: "补水面膜" },
  { id: "earphone",name: "南卡 Lite Pro 蓝牙耳机",   cat: "数码 3C", price: 199,   imgs: 5, points: ["主动降噪", "32 小时续航", "IP55 防水"],         tags: ["通勤", "运动"],       thumb: "蓝牙耳机" },
  { id: "noodle",  name: "滋啦速食牛肉面 · 6 桶装",  cat: "食品饮料", price: 49.9,  imgs: 4, points: ["3 分钟出餐", "真材实料牛肉", "0 防腐剂"],       tags: ["加班", "独居"],       thumb: "速食牛肉面" },
  { id: "sun",     name: "透真清透物理防晒霜",       cat: "美妆个护", price: 69,    imgs: 4, points: ["SPF50 PA+++", "纯物理防晒", "不泛白不假面"],     tags: ["SPF50", "通勤"],      thumb: "防晒霜" },
  { id: "coffee",  name: "三顿半同款冻干咖啡粉",     cat: "食品饮料", price: 89,    imgs: 6, points: ["冷热水秒溶", "意式深烘", "24 颗轻便装"],         tags: ["提神", "早八"],       thumb: "咖啡冻干粉" },
  { id: "fryer",   name: "小熊 4L 可视空气炸锅",     cat: "家电",     price: 159,   imgs: 5, points: ["可视化窗口", "4L 大容量", "低脂少油"],           tags: ["小户型", "健康"],     thumb: "空气炸锅" },
  { id: "yoga",    name: "露露同款裸感瑜伽裤",       cat: "服饰",     price: 119,   imgs: 8, points: ["裸感面料", "高弹回弹", "随心动随心穿"],           tags: ["健身房", "通勤"],     thumb: "瑜伽裤" },
];

const RECENT_IDS = ["mask", "sun", "coffee", "earphone"];
const CATS = ["全部", "美妆个护", "数码 3C", "食品饮料", "服饰", "家电"];

type SourceId = "ai" | "theme" | "manual";
const SOURCES: Array<{ id: SourceId; name: string; icon: "sparkles" | "lightbulb" | "doc"; tag: string; desc: string }> = [
  { id: "ai",     name: "AI 全生",     icon: "sparkles",  tag: "最常用",  desc: "LLM 全权决定脚本走向，最省事。后续仍可在故事板阶段微调。" },
  { id: "theme",  name: "一句话主题",   icon: "lightbulb", tag: "轻引导",  desc: "你给一句切入主题，AI 按此扩写。推荐 5–30 字。" },
  { id: "manual", name: "自带脚本",     icon: "doc",       tag: "我已有稿", desc: "粘贴或上传完整脚本，系统按镜头自动切分并适配商品。" },
];

type DurationId = "0-10" | "0-15" | "0-30" | "0-60";
const DURATIONS: Array<{ id: DurationId; label: string; shotsRange: [number, number]; tag: string; completion: number; conversion: number; }> = [
  { id: "0-10", label: "0-10 秒", shotsRange: [3, 4],   tag: "黄金完播",   completion: 52, conversion: 1.6 },
  { id: "0-15", label: "0-15 秒", shotsRange: [4, 5],   tag: "完播率最佳", completion: 42, conversion: 1.8 },
  { id: "0-30", label: "0-30 秒", shotsRange: [6, 8],   tag: "卖点详解",   completion: 32, conversion: 2.1 },
  { id: "0-60", label: "0-60 秒", shotsRange: [10, 12], tag: "故事化",     completion: 26, conversion: 2.4 },
];

type StyleId = "pain" | "review" | "compare";
const STYLES: Array<{ id: StyleId; name: string; note: string; tag?: string; flow: string[] }> = [
  { id: "pain",    name: "痛点种草", note: "用户痛点切入，以「我懂你」的口吻引出产品。", tag: "最常用", flow: ["痛点", "共鸣", "产品", "效果", "引导"] },
  { id: "review",  name: "开箱测评", note: "朋友式分享，从开箱到使用感受娓娓道来。",                  flow: ["开箱", "首印象", "试用", "对比", "结论"] },
  { id: "compare", name: "对比展示", note: "「用前 vs 用后 / 同类 vs 本品」直观呈现。",               flow: ["对照", "差距", "本品", "数据", "购买"] },
];

type PersonaId = "urban" | "bestie" | "ceo" | "reviewer" | "mom" | "genz";
const PERSONAS: Array<{ id: PersonaId; name: string; sub: string; metric: string; defaults: { duration: DurationId; style: StyleId } }> = [
  { id: "urban",    name: "都市白领女性", sub: "25-30 岁",     metric: "大盘消费力",     defaults: { duration: "0-15", style: "pain" } },
  { id: "bestie",   name: "闺蜜种草",     sub: "邻家女孩",     metric: "复购最高",       defaults: { duration: "0-15", style: "pain" } },
  { id: "ceo",      name: "总裁亲选",     sub: "创始人 IP",    metric: "30 万销额案例",  defaults: { duration: "0-30", style: "pain" } },
  { id: "reviewer", name: "专业测评师",   sub: "垂类达人",     metric: "互动 +30%",      defaults: { duration: "0-30", style: "review" } },
  { id: "mom",      name: "实用宝妈",     sub: "家庭决策者",   metric: "母婴/家清稳",    defaults: { duration: "0-30", style: "pain" } },
  { id: "genz",     name: "学生党",       sub: "Z 世代 18-24", metric: "平价快消",       defaults: { duration: "0-10", style: "compare" } },
];

/* ============================================================
   Helpers
   ============================================================ */

const USER_EMAIL = "airlabsv001@gmail.com";
const ACCOUNT_BALANCE = 327.4;

function avg([a, b]: [number, number]) { return (a + b) / 2; }

/* ============================================================
   Component
   ============================================================ */

type StepNum = 1 | 2 | 3 | 4;

export default function NewProjectPage() {
  const router = useRouter();
  const [step, setStep] = useState<StepNum>(1);

  // Step 1
  const [productId, setProductId] = useState<string | null>(null);
  const [pickSearch, setPickSearch] = useState("");
  const [pickCat, setPickCat] = useState("全部");

  // Step 2
  const [sourceId, setSourceId] = useState<SourceId | null>(null);
  const [themeText, setThemeText] = useState("");
  const [manualScript, setManualScript] = useState("");

  // Step 3
  const [projectName, setProjectName] = useState("");
  const [duration, setDuration] = useState<DurationId>("0-15");
  const [scriptStyle, setScriptStyle] = useState<StyleId>("pain");
  const [persona, setPersona] = useState<PersonaId>("urban");
  const [recoDismissed, setRecoDismissed] = useState(false);
  const [points, setPoints] = useState<Record<string, boolean>>({});

  // Step 4
  const [notifyEmail, setNotifyEmail] = useState(true);
  const [notifyWeChat, setNotifyWeChat] = useState(false);
  const [agreed, setAgreed] = useState(false);

  /* ---- derived ---- */
  const product   = useMemo(() => PRODUCTS.find((p) => p.id === productId) ?? null, [productId]);
  const source    = useMemo(() => SOURCES.find((s) => s.id === sourceId)   ?? null, [sourceId]);
  const personaObj  = useMemo(() => PERSONAS.find((p) => p.id === persona)!, [persona]);
  const durationObj = useMemo(() => DURATIONS.find((d) => d.id === duration)!, [duration]);
  const styleObj    = useMemo(() => STYLES.find((s) => s.id === scriptStyle)!, [scriptStyle]);

  const shots = avg(durationObj.shotsRange);
  const completion = durationObj.completion;
  const conversion = durationObj.conversion;

  // Live cost: roughly 4 line items
  const cost = useMemo(() => {
    const script = 0.20;
    const storyboard = 0.40;
    const assets = product ? product.imgs * 0.30 : 0;
    const render = shots * 0.30;
    const subtotal = script + storyboard + assets + render;
    const fee = +(subtotal * 0.05).toFixed(2);
    return { script, storyboard, assets, render, subtotal: +subtotal.toFixed(2), fee, total: +(subtotal + fee).toFixed(2) };
  }, [product, shots]);

  const balanceAfter = +(ACCOUNT_BALANCE - cost.total).toFixed(2);
  const lowBalance = balanceAfter < 5;

  const etaMinutes = Math.max(3, Math.round(2 + shots * 0.4 + (product?.imgs ?? 0) * 0.2));

  // Reco bubble (Step 3)
  const recoMismatch =
    personaObj.defaults.duration !== duration || personaObj.defaults.style !== scriptStyle;
  const showReco = step === 3 && recoMismatch && !recoDismissed;
  const recoDuration = DURATIONS.find((d) => d.id === personaObj.defaults.duration)!;
  const recoStyle = STYLES.find((s) => s.id === personaObj.defaults.style)!;

  /* ---- validation gates ---- */
  const canPass1 = !!product;
  const canPass2 =
    !!source &&
    (source.id !== "theme"  || themeText.trim().length >= 4) &&
    (source.id !== "manual" || manualScript.trim().length >= 20);
  const canPass3 = projectName.trim().length >= 2;
  const canFinish = agreed && !lowBalance;

  /* ---- actions ---- */
  function selectProduct(p: Product) {
    setProductId(p.id);
    // seed defaults derived from product
    if (!projectName) setProjectName(`${p.name.split(" ")[0]} · 痛点种草 · v1`);
    const seeded: Record<string, boolean> = {};
    p.points.forEach((pt, i) => { seeded[pt] = i < 2; });
    setPoints(seeded);
  }

  function applyPreset() {
    setDuration(personaObj.defaults.duration);
    setScriptStyle(personaObj.defaults.style);
    setRecoDismissed(false);
  }
  function pickPersona(id: PersonaId) {
    setPersona(id);
    setRecoDismissed(false);
  }
  function togglePoint(k: string) { setPoints((p) => ({ ...p, [k]: !p[k] })); }

  function goPrev() { setStep((s) => (s > 1 ? ((s - 1) as StepNum) : s)); }
  function goNext() {
    if (step === 1 && !canPass1) return;
    if (step === 2 && !canPass2) return;
    if (step === 3 && !canPass3) return;
    setStep((s) => (s < 4 ? ((s + 1) as StepNum) : s));
  }
  function startGenerate() {
    if (!canFinish) return;
    router.push("/pipeline?stage=1");
  }
  function jumpTo(target: StepNum) {
    // only allow going to a completed step or current
    if (target < step) setStep(target);
  }

  /* ---- step rail config ---- */
  const stepConfig: Array<{ n: StepNum; label: string; desc: string }> = [
    { n: 1, label: "选择商品",   desc: product ? product.name : "未选择" },
    { n: 2, label: "脚本来源",   desc: source ? source.name + (source.id === "theme" && themeText ? " · 有主题" : "") : "未选择" },
    { n: 3, label: "项目配置",   desc: step >= 3 ? `${durationObj.label} · ${styleObj.name}` : "时长 · 风格 · 人设" },
    { n: 4, label: "确认与计费", desc: `预估 ¥${cost.total.toFixed(2)}` },
  ];

  /* ---- filtered products for Step 1 ---- */
  const filteredProducts = useMemo(() => {
    return PRODUCTS.filter((p) => {
      if (pickCat !== "全部" && p.cat !== pickCat) return false;
      if (pickSearch && !p.name.includes(pickSearch)) return false;
      return true;
    });
  }, [pickCat, pickSearch]);

  const recentProducts = useMemo(
    () => RECENT_IDS.map((id) => PRODUCTS.find((p) => p.id === id)!).filter(Boolean),
    []
  );

  return (
    <>
      <Topbar
        crumbs={[
          { label: "工作台", href: "/" },
          { label: "视频项目", href: "/projects" },
          { label: "新建项目" },
        ]}
      />
      <section className="content wizard-content">
        <div className="page-head">
          <div>
            <h1>新建项目</h1>
            <div className="sub">
              <span className="mono-sub">// 商品 → 脚本来源 → 配置 → 确认 · 4 步开始生成</span>
            </div>
          </div>
          <div className="actions">
            <Link className="btn btn-ghost" href="/projects">退出</Link>
          </div>
        </div>

        <div className="wizard-shell">
          {/* ── Steps rail ─────────────────────────── */}
          <nav className="steps">
            {stepConfig.map((s, i) => {
              const state: "done" | "active" | "pending" =
                s.n < step ? "done" : s.n === step ? "active" : "pending";
              const clickable = s.n < step;
              return (
                <div
                  key={s.n}
                  className={`step ${state}${clickable ? " clickable" : ""}${i === stepConfig.length - 1 ? " last" : ""}`}
                  onClick={() => clickable && jumpTo(s.n)}
                >
                  <div className="num">
                    {state === "done"
                      ? <Icon name="check" size={12} strokeWidth={3} />
                      : s.n}
                  </div>
                  <div>
                    <div className="step-label">{s.label}</div>
                    <div className="step-desc">{s.desc}</div>
                  </div>
                </div>
              );
            })}
          </nav>

          {/* ── Wiz main ───────────────────────────── */}
          <div className="wiz-main">
            {/* Step 1 · 选择商品 ───────────────── */}
            {step === 1 && (
              <section className="card active-step">
                <div className="step-h">
                  <h2>第 1 步 · 选择商品</h2>
                  <p>从商品库选一个 SKU。它的主图与卖点会被 LLM 作为脚本/资产生成的素材。</p>
                </div>

                <div className="pick-toolbar">
                  <div className="toolbar-search">
                    <Icon name="search" />
                    <input
                      className="input"
                      placeholder="搜索商品名称、品牌"
                      value={pickSearch}
                      onChange={(e) => setPickSearch(e.target.value)}
                    />
                  </div>
                  {CATS.map((c) => (
                    <button
                      key={c}
                      className={`filter-chip${pickCat === c ? " active" : ""}`}
                      onClick={() => setPickCat(c)}
                    >
                      {c}
                    </button>
                  ))}
                </div>

                {pickCat === "全部" && !pickSearch && (
                  <>
                    <div className="pick-section-h">
                      <span>最近使用</span>
                      <span className="count">{recentProducts.length}</span>
                    </div>
                    <div className="product-pick-grid">
                      {recentProducts.map((p) => (
                        <ProductPickCard key={p.id} p={p} selected={productId === p.id} onSelect={() => selectProduct(p)} />
                      ))}
                    </div>
                  </>
                )}

                <div className="pick-section-h">
                  <span>{pickCat === "全部" && !pickSearch ? "全部商品" : "搜索结果"}</span>
                  <span className="count">{filteredProducts.length}</span>
                </div>
                <div className="product-pick-grid">
                  {filteredProducts.map((p) => (
                    <ProductPickCard key={p.id} p={p} selected={productId === p.id} onSelect={() => selectProduct(p)} />
                  ))}
                  <div className="product-pick add">
                    <div className="pc"><Icon name="plus" size={16} /></div>
                    <div>新建商品</div>
                  </div>
                </div>
              </section>
            )}

            {/* Step 2 · 脚本来源 ───────────────── */}
            {step === 2 && (
              <>
                <CollapsedStep
                  title="第 1 步 · 选择商品"
                  onEdit={() => setStep(1)}
                  body={product && <ProductSummary p={product} />}
                />
                <section className="card active-step">
                  <div className="step-h">
                    <h2>第 2 步 · 脚本来源</h2>
                    <p>决定 LLM 如何获得初稿脚本。三种方式由「最省事」到「最保真原意」。</p>
                  </div>

                  <div className="source-row">
                    {SOURCES.map((s) => (
                      <div
                        key={s.id}
                        className={`source-card${sourceId === s.id ? " selected" : ""}`}
                        onClick={() => setSourceId(s.id)}
                      >
                        <span className="src-ic"><Icon name={s.icon} size={16} /></span>
                        <h4>{s.name}</h4>
                        <span className="src-tag">[ {s.tag} ]</span>
                        <p className="src-desc">{s.desc}</p>
                      </div>
                    ))}
                  </div>

                  {source && (
                    <div className="source-detail">
                      <div className="sd-h">
                        // 已选 · <b>{source.name}</b>
                      </div>
                      {source.id === "ai" && (
                        <div className="field-hint" style={{ fontSize: 12.5, color: "var(--ink-2)" }}>
                          AI 全生模式无需额外输入。下一步选定时长 / 风格 / 人设后，LLM 会自动决定切入点和卖点权重。
                        </div>
                      )}
                      {source.id === "theme" && (
                        <div className="field" style={{ marginBottom: 0 }}>
                          <label className="field-label">一句话主题<span className="req">*</span></label>
                          <input
                            className="input"
                            placeholder="例：熬夜党的急救面膜 / 加班吃啥不内疚"
                            value={themeText}
                            onChange={(e) => setThemeText(e.target.value)}
                          />
                          <div className="field-hint">推荐 5–30 字。这句话会作为 LLM 扩写的锚点，越具体越聚焦。</div>
                        </div>
                      )}
                      {source.id === "manual" && (
                        <div className="field" style={{ marginBottom: 0 }}>
                          <label className="field-label">粘贴脚本内容<span className="req">*</span></label>
                          <textarea
                            className="input textarea"
                            style={{ minHeight: 140 }}
                            placeholder="粘贴你的脚本内容（旁白 / 镜头描述均可，系统会自动切分镜头）"
                            value={manualScript}
                            onChange={(e) => setManualScript(e.target.value)}
                          />
                          <div className="field-hint">
                            最少 20 字。镜头数由你的脚本自然段落决定，时长 / 风格仍会影响后期渲染节奏。
                          </div>
                        </div>
                      )}
                    </div>
                  )}
                </section>
              </>
            )}

            {/* Step 3 · 项目配置 ───────────────── */}
            {step === 3 && (
              <>
                <CollapsedStep
                  title="第 1 步 · 选择商品"
                  onEdit={() => setStep(1)}
                  body={product && <ProductSummary p={product} />}
                />
                <CollapsedStep
                  title="第 2 步 · 脚本来源"
                  onEdit={() => setStep(2)}
                  body={source && <SourceSummary source={source} themeText={themeText} manualScript={manualScript} />}
                />

                <section className="card active-step">
                  <div className="step-h">
                    <h2>第 3 步 · 项目配置</h2>
                    <p>这些设置会影响 LLM 生成脚本的方向，确认后会进入流水线第 1 步（脚本生成）。</p>
                  </div>

                  <div className="field">
                    <label className="field-label">项目名称<span className="req">*</span></label>
                    <input className="input" value={projectName} onChange={(e) => setProjectName(e.target.value)} />
                  </div>

                  <div className="field">
                    <label className="field-label">视频时长<span className="req">*</span></label>
                    <div className="option-row cols-4">
                      {DURATIONS.map((d) => (
                        <div
                          key={d.id}
                          className={`option-card${duration === d.id ? " selected" : ""}`}
                          onClick={() => setDuration(d.id)}
                        >
                          <h4>{d.label}</h4>
                          <div className="sub">{d.shotsRange[0]}-{d.shotsRange[1]} 镜</div>
                          <div className="note">{d.tag}</div>
                          <div className="metric">完播 <span className="val">{d.completion}%</span></div>
                        </div>
                      ))}
                    </div>
                    <div className="field-hint">数据来源：抖音同品类 TOP 视频均值 · 实际镜头数由 LLM 决定</div>
                  </div>

                  <div className="field">
                    <label className="field-label">脚本风格</label>
                    <div className="option-row">
                      {STYLES.map((s) => (
                        <div
                          key={s.id}
                          className={`option-card${scriptStyle === s.id ? " selected" : ""}`}
                          onClick={() => setScriptStyle(s.id)}
                        >
                          <h4>{s.name}</h4>
                          <div className="note">{s.note}</div>
                          {s.tag && <span className="tag-mono">[ {s.tag} ]</span>}
                        </div>
                      ))}
                    </div>
                  </div>

                  <div className="field">
                    <label className="field-label">人物设定</label>
                    <div className="option-row cols-6">
                      {PERSONAS.map((p) => (
                        <div
                          key={p.id}
                          className={`option-card${persona === p.id ? " selected" : ""}`}
                          onClick={() => pickPersona(p.id)}
                        >
                          <h4>{p.name}</h4>
                          <div className="sub">{p.sub}</div>
                          <div className="metric"><span className="val">{p.metric}</span></div>
                        </div>
                      ))}
                    </div>

                    {showReco && (
                      <div className="reco-bubble">
                        <span className="ic"><Icon name="lightbulb" size={14} /></span>
                        <div className="txt">
                          <span>
                            抖音同人设 TOP 视频更常用 <strong>{recoDuration.label}</strong> + <strong>{recoStyle.name}</strong>
                          </span>
                          <span className="meta">
                            当前 {durationObj.label} · {styleObj.name} → 推荐换为同人设最优组合
                          </span>
                        </div>
                        <button onClick={applyPreset}>一键套用</button>
                        <button className="dismiss" onClick={() => setRecoDismissed(true)} aria-label="忽略">
                          <Icon name="x" size={14} />
                        </button>
                      </div>
                    )}
                  </div>

                  {Object.keys(points).length > 0 && (
                    <div className="field" style={{ marginBottom: 0 }}>
                      <label className="field-label">关键卖点（可勾选要重点突出的）</label>
                      <div className="hstack" style={{ gap: 6, flexWrap: "wrap" }}>
                        {Object.entries(points).map(([k, v]) => (
                          <span key={k} className={`theme-pill${v ? " on" : ""}`} onClick={() => togglePoint(k)}>
                            {v ? "✓" : "+"} {k}
                          </span>
                        ))}
                      </div>
                    </div>
                  )}
                </section>
              </>
            )}

            {/* Step 4 · 确认与计费 ───────────── */}
            {step === 4 && (
              <section className="card active-step">
                <div className="step-h">
                  <h2>第 4 步 · 确认与计费</h2>
                  <p>核对前 3 步的选择 + 计费明细。点击「开始生成」会立刻扣款并进入流水线。</p>
                </div>

                <div className="confirm-grid">
                  <div className="confirm-card">
                    <div className="cc-h">
                      <span>// 商品</span>
                      <button className="cc-edit" onClick={() => setStep(1)}>修改</button>
                    </div>
                    {product && (
                      <div className="hstack" style={{ gap: 12, alignItems: "flex-start" }}>
                        <div className="placeholder" style={{ width: 44, height: 56, flexShrink: 0 }}>
                          <span className="ph-frame">9:16</span>
                        </div>
                        <div className="cc-body" style={{ minWidth: 0 }}>
                          <div style={{ fontWeight: 600, fontSize: 13 }}>{product.name}</div>
                          <div className="ln">
                            <span>{product.cat}</span>
                            <span style={{ color: "var(--ink-4)" }}>·</span>
                            <b>¥{product.price}</b>
                            <span style={{ color: "var(--ink-4)" }}>·</span>
                            <span>{product.imgs} 张图</span>
                          </div>
                        </div>
                      </div>
                    )}
                  </div>

                  <div className="confirm-card">
                    <div className="cc-h">
                      <span>// 脚本来源</span>
                      <button className="cc-edit" onClick={() => setStep(2)}>修改</button>
                    </div>
                    {source && (
                      <div className="cc-body">
                        <div style={{ fontWeight: 600, fontSize: 13 }}>{source.name}</div>
                        <div className="ln">
                          {source.id === "ai"     && <span>LLM 全权 · 走向由 Step 3 决定</span>}
                          {source.id === "theme"  && <span>主题：<b>{themeText || "（未填）"}</b></span>}
                          {source.id === "manual" && <span><b>{manualScript.length}</b> 字 · 自动切镜</span>}
                        </div>
                      </div>
                    )}
                  </div>

                  <div className="confirm-card">
                    <div className="cc-h">
                      <span>// 项目配置</span>
                      <button className="cc-edit" onClick={() => setStep(3)}>修改</button>
                    </div>
                    <div className="cc-body">
                      <div style={{ fontWeight: 600, fontSize: 13 }}>{projectName}</div>
                      <div className="ln"><b>{styleObj.name}</b> · {personaObj.name} · {personaObj.sub}</div>
                      <div className="ln" style={{ fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--ink-3)" }}>
                        卖点：{Object.entries(points).filter(([, v]) => v).map(([k]) => k).join(" / ") || "未选"}
                      </div>
                    </div>
                  </div>

                  <div className="confirm-card">
                    <div className="cc-h">
                      <span>// 输出参数</span>
                    </div>
                    <div className="cc-body">
                      <div className="ln"><b>{durationObj.label}</b> · <b>{durationObj.shotsRange[0]}-{durationObj.shotsRange[1]} 镜</b> · 9:16</div>
                      <div className="ln">预估完播 <b>{completion}%</b> · 预估转化 <b>{conversion}%</b></div>
                      <div className="ln" style={{ fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--ink-3)" }}>
                        // 数据来源：抖音同品类 TOP 均值
                      </div>
                    </div>
                  </div>
                </div>

                <div className="section-sub">计费明细 · 按量计费</div>
                <div className="bill-list">
                  <div className="bill-row">
                    <div className="l">脚本生成 <span className="l-sub">LLM · 1 稿</span></div>
                    <div className="qty">× 1</div>
                    <div className="amt">¥{cost.script.toFixed(2)}</div>
                  </div>
                  <div className="bill-row">
                    <div className="l">故事板生成 <span className="l-sub">含分镜画面描述</span></div>
                    <div className="qty">× 1</div>
                    <div className="amt">¥{cost.storyboard.toFixed(2)}</div>
                  </div>
                  <div className="bill-row">
                    <div className="l">资产生成 <span className="l-sub">主图 → 镜头素材</span></div>
                    <div className="qty">× {product?.imgs ?? 0} 张</div>
                    <div className="amt">¥{cost.assets.toFixed(2)}</div>
                  </div>
                  <div className="bill-row">
                    <div className="l">视频渲染 <span className="l-sub">合成 · 配乐 · 字幕</span></div>
                    <div className="qty">× {shots} 镜</div>
                    <div className="amt">¥{cost.render.toFixed(2)}</div>
                  </div>
                  <div className="bill-row subtotal">
                    <div className="l">小计</div>
                    <div className="qty" />
                    <div className="amt">¥{cost.subtotal.toFixed(2)}</div>
                  </div>
                  <div className="bill-row subtotal">
                    <div className="l">平台服务费 <span className="l-sub">5%</span></div>
                    <div className="qty" />
                    <div className="amt">¥{cost.fee.toFixed(2)}</div>
                  </div>
                  <div className="bill-row total">
                    <div className="l">合计</div>
                    <div className="qty" />
                    <div className="amt">¥{Math.floor(cost.total)}<small>.{cost.total.toFixed(2).split(".")[1]}</small></div>
                  </div>
                </div>

                <div className={`balance-row${lowBalance ? " low" : ""}`}>
                  <div className="bl">
                    <Icon name="wallet" size={14} />
                    <span className="lbl">账户余额</span>
                    <span className="val">¥{ACCOUNT_BALANCE.toFixed(2)}</span>
                    <span className="arrow">→</span>
                    <span className="lbl">扣款后</span>
                    <span className="val after">¥{balanceAfter.toFixed(2)}</span>
                  </div>
                  {lowBalance ? (
                    <span className="pill pill-err"><span className="dot" />余额不足 · <a style={{ marginLeft: 4, textDecoration: "underline" }}>去充值</a></span>
                  ) : (
                    <span className="pill pill-ok"><span className="dot" />余额充足</span>
                  )}
                </div>

                <div className="section-sub">预估耗时 · 通知</div>
                <div className="eta-block">
                  <div className="eta-tile">
                    <div className="lbl">预估出片</div>
                    <div className="v">~ {etaMinutes}<small>分钟</small></div>
                    <div className="desc">// pipeline 5 阶段累计 · 不含人工审核</div>
                  </div>
                  <div className="eta-tile">
                    <div className="lbl">完成后通知</div>
                    <div
                      className={`check-row${notifyEmail ? " on" : ""}`}
                      style={{ padding: "4px 0" }}
                      onClick={() => setNotifyEmail((v) => !v)}
                    >
                      <span className="check-box" />
                      <span className="lab">邮件 <span className="mono">{USER_EMAIL}</span></span>
                    </div>
                    <div
                      className={`check-row${notifyWeChat ? " on" : ""}`}
                      style={{ padding: "4px 0" }}
                      onClick={() => setNotifyWeChat((v) => !v)}
                    >
                      <span className="check-box" />
                      <span className="lab">微信 <span className="mono">未绑定 · 去绑定</span></span>
                    </div>
                  </div>
                </div>

                <div className={`tos-row${agreed ? " on" : ""}`} onClick={() => setAgreed((v) => !v)}>
                  <span className="check-box" />
                  <span className="lab">
                    我已阅读并同意 <a>《按量计费协议》</a> 与 <a>《商品素材使用授权》</a>
                  </span>
                </div>
              </section>
            )}

            {/* ── Wiz foot ──────────────────────── */}
            <div className="wiz-foot">
              <button className="btn btn-ghost" onClick={goPrev} disabled={step === 1}>
                ← 上一步
              </button>
              <div className="hstack" style={{ gap: 12 }}>
                {step < 4 ? (
                  <>
                    <span className="muted-2" style={{ fontSize: 12.5, fontFamily: "var(--mono)", letterSpacing: ".02em" }}>
                      // 下一步：{stepConfig[step].label}
                    </span>
                    <button
                      className="btn btn-primary btn-lg"
                      disabled={
                        (step === 1 && !canPass1) ||
                        (step === 2 && !canPass2) ||
                        (step === 3 && !canPass3)
                      }
                      onClick={goNext}
                    >
                      下一步 →
                    </button>
                  </>
                ) : (
                  <>
                    <span className="muted-2" style={{ fontSize: 12.5, fontFamily: "var(--mono)", letterSpacing: ".02em" }}>
                      // 扣款 ¥{cost.total.toFixed(2)} · 进入 pipeline
                    </span>
                    <button className="btn btn-primary btn-lg" disabled={!canFinish} onClick={startGenerate}>
                      开始生成 →
                    </button>
                  </>
                )}
              </div>
            </div>
          </div>

          {/* ── Live preview panel ─────────────────── */}
          <aside className="wiz-preview">
            <div className="pv-h">
              <span>实时预估</span>
              <span className="live-dot">LIVE</span>
            </div>

            <div className="pv-title">
              {projectName || (product ? `${product.name} · 待命名` : "未命名项目")}
            </div>

            <div className="pv-metrics">
              <div className="pv-metric">
                <div className="l">镜头</div>
                <div className="v">{shots}<small>镜</small></div>
              </div>
              <div className="pv-metric accent">
                <div className="l">预估完播</div>
                <div className="v">{completion}<small>%</small></div>
              </div>
              <div className="pv-metric">
                <div className="l">预估转化</div>
                <div className="v">{conversion}<small>%</small></div>
              </div>
              <div className="pv-metric">
                <div className="l">预估成本</div>
                <div className="v">¥{cost.total.toFixed(2)}</div>
              </div>
            </div>

            {product ? (
              <>
                <div className="pv-section">
                  <div className="lbl">// 商品</div>
                  <ul className="pv-list">
                    <li>{product.name}</li>
                    <li>{product.cat} · ¥{product.price}</li>
                  </ul>
                </div>
                <div className="pv-section">
                  <div className="lbl">// 人设 · 风格</div>
                  <ul className="pv-list">
                    <li>{personaObj.name} · {personaObj.sub}</li>
                    <li>{styleObj.name} · {durationObj.tag}</li>
                  </ul>
                </div>
                <div className="pv-section">
                  <div className="lbl">// 脚本走向</div>
                  <div className="pv-flow">
                    {styleObj.flow.map((n, i) => (
                      <span key={i} style={{ display: "inline-flex", alignItems: "center" }}>
                        <span className="node">{n}</span>
                        {i < styleObj.flow.length - 1 && <span className="arrow">→</span>}
                      </span>
                    ))}
                  </div>
                </div>
                <div className="pv-section">
                  <div className="lbl">// 突出卖点</div>
                  <ul className="pv-list">
                    {Object.entries(points).filter(([, v]) => v).map(([k]) => <li key={k}>{k}</li>)}
                    {Object.values(points).every((v) => !v) && (
                      <li style={{ color: "var(--ink-3)" }}>未选 · 由 LLM 自动权衡</li>
                    )}
                  </ul>
                </div>
              </>
            ) : (
              <div className="pv-section">
                <div className="lbl">// 待选择</div>
                <ul className="pv-list" style={{ opacity: 0.6 }}>
                  <li style={{ color: "var(--ink-3)" }}>先选一个商品</li>
                  <li style={{ color: "var(--ink-3)" }}>预估指标会自动填充</li>
                </ul>
              </div>
            )}

            <div className="pv-foot">
              <span>Step {step} / 4 · Restraint</span>
              <strong>
                {step < 4 ? "进行中" : canFinish ? "就绪" : (lowBalance ? "余额不足" : "待确认")}
              </strong>
            </div>
          </aside>
        </div>
      </section>
    </>
  );
}

/* ============================================================
   Sub-components
   ============================================================ */

function ProductPickCard({ p, selected, onSelect }: { p: Product; selected: boolean; onSelect: () => void }) {
  return (
    <div className={`product-pick${selected ? " selected" : ""}`} onClick={onSelect}>
      <div className="placeholder thumb"><span className="ph-frame">9:16</span></div>
      <div className="body">
        <div className="name">{p.name}</div>
        <div className="meta">
          {p.cat} · <b>¥{p.price}</b> · {p.imgs} 张图
        </div>
        <div className="tags">
          {p.tags.map((t) => <span key={t} className="tag-s">{t}</span>)}
        </div>
      </div>
    </div>
  );
}

function CollapsedStep({ title, onEdit, body }: { title: string; onEdit: () => void; body: React.ReactNode }) {
  return (
    <section className="card collapsed-step">
      <div className="hstack">
        <h3>{title}</h3>
        <span className="spacer" />
        <button className="btn btn-ghost btn-sm" onClick={onEdit}>修改</button>
      </div>
      <div style={{ marginTop: 10 }}>{body}</div>
    </section>
  );
}

function ProductSummary({ p }: { p: Product }) {
  return (
    <div className="hstack" style={{ gap: 12, alignItems: "flex-start" }}>
      <div className="placeholder" style={{ width: 44, height: 56, flexShrink: 0 }}>
        <span className="ph-frame">9:16</span>
      </div>
      <div style={{ minWidth: 0 }}>
        <div style={{ fontWeight: 600, fontSize: 13.5 }}>{p.name}</div>
        <div className="muted-2 mono" style={{ fontSize: 11.5, marginTop: 3, letterSpacing: ".02em" }}>
          {p.cat} · ¥{p.price} · {p.imgs} 张图 · {p.points.length} 个卖点
        </div>
      </div>
    </div>
  );
}

function SourceSummary({ source, themeText, manualScript }: { source: { id: SourceId; name: string }; themeText: string; manualScript: string }) {
  return (
    <div className="hstack" style={{ gap: 8, flexWrap: "wrap" }}>
      <span className="pill pill-info"><span className="dot" />{source.name}</span>
      {source.id === "theme" && themeText && (
        <>
          <span className="muted">主题：</span>
          <span style={{ fontSize: 13 }}>{themeText}</span>
        </>
      )}
      {source.id === "manual" && (
        <>
          <span className="muted">脚本：</span>
          <span style={{ fontSize: 13 }}>{manualScript.length} 字</span>
        </>
      )}
      {source.id === "ai" && (
        <span className="muted-2 mono" style={{ fontSize: 11.5, letterSpacing: ".02em" }}>
          // 走向由 Step 3 决定
        </span>
      )}
    </div>
  );
}
