'use client';

import { useRef, useState } from 'react';
import type { AssetPack, AssetTemplate, GenImage, GenSession, PackKind, ToyAsset } from '@/lib/types';
import { PACK_LABELS, PACK_ORDER, PACK_TEMPLATES, TEXT_TEMPLATES, VIDEO_TEMPLATES } from '@/lib/templates';
import { HoverImagePreview } from './HoverImagePreview';

const PACK_DESCRIPTIONS: Record<PackKind, string> = {
  patent: '六面视图 · 45° 立体图 · 局部放大',
  accessories: '配件六视图 · 连接结构 · 尺寸 · 组合图',
  production: '尺寸 · 材料 · 颜色 · 拆件 · 包装',
  marketing: '白底商品图 · 场景图 · 细节图 · 社媒图',
};

const PACK_ACCENT: Record<PackKind, { bar: string; chip: string; icon: string }> = {
  patent: { bar: 'from-[#e6f578] to-[#d6b36a]', chip: 'bg-[#e6f578]/15 text-[#e6f578] border-[#e6f578]/30', icon: 'P' },
  accessories: { bar: 'from-[#8cb478] to-[#547a43]', chip: 'bg-[#8cb478]/15 text-[#cfe7a7] border-[#8cb478]/30', icon: 'A' },
  production: { bar: 'from-[#d6b36a] to-[#b6813c]', chip: 'bg-[#d6b36a]/15 text-[#f2d38c] border-[#d6b36a]/30', icon: 'F' },
  marketing: { bar: 'from-[#b6df72] to-[#4f9a56]', chip: 'bg-[#b6df72]/15 text-[#dff5a8] border-[#b6df72]/30', icon: 'M' },
};

const ASPECT_PX: Record<AssetTemplate['aspectRatio'], string> = {
  '1:1': '1024×1024',
  '3:4': '1024×1365',
  '4:5': '1024×1280',
  '9:16': '1080×1920',
  '16:9': '1920×1080',
  'long': '1024×3200',
};

function aspectCss(aspectRatio: AssetTemplate['aspectRatio'] | string | undefined): string {
  if (!aspectRatio) return '1 / 1';
  if (aspectRatio === 'long') return '1 / 3';
  return aspectRatio.replace(':', ' / ');
}

/* ── Asset Row ────────────────────────────────── */
function AssetRow({ template, asset, accent, onRegenerate }: {
  template: AssetTemplate;
  asset: ToyAsset | undefined;
  accent: typeof PACK_ACCENT[PackKind];
  onRegenerate?: (assetId: string, userRefinement?: string) => Promise<void>;
}) {
  const [showPrompt, setShowPrompt] = useState(false);
  const [showRedo, setShowRedo] = useState(false);
  const [refinement, setRefinement] = useState('');
  const [regenerating, setRegenerating] = useState(false);
  const ready = !!asset;
  async function handleRedo() {
    if (!asset || !onRegenerate || regenerating) return;
    const ok = window.confirm('重新生成这 1 张会再次调用图片模型并产生费用。确认继续？');
    if (!ok) return;
    setRegenerating(true);
    try {
      await onRegenerate(asset.id, refinement);
      setShowRedo(false);
      setRefinement('');
    } finally {
      setRegenerating(false);
    }
  }
  return (
    <div className="grid grid-cols-[76px_1fr_auto] gap-3 p-3 rounded-[8px] bg-white/[0.025] ring-1 ring-white/[0.05] hover:ring-white/[0.1] transition-all">
      {/* thumbnail */}
      <div className="group/thumb relative w-[72px] h-[72px] rounded-[8px] overflow-visible bg-white/[0.04] ring-1 ring-white/[0.07] flex items-center justify-center">
        {ready ? (
          <HoverImagePreview
            src={asset!.url}
            alt={template.title}
            aspectRatio={asset!.aspectRatio}
            imageClassName="max-w-full max-h-full object-contain rounded-lg bg-white"
          />
        ) : (
          <div className="flex flex-col items-center gap-1 rounded-lg bg-black/25 px-2 py-1" style={{ aspectRatio: aspectCss(template.aspectRatio) }}>
            <span className="text-[9px] text-white/30 font-mono">{template.aspectRatio}</span>
          </div>
        )}
        {template.required && !ready && (
          <span className="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-[#e6f578] ring-2 ring-[#e6f578]/30" />
        )}
      </div>

      {/* info */}
      <div className="min-w-0 space-y-1">
        <div className="flex items-center gap-1.5 flex-wrap">
          <span className="text-[13px] font-medium text-white leading-snug">{template.title}</span>
          {template.required && !ready && (
            <span className="text-[9px] text-[#e6f578]/80 uppercase tracking-widest">必备</span>
          )}
          {ready && <span className="chip chip-live text-[10px] py-0">✓</span>}
        </div>
        <p className="text-[11px] text-white/45 leading-relaxed line-clamp-1">{template.description}</p>
        <button
          onClick={() => setShowPrompt(s => !s)}
          className="text-[10px] text-white/30 hover:text-[#e6f578] transition-colors flex items-center gap-1"
        >
          <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
            <path d={showPrompt ? 'M6 9l6 6 6-6' : 'M9 6l6 6-6 6'} strokeLinecap="round" />
          </svg>
          Prompt
        </button>
        {showPrompt && (
          <pre className="p-2 text-[10px] text-white/60 bg-black/40 rounded-lg ring-1 ring-white/[0.07] font-mono whitespace-pre-wrap break-all max-h-28 overflow-y-auto">
            {asset?.prompt ?? template.promptTemplate}
          </pre>
        )}
        {ready && asset.anchorImageUrl && (
          <div className="text-[10px] text-white/35 font-mono truncate">
            anchor: {asset.anchorAssetId ?? asset.anchorImageUrl}
          </div>
        )}
        {ready && onRegenerate && (
          <div className="mt-2 pt-2 border-t border-amber-400/10 flex items-center justify-between gap-3">
            <span className="text-[10px] text-white/35">重新生成</span>
            <button
              onClick={() => setShowRedo(value => !value)}
              className="text-[10px] text-white/55 hover:text-white rounded-lg px-2 py-1 ring-1 ring-white/[0.08] bg-white/[0.04]"
            >
              {showRedo ? '收起' : '调整后生成'}
            </button>
          </div>
        )}
        {showRedo && ready && (
          <div className="flex items-center gap-2 pt-1">
            <input
              value={refinement}
              onChange={event => setRefinement(event.target.value)}
              placeholder="重做要求"
              className="min-w-0 flex-1 rounded-lg bg-black/30 ring-1 ring-white/[0.08] px-2 py-1 text-[11px] text-white/80 outline-none focus:ring-[#e6f578]/40"
            />
            <button
              onClick={handleRedo}
              disabled={regenerating}
              className="rounded-lg bg-white/[0.12] hover:bg-white/[0.18] text-white text-[10px] px-2 py-1 disabled:opacity-40"
            >
              {regenerating ? '...' : '确认生成'}
            </button>
          </div>
        )}
      </div>

      {/* meta */}
      <div className="flex flex-col items-end justify-between text-right shrink-0">
        <span className="text-[10px] font-mono text-white/40">{ASPECT_PX[template.aspectRatio]}</span>
        <div className="flex flex-col items-end gap-1">
          <span className={`text-[10px] ${ready ? 'text-[#dff5a8]' : 'text-white/25'}`}>
            {ready ? `L${asset!.derivationLevel}` : '待生成'}
          </span>
        </div>
      </div>
    </div>
  );
}

/* ── Pack Section (collapsible) ───────────────── */
function PackSection({ kind, session, primaryImage, pack, isLoading, onGenerate, onRegenerateAsset }: {
  kind: PackKind;
  session: GenSession;
  primaryImage: GenImage;
  pack: AssetPack | undefined;
  isLoading: boolean;
  onGenerate: () => void;
  onRegenerateAsset: (assetId: string, userRefinement?: string) => Promise<void>;
}) {
  const [open, setOpen] = useState(false);
  const accent = PACK_ACCENT[kind];
  const templates = PACK_TEMPLATES[kind];
  const generatedCount = pack?.assets.length ?? 0;
  const total = templates.length;
  const progressPct = Math.round((generatedCount / total) * 100);

  function handleGenerateClick() {
    if (pack) {
      const ok = window.confirm(`${PACK_LABELS[kind]} 已有 ${generatedCount} 张图，重新生成会再次调用图片模型并产生费用。确认继续？`);
      if (!ok) return;
    }
    onGenerate();
  }

  return (
    <section className="card" id={`pack-${kind}`}>
      {/* header — always visible */}
      <div className="p-4 flex items-center gap-3">
        <div className={`w-8 h-8 rounded-lg bg-gradient-to-br ${accent.bar} flex items-center justify-center text-white text-[11px] font-bold shrink-0`}>
          {accent.icon}
        </div>
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2 flex-wrap">
            <span className="text-sm font-semibold text-white">{PACK_LABELS[kind]}</span>
            <span className="text-[10px] text-white/35">{PACK_DESCRIPTIONS[kind]}</span>
          </div>
          {/* progress bar */}
          <div className="mt-2 flex items-center gap-2">
            <div className="flex-1 h-1 rounded-full bg-white/[0.06] overflow-hidden">
              <div
                className={`h-full bg-gradient-to-r ${accent.bar} transition-all`}
                style={{ width: `${progressPct}%` }}
              />
            </div>
            <span className="text-[10px] font-mono text-white/35 shrink-0">{generatedCount}/{total}</span>
          </div>
        </div>
        {/* actions */}
        <div className="flex items-center gap-2 shrink-0">
          <button
            onClick={handleGenerateClick}
            disabled={isLoading}
            className={`${pack ? 'btn btn-outline' : 'btn btn-primary'} text-xs px-3 py-1.5 disabled:opacity-40`}
          >
            {isLoading ? (
              <svg width="12" height="12" viewBox="0 0 24 24" className="animate-spin" fill="none" stroke="currentColor" strokeWidth="2.5">
                <path d="M12 2a10 10 0 0 1 10 10" strokeLinecap="round" />
              </svg>
            ) : pack ? '重新生成' : `生成 ${total} 张`}
          </button>
          <button
            onClick={() => setOpen(v => !v)}
            title={open ? '收起' : '展开'}
            className="w-7 h-7 rounded-lg bg-white/[0.04] hover:bg-white/[0.08] flex items-center justify-center text-white/50 transition-colors"
          >
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
              <path d={open ? 'M6 9l6 6 6-6' : 'M9 6l6 6-6 6'} strokeLinecap="round" />
            </svg>
          </button>
        </div>
      </div>

      {/* asset list — collapsible */}
      {open && (
        <div className="px-4 pb-4 space-y-2 border-t border-white/[0.05]">
          <div className="pt-3 space-y-2">
            {templates.map(template => {
              const asset = pack?.assets.find(a => a.templateId === template.id);
              return (
                <AssetRow key={template.id} template={template} asset={asset} accent={accent} onRegenerate={onRegenerateAsset} />
              );
            })}
          </div>
        </div>
      )}
    </section>
  );
}

/* ── Text Template Section (collapsible) ──────── */
function TextTemplateSection() {
  const [open, setOpen] = useState(false);
  const [showPromptId, setShowPromptId] = useState<string | null>(null);

  return (
    <section className="card overflow-hidden" id="pack-text">
      <div className="p-4 flex items-center gap-3">
        <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#8cb478] to-[#d6b36a] flex items-center justify-center text-white text-[11px] font-bold shrink-0">T</div>
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2">
            <span className="text-sm font-semibold text-white">设计说明文字</span>
            <span className="text-[10px] text-white/35">专利说明 · 工厂说明 · 宣发文案 · 视频脚本</span>
          </div>
          <div className="mt-2 flex items-center gap-2">
            <div className="flex-1 h-1 rounded-full bg-white/[0.06]" />
            <span className="text-[10px] font-mono text-white/35 shrink-0">0/{TEXT_TEMPLATES.length}</span>
          </div>
        </div>
        <div className="flex items-center gap-2 shrink-0">
          <span className="chip bg-[#8cb478]/15 text-[#cfe7a7] border-[#8cb478]/30 text-[10px]">GPT Text</span>
          <button
            onClick={() => setOpen(v => !v)}
            title={open ? '收起' : '展开'}
            className="w-7 h-7 rounded-lg bg-white/[0.04] hover:bg-white/[0.08] flex items-center justify-center text-white/50 transition-colors"
          >
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
              <path d={open ? 'M6 9l6 6 6-6' : 'M9 6l6 6-6 6'} strokeLinecap="round" />
            </svg>
          </button>
        </div>
      </div>

      {open && (
        <div className="px-4 pb-4 border-t border-white/[0.05] space-y-2 pt-3">
          {TEXT_TEMPLATES.map(template => {
            const isOpen = showPromptId === template.id;
            return (
              <div key={template.id} className="grid grid-cols-[72px_1fr_auto] gap-3 p-3 rounded-[8px] bg-white/[0.025] ring-1 ring-white/[0.05] hover:ring-white/[0.1] transition-all">
                <div className="aspect-square rounded-[8px] bg-gradient-to-br from-[#8cb478]/15 to-[#d6b36a]/15 ring-1 ring-[#8cb478]/20 flex flex-col items-center justify-center text-[#cfe7a7] text-[9px] font-mono gap-0.5">
                  <span>text</span>
                  <span className="text-[8px] text-[#cfe7a7]/60">{template.outputFormat}</span>
                </div>
                <div className="min-w-0 space-y-1">
                  <div className="flex items-center gap-1.5 flex-wrap">
                    <span className="text-[13px] font-medium text-white">{template.title}</span>
                    <span className={`chip text-[10px] py-0 ${template.kind === 'patent' ? 'bg-[#e6f578]/15 text-[#e6f578] border-[#e6f578]/30' : template.kind === 'production' ? 'bg-[#d6b36a]/15 text-[#f2d38c] border-[#d6b36a]/30' : template.kind === 'accessories' ? 'bg-[#8cb478]/15 text-[#cfe7a7] border-[#8cb478]/30' : template.kind === 'marketing' ? 'bg-[#b6df72]/15 text-[#dff5a8] border-[#b6df72]/30' : 'chip-neutral'}`}>
                      {template.kind}
                    </span>
                    {template.required && <span className="text-[9px] text-[#e6f578]/80 uppercase tracking-widest">必备</span>}
                  </div>
                  <p className="text-[11px] text-white/45 line-clamp-1">{template.description}</p>
                  <button
                    onClick={() => setShowPromptId(isOpen ? null : template.id)}
                    className="text-[10px] text-white/30 hover:text-[#cfe7a7] transition-colors flex items-center gap-1"
                  >
                    <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
                      <path d={isOpen ? 'M6 9l6 6 6-6' : 'M9 6l6 6-6 6'} strokeLinecap="round" />
                    </svg>
                    Prompt
                  </button>
                  {isOpen && (
                    <pre className="p-2 text-[10px] text-white/60 bg-black/40 rounded-lg ring-1 ring-white/[0.07] font-mono whitespace-pre-wrap break-all max-h-28 overflow-y-auto">
                      {template.promptTemplate}
                    </pre>
                  )}
                </div>
                <div className="flex flex-col items-end justify-between text-right shrink-0">
                  <span className="text-[9px] text-white/25 uppercase tracking-wider">{template.outputFormat}</span>
                  <span className="text-[10px] text-white/25">待生成</span>
                </div>
              </div>
            );
          })}
        </div>
      )}
    </section>
  );
}

/* ── Video Section (collapsible) ──────────────── */
function VideoSection({ videoLoading, primaryImage, onGenerateVideo }: {
  videoLoading: boolean;
  primaryImage: GenImage;
  onGenerateVideo: (image: GenImage, template: typeof VIDEO_TEMPLATES[number]) => void;
}) {
  const [open, setOpen] = useState(false);
  const [showPromptId, setShowPromptId] = useState<string | null>(null);

  return (
    <section className="card overflow-hidden" id="pack-video">
      <div className="p-4 flex items-center gap-3">
        <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#e6f578] to-[#8cb478] flex items-center justify-center text-white text-[11px] font-bold shrink-0">V</div>
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2">
            <span className="text-sm font-semibold text-white">Seedance 视频</span>
            <span className="text-[10px] text-white/35">异步任务 · 宣发 / 展示短片</span>
          </div>
          <div className="mt-2 flex items-center gap-2">
            <div className="flex-1 h-1 rounded-full bg-white/[0.06]" />
            <span className="text-[10px] font-mono text-white/35 shrink-0">{VIDEO_TEMPLATES.length} 个模板</span>
          </div>
        </div>
        <div className="flex items-center gap-2 shrink-0">
          <span className="chip chip-violet text-[10px]">Seedance</span>
          <button
            onClick={() => setOpen(v => !v)}
            title={open ? '收起' : '展开'}
            className="w-7 h-7 rounded-lg bg-white/[0.04] hover:bg-white/[0.08] flex items-center justify-center text-white/50 transition-colors"
          >
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
              <path d={open ? 'M6 9l6 6 6-6' : 'M9 6l6 6-6 6'} strokeLinecap="round" />
            </svg>
          </button>
        </div>
      </div>

      {open && (
        <div className="px-4 pb-4 border-t border-white/[0.05] space-y-2 pt-3">
          {VIDEO_TEMPLATES.map(template => {
            const isOpen = showPromptId === template.id;
            return (
              <div key={template.id} className="grid grid-cols-[72px_1fr_auto] gap-3 p-3 rounded-[8px] bg-white/[0.025] ring-1 ring-white/[0.05] hover:ring-white/[0.1] transition-all">
                <div className="aspect-square rounded-[8px] bg-gradient-to-br from-[#e6f578]/15 to-[#8cb478]/15 ring-1 ring-[#e6f578]/20 flex flex-col items-center justify-center text-[#e6f578] text-[9px] font-mono gap-1">
                  <span>▶</span>
                  <span className="text-[8px]">{template.duration}s</span>
                </div>
                <div className="min-w-0 space-y-1">
                  <div className="flex items-center gap-1.5 flex-wrap">
                    <span className="text-[13px] font-medium text-white">{template.title}</span>
                    <span className="chip chip-neutral text-[10px] py-0">{template.ratio}</span>
                  </div>
                  <p className="text-[11px] text-white/45 line-clamp-1">{template.description}</p>
                  <button
                    onClick={() => setShowPromptId(isOpen ? null : template.id)}
                    className="text-[10px] text-white/30 hover:text-[#e6f578] transition-colors flex items-center gap-1"
                  >
                    <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
                      <path d={isOpen ? 'M6 9l6 6 6-6' : 'M9 6l6 6-6 6'} strokeLinecap="round" />
                    </svg>
                    Prompt
                  </button>
                  {isOpen && (
                    <pre className="p-2 text-[10px] text-white/60 bg-black/40 rounded-lg ring-1 ring-white/[0.07] font-mono whitespace-pre-wrap break-all max-h-28 overflow-y-auto">
                      {template.promptTemplate}
                    </pre>
                  )}
                </div>
                <button
                  onClick={() => onGenerateVideo(primaryImage, template)}
                  disabled={videoLoading}
                  className="self-center btn btn-primary text-[11px] px-3 py-1.5 disabled:opacity-40"
                >
                  {videoLoading ? '...' : '提交'}
                </button>
              </div>
            );
          })}
        </div>
      )}
    </section>
  );
}

/* ── Section Nav ──────────────────────────────── */
const NAV_ITEMS = [
  { id: 'pack-patent', label: '专利', accent: 'from-[#e6f578] to-[#d6b36a]' },
  { id: 'pack-accessories', label: '配件', accent: 'from-[#8cb478] to-[#547a43]' },
  { id: 'pack-production', label: '生产', accent: 'from-[#d6b36a] to-[#b6813c]' },
  { id: 'pack-marketing', label: '宣发', accent: 'from-[#b6df72] to-[#4f9a56]' },
  { id: 'pack-text', label: '文字', accent: 'from-[#8cb478] to-[#d6b36a]' },
  { id: 'pack-video', label: '视频', accent: 'from-[#e6f578] to-[#8cb478]' },
];

function SectionNav({ active, onChange }: { active: string; onChange: (id: string) => void }) {
  return (
    <div className="flex items-center gap-1 overflow-x-auto scrollbar-none">
      {NAV_ITEMS.map(item => (
        <button
          key={item.id}
          onClick={() => {
            onChange(item.id);
            document.getElementById(item.id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
          }}
          className={`shrink-0 px-3 py-1.5 rounded-lg text-xs font-medium transition-all ${
            active === item.id
              ? `text-[#081006] bg-gradient-to-r ${item.accent} shadow-sm`
              : 'text-white/45 hover:text-white/70 hover:bg-[#e6f578]/[0.06]'
          }`}
        >
          {item.label}
        </button>
      ))}
    </div>
  );
}

/* ── Main Export ──────────────────────────────── */
const totalImageSlots = PACK_ORDER.reduce((s, k) => s + PACK_TEMPLATES[k].length, 0);

export default function PackPanel({
  session,
  loadingKind,
  allLoading,
  characterLoading,
  videoLoading,
  onGenerate,
  onGenerateAll,
  onLockCharacter,
  onRegenerateAsset,
  onGenerateVideo,
}: {
  session: GenSession;
  loadingKind: PackKind | null;
  allLoading: boolean;
  characterLoading: boolean;
  videoLoading: boolean;
  onGenerate: (image: GenImage, kind: PackKind) => void;
  onGenerateAll: (image: GenImage) => void;
  onLockCharacter: (image: GenImage) => void;
  onRegenerateAsset: (assetId: string, userRefinement?: string) => Promise<void>;
  onGenerateVideo: (image: GenImage, template: typeof VIDEO_TEMPLATES[number]) => void;
}) {
  const [activeNav, setActiveNav] = useState('pack-patent');
  const selectedImages = session.images.filter(image => image.status === 'selected');
  const primaryImage = selectedImages[0] ?? null;
  const packs = session.packs ?? [];

  if (!primaryImage) {
    return (
      <section className="card p-6">
        <div className="flex items-start gap-4">
          <div className="w-8 h-8 rounded-[8px] bg-white/[0.05] border border-white/[0.08] flex items-center justify-center text-white/30 text-sm">⌗</div>
          <div>
            <span className="section-eyebrow">Step · 03 · Assets</span>
            <h2 className="mt-1.5 text-sm font-semibold text-white">选中主方案后展开资产清单</h2>
            <p className="mt-1 text-[11px] text-white/40">
              在上方九宫格选中一个方案，这里会展开 {totalImageSlots} 个图片位 + {TEXT_TEMPLATES.length} 个文案位 + {VIDEO_TEMPLATES.length} 个视频任务。
            </p>
          </div>
        </div>
      </section>
    );
  }

  const generatedTotal = packs.reduce((s, p) => s + p.assets.length, 0);

  return (
    <div className="space-y-4">
      {/* Step 03 header card */}
      <section className="card p-5 space-y-4">
        <div className="flex items-start justify-between gap-4">
          <div className="flex-1 min-w-0">
            <span className="section-eyebrow">Step · 03 · Lock & Generate</span>
            <h2 className="mt-1.5 text-base font-semibold text-white">角色锁定 & 资产清单</h2>
            <p className="mt-0.5 text-[11px] text-white/40 max-w-[440px]">
              锁定角色设定后，下方四类图片包 + 文案包 + 视频任务已固化 Prompt，随时生成。
            </p>
          </div>
          {/* primary image + stats */}
          <div className="flex items-center gap-4 shrink-0">
            <div className="text-right text-[11px] text-white/40 space-y-0.5">
              <div className="font-mono text-white/70">{generatedTotal} <span className="text-white/30">/ {totalImageSlots} 张</span></div>
              <div className="text-[10px]">图片位</div>
            </div>
            <div className="relative w-16 h-16 rounded-[8px] overflow-hidden ring-1 ring-white/15 shadow-glow-violet shrink-0">
              <img src={primaryImage.url} alt="selected" className="w-full h-full object-contain bg-white" />
              <div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" />
              <div className="absolute bottom-1 inset-x-0 text-center text-[8px] font-semibold text-white/80 uppercase tracking-wider">主方案</div>
            </div>
          </div>
        </div>

        {/* action buttons */}
        <div className="flex flex-wrap items-center gap-2">
          <button
            onClick={() => onLockCharacter(primaryImage)}
            disabled={characterLoading || !!loadingKind || allLoading}
            className="btn btn-glass text-xs disabled:opacity-40"
          >
            {characterLoading ? (
              <svg width="12" height="12" viewBox="0 0 24 24" className="animate-spin" fill="none" stroke="currentColor" strokeWidth="2.5">
                <path d="M12 2a10 10 0 0 1 10 10" strokeLinecap="round" />
              </svg>
            ) : (
              <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
                <rect x="5" y="11" width="14" height="10" rx="2" />
                <path d="M8 11V7a4 4 0 0 1 8 0v4" strokeLinecap="round" />
              </svg>
            )}
            {characterLoading ? '锁定中' : session.characterSpec ? '刷新角色设定' : '锁定角色设定'}
          </button>
          <button
            onClick={() => {
              const ok = window.confirm(`一键全包最多会生成 ${totalImageSlots} 张图片，费用会明显高于单张。确认启动？`);
              if (ok) onGenerateAll(primaryImage);
            }}
            disabled={allLoading || !!loadingKind || characterLoading}
            className="btn btn-primary text-xs disabled:opacity-40"
          >
            {allLoading ? (
              <>
                <svg width="12" height="12" viewBox="0 0 24 24" className="animate-spin" fill="none" stroke="currentColor" strokeWidth="2.5">
                  <path d="M12 2a10 10 0 0 1 10 10" strokeLinecap="round" />
                </svg>
                生成中
              </>
            ) : (
              `一键全包 · ${totalImageSlots} 张`
            )}
          </button>
        </div>

        {/* CharacterSpec */}
        {session.characterSpec && (
          <div className="card-2 p-3.5">
            <div className="flex items-center justify-between gap-3 mb-2.5">
              <div className="min-w-0">
                <div className="text-[13px] font-semibold text-white truncate">{session.characterSpec.name}</div>
                <div className="text-[11px] text-white/50 mt-0.5 line-clamp-1">{session.characterSpec.oneLiner}</div>
              </div>
              <span className="chip chip-violet text-[10px] shrink-0">CharacterSpec</span>
            </div>
            <div className="divider-line mb-2.5" />
            <div className="grid grid-cols-2 gap-x-4 gap-y-1 text-[11px]">
              {[
                ['形态', session.characterSpec.speciesShape],
                ['比例', session.characterSpec.bodyRatio],
                ['配色', session.characterSpec.colorPalette.join('、')],
                ['材料', session.characterSpec.materials.join('、')],
                ['L1', session.characterSpec.cleanReferenceImageUrl ? '白底锚图已生成' : '未生成'],
              ].map(([label, value]) => (
                <div key={label} className="flex gap-2 min-w-0">
                  <span className="text-white/35 w-10 shrink-0">{label}</span>
                  <span className="text-white/75 truncate">{value}</span>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* Section nav */}
        <SectionNav active={activeNav} onChange={setActiveNav} />
      </section>

      {/* Pack sections */}
      {PACK_ORDER.map(kind => {
        const pack = packs.find(p => p.kind === kind && p.sourceImageId === primaryImage.id);
        return (
          <PackSection
            key={kind}
            kind={kind}
            session={session}
            primaryImage={primaryImage}
            pack={pack}
            isLoading={loadingKind === kind}
            onGenerate={() => onGenerate(primaryImage, kind)}
            onRegenerateAsset={onRegenerateAsset}
          />
        );
      })}

      {/* Text + Video */}
      <TextTemplateSection />
      <VideoSection videoLoading={videoLoading} primaryImage={primaryImage} onGenerateVideo={onGenerateVideo} />
    </div>
  );
}
