'use client';

import { useCallback, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import PromptPanel from '@/components/PromptPanel';
import Sidebar from '@/components/Sidebar';
import PackPanel from '@/components/PackPanel';
import ProjectGalleryDrawer from '@/components/ProjectGalleryDrawer';
import { OasisCanvas } from '@/components/login/OasisCanvas';
import { PACK_LABELS, PACK_ORDER, PACK_TEMPLATES } from '@/lib/templates';
import type {
  GenImage,
  GenSession,
  GeneratePackResponse,
  GenerateResponse,
  GenerateTextResponse,
  LockCharacterResponse,
  PackKind,
  ProjectFromUploadResponse,
  UploadImageResponse,
  UploadedImageRole,
  VideoGenerationResponse,
} from '@/lib/types';
import type { VIDEO_TEMPLATES } from '@/lib/templates';

function modeLabel(mode?: GenSession['inputMode']) {
  if (mode === 'remix') return '二创项目';
  if (mode === 'replicate') return '复刻项目';
  if (mode === 'extend') return '补全项目';
  return '想法项目';
}

function projectTitle(session: GenSession) {
  return session.characterSpec?.name || session.prompt || '未命名项目';
}

function defaultPrimaryAspectRatio(session: GenSession) {
  return session.inputMode === 'replicate' || session.inputMode === 'extend' ? '2 / 3' : '1 / 1';
}

function imageSourcesForSession(session: GenSession) {
  const uploaded = session.uploadedImages?.map(image => ({
    url: image.url,
    label: image.role === 'subject' ? '主体图' : image.role === 'reference' ? '参考图' : image.accessoryName || image.role,
  })) ?? [];
  if (uploaded.length) return uploaded;
  return session.refImages.map((url, index) => ({ url, label: `参考 ${index + 1}` }));
}

function packSlotTotal() {
  return PACK_ORDER.reduce((sum, kind) => sum + PACK_TEMPLATES[kind].length, 0);
}

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

function ProjectStat({ label, value, tone }: { label: string; value: string | number; tone?: 'accent' | 'soft' }) {
  return (
    <div className={`project-stat ${tone === 'accent' ? 'project-stat--accent' : ''}`}>
      <div className="text-[10px] text-white/40">{label}</div>
      <div className="mt-1 text-[18px] font-semibold tracking-tight">{value}</div>
    </div>
  );
}

function ProjectSpecField({ label, value }: { label: string; value?: string | string[] }) {
  const text = Array.isArray(value) ? value.filter(Boolean).join('、') : value;
  if (!text) return null;

  return (
    <div className="project-spec-row">
      <span className="project-spec-label">{label}</span>
      <span className="project-spec-value">{text}</span>
    </div>
  );
}

function ReferenceStrip({ session }: { session: GenSession }) {
  const refs = imageSourcesForSession(session);
  if (!refs.length) return null;

  return (
    <div className="project-reference-strip">
      {refs.slice(0, 6).map((ref, index) => (
        <div key={`${ref.url}-${index}`} className="project-reference-tile">
          <img src={ref.url} alt={ref.label} className="h-full w-full object-contain" />
          <span>{ref.label}</span>
        </div>
      ))}
    </div>
  );
}

function packForKind(session: GenSession, kind: PackKind, sourceImageId?: string) {
  return session.packs?.find(pack => pack.kind === kind && (!sourceImageId || pack.sourceImageId === sourceImageId));
}

function isPackComplete(session: GenSession, kind: PackKind, sourceImageId?: string) {
  const pack = packForKind(session, kind, sourceImageId);
  return Boolean(pack && pack.assets.length >= PACK_TEMPLATES[kind].length);
}

function ProjectPackOverview({
  session,
  primaryImage,
  loadingKind,
  onGeneratePack,
  onOpenPack,
}: {
  session: GenSession;
  primaryImage: GenImage | null;
  loadingKind: PackKind | null;
  onGeneratePack: (image: GenImage, kind: PackKind) => void;
  onOpenPack: (kind: PackKind) => void;
}) {
  if (!primaryImage) return null;
  const sourceImage = primaryImage;

  return (
    <div className="project-spec-card mt-5">
      <div className="mb-3 flex items-center justify-between gap-3">
        <span className="text-[11px] font-semibold text-white/80">生产进度</span>
        <span className="text-[10px] text-white/36">串行生成</span>
      </div>
      <div className="space-y-2">
        {PACK_ORDER.map((kind, index) => {
          const pack = packForKind(session, kind, sourceImage.id);
          const total = PACK_TEMPLATES[kind].length;
          const count = pack?.assets.length ?? 0;
          const complete = count >= total;
          const previousKind = index > 0 ? PACK_ORDER[index - 1] : null;
          const locked = !session.characterSpec || Boolean(previousKind && !isPackComplete(session, previousKind, sourceImage.id));
          const running = loadingKind === kind;
          const progress = Math.round((count / total) * 100);

          function handleGenerate() {
            if (locked || running || pack) return;
            onGeneratePack(sourceImage, kind);
          }

          return (
            <div key={kind} className={`project-pack-row ${locked ? 'project-pack-row--locked' : ''}`}>
              <div className="project-pack-index">{index + 1}</div>
              <div className="min-w-0 flex-1">
                <div className="flex min-w-0 items-center gap-2">
                  <span className="shrink-0 text-[12px] font-semibold text-white">{PACK_LABELS[kind]}</span>
                  <span className="truncate text-[10px] text-white/36">{PACK_BRIEF_DESCRIPTIONS[kind]}</span>
                </div>
                <div className="mt-2 flex items-center gap-2">
                  <div className="h-1 flex-1 overflow-hidden rounded-full bg-white/[0.06]">
                    <div className="h-full rounded-full bg-gradient-to-r from-[#e6f578] to-[#d6b36a]" style={{ width: `${progress}%` }} />
                  </div>
                  <span className="shrink-0 font-mono text-[10px] text-white/36">{count}/{total}</span>
                </div>
              </div>
              <div className="flex shrink-0 items-center gap-1.5">
                <span className={`project-pack-status ${complete ? 'project-pack-status--done' : locked ? 'project-pack-status--locked' : 'project-pack-status--ready'}`}>
                  {complete ? '完成' : locked ? '锁定' : '可生成'}
                </span>
                {!pack && (
                  <button
                    onClick={handleGenerate}
                    disabled={running || locked}
                    className="project-pack-action"
                    title={locked ? '先完成前置步骤' : '生成该包'}
                  >
                    {running ? '...' : '生成'}
                  </button>
                )}
                <button
                  type="button"
                  className="project-pack-jump"
                  title="查看明细"
                  onClick={() => onOpenPack(kind)}
                >
                  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
                    <path d="M9 6l6 6-6 6" strokeLinecap="round" />
                  </svg>
                </button>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function ProjectBrief({
  session,
  loadingKind,
  characterLoading,
  onGeneratePack,
  onLockCharacter,
  onOpenPack,
}: {
  session: GenSession;
  loadingKind: PackKind | null;
  characterLoading: boolean;
  onGeneratePack: (image: GenImage, kind: PackKind) => void;
  onLockCharacter: (image: GenImage) => void;
  onOpenPack: (kind: PackKind) => void;
}) {
  const selectedImages = session.images.filter(image => image.status === 'selected');
  const primaryImage = selectedImages[0] ?? session.images[0] ?? null;
  const generatedAssets = (session.packs ?? []).reduce((sum, pack) => sum + pack.assets.length, 0);
  const totalSlots = packSlotTotal();
  const [previewOpen, setPreviewOpen] = useState(false);
  const [primaryAspectRatio, setPrimaryAspectRatio] = useState(defaultPrimaryAspectRatio(session));

  useEffect(() => {
    setPrimaryAspectRatio(defaultPrimaryAspectRatio(session));
  }, [primaryImage?.url, session.inputMode]);

  return (
    <section className="project-brief-panel">
      <div className="flex items-start justify-between gap-4">
        <div className="min-w-0">
          <span className="section-eyebrow">Project Core</span>
          <h2 className="mt-2 text-[20px] font-semibold leading-tight text-white">{projectTitle(session)}</h2>
          <p className="mt-2 line-clamp-3 text-[12px] leading-relaxed text-white/50">
            {session.characterSpec?.oneLiner || session.prompt || '当前项目暂无描述'}
          </p>
        </div>
        <span className="shrink-0 whitespace-nowrap rounded-full bg-[#e6f578]/15 px-3 py-1 text-[10px] font-semibold text-[#e6f578] ring-1 ring-[#e6f578]/25">
          {modeLabel(session.inputMode)}
        </span>
      </div>

      {primaryImage && (
        <div
          className="project-primary-card mt-5"
          onMouseEnter={() => setPreviewOpen(true)}
          onMouseLeave={() => setPreviewOpen(false)}
          onFocus={() => setPreviewOpen(true)}
          onBlur={() => setPreviewOpen(false)}
        >
          <div className="project-primary-preview" style={{ aspectRatio: primaryAspectRatio }}>
            <img
              src={primaryImage.url}
              alt="当前主方案"
              className="project-primary-image"
              onLoad={event => {
                const image = event.currentTarget;
                if (image.naturalWidth && image.naturalHeight) {
                  setPrimaryAspectRatio(`${image.naturalWidth} / ${image.naturalHeight}`);
                }
              }}
            />
          </div>
          <div className="project-primary-meta">
            <span className="text-[10px] font-semibold uppercase tracking-[0.16em] text-white/70">Primary</span>
            <span className="text-[10px] text-white/50">{primaryImage.status === 'selected' ? '已选中' : '待筛选'}</span>
          </div>
          {previewOpen && typeof document !== 'undefined' && createPortal(
            <div className="project-image-popover project-image-popover--open" aria-hidden="true">
              <img src={primaryImage.url} alt="" />
            </div>,
            document.body,
          )}
        </div>
      )}

      <div className="mt-5 grid grid-cols-3 gap-2">
        <ProjectStat label="图库" value={session.images.length} />
        <ProjectStat label="选中" value={selectedImages.length} tone="accent" />
        <ProjectStat label="产出" value={`${generatedAssets}/${totalSlots}`} />
      </div>

      <ProjectPackOverview
        session={session}
        primaryImage={primaryImage}
        loadingKind={loadingKind}
        onGeneratePack={onGeneratePack}
        onOpenPack={onOpenPack}
      />

      {session.characterSpec ? (
        <div className="project-spec-card mt-5">
          <div className="flex items-center justify-between gap-3">
            <span className="text-[11px] font-semibold text-white/80">角色设定</span>
            <div className="flex shrink-0 items-center gap-2">
              <span className="text-[10px] text-[#e6f578]/70">已锁定</span>
              <button
                type="button"
                onClick={() => primaryImage && onLockCharacter(primaryImage)}
                disabled={!primaryImage || characterLoading || !!loadingKind}
                className="project-spec-action"
              >
                {characterLoading ? '锁定中' : '刷新'}
              </button>
            </div>
          </div>
          <div className="mt-3 space-y-2 text-[11px]">
            <ProjectSpecField label="形态" value={session.characterSpec.speciesShape} />
            <ProjectSpecField label="比例" value={session.characterSpec.bodyRatio} />
            <ProjectSpecField label="五官" value={session.characterSpec.faceFeatures} />
            <ProjectSpecField label="配色" value={session.characterSpec.colorPalette} />
            <ProjectSpecField label="材料" value={session.characterSpec.materials} />
            <ProjectSpecField label="配件" value={session.characterSpec.accessories} />
            <ProjectSpecField label="识别" value={session.characterSpec.signatureElements} />
            <ProjectSpecField label="打样" value={session.characterSpec.manufacturingNotes} />
          </div>
        </div>
      ) : (
        <div className="project-spec-card mt-5">
          <div className="flex items-center justify-between gap-3">
            <div className="text-[11px] font-semibold text-white/80">角色设定</div>
            <button
              type="button"
              onClick={() => primaryImage && onLockCharacter(primaryImage)}
              disabled={!primaryImage || characterLoading || !!loadingKind}
              className="project-spec-action project-spec-action--primary"
            >
              {characterLoading ? '锁定中' : '锁定'}
            </button>
          </div>
          <p className="mt-2 text-[11px] leading-relaxed text-white/45">
            从右侧图库选中主方案后，在生产矩阵里锁定角色设定。
          </p>
        </div>
      )}
    </section>
  );
}

export default function Home() {
  const [sessions, setSessions] = useState<GenSession[]>([]);
  const [current, setCurrent] = useState<GenSession | null>(null);
  const [loading, setLoading] = useState(false);
  const [loadingKind, setLoadingKind] = useState<PackKind | null>(null);
  const [characterLoading, setCharacterLoading] = useState(false);
  const [textLoading, setTextLoading] = useState(false);
  const [videoLoading, setVideoLoading] = useState<string | null>(null);
  const [uploadLoading, setUploadLoading] = useState(false);
  const [provider, setProvider] = useState<string>('?');
  const [sidebarOpen, setSidebarOpen] = useState(true);
  const [activeAssetPanel, setActiveAssetPanel] = useState('pack-patent');

  const refreshSessions = useCallback(async () => {
    const r = await fetch('/api/sessions');
    const d = await r.json();
    setSessions(d.sessions);
    return d.sessions as GenSession[];
  }, []);

  useEffect(() => { refreshSessions(); }, [refreshSessions]);
  useEffect(() => { setActiveAssetPanel('pack-patent'); }, [current?.id]);

  async function handleGenerate(opts: { prompt: string; refImages: string[]; count: number; style?: string }) {
    setLoading(true);
    try {
      const r = await fetch('/api/generate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(opts),
      });
      if (!r.ok) {
        alert('生成失败：' + (await r.text()));
        return;
      }
      const d: GenerateResponse = await r.json();
      setProvider(d.provider);
      const all = await refreshSessions();
      const s = all.find(x => x.id === d.sessionId) ?? null;
      setCurrent(s);
    } finally {
      setLoading(false);
    }
  }

  async function uploadImage(file: File, role: UploadedImageRole) {
    const form = new FormData();
    form.set('image', file);
    form.set('role', role);
    const r = await fetch('/api/uploads', { method: 'POST', body: form });
    if (!r.ok) throw new Error(await r.text());
    const d: UploadImageResponse = await r.json();
    return d.uploadedImage;
  }

  async function handleUploadProject(opts: {
    mode: 'remix' | 'replicate';
    files: Array<{ file: File; role: 'reference' | 'subject' }>;
    prompt?: string;
    styleId?: string;
    count?: number;
  }) {
    if (uploadLoading) return;
    setUploadLoading(true);
    try {
      const uploadedImages = await Promise.all(opts.files.map(item => uploadImage(item.file, item.role)));
      const r = await fetch('/api/projects/from-upload', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          uploadedImages,
          mode: opts.mode,
          remixPrompt: opts.mode === 'remix' ? opts.prompt : undefined,
          userHint: opts.mode === 'replicate' ? opts.prompt : undefined,
          styleId: opts.styleId,
          count: opts.count,
        }),
      });
      if (!r.ok) {
        alert('上传项目创建失败：' + (await r.text()));
        return;
      }
      const d: ProjectFromUploadResponse = await r.json();
      setProvider(d.provider);
      const all = await refreshSessions();
      const s = all.find(x => x.id === d.sessionId) ?? null;
      setCurrent(s);
    } catch (error) {
      alert('上传失败：' + String(error));
    } finally {
      setUploadLoading(false);
    }
  }

  async function handleAction(imageId: string, action: 'select' | 'reject' | 'reset') {
    if (!current) return;
    const r = await fetch('/api/select', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ sessionId: current.id, imageId, action }),
    });
    if (!r.ok) return;
    const d: { image: GenImage } = await r.json();
    setCurrent(prev => prev ? {
      ...prev,
      images: prev.images.map(i => i.id === imageId ? d.image : i),
    } : prev);
    refreshSessions();
  }

  async function handleGeneratePack(image: GenImage, kind: PackKind) {
    if (!current || loadingKind) return;
    setLoadingKind(kind);
    try {
      const r = await fetch('/api/packs/generate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ sessionId: current.id, imageId: image.id, kind, background: true }),
      });
      if (!r.ok) {
        alert('素材包生成失败：' + (await r.text()));
        return;
      }
      const d: GeneratePackResponse = await r.json();
      setProvider(d.provider);
      const all = await refreshSessions();
      const updated = all.find(x => x.id === current.id) ?? null;
      setCurrent(updated);
      scheduleSessionRefresh(current.id);
    } finally {
      setLoadingKind(null);
    }
  }

  async function reloadCurrent(sessionId: string) {
    const all = await refreshSessions();
    const updated = all.find(x => x.id === sessionId) ?? null;
    setCurrent(updated);
    return updated;
  }

  function scheduleSessionRefresh(sessionId: string, remaining = 90) {
    if (remaining <= 0) return;
    window.setTimeout(async () => {
      const updated = await reloadCurrent(sessionId);
      const hasDraftPack = updated?.packs?.some(pack => pack.status === 'draft') ?? false;
      if (hasDraftPack || remaining > 84) scheduleSessionRefresh(sessionId, remaining - 1);
    }, 5000);
  }

  function scheduleVideoRefresh(sessionId: string, taskId: string, remaining = 30) {
    if (remaining <= 0) return;
    window.setTimeout(async () => {
      const r = await fetch(`/api/video/status/${encodeURIComponent(taskId)}?sessionId=${encodeURIComponent(sessionId)}`);
      if (r.ok) {
        const d: VideoGenerationResponse = await r.json();
        await reloadCurrent(sessionId);
        if ((d.status === 'submitted' || d.status === 'processing') && remaining > 1) {
          scheduleVideoRefresh(sessionId, taskId, remaining - 1);
        }
      }
    }, 15000);
  }

  async function handleLockCharacter(image: GenImage) {
    if (!current || characterLoading) return;
    setCharacterLoading(true);
    try {
      const r = await fetch('/api/character/lock', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ sessionId: current.id, imageId: image.id, force: true }),
      });
      if (!r.ok) {
        alert('角色锁定失败：' + (await r.text()));
        return;
      }
      const d: LockCharacterResponse = await r.json();
      setProvider(d.provider);
      await reloadCurrent(current.id);
    } finally {
      setCharacterLoading(false);
    }
  }

  function resolveVideoAnchor(image: GenImage) {
    const packs = current?.packs ?? [];
    const mktFront = packs.find(pack => pack.kind === 'marketing')?.assets.find(asset => asset.templateId === 'mkt_white_front');
    const patentFront = packs.find(pack => pack.kind === 'patent')?.assets.find(asset => asset.templateId === 'patent_front');
    const cleanReference = current?.characterSpec?.cleanReferenceImageUrl;
    if (mktFront) return { url: mktFront.url, label: '宣发白底图' };
    if (patentFront) return { url: patentFront.url, label: '专利主图' };
    if (cleanReference) return { url: cleanReference, label: 'L1 白底锚图' };
    return { url: image.url, label: '意向图' };
  }

  async function handleGenerateText() {
    if (!current || textLoading) return;
    setTextLoading(true);
    try {
      const r = await fetch('/api/text/generate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ sessionId: current.id }),
      });
      if (!r.ok) {
        alert('文字生成失败：' + (await r.text()));
        return;
      }
      const d: GenerateTextResponse = await r.json();
      setProvider(d.provider);
      await reloadCurrent(current.id);
    } finally {
      setTextLoading(false);
    }
  }

  async function handleGenerateVideo(image: GenImage, template: typeof VIDEO_TEMPLATES[number]) {
    if (!current || videoLoading) return;
    setVideoLoading(template.id);
    try {
      const character = current.characterSpec
        ? `${current.characterSpec.name}，${current.characterSpec.oneLiner}`
        : current.prompt;
      const prompt = template.promptTemplate.replace('{character}', character);
      const anchor = resolveVideoAnchor(image);
      const r = await fetch('/api/video/generate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          sessionId: current.id,
          templateId: template.id,
          templateTitle: template.title,
          prompt,
          imageUrl: anchor.url,
          duration: template.duration,
          ratio: template.ratio,
          generateAudio: true,
          watermark: false,
        }),
      });
      if (!r.ok) {
        alert('Seedance 视频提交失败：' + (await r.text()));
        return;
      }
      const d: VideoGenerationResponse = await r.json();
      await reloadCurrent(current.id);
      if (d.taskId) scheduleVideoRefresh(current.id, d.taskId);
    } finally {
      setVideoLoading(null);
    }
  }

  async function handleRefreshVideo(taskId: string) {
    if (!current || videoLoading) return;
    setVideoLoading(taskId);
    try {
      const r = await fetch(`/api/video/status/${encodeURIComponent(taskId)}?sessionId=${encodeURIComponent(current.id)}`);
      if (!r.ok) {
        alert('视频状态刷新失败：' + (await r.text()));
        return;
      }
      await reloadCurrent(current.id);
    } finally {
      setVideoLoading(null);
    }
  }

  async function handleLogout() {
    await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' });
    window.location.href = '/login';
  }

  return (
    <div className="app-oasis relative h-screen overflow-hidden text-white">
      <OasisCanvas />
      <div className="app-oasis-shade" />
      <div className="app-grass-floor" />
      <div className="project-shell relative z-10 flex h-screen text-white">
        <Sidebar
          open={sidebarOpen}
          onToggle={() => setSidebarOpen(v => !v)}
          sessions={sessions}
          currentId={current?.id ?? null}
          onPick={id => setCurrent(sessions.find(s => s.id === id) ?? null)}
          onNew={() => setCurrent(null)}
        />
        <main className="project-stage min-w-0 flex-1">
          <div className="project-stage-inner">
            <header className="project-topbar">
              <div className="min-w-0">
                <span className="section-eyebrow">AI Toy Patent</span>
                <h1 className="mt-1 text-[18px] font-semibold tracking-tight text-[#f8f7ef]">项目生产工作台</h1>
              </div>
              <div className="flex min-w-0 items-center gap-2">
                {current && (
                  <a
                    href={`/api/audit/${encodeURIComponent(current.id)}`}
                    target="_blank"
                    rel="noreferrer"
                    className="chip chip-neutral hover:border-[#e6f578]/40 hover:text-white transition-colors"
                  >
                    记录
                  </a>
                )}
                <button
                  onClick={handleLogout}
                  className="chip chip-neutral hover:border-[#e6f578]/40 hover:text-white transition-colors"
                >
                  退出
                </button>
                <span className={provider === 'gpt' ? 'chip chip-live' : provider === '?' ? 'chip chip-neutral' : 'chip chip-mock'}>
                  <span className={`h-1.5 w-1.5 rounded-full ${provider === 'gpt' ? 'bg-emerald-400' : provider === '?' ? 'bg-white/40' : 'bg-amber-400'}`} />
                  {provider === 'gpt' ? 'GPT · gpt-image-2' : provider === 'mock' ? 'Mock · 占位图' : provider === '?' ? '待连接' : provider}
                </span>
              </div>
            </header>

            {!current && (
              <section className="project-empty-canvas">
                <PromptPanel
                  session={null}
                  onGenerate={handleGenerate}
                  onUploadProject={handleUploadProject}
                  loading={loading}
                  uploadLoading={uploadLoading}
                />
              </section>
            )}

            {current && (
              <section className="project-board">
                <div className="project-board-head">
                  <div className="min-w-0">
                    <span className="section-eyebrow">Current Project</span>
                    <h2 className="mt-2 line-clamp-1 text-[24px] font-semibold leading-tight text-white">{projectTitle(current)}</h2>
                    <p className="mt-1 text-[11px] text-white/40">
                      {new Date(current.createdAt).toLocaleString('zh-CN')} · {current.id}
                    </p>
                  </div>
                  <ReferenceStrip session={current} />
                </div>

                <div className="project-board-grid">
                  <ProjectBrief
                    session={current}
                    loadingKind={loadingKind}
                    characterLoading={characterLoading}
                    onGeneratePack={handleGeneratePack}
                    onLockCharacter={handleLockCharacter}
                    onOpenPack={kind => setActiveAssetPanel(`pack-${kind}`)}
                  />
                  <section className="project-production-panel">
                    <PackPanel
                      session={current}
                      activeNav={activeAssetPanel}
                      onActiveNavChange={setActiveAssetPanel}
                      textLoading={textLoading}
                      videoLoading={videoLoading}
                      onGenerateText={handleGenerateText}
                      onGenerateVideo={handleGenerateVideo}
                      onRefreshVideo={handleRefreshVideo}
                    />
                  </section>
                  <ProjectGalleryDrawer
                    session={current}
                    activeNav={activeAssetPanel}
                    onAction={handleAction}
                  />
                </div>
              </section>
            )}
          </div>
        </main>
      </div>
    </div>
  );
}
