﻿import {
  AppstoreOutlined,
  CloudUploadOutlined,
  CloseOutlined,
  DeleteOutlined,
  FileImageOutlined,
  FolderOpenOutlined,
  FrownOutlined,
  GlobalOutlined,
  LoadingOutlined,
  MenuFoldOutlined,
  MenuUnfoldOutlined,
  QuestionCircleOutlined,
  ReloadOutlined,
  SettingOutlined,
  SkinOutlined,
  TableOutlined,
} from "@ant-design/icons";
import { useEffect, useMemo, useRef, useState, type CSSProperties, type ChangeEvent, type DragEvent, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, type ReactNode } from "react";
import { useTypewriter } from "../../hooks/useTypewriter";
import "../../styles/pages/ecommerce.css";
import "../../styles/pages/local-theme-parity.css";
import { ossAssets } from "../../data/ossAssets";
import { EcommerceProgressBar } from "./EcommerceProgressBar";
import ImageMentionMenu, { getImageMentionQuery, insertImageMentionValue, type MentionImageOption } from "./ImageMentionMenu";
import EcommerceVideoWorkspace from "./EcommerceVideoWorkspace";
import EcommerceVideoHistoryPanel from "./panels/EcommerceVideoHistoryPanel";
import EcommerceDetailPanel from "./panels/EcommerceDetailPanel";
import EcommerceSetPanel from "./panels/EcommerceSetPanel";
import EcommerceTryOnPanel from "./panels/EcommerceTryOnPanel";
import EcommerceClonePanel from "./panels/EcommerceClonePanel";
import aliexpressLogo from "../../assets/platform-logos/aliexpress.webp";
import amazonLogo from "../../assets/platform-logos/amazon.webp";
import douyinLogo from "../../assets/platform-logos/douyin.webp";
import ebayLogo from "../../assets/platform-logos/ebay.webp";
import instagramLogo from "../../assets/platform-logos/instagram.webp";
import jdLogo from "../../assets/platform-logos/jd.webp";
import lazadaLogo from "../../assets/platform-logos/lazada.webp";
import pinduoduoLogo from "../../assets/platform-logos/pinduoduo.webp";
import shopeeLogo from "../../assets/platform-logos/shopee.webp";
import taobaoLogo from "../../assets/platform-logos/taobao.webp";
import tiktokShopLogo from "../../assets/platform-logos/tiktok-shop.webp";

const smartCutoutColorPresets = [
  "#ffffff",
  "#111111",
  "#ff3131",
  "#ff7a1a",
  "#f7c600",
  "#29b34a",
  "#25a9e0",
  "#438df5",
  "#9029d9",
  "#8aa3ad",
  "#6b7b86",
  "#f46f7b",
  "#ff9451",
  "#f7d34f",
  "#55c66f",
  "#73c7f3",
  "#6dabf5",
  "#b45adb",
  "#bcc8ce",
  "#aeb7bd",
  "#ffbec4",
  "#ffd1ac",
  "#f8e69d",
  "#91de9e",
  "#b7e5fb",
  "#b9d9fb",
  "#d7abe8",
  "#dfe5e8",
  "#d7dde0",
  "#ffe2e4",
  "#ffe5d1",
  "#f8efcf",
  "#c9efcf",
  "#d8f0fb",
  "#d8eafa",
  "#ead2f1",
];

const smartCutoutSizeOptions = [
  { key: "original", label: "原尺寸", icon: "image", frameWidth: "min(520px, 78%)", frameAspect: "auto", imageMaxWidth: "78%", imageMaxHeight: "310px" },
  { key: "trim", label: "裁剪到边缘", icon: "crop", frameWidth: "min(420px, 70%)", frameAspect: "auto", imageMaxWidth: "92%", imageMaxHeight: "360px" },
  { key: "one-inch", label: "一寸头像", sizeLabel: "295*413", icon: "portrait", frameWidth: "min(290px, 50%)", frameAspect: "295 / 413", imageMaxWidth: "86%", imageMaxHeight: "86%", outputWidth: 295, outputHeight: 413 },
  { key: "two-inch", label: "二寸头像", sizeLabel: "413*579", icon: "portrait", frameWidth: "min(320px, 54%)", frameAspect: "413 / 579", imageMaxWidth: "86%", imageMaxHeight: "86%", outputWidth: 413, outputHeight: 579 },
  { key: "taobao-1-1", label: "淘宝1:1主图", sizeLabel: "800*800", icon: "shop", frameWidth: "min(430px, 72%)", frameAspect: "800 / 800", imageMaxWidth: "82%", imageMaxHeight: "82%", outputWidth: 800, outputHeight: 800 },
  { key: "taobao-3-4", label: "淘宝3:4主图", sizeLabel: "750*1000", icon: "shop", frameWidth: "min(330px, 56%)", frameAspect: "750 / 1000", imageMaxWidth: "82%", imageMaxHeight: "82%", outputWidth: 750, outputHeight: 1000 },
  { key: "pdd-main", label: "拼多多主图", sizeLabel: "800*800", icon: "pdd", frameWidth: "min(430px, 72%)", frameAspect: "800 / 800", imageMaxWidth: "82%", imageMaxHeight: "82%", outputWidth: 800, outputHeight: 800 },
  { key: "xiaohongshu-cover", label: "小红书封面", sizeLabel: "1242*1660", icon: "text", frameWidth: "min(330px, 56%)", frameAspect: "1242 / 1660", imageMaxWidth: "82%", imageMaxHeight: "82%", outputWidth: 1242, outputHeight: 1660 },
  { key: "ratio-1-1", label: "1:1", icon: "square", frameWidth: "min(430px, 72%)", frameAspect: "1 / 1", imageMaxWidth: "82%", imageMaxHeight: "82%" },
  { key: "ratio-3-2", label: "3:2", icon: "landscape", frameWidth: "min(520px, 78%)", frameAspect: "3 / 2", imageMaxWidth: "82%", imageMaxHeight: "82%" },
  { key: "ratio-2-3", label: "2:3", icon: "portrait-ratio", frameWidth: "min(330px, 56%)", frameAspect: "2 / 3", imageMaxWidth: "82%", imageMaxHeight: "82%" },
  { key: "ratio-4-3", label: "4:3", icon: "landscape", frameWidth: "min(520px, 78%)", frameAspect: "4 / 3", imageMaxWidth: "82%", imageMaxHeight: "82%" },
  { key: "ratio-3-4", label: "3:4", icon: "portrait-ratio", frameWidth: "min(330px, 56%)", frameAspect: "3 / 4", imageMaxWidth: "82%", imageMaxHeight: "82%" },
  { key: "ratio-16-9", label: "16:9", icon: "wide", frameWidth: "min(560px, 82%)", frameAspect: "16 / 9", imageMaxWidth: "82%", imageMaxHeight: "82%" },
  { key: "ratio-9-16", label: "9:16", icon: "phone", frameWidth: "min(260px, 46%)", frameAspect: "9 / 16", imageMaxWidth: "82%", imageMaxHeight: "82%" },
] as const;

type SmartCutoutSizeKey = (typeof smartCutoutSizeOptions)[number]["key"];
type SmartCutoutImageItem = { src: string; name: string; originalSrc?: string };

const clampNumber = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));

const normalizeHexColor = (value: string) => {
  const clean = value.trim().replace(/^#/, "");
  if (!/^[0-9a-fA-F]{6}$/.test(clean)) return null;
  return `#${clean.toLowerCase()}`;
};

const hexToRgb = (value: string) => {
  const normalized = normalizeHexColor(value);
  if (!normalized) return null;
  const numeric = Number.parseInt(normalized.slice(1), 16);
  return {
    r: (numeric >> 16) & 255,
    g: (numeric >> 8) & 255,
    b: numeric & 255,
  };
};

const rgbToHex = (r: number, g: number, b: number) =>
  `#${[r, g, b].map((item) => clampNumber(Math.round(item), 0, 255).toString(16).padStart(2, "0")).join("")}`;

const parseSmartCutoutAspect = (aspect: string) => {
  const match = aspect.match(/(\d+(?:\.\d+)?)\s*\/\s*(\d+(?:\.\d+)?)/);
  if (!match) return null;
  const width = Number(match[1]);
  const height = Number(match[2]);
  if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return null;
  return width / height;
};

const parseSmartCutoutPercent = (value: string, fallback: number) => {
  const numeric = Number(value.replace("%", ""));
  if (!Number.isFinite(numeric)) return fallback;
  return clampNumber(numeric / 100, 0.05, 1);
};

const hsvToRgb = (h: number, s: number, v: number) => {
  const hue = ((h % 360) + 360) % 360;
  const saturation = clampNumber(s, 0, 100) / 100;
  const value = clampNumber(v, 0, 100) / 100;
  const chroma = value * saturation;
  const x = chroma * (1 - Math.abs(((hue / 60) % 2) - 1));
  const match = value - chroma;
  const [red, green, blue] =
    hue < 60
      ? [chroma, x, 0]
      : hue < 120
        ? [x, chroma, 0]
        : hue < 180
          ? [0, chroma, x]
          : hue < 240
            ? [0, x, chroma]
            : hue < 300
              ? [x, 0, chroma]
              : [chroma, 0, x];
  return {
    r: (red + match) * 255,
    g: (green + match) * 255,
    b: (blue + match) * 255,
  };
};

const hexToHsv = (value: string) => {
  const rgb = hexToRgb(value) ?? { r: 255, g: 255, b: 255 };
  const red = rgb.r / 255;
  const green = rgb.g / 255;
  const blue = rgb.b / 255;
  const max = Math.max(red, green, blue);
  const min = Math.min(red, green, blue);
  const delta = max - min;
  const hue =
    delta === 0
      ? 0
      : max === red
        ? 60 * (((green - blue) / delta) % 6)
        : max === green
          ? 60 * ((blue - red) / delta + 2)
          : 60 * ((red - green) / delta + 4);
  return {
    h: Math.round((hue + 360) % 360),
    s: max === 0 ? 0 : Math.round((delta / max) * 100),
    v: Math.round(max * 100),
  };
};
import tmallLogo from "../../assets/platform-logos/tmall.webp";
import { aiGenerationClient } from "../../api/aiGenerationClient";
import { ServerRequestError } from "../../api/serverConnection";
import { waitForTask } from "../../api/taskSubscription";
import { toast } from "../../components/toast/toastStore";
import { useGenerationTasks } from "../../hooks/useGenerationTasks";
import { useAppStore } from "../../stores";
import {
  normalizeEcommerceImageMime,
  summarizeRejectedImages,
  validateEcommerceImageFiles,
} from "./ecommerceImageValidation";


interface ProductClonePageProps {
  [key: string]: unknown;
}

type ProductCloneStatus = "idle" | "ready" | "generating" | "done" | "failed";
type ProductSetOutputKey = "set" | "detail" | "model" | "video";
type CloneOutputKey = ProductSetOutputKey | "hot" | "video-outfit";
type CloneSetCountKey = "selling" | "white" | "scene";
type CloneModelPanelTab = "scene" | "model";
type CloneVideoQualityKey = "standard" | "high" | "ultra";
type ProductSetStatus = "idle" | "ready" | "generating" | "done" | "failed";
type ProductKitToolKey = "set" | "detail" | "wear" | "clone";
type ComposerMenuKey = "mode" | "platform" | "language" | "ratio" | "settings";
type CloneBasicSelectKey = "platform" | "market" | "language" | "ratio";
type CloneModelSelectKey = "gender" | "age" | "ethnicity" | "body";
type CloneReferenceMode = "upload" | "link";
type CloneReplicateLevelKey = "style" | "high";
type TryOnModelSource = "ai" | "library";
type TryOnStatus = "idle" | "modeling" | "ready" | "generating" | "done" | "failed";
type DetailStatus = "idle" | "ready" | "generating" | "done" | "failed";

interface CloneImageItem {
  id: string;
  src: string;
  name: string;
  file?: File;
  width?: number;
  height?: number;
  format?: string;
  mimeType?: string;
  ossKey?: string;
}

interface CloneResult {
  id: string;
  src: string;
  label: string;
  type?: "image" | "video";
}

interface CloneSavedSetting {
  id: string;
  name: string;
  savedAt: string;
  output: CloneOutputKey;
  platform: string;
  market: string;
  language: string;
  ratio: string;
  setCounts: Record<CloneSetCountKey, number>;
  detailModules: string[];
  modelPanelTab: CloneModelPanelTab;
  modelScenes: string[];
  modelCustomScene: string;
  modelGender: string;
  modelAge: string;
  modelEthnicity: string;
  modelBody: string;
  modelAppearance: string;
  videoQuality: CloneVideoQualityKey;
  videoDurationSeconds: number;
  videoSmart: boolean;
  referenceMode?: CloneReferenceMode;
  replicateLevel?: CloneReplicateLevelKey;
  requirement: string;
}

interface EcommerceHistoryRecord {
  id: string;
  title: string;
  createdAt: number;
  output: CloneOutputKey;
  platform: string;
  market: string;
  language: string;
  ratio: string;
  requirement: string;
  productImages: CloneImageItem[];
  results: CloneResult[];
  setResultImages: string[];
  setCounts: Record<CloneSetCountKey, number>;
  detailModules: string[];
  modelScenes: string[];
  referenceImages: CloneImageItem[];
  replicateLevel: CloneReplicateLevelKey;
}

interface EcommerceImagePromptOptions {
  gender?: string;
  age?: string;
  ethnicity?: string;
  body?: string;
  appearance?: string;
  scenes?: string[];
  customScene?: string;
  smartScene?: boolean;
  detailModules?: string[];
}

type PlatformRatioModeKey = ProductSetOutputKey | "hot" | "video-outfit";

interface PlatformRatioGroup {
  ratios: string[];
  defaultRatio: string;
}

const sideTools: Array<{ key: ProductKitToolKey; label: string; icon: ReactNode }> = [
  { key: "set", label: "商品套图", icon: <AppstoreOutlined /> },
  { key: "detail", label: "A+详情", icon: <FileImageOutlined /> },
  { key: "wear", label: "服饰穿搭", icon: <SkinOutlined /> },
  { key: "clone", label: "电商AI作图", icon: <AppstoreOutlined /> },
];

const platformSpecOptions: Array<{
  label: string;
  ratios: string[];
  defaultRatio: string;
  ratioGroups?: Partial<Record<PlatformRatioModeKey, PlatformRatioGroup>>;
  specs: string[];
  tip?: string;
  aliases?: string[];
}> = [
  {
    label: "淘宝/天猫",
    ratios: ["娣樺疂涓诲浘 / SKU 鍥?800脳800px", "璇︽儏椤靛 750px", "璇︽儏椤靛 790px"],
    defaultRatio: "娣樺疂涓诲浘 / SKU 鍥?800脳800px",
    ratioGroups: {
      set: {
        ratios: ["1000脳1000px\u00a0\u00a0\u00a01锛?", "800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "1000脳1000px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: [
          "750脳1000px\u00a0\u00a0\u00a03锛?",
          "790脳1053px\u00a0\u00a0\u00a03锛?",
          "750脳1125px\u00a0\u00a0\u00a02锛?",
          "790脳1185px\u00a0\u00a0\u00a02锛?",
        ],
        defaultRatio: "750脳1000px\u00a0\u00a0\u00a03锛?",
      },
      model: {
        ratios: ["750脳1000px\u00a0\u00a0\u00a03锛?"],
        defaultRatio: "750脳1000px\u00a0\u00a0\u00a03锛?",
      },
      video: {
        ratios: ["1080脳1920px\u00a0\u00a0\u00a09锛?6", "1080脳1440px\u00a0\u00a0\u00a03锛?", "1080脳1080px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "1080脳1920px\u00a0\u00a0\u00a09锛?6",
      },
      hot: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["涓诲浘 / SKU 鍥?800脳800px锛屸墹3MB", "璇︽儏椤靛 750px 鎴?790px锛屽崟寮犻珮鈮?546px"],
    tip: "寤鸿涓诲浘 200-400KB JPG锛岃秴杩?500KB 浼氬奖鍝嶅姞杞介€熷害銆?",
  },
  {
    label: "京东",
    ratios: ["浜笢涓诲浘 / SKU 鍥?800脳800px", "璇︽儏椤靛 750px", "棣栧浘涓讳綋鍗犳瘮 鈮?0%"],
    defaultRatio: "浜笢涓诲浘 / SKU 鍥?800脳800px",
    ratioGroups: {
      set: {
        ratios: ["1000脳1000px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "1000脳1000px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: [
          "750脳1000px\u00a0\u00a0\u00a03锛?",
          "990脳1320px\u00a0\u00a0\u00a03锛?",
          "750脳1125px\u00a0\u00a0\u00a02锛?",
          "990脳1485px\u00a0\u00a0\u00a02锛?",
        ],
        defaultRatio: "750脳1000px\u00a0\u00a0\u00a03锛?",
      },
      model: {
        ratios: ["750脳1125px\u00a0\u00a0\u00a02锛?", "990脳1485px\u00a0\u00a0\u00a02锛?"],
        defaultRatio: "750脳1125px\u00a0\u00a0\u00a02锛?",
      },
      video: {
        ratios: ["1080脳1920px\u00a0\u00a0\u00a09锛?6", "1920脳1080px\u00a0\u00a0\u00a016锛?"],
        defaultRatio: "1080脳1920px\u00a0\u00a0\u00a09锛?6",
      },
      hot: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["涓诲浘 / SKU 鍥?800脳800px锛岀櫧搴曪紝鈮?MB", "璇︽儏椤靛 750px锛岄鍥句富浣撳崰姣?鈮?0%"],
  },
  {
    label: "拼多多",
    ratios: ["涓诲浘 750脳352px", "涓诲浘 800脳800px", "璇︽儏椤靛 750px"],
    defaultRatio: "涓诲浘 750脳352px",
    ratioGroups: {
      set: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?", "750脳1000px\u00a0\u00a0\u00a03锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: ["750脳1000px\u00a0\u00a0\u00a03锛?", "750脳1125px\u00a0\u00a0\u00a02锛?"],
        defaultRatio: "750脳1000px\u00a0\u00a0\u00a03锛?",
      },
      model: {
        ratios: ["750脳1000px\u00a0\u00a0\u00a03锛?"],
        defaultRatio: "750脳1000px\u00a0\u00a0\u00a03锛?",
      },
      video: {
        ratios: ["1080脳1920px\u00a0\u00a0\u00a09锛?6"],
        defaultRatio: "1080脳1920px\u00a0\u00a0\u00a09锛?6",
      },
      hot: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["涓诲浘 750脳352px 鎴?800脳800px锛屸墹1MB", "璇︽儏椤靛 750px锛岃姹傜函鐧藉簳銆佹棤姘村嵃銆佹棤鎷兼帴"],
  },
  {
    label: "抖音电商",
    ratios: ["鐭棰?1080脳1920px"],
    defaultRatio: "鐭棰?1080脳1920px",
    ratioGroups: {
      video: {
        ratios: ["1080脳1920px\u00a0\u00a0\u00a09锛?6"],
        defaultRatio: "1080脳1920px\u00a0\u00a0\u00a09锛?6",
      },
      hot: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["鐭棰?1080脳1920px锛?:16", "30s 鍐呮渶浣?"],
  },
  {
    label: "亚马逊 Amazon",
    ratios: ["涓诲浘 鈮?600脳1600px", "寤鸿 2000脳2000px+", "鏈€灏?500脳500px"],
    defaultRatio: "涓诲浘 鈮?600脳1600px",
    ratioGroups: {
      set: {
        ratios: ["1600脳1600px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "1600脳1600px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: ["1600脳1600px\u00a0\u00a0\u00a01锛?", "1200脳1800px\u00a0\u00a0\u00a02锛?", "1200脳1600px\u00a0\u00a0\u00a03锛?"],
        defaultRatio: "1200脳1800px\u00a0\u00a0\u00a02锛?",
      },
      model: {
        ratios: ["1200脳1800px\u00a0\u00a0\u00a02锛?"],
        defaultRatio: "1200脳1800px\u00a0\u00a0\u00a02锛?",
      },
      video: {
        ratios: ["1920脳1080px\u00a0\u00a0\u00a016锛?"],
        defaultRatio: "1920脳1080px\u00a0\u00a0\u00a016锛?",
      },
      hot: {
        ratios: ["1600脳1600px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "1600脳1600px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["涓诲浘 1600脳1600px+锛岀函鐧藉簳锛屸墹10MB", "鏈€灏?500脳500px锛屽缓璁?2000px+ 浠ユ敮鎸佺缉鏀?"],
    aliases: ["浜氶┈閫?"],
  },
  {
    label: "Shopee",
    ratios: ["鍟嗗搧涓诲浘 1024脳1024px", "鍩虹涓诲浘 800脳800px"],
    defaultRatio: "鍟嗗搧涓诲浘 1024脳1024px",
    ratioGroups: {
      set: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: ["750脳1000px\u00a0\u00a0\u00a03锛?", "750脳1125px\u00a0\u00a0\u00a02锛?"],
        defaultRatio: "750脳1000px\u00a0\u00a0\u00a03锛?",
      },
      model: {
        ratios: ["750脳1000px\u00a0\u00a0\u00a03锛?"],
        defaultRatio: "750脳1000px\u00a0\u00a0\u00a03锛?",
      },
      video: {
        ratios: ["1080脳1920px\u00a0\u00a0\u00a09锛?6"],
        defaultRatio: "1080脳1920px\u00a0\u00a0\u00a09锛?6",
      },
      hot: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["鍟嗗搧涓诲浘鎺ㄨ崘 1024脳1024px锛屽熀纭€ 800脳800px", "鈮?MB锛岀櫧搴曟垨娴呰壊搴?"],
    aliases: ["铏剧毊 Shopee/Lazada", "铏剧毊"],
  },
  {
    label: "Lazada",
    ratios: ["鍟嗗搧涓诲浘 800脳800px"],
    defaultRatio: "鍟嗗搧涓诲浘 800脳800px",
    ratioGroups: {
      set: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: ["750脳1000px\u00a0\u00a0\u00a03锛?", "750脳1125px\u00a0\u00a0\u00a02锛?"],
        defaultRatio: "750脳1000px\u00a0\u00a0\u00a03锛?",
      },
      model: {
        ratios: ["750脳1000px\u00a0\u00a0\u00a03锛?"],
        defaultRatio: "750脳1000px\u00a0\u00a0\u00a03锛?",
      },
      video: {
        ratios: ["1080脳1920px\u00a0\u00a0\u00a09锛?6"],
        defaultRatio: "1080脳1920px\u00a0\u00a0\u00a09锛?6",
      },
      hot: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["鍟嗗搧涓诲浘 800脳800px锛?:1"],
  },
  {
    label: "Instagram",
    ratios: ["甯栧瓙 1080脳1350px", "甯栧瓙 1080脳1080px", "Stories / Reels 1080脳1920px", "澶村儚 320脳320px"],
    defaultRatio: "甯栧瓙 1080脳1350px",
    ratioGroups: {
      set: {
        ratios: ["1080脳1080px\u00a0\u00a0\u00a01锛?", "1080脳1350px\u00a0\u00a0\u00a04锛?"],
        defaultRatio: "1080脳1080px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: ["1080脳1350px\u00a0\u00a0\u00a04锛?"],
        defaultRatio: "1080脳1350px\u00a0\u00a0\u00a04锛?",
      },
      model: {
        ratios: ["1080脳1350px\u00a0\u00a0\u00a04锛?"],
        defaultRatio: "1080脳1350px\u00a0\u00a0\u00a04锛?",
      },
      video: {
        ratios: ["1080脳1920px\u00a0\u00a0\u00a09锛?6", "1080脳1350px\u00a0\u00a0\u00a04锛?"],
        defaultRatio: "1080脳1920px\u00a0\u00a0\u00a09锛?6",
      },
    },
    specs: ["甯栧瓙 1080脳1350px 鎴?1080脳1080px", "Stories / Reels 灏侀潰 1080脳1920px锛屽ご鍍?320脳320px"],
    tip: "寤鸿 鈮?MB JPG銆?",
    aliases: ["Instagram Reels"],
  },
  {
    label: "速卖通",
    ratios: ["涓诲浘 800脳800px", "涓诲浘 1000脳1000px+"],
    defaultRatio: "涓诲浘 800脳800px",
    ratioGroups: {
      set: {
        ratios: ["1000脳1000px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "1000脳1000px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: ["750脳1125px\u00a0\u00a0\u00a02锛?", "750脳1000px\u00a0\u00a0\u00a03锛?"],
        defaultRatio: "750脳1125px\u00a0\u00a0\u00a02锛?",
      },
      model: {
        ratios: ["750脳1125px\u00a0\u00a0\u00a02锛?"],
        defaultRatio: "750脳1125px\u00a0\u00a0\u00a02锛?",
      },
      video: {
        ratios: ["1080脳1920px\u00a0\u00a0\u00a09锛?6", "1920脳1080px\u00a0\u00a0\u00a016锛?"],
        defaultRatio: "1080脳1920px\u00a0\u00a0\u00a09锛?6",
      },
      hot: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["涓诲浘寤鸿 800脳800px 鎴栨洿楂橈紝1:1", "閫傚悎璺ㄥ鐢靛晢涓诲浘銆丼KU 鍥惧拰鍦烘櫙鍥?"],
  },
  {
    label: "eBay",
    ratios: ["鍟嗗搧鍥?1:1", "鐧藉簳澶氳搴﹀睍绀哄浘 1:1"],
    defaultRatio: "鍟嗗搧鍥?1:1",
    ratioGroups: {
      set: {
        ratios: ["1600脳1600px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "1600脳1600px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: ["1000脳1500px\u00a0\u00a0\u00a02锛?", "1200脳1600px\u00a0\u00a0\u00a03锛?"],
        defaultRatio: "1000脳1500px\u00a0\u00a0\u00a02锛?",
      },
      model: {
        ratios: ["1000脳1500px\u00a0\u00a0\u00a02锛?"],
        defaultRatio: "1000脳1500px\u00a0\u00a0\u00a02锛?",
      },
      video: {
        ratios: ["1920脳1080px\u00a0\u00a0\u00a016锛?", "1080脳1920px\u00a0\u00a0\u00a09锛?6"],
        defaultRatio: "1920脳1080px\u00a0\u00a0\u00a016锛?",
      },
      hot: {
        ratios: ["1600脳1600px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "1600脳1600px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["鍟嗗搧鍥惧缓璁?1:1锛屼富浣撴竻鏅板眳涓?", "閫傚悎鐧藉簳涓诲浘鍜屽瑙掑害灞曠ず鍥?"],
  },
  {
    label: "TikTok Shop",
    ratios: ["鍟嗗搧涓诲浘 1:1", "鐭棰?/ 绔栫増灏侀潰 9:16"],
    defaultRatio: "鍟嗗搧涓诲浘 1:1",
    ratioGroups: {
      set: {
        ratios: ["1280脳1280px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "1280脳1280px\u00a0\u00a0\u00a01锛?",
      },
      detail: {
        ratios: ["1080脳1350px\u00a0\u00a0\u00a04锛?"],
        defaultRatio: "1080脳1350px\u00a0\u00a0\u00a04锛?",
      },
      model: {
        ratios: ["1080脳1350px\u00a0\u00a0\u00a04锛?"],
        defaultRatio: "1080脳1350px\u00a0\u00a0\u00a04锛?",
      },
      video: {
        ratios: ["1080脳1920px\u00a0\u00a0\u00a09锛?6"],
        defaultRatio: "1080脳1920px\u00a0\u00a0\u00a09锛?6",
      },
      hot: {
        ratios: ["800脳800px\u00a0\u00a0\u00a01锛?"],
        defaultRatio: "800脳800px\u00a0\u00a0\u00a01锛?",
      },
    },
    specs: ["鍟嗗搧涓诲浘寤鸿 1:1", "鐭棰?绔栫増灏侀潰寤鸿 9:16"],
  },
];
const platformOptions = platformSpecOptions.map((option) => option.label);
const getPlatformLogoSources = (value: string) => {
  const normalized = value.toLowerCase();
  if (value.includes("淘宝") || value.includes("天猫") || value.includes("娣樺疂") || value.includes("澶╃尗")) return [taobaoLogo, tmallLogo];
  if (value.includes("京东") || value.includes("浜笢")) return [jdLogo];
  if (value.includes("拼多多") || value.includes("鎷煎澶")) return [pinduoduoLogo];
  if (value.includes("抖音") || value.includes("鎶栭煶")) return [douyinLogo];
  if (normalized.includes("amazon")) return [amazonLogo];
  if (normalized.includes("shopee")) return [shopeeLogo];
  if (normalized.includes("lazada")) return [lazadaLogo];
  if (normalized.includes("instagram")) return [instagramLogo];
  if (value.includes("速卖通") || value.includes("閫熷崠閫")) return [aliexpressLogo];
  if (normalized.includes("ebay")) return [ebayLogo];
  if (normalized.includes("tiktok")) return [tiktokShopLogo];
  return [];
};
const renderPlatformLogo = (value: string) => {
  const sources = getPlatformLogoSources(value);
  return (
    <span className={`ecom-platform-logo-mark${sources.length > 1 ? " ecom-platform-logo-mark--duo" : ""}`} aria-hidden="true">
      {sources.map((src) => (
        <img key={src} src={src} alt="" />
      ))}
    </span>
  );
};
const marketLanguageOptions: Array<{ country: string; languages: string[] }> = [
  { country: "中国", languages: ["中文"] },
  { country: "美国", languages: ["英文"] },
  { country: "加拿大", languages: ["英文", "法文"] },
  { country: "英国", languages: ["英文"] },
  { country: "德国", languages: ["德文"] },
  { country: "法国", languages: ["法文"] },
  { country: "意大利", languages: ["意大利语"] },
  { country: "西班牙", languages: ["西班牙语"] },
  { country: "日本", languages: ["日文"] },
  { country: "韩国", languages: ["韩文"] },
  { country: "澳大利亚", languages: ["英文"] },
  { country: "新加坡", languages: ["英文", "中文"] },
  { country: "马来西亚", languages: ["马来语", "英文", "中文"] },
  { country: "印尼", languages: ["印度尼西亚语", "英文"] },
  { country: "越南", languages: ["越南语", "英文"] },
  { country: "泰国", languages: ["泰语", "英文"] },
  { country: "菲律宾", languages: ["菲律宾语（他加禄语）", "英文"] },
  { country: "巴西", languages: ["葡萄牙语"] },
  { country: "墨西哥", languages: ["西班牙语"] },
  { country: "智利", languages: ["西班牙语"] },
  { country: "哥伦比亚", languages: ["西班牙语"] },
  { country: "阿联酋", languages: ["阿拉伯语", "英文"] },
  { country: "沙特阿拉伯", languages: ["阿拉伯语", "英文"] },
  { country: "俄罗斯", languages: ["俄语"] },
  { country: "波兰", languages: ["波兰语"] },
];
const marketOptions = marketLanguageOptions.map((option) => option.country);
const languageOptions = Array.from(new Set(marketLanguageOptions.flatMap((option) => option.languages)));
const languageAliases: Record<string, string> = {
  "鑻辨枃": "英文",
  "涓枃": "中文",
  "鑻辫": "英文",
  "鏃ヨ": "日文",
  "鏃ユ枃": "日文",
  "寰疯": "德文",
  "寰锋枃": "德文",
  "娉曡": "法文",
  "娉曟枃": "法文",
  "闊╄": "韩文",
  "闊╂枃": "韩文",
  "瑗挎枃": "西班牙语",
  "瑗跨彮鐗欒": "西班牙语",
  "钁℃枃": "葡萄牙语",
  "钁¤悇鐗欒": "葡萄牙语",
  "鍗板凹璇?": "印度尼西亚语",
  "鍗板害灏艰タ浜氳": "印度尼西亚语",
  "鑿插緥瀹捐": "菲律宾语（他加禄语）",
  "鑿插緥瀹捐锛堜粬鍔犵璇級": "菲律宾语（他加禄语）",
};
const defaultPlatformSpec = platformSpecOptions[0]!;
const getPlatformSpec = (value: string) =>
  platformSpecOptions.find((option) => option.label === value || option.aliases?.includes(value)) ?? defaultPlatformSpec;
const legacyPlatformAliases: Record<string, string> = {
  "娣樺疂/澶╃尗": "淘宝/天猫",
  "浜笢": "京东",
  "鎷煎澶?": "拼多多",
  "鎶栭煶鐢靛晢": "抖音电商",
  "浜氶┈閫?Amazon": "亚马逊 Amazon",
  "閫熷崠閫?": "速卖通",
};
const normalizePlatform = (value: string) => getPlatformSpec(legacyPlatformAliases[value] ?? value).label;
const domesticPlatformLabels = new Set(["淘宝/天猫", "京东", "拼多多", "抖音电商"]);
const domesticPlatformLanguages = ["中文"];
const isDomesticPlatform = (platformValue: string) => domesticPlatformLabels.has(normalizePlatform(platformValue));
const getPlatformRatioGroup = (value: string, mode?: PlatformRatioModeKey): PlatformRatioGroup => {
  const platformSpec = getPlatformSpec(value);
  return (mode ? platformSpec.ratioGroups?.[mode] : null) ?? {
    ratios: platformSpec.ratios,
    defaultRatio: platformSpec.defaultRatio,
  };
};
const getPlatformRatioOptions = (value: string, mode?: PlatformRatioModeKey) => getPlatformRatioGroup(value, mode).ratios;
const getPlatformDefaultRatio = (value: string, mode?: PlatformRatioModeKey) => getPlatformRatioGroup(value, mode).defaultRatio;
const getUniqueRatioOptions = (ratios: string[]) => Array.from(new Set(ratios));
const normalizeRatioToken = (value: string) => value.replaceAll("锛?", ":").replaceAll("：", ":").replaceAll("脳", "×").trim();
const normalizeRatioForPlatform = (platformValue: string, ratioValue: string, mode?: PlatformRatioModeKey) => {
  const platformRatios = getPlatformRatioOptions(platformValue, mode);
  if (platformRatios.includes(ratioValue)) return ratioValue;
  const normalizedRatio = normalizeRatioToken(ratioValue);
  const matchedRatio = platformRatios.find((option) => normalizeRatioToken(option).includes(normalizedRatio));
  return matchedRatio ?? getPlatformDefaultRatio(platformValue, mode);
};
const quickSetRatioOptions = ["1:1", "3:4", "9:16", "16:9"];
const getQuickSetRatioValue = (value: string) => {
  const normalizedValue = normalizeRatioToken(value);
  if (quickSetRatioOptions.includes(normalizedValue)) return normalizedValue;
  const sizeMatch = normalizedValue.match(/(\d+)\s*[×xX]\s*(\d+)/u);
  if (sizeMatch) {
    const width = Number(sizeMatch[1]);
    const height = Number(sizeMatch[2]);
    if (Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0) {
      const aspect = formatAspectRatio(width, height);
      if (quickSetRatioOptions.includes(aspect)) return aspect;
    }
  }
  const ratioMatch = normalizedValue.match(/(\d+)\s*[:：]\s*(\d+)/u);
  if (ratioMatch) {
    const aspect = `${Number(ratioMatch[1])}:${Number(ratioMatch[2])}`;
    if (quickSetRatioOptions.includes(aspect)) return aspect;
  }
  return quickSetRatioOptions[0]!;
};
const formatRatioDisplayValue = (value: string) => {
  const normalizedValue = normalizeRatioToken(value);
  const sizeMatch = normalizedValue.match(/(\d+)\s*[×xX]\s*(\d+)\s*px?/u);
  if (sizeMatch) {
    const width = Number(sizeMatch[1]);
    const height = Number(sizeMatch[2]);
    return `${width}×${height}px\u00a0\u00a0\u00a0${formatAspectRatio(width, height)}`;
  }
  return normalizedValue
    .replace("娣樺疂涓诲浘 / SKU 鍥?", "淘宝主图 / SKU 图 ")
    .replace("浜笢涓诲浘 / SKU 鍥?", "京东主图 / SKU 图 ")
    .replace("璇︽儏椤靛", "详情页宽")
    .replace("鐭棰?", "短视频")
    .replace("涓诲浘", "主图")
    .replace("鍟嗗搧涓诲浘", "商品主图")
    .replace("鍟嗗搧鍥?", "商品图");
};
/** Extract CSS aspect-ratio from a ratio string like "1000x1000px   1:1" -> "1 / 1" */
const parseRatioToAspectCss = (ratioStr: string): string => {
  const match = ratioStr.match(/(\d+)\D+(\d+)/u);
  if (!match) return "1 / 1";
  return `${match[1]} / ${match[2]}`;
};
/** Normalize ratio display string ("1000脳1000px   1锛?") to API format ("1:1") */
const normalizeRatioForApi = (ratioStr: string): string => {
  const match = ratioStr.match(/(\d+)\D+(\d+)/u);
  if (!match) return "1:1";
  return `${match[1]}:${match[2]}`;
};
const greatestCommonDivisor = (left: number, right: number): number => {
  let a = Math.abs(left);
  let b = Math.abs(right);
  while (b) {
    [a, b] = [b, a % b];
  }
  return a || 1;
};
const formatAspectRatio = (width: number, height: number) => {
  const divisor = greatestCommonDivisor(width, height);
  return `${Math.round(width / divisor)}：${Math.round(height / divisor)}`;
};
const formatUploadedImageRatio = (image?: CloneImageItem) => {
  if (!image) return null;
  const format = image.format ? `\u00a0\u00a0\u00a0${image.format}` : "";
  if (!image.width || !image.height) return `上传图片\u00a0\u00a0\u00a0原图比例${format}`;
  return `上传图片 ${image.width}×${image.height}px\u00a0\u00a0\u00a0${formatAspectRatio(image.width, image.height)}${format}`;
};
const defaultMarketLanguageOption = marketLanguageOptions[0]!;
const normalizeMarket = (value: string) =>
  marketLanguageOptions.some((option) => option.country === value) ? value : defaultMarketLanguageOption.country;
const normalizeLanguage = (value: string) => languageAliases[value] ?? value;
const uniqueLanguages = (languages: string[]) => Array.from(new Set(languages));
const appendEnglish = (languages: string[]) => Array.from(new Set([...languages, "英文"]));
const getMarketLanguageOptions = (marketValue: string) =>
  appendEnglish((marketLanguageOptions.find((option) => option.country === marketValue) ?? defaultMarketLanguageOption).languages);
const getPlatformLanguageOptions = (platformValue: string, marketValue: string) => {
  const marketLanguages = getMarketLanguageOptions(marketValue);
  if (!isDomesticPlatform(platformValue)) return marketLanguages;
  const localLanguages = marketLanguages.filter((item) => item !== "英文");
  return uniqueLanguages([...localLanguages, ...domesticPlatformLanguages, "英文"]);
};
const getPlatformDefaultLanguage = (platformValue: string, marketValue: string) =>
  isDomesticPlatform(platformValue) ? "中文" : (getPlatformLanguageOptions(platformValue, marketValue)[0] ?? languageOptions[0] ?? "英文");
const normalizeLanguageForPlatform = (platformValue: string, marketValue: string, languageValue: string) => {
  const normalizedLanguage = normalizeLanguage(languageValue);
  const platformLanguages = getPlatformLanguageOptions(platformValue, marketValue);
  return platformLanguages.includes(normalizedLanguage) ? normalizedLanguage : getPlatformDefaultLanguage(platformValue, marketValue);
};
const productSetOutputOptions: Array<{ key: ProductSetOutputKey; label: string }> = [
  { key: "set", label: "套图" },
  { key: "detail", label: "详情图" },
  { key: "model", label: "模特图" },
  { key: "video", label: "短视频" },
];
const cloneOutputOptions: Array<{ key: CloneOutputKey; label: string }> = [
  ...productSetOutputOptions,
  { key: "hot", label: "爆款复刻" },
  { key: "video-outfit", label: "视频换装" },
];
const cloneSetCountOptions: Array<{
  key: CloneSetCountKey;
  title: string;
  desc: string;
}> = [
  { key: "selling", title: "卖点图", desc: "展示商品核心卖点和细节特写" },
  { key: "white", title: "白底图", desc: "白底主图，多角度呈现商品细节" },
  { key: "scene", title: "场景图", desc: "展示商品生活使用场景和人物搭配" },
];
const cloneSetCountKeys = cloneSetCountOptions.map((option) => option.key);
const defaultCloneSetCounts: Record<CloneSetCountKey, number> = {
  selling: 3,
  white: 1,
  scene: 3,
};
const minCloneSetTotal = 1;
const maxCloneSetTotal = 16;
const maxCloneProductImages = 7;
const maxCloneReferenceImages = 20;
const cloneVideoDurationMin = 5;
const cloneVideoDurationMax = 45;
const defaultEcommercePlatform = "淘宝/天猫";
const defaultProductSetOutput: ProductSetOutputKey = "set";
const defaultCloneOutput: CloneOutputKey = "set";
const cloneLatestSettingStorageKey = "omniai.clone-ai.latest-setting";
const ecommerceHistoryStorageKey = "omniai.ecommerce.history.records";
const cloneVideoQualityOptions: Array<{ key: CloneVideoQualityKey; label: string; desc: string }> = [
  { key: "standard", label: "标准", desc: "快速出片" },
  { key: "high", label: "高清", desc: "推荐" },
  { key: "ultra", label: "超清", desc: "细节增强" },
];
const cloneReplicateLevelOptions: Array<{ key: CloneReplicateLevelKey; title: string; desc: string }> = [
  { key: "style", title: "参考风格", desc: "参考整体风格和结构，自动调整色彩和重构场景。" },
  { key: "high", title: "高度复刻", desc: "参考视觉结构替换产品和文案，保留主要场景细节。" },
];
const tryOnRatioOptions = ["3:4", "1:1", "9:16"];
const tryOnScenes = ["纯色棚拍", "都市街头", "街角咖啡", "自然草坪", "度假海滩", "温馨居家", "艺术展馆"];
const normalizeCloneModelSceneSelection = (scenes: string[] | null | undefined) => {
  const validScenes = (scenes ?? []).filter((scene) => typeof scene === "string" && scene.trim());
  const latestScene = validScenes[validScenes.length - 1];
  return latestScene ? [latestScene] : [];
};
const tryOnModelOptions = {
  gender: ["女", "男"],
  age: ["青年", "少年", "中年"],
  ethnicity: ["欧美白人", "亚洲人", "拉美裔", "非洲裔"],
  body: ["标准", "高挑", "微胖", "运动"],
};
const sampleResults = [
  ossAssets.ecommerce.slides.slide4,
  ossAssets.ecommerce.generated,
  ossAssets.ecommerce.slides.slide5,
];
const productSetAssets = ossAssets.ecommerce.productSet;
const productSetPreviewCards = [
  { id: "main", label: "01 涓诲浘 (鐧藉簳/鍚堣)", src: productSetAssets.main },
  { id: "scene", label: "02 鍦烘櫙灞曠ず", src: productSetAssets.scene },
  { id: "model", label: "03 妯＄壒鍦烘櫙鍥?", src: productSetAssets.model },
  { id: "detail", label: "04 缁嗚妭璇存槑", src: productSetAssets.detail },
  { id: "selling", label: "05 鍗栫偣璇﹁В", src: productSetAssets.selling },
];
const tryOnAssets = ossAssets.ecommerce.tryOn;

const tryOnCards = [
  {
    title: "澶氫欢娣锋惌鑷姩铻嶅悎",
    tone: "red",
    inputs: [tryOnAssets.dressA, tryOnAssets.dressB, tryOnAssets.modelWoman],
    results: [tryOnAssets.tryA, tryOnAssets.tryB],
  },
  {
    title: "涓€浠朵篃鑳藉嚭澶х墖",
    tone: "brown",
    inputs: [tryOnAssets.jacket, tryOnAssets.modelMan],
    results: [tryOnAssets.jacketResultA, tryOnAssets.jacketResultB],
  },
  {
    title: "闉嬪附楗板搧瀹岀編閫傞厤",
    tone: "gold",
    inputs: [tryOnAssets.hat, tryOnAssets.modelAsian],
    results: [tryOnAssets.hatResultA, tryOnAssets.hatResultB],
  },
];

const detailTypeOptions = ["普通A+", "品牌A+", "标准详情页", "移动端长图"];
const detailModules = [
  { id: "hero", title: "首页焦点图", desc: "集中呈现核心利益点" },
  { id: "selling", title: "卖点强化图", desc: "放大产品优势" },
  { id: "usage", title: "使用情境图", desc: "还原实际使用画面" },
  { id: "angle", title: "外观角度图", desc: "展示不同视角造型" },
  { id: "scene", title: "氛围场景图", desc: "营造产品应用环境" },
  { id: "detail", title: "细节特写图", desc: "突出材质和做工" },
  { id: "story", title: "品牌理念图", desc: "表达品牌主张" },
  { id: "size", title: "规格尺寸图", desc: "说明尺寸容量尺码" },
  { id: "compare", title: "效果对照图", desc: "呈现前后差异" },
  { id: "spec", title: "参数信息表", desc: "整理商品关键数据" },
  { id: "craft", title: "工艺流程图", desc: "说明制作与处理步骤" },
  { id: "gift", title: "清单配件图", desc: "展示包装内全部内容" },
  { id: "series", title: "SKU组合图", desc: "呈现颜色款式组合" },
  { id: "ingredient", title: "成分材质图", desc: "说明配方或材料构成" },
  { id: "service", title: "保障说明图", desc: "传达质保退换承诺" },
  { id: "tips", title: "使用提示图", desc: "提醒操作与保养要点" },
];
const defaultDetailModuleIds: string[] = [];
const defaultCloneDetailModuleIds = ["hero", "selling", "usage", "angle", "scene", "detail"];
const maxDetailModuleSelection = 6;
const cloneDetailModules = detailModules;
const detailAssets = ossAssets.ecommerce.detail;
const detailProductSamples = [detailAssets.productA, detailAssets.productB, detailAssets.productC];
const detailGridSamples = [detailAssets.gridA, detailAssets.gridB, detailAssets.gridC, detailAssets.gridD, detailAssets.gridE, detailAssets.gridF];

function getImageFileFormat(file: File) {
  const mimeFormat = file.type.split("/")[1]?.replace("jpeg", "jpg").toUpperCase();
  if (mimeFormat) return mimeFormat;
  return file.name.split(".").pop()?.toUpperCase() ?? "";
}

function readImageDimensions(src: string): Promise<{ width: number; height: number }> {
  return new Promise((resolve, reject) => {
    const image = new Image();
    image.onload = () => resolve({ width: image.naturalWidth, height: image.naturalHeight });
    image.onerror = reject;
    image.src = src;
  });
}

const blobToDataUrl = (blob: Blob): Promise<string> =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(String(reader.result || ""));
    reader.onerror = () => reject(reader.error || new Error("鏂囦欢璇诲彇澶辫触"));
    reader.readAsDataURL(blob);
  });

async function createUploadedImageItems(files: File[], limit: number, prefix: string): Promise<CloneImageItem[]> {
  const selectedFiles = Array.from(files).slice(0, limit);
  const stamp = Date.now();

  const items = await Promise.all(selectedFiles.map(async (file, index) => {
    const localPreviewUrl = URL.createObjectURL(file);
    let src = localPreviewUrl;
    let ossKey: string | undefined;
    let shouldRevokeLocalPreview = false;
    let dimensions: { width?: number; height?: number } = {};
    try {
      dimensions = await readImageDimensions(localPreviewUrl);
    } catch {
      dimensions = {};
    }

    const mimeType = normalizeEcommerceImageMime(file.type);
    try {
      const uploadBlob = file.type === mimeType ? file : new Blob([file], { type: mimeType });
      const uploaded = await aiGenerationClient.uploadAssetBinary(uploadBlob, {
        name: file.name,
        mimeType,
        scope: "ecommerce-product",
      });
      src = uploaded.url;
      ossKey = uploaded.ossKey;
      shouldRevokeLocalPreview = true;
    } catch {
      src = localPreviewUrl;
    } finally {
      if (shouldRevokeLocalPreview) URL.revokeObjectURL(localPreviewUrl);
    }

    return {
      id: `${prefix}-${stamp}-${index}`,
      src,
      name: file.name,
      file,
      format: getImageFileFormat(file),
      mimeType,
      ossKey,
      ...dimensions,
    };
  }));

  return items;
}

async function persistGeneratedImageUrl(sourceUrl: string, scope: string, namePrefix: string): Promise<string> {
  if (!sourceUrl) return sourceUrl;
  try {
    if (sourceUrl.startsWith("data:")) {
      const { url } = await aiGenerationClient.uploadAsset({
        dataUrl: sourceUrl,
        name: `${namePrefix}-${Date.now()}.png`,
        scope,
      });
      return url || sourceUrl;
    }

    if (sourceUrl.startsWith("blob:")) {
      const rawBlob = await fetch(sourceUrl).then((res) => res.blob());
      const mimeType = normalizeEcommerceImageMime(rawBlob.type);
      const blob = rawBlob.type === mimeType ? rawBlob : new Blob([rawBlob], { type: mimeType });
      const { url } = await aiGenerationClient.uploadAssetBinary(blob, {
        name: `${namePrefix}-${Date.now()}.png`,
        mimeType,
        scope,
      });
      return url;
    }

    const { url } = await aiGenerationClient.uploadAssetByUrl({
      sourceUrl,
      name: `${namePrefix}-${Date.now()}`,
      scope,
    });
    return url || sourceUrl;
  } catch {
    return sourceUrl;
  }
}

function notifyRejectedImages(files: File[]): File[] {
  const { accepted, rejected } = validateEcommerceImageFiles(files);
  const message = summarizeRejectedImages(rejected);
  if (message) toast.error(message);
  return accepted;
}

function isCloneSavedSetting(item: unknown): item is CloneSavedSetting {
  const candidate = item as Partial<CloneSavedSetting>;
  return (
    typeof candidate.id === "string" &&
    typeof candidate.name === "string" &&
    typeof candidate.savedAt === "string" &&
    typeof candidate.output === "string" &&
    typeof candidate.platform === "string" &&
    typeof candidate.market === "string" &&
    typeof candidate.language === "string" &&
    typeof candidate.ratio === "string" &&
    typeof candidate.videoDurationSeconds === "number"
  );
}

function readCloneLatestSetting() {
  if (typeof window === "undefined") return null;
  try {
    const rawValue = window.localStorage.getItem(cloneLatestSettingStorageKey);
    if (rawValue) {
      const parsedValue: unknown = JSON.parse(rawValue);
      if (isCloneSavedSetting(parsedValue)) return parsedValue;
    }
  } catch {
    return null;
  }
  return null;
}

function writeCloneLatestSetting(setting: CloneSavedSetting) {
  if (typeof window === "undefined") return;
  window.localStorage.setItem(cloneLatestSettingStorageKey, JSON.stringify(setting));
}

function isCloneImageItem(item: unknown): item is CloneImageItem {
  const candidate = item as Partial<CloneImageItem>;
  return typeof candidate.id === "string" && typeof candidate.src === "string" && typeof candidate.name === "string";
}

function isCloneResult(item: unknown): item is CloneResult {
  const candidate = item as Partial<CloneResult>;
  return typeof candidate.id === "string" && typeof candidate.src === "string" && typeof candidate.label === "string";
}

function isEcommerceHistoryRecord(item: unknown): item is EcommerceHistoryRecord {
  const candidate = item as Partial<EcommerceHistoryRecord>;
  return (
    typeof candidate.id === "string" &&
    typeof candidate.title === "string" &&
    typeof candidate.createdAt === "number" &&
    typeof candidate.output === "string" &&
    typeof candidate.platform === "string" &&
    typeof candidate.market === "string" &&
    typeof candidate.language === "string" &&
    typeof candidate.ratio === "string" &&
    typeof candidate.requirement === "string" &&
    Array.isArray(candidate.productImages) &&
    candidate.productImages.every(isCloneImageItem) &&
    Array.isArray(candidate.results) &&
    candidate.results.every(isCloneResult)
  );
}

function removeFilePayloadFromImages(images: CloneImageItem[]): CloneImageItem[] {
  return images.map(({ file: _file, ...image }) => image);
}

function normalizeEcommerceHistoryRecord(record: EcommerceHistoryRecord): EcommerceHistoryRecord {
  return {
    ...record,
    productImages: removeFilePayloadFromImages(record.productImages),
    referenceImages: removeFilePayloadFromImages(record.referenceImages ?? []),
    results: record.results ?? [],
    setResultImages: record.setResultImages ?? [],
    setCounts: record.setCounts ?? defaultCloneSetCounts,
    detailModules: record.detailModules ?? defaultCloneDetailModuleIds,
    modelScenes: record.modelScenes ?? [],
    replicateLevel: record.replicateLevel ?? "high",
  };
}

function readEcommerceHistoryRecords() {
  if (typeof window === "undefined") return [];
  try {
    const rawValue = window.localStorage.getItem(ecommerceHistoryStorageKey);
    if (!rawValue) return [];
    const parsedValue: unknown = JSON.parse(rawValue);
    if (!Array.isArray(parsedValue)) return [];
    return parsedValue
      .filter(isEcommerceHistoryRecord)
      .map(normalizeEcommerceHistoryRecord)
      .sort((a, b) => b.createdAt - a.createdAt)
      .slice(0, 30);
  } catch {
    return [];
  }
}

function writeEcommerceHistoryRecords(records: EcommerceHistoryRecord[]) {
  if (typeof window === "undefined") return;
  window.localStorage.setItem(ecommerceHistoryStorageKey, JSON.stringify(records.map(normalizeEcommerceHistoryRecord).slice(0, 30)));
}

function clampCloneVideoDuration(value: number) {
  return Math.min(cloneVideoDurationMax, Math.max(cloneVideoDurationMin, Math.round(value)));
}

function ProductClonePage(_props: ProductClonePageProps = {}) {
  const setInputRef = useRef<HTMLInputElement>(null);
  const productInputRef = useRef<HTMLInputElement>(null);
  const quickProductInputRef = useRef<HTMLInputElement>(null);
  const cloneReferenceInputRef = useRef<HTMLInputElement>(null);
  const smartCutoutInputRef = useRef<HTMLInputElement>(null);
  const imageWorkbenchInputRef = useRef<HTMLInputElement>(null);
  const watermarkInputRef = useRef<HTMLInputElement>(null);
  const watermarkProcessTimeoutRef = useRef<number | null>(null);
  const smartCutoutTransitionTimeoutRef = useRef<number | null>(null);
  const smartCutoutPendingUrlsRef = useRef<string[]>([]);
  const smartCutoutPaletteRef = useRef<HTMLDivElement>(null);
  const smartCutoutToolsRef = useRef<HTMLDivElement>(null);
  const composerMenuCloseTimeoutRef = useRef<number | null>(null);
  const requirementTextareaRef = useRef<HTMLTextAreaElement>(null);
  const commandComposerWrapRef = useRef<HTMLElement | null>(null);
  const garmentInputRef = useRef<HTMLInputElement>(null);
  const detailInputRef = useRef<HTMLInputElement>(null);
  const countHoldTimeoutRef = useRef<number | null>(null);
  const countHoldIntervalRef = useRef<number | null>(null);
  const isAuthenticated = Boolean((_props as Record<string, unknown>).isAuthenticated);
  const requestLogin = () => {
    const handler = (_props as Record<string, unknown>).onRequireLogin;
    if (typeof handler === "function") handler();
  };
  const imageGen = useGenerationTasks({ sourceView: "ecommerce" });
  const appUsage = useAppStore((s) => s.usage);
  const latestCloneSettingRef = useRef<CloneSavedSetting | null>(null);
  const skipInitialCloneAutoSaveRef = useRef(true);
  const skipNextCloneAutoSaveRef = useRef(false);
  const [activeTool, setActiveTool] = useState<ProductKitToolKey>("clone");
  useEffect(() => {
    setPreviewZoom(1);
    setIsCommandComposerCompact(false);
  }, [activeTool]);
  const [setImages, setSetImages] = useState<CloneImageItem[]>([]);
  const [productSetPlatform, setProductSetPlatform] = useState(defaultEcommercePlatform);
  const [productSetMarket, setProductSetMarket] = useState(marketOptions[0]);
  const [productSetLanguage, setProductSetLanguage] = useState(getPlatformDefaultLanguage(defaultEcommercePlatform, marketOptions[0]));
  const [productSetRatio, setProductSetRatio] = useState(getPlatformDefaultRatio(defaultEcommercePlatform, defaultProductSetOutput));
  const [productSetRequirement, setProductSetRequirement] = useState("");
  const [productSetOutput, setProductSetOutput] = useState<ProductSetOutputKey>(defaultProductSetOutput);
  const [productSetStatus, setProductSetStatus] = useState<ProductSetStatus>("idle");
  const [productSetResultImages, setProductSetResultImages] = useState<string[]>([]);
  const [isSetUploadDragging, setIsSetUploadDragging] = useState(false);
  const [selectedProductSetPreview, setSelectedProductSetPreview] = useState<{ src: string; label: string } | null>(null);
  const [showHostingModal, setShowHostingModal] = useState(false);
  const [productImages, setProductImages] = useState<CloneImageItem[]>([]);
  const [activeQuickTool, setActiveQuickTool] = useState<"cutout" | "set" | "detail" | "watermark" | "image-edit" | "hot-video" | null>(null);
  const [smartCutoutImage, setSmartCutoutImage] = useState<SmartCutoutImageItem | null>(null);
  const [smartCutoutBatchImages, setSmartCutoutBatchImages] = useState<SmartCutoutImageItem[]>([]);
  const [smartCutoutBackgroundColor, setSmartCutoutBackgroundColor] = useState("#ffffff");
  const [smartCutoutBackgroundAlpha, setSmartCutoutBackgroundAlpha] = useState(100);
  const [smartCutoutHexDraft, setSmartCutoutHexDraft] = useState("#ffffff");
  const [isSmartCutoutPaletteOpen, setIsSmartCutoutPaletteOpen] = useState(false);
  const [smartCutoutSizeKey, setSmartCutoutSizeKey] = useState<SmartCutoutSizeKey>("original");
  const [isSmartCutoutDragging, setIsSmartCutoutDragging] = useState(false);
  const [isSmartCutoutComparing, setIsSmartCutoutComparing] = useState(false);
  const [isSmartCutoutTransitioning, setIsSmartCutoutTransitioning] = useState(false);
  const [smartCutoutTransitionMessage, setSmartCutoutTransitionMessage] = useState({
    title: "正在切换页面",
    subtitle: "请稍候",
  });
  const [watermarkImage, setWatermarkImage] = useState<{ src: string; name: string; format: string } | null>(null);
  const [watermarkStatus, setWatermarkStatus] = useState<"idle" | "processing" | "done">("idle");
  const [isWatermarkDragging, setIsWatermarkDragging] = useState(false);
  const [imageWorkbenchImage, setImageWorkbenchImage] = useState<{ src: string; name: string; format: string } | null>(null);
  const [imageWorkbenchPrompt, setImageWorkbenchPrompt] = useState("");
  const [imageWorkbenchBrushSize, setImageWorkbenchBrushSize] = useState(50);
  const [imageWorkbenchRatio, setImageWorkbenchRatio] = useState("1:1");
  const [imageWorkbenchStatus, setImageWorkbenchStatus] = useState<"idle" | "processing" | "done">("idle");
  const [isImageWorkbenchDragging, setIsImageWorkbenchDragging] = useState(false);
  const [imageWorkbenchMaskStrokes, setImageWorkbenchMaskStrokes] = useState<Array<{ id: string; size: number; points: Array<{ x: number; y: number }> }>>([]);
  const [imageWorkbenchBrushCursor, setImageWorkbenchBrushCursor] = useState<{ x: number; y: number } | null>(null);
  const [isProductUploadDragging, setIsProductUploadDragging] = useState(false);
  const [cloneOutput, setCloneOutput] = useState<CloneOutputKey>(defaultCloneOutput);
  const [videoHistoryVisible, setVideoHistoryVisible] = useState(false);
  const [videoPlanTrigger, setVideoPlanTrigger] = useState(0);
  const [openCloneBasicSelect, setOpenCloneBasicSelect] = useState<CloneBasicSelectKey | null>(null);
  const [openQuickSetSelect, setOpenQuickSetSelect] = useState<CloneBasicSelectKey | null>(null);
  const [visibleQuickSetSelect, setVisibleQuickSetSelect] = useState<CloneBasicSelectKey | null>(null);
  const [isQuickSetSelectClosing, setIsQuickSetSelectClosing] = useState(false);
  const [composerMenu, setComposerMenu] = useState<ComposerMenuKey | null>(null);
  const [visibleComposerMenu, setVisibleComposerMenu] = useState<ComposerMenuKey | null>(null);
  const [isComposerMenuClosing, setIsComposerMenuClosing] = useState(false);
  const [composerPopoverLeft, setComposerPopoverLeft] = useState(0);
  const [isCommandHistoryCollapsed, setIsCommandHistoryCollapsed] = useState(() => (typeof window !== "undefined" ? window.innerWidth <= 1180 : false));
  const [isQuickPanelCollapsed, setIsQuickPanelCollapsed] = useState(false);
  const [openCloneModelSelect, setOpenCloneModelSelect] = useState<CloneModelSelectKey | null>(null);
  const [cloneModelSelectDropUp, setCloneModelSelectDropUp] = useState(false);
  const [cloneReferenceMode, setCloneReferenceMode] = useState<CloneReferenceMode>("upload");
  const [cloneReferenceImages, setCloneReferenceImages] = useState<CloneImageItem[]>([]);
  const [cloneReplicateLevel, setCloneReplicateLevel] = useState<CloneReplicateLevelKey>("high");
  const [cloneSetCounts, setCloneSetCounts] = useState(defaultCloneSetCounts);
  const [selectedCloneDetailModules, setSelectedCloneDetailModules] = useState<string[]>(defaultCloneDetailModuleIds);
  const [cloneModelPanelTab, setCloneModelPanelTab] = useState<CloneModelPanelTab>("scene");
  const [selectedCloneModelScenes, setSelectedCloneModelScenes] = useState<string[]>([]);
  const [cloneModelCustomScene, setCloneModelCustomScene] = useState("");
  const [cloneModelGender, setCloneModelGender] = useState(tryOnModelOptions.gender[0]);
  const [cloneModelAge, setCloneModelAge] = useState(tryOnModelOptions.age[0]);
  const [cloneModelEthnicity, setCloneModelEthnicity] = useState(tryOnModelOptions.ethnicity[0]);
  const [cloneModelBody, setCloneModelBody] = useState(tryOnModelOptions.body[0]);
  const [cloneModelAppearance, setCloneModelAppearance] = useState("");
  const [cloneVideoQuality, setCloneVideoQuality] = useState<CloneVideoQualityKey>("high");
  const [cloneVideoDuration, setCloneVideoDuration] = useState(10);
  const [cloneVideoSmart, setCloneVideoSmart] = useState(true);
  const [videoOutfitVideoFile, setVideoOutfitVideoFile] = useState<File | null>(null);
  const [videoOutfitRefFile, setVideoOutfitRefFile] = useState<File | null>(null);
  const videoOutfitPreviewUrl = useMemo(() => (videoOutfitVideoFile ? URL.createObjectURL(videoOutfitVideoFile) : ""), [videoOutfitVideoFile]);
  const [isCloneSettingsCollapsed, setIsCloneSettingsCollapsed] = useState(false);
  const [previewZoom, setPreviewZoom] = useState(1);
  const quickSetSelectTimerRef = useRef<number | null>(null);
  const openQuickSetSelectRef = useRef<CloneBasicSelectKey | null>(null);
  const visibleQuickSetSelectRef = useRef<CloneBasicSelectKey | null>(null);
  const [previewOffset, setPreviewOffset] = useState({ x: 0, y: 0 });
  const previewSurfaceRef = useRef<HTMLElement | null>(null);
  const previewZoomRef = useRef(previewZoom);
  const previewOffsetRef = useRef(previewOffset);
  const imageWorkbenchMaskPaintingRef = useRef(false);
  const imageWorkbenchActiveStrokeIdRef = useRef<string | null>(null);
  const imageWorkbenchMaskCanvasRef = useRef<HTMLCanvasElement>(null);
  const imageWorkbenchLastMaskPointRef = useRef<{ x: number; y: number } | null>(null);
  const previewPanRef = useRef<{ active: boolean; startX: number; startY: number; offsetX: number; offsetY: number }>({
    active: false,
    startX: 0,
    startY: 0,
    offsetX: 0,
    offsetY: 0,
  });
  const [isCommandComposerCompact, setIsCommandComposerCompact] = useState(false);
  const typewriterText = useTypewriter("万物皆可AI，广告素材一键生成");

  useEffect(() => {
    return () => {
      if (videoOutfitPreviewUrl) URL.revokeObjectURL(videoOutfitPreviewUrl);
    };
  }, [videoOutfitPreviewUrl]);

  useEffect(() => {
    previewZoomRef.current = previewZoom;
  }, [previewZoom]);

  useEffect(() => {
    previewOffsetRef.current = previewOffset;
  }, [previewOffset]);

  useEffect(() => {
    if (typeof window === "undefined") return undefined;
    const syncHistoryPanel = () => {
      setIsCommandHistoryCollapsed(window.innerWidth <= 1180);
    };
    syncHistoryPanel();
    window.addEventListener("resize", syncHistoryPanel);
    return () => window.removeEventListener("resize", syncHistoryPanel);
  }, []);

  const previewTransformStyle = useMemo<CSSProperties>(
    () => ({
      transform: `translate3d(${previewOffset.x}px, ${previewOffset.y}px, 0) scale(${previewZoom})`,
    }),
    [previewOffset.x, previewOffset.y, previewZoom],
  );

  useEffect(() => {
    const container = previewSurfaceRef.current;
    if (!container) return undefined;

    const handleWheel = (event: WheelEvent) => {
      const target = event.target as HTMLElement | null;
      if (target?.closest(".ecom-command-composer-wrap, .clone-ai-preview-header")) return;
      event.preventDefault();
      const currentZoom = previewZoomRef.current;
      const rect = container.getBoundingClientRect();
      const cursorX = event.clientX - rect.left;
      const cursorY = event.clientY - rect.top;
      const zoomDelta = event.deltaY < 0 ? 1.08 : 0.92;
      const nextZoom = Math.min(2, Math.max(0.25, currentZoom * zoomDelta));
      if (nextZoom === currentZoom) return;

      const currentOffset = previewOffsetRef.current;
      const contentX = (cursorX - currentOffset.x) / currentZoom;
      const contentY = (cursorY - currentOffset.y) / currentZoom;
      const nextOffset = {
        x: cursorX - contentX * nextZoom,
        y: cursorY - contentY * nextZoom,
      };

      previewZoomRef.current = nextZoom;
      previewOffsetRef.current = nextOffset;
      setPreviewZoom(nextZoom);
      setPreviewOffset(nextOffset);
    };

    container.addEventListener("wheel", handleWheel, { passive: false, capture: true });
    return () => container.removeEventListener("wheel", handleWheel, { capture: true });
  }, [activeTool, cloneOutput]);

  useEffect(() => {
    const container = previewSurfaceRef.current;
    if (!container) return undefined;
    const listenerOptions = { capture: true };

    const handleMouseDown = (event: MouseEvent) => {
      if (event.button !== 1) return;
      const target = event.target as HTMLElement | null;
      if (target?.closest(".ecom-command-composer-wrap, .clone-ai-preview-header, input, textarea, select, a")) return;
      event.preventDefault();
      const currentOffset = previewOffsetRef.current;
      previewPanRef.current = {
        active: true,
        startX: event.clientX,
        startY: event.clientY,
        offsetX: currentOffset.x,
        offsetY: currentOffset.y,
      };
      container.classList.add("is-middle-panning");
    };

    const handleMouseMove = (event: MouseEvent) => {
      const pan = previewPanRef.current;
      if (!pan.active) return;
      event.preventDefault();
      const nextOffset = {
        x: pan.offsetX + event.clientX - pan.startX,
        y: pan.offsetY + event.clientY - pan.startY,
      };
      previewOffsetRef.current = nextOffset;
      setPreviewOffset(nextOffset);
    };

    const stopMousePan = () => {
      const pan = previewPanRef.current;
      if (!pan.active) return;
      previewPanRef.current = { ...pan, active: false };
      container.classList.remove("is-middle-panning");
    };

    const preventAuxClick = (event: MouseEvent) => {
      if (event.button === 1) event.preventDefault();
    };

    container.addEventListener("mousedown", handleMouseDown, listenerOptions);
    container.addEventListener("auxclick", preventAuxClick, listenerOptions);
    window.addEventListener("mousemove", handleMouseMove);
    window.addEventListener("mouseup", stopMousePan);
    window.addEventListener("blur", stopMousePan);

    return () => {
      container.removeEventListener("mousedown", handleMouseDown, listenerOptions);
      container.removeEventListener("auxclick", preventAuxClick, listenerOptions);
      window.removeEventListener("mousemove", handleMouseMove);
      window.removeEventListener("mouseup", stopMousePan);
      window.removeEventListener("blur", stopMousePan);
    };
  }, [activeTool, cloneOutput]);

  const bindPreviewSurface = (node: HTMLElement | null) => {
    previewSurfaceRef.current = node;
  };

  const getPreviewSurfaceProps = () => ({
    ref: bindPreviewSurface,
    onMouseDown: (event: ReactMouseEvent<HTMLElement>) => {
      if (event.button === 1) event.preventDefault();
    },
    onAuxClick: (event: ReactMouseEvent<HTMLElement>) => {
      if (event.button === 1) event.preventDefault();
    },
  });

  const handlePreviewWheel = (event: React.WheelEvent<HTMLElement>) => {
    if (!event.currentTarget) return;
    const container = event.currentTarget as HTMLElement;
    const rect = container.getBoundingClientRect();
    const cursorX = event.clientX - rect.left;
    const cursorY = event.clientY - rect.top;
    const zoomDelta = event.deltaY < 0 ? 1.08 : 0.92;

    const nextZoom = Math.min(2, Math.max(0.25, previewZoom * zoomDelta));
    if (nextZoom === previewZoom) return;

    const contentX = (cursorX + container.scrollLeft) / previewZoom;
    const contentY = (cursorY + container.scrollTop) / previewZoom;

    setPreviewZoom(nextZoom);

    requestAnimationFrame(() => {
      container.scrollLeft = contentX * nextZoom - cursorX;
      container.scrollTop = contentY * nextZoom - cursorY;
    });
  };

  const handleQuickPreviewWheel = (event: React.WheelEvent<HTMLDivElement>) => {
    event.preventDefault();
    event.stopPropagation();
    const zoomDelta = event.deltaY < 0 ? 1.08 : 0.92;
    setPreviewZoom((value) => Math.min(2, Math.max(0.25, value * zoomDelta)));
  };

  const handleQuickPanelWheel = (event: React.WheelEvent<HTMLElement>) => {
    const panel = event.currentTarget;
    if (panel.scrollHeight <= panel.clientHeight) return;
    event.stopPropagation();
    panel.scrollTop += event.deltaY;
  };

  const [requirement, setRequirement] = useState("");
  const [requirementImageMentionQuery, setRequirementImageMentionQuery] = useState<string | null>(null);
  const [cloneSettingName, setCloneSettingName] = useState("鏂板缓鍒涗綔");
  const [platform, setPlatform] = useState(defaultEcommercePlatform);
  const [market, setMarket] = useState(marketOptions[0]);
  const [language, setLanguage] = useState(getPlatformDefaultLanguage(defaultEcommercePlatform, marketOptions[0]));
  const [ratio, setRatio] = useState(getPlatformDefaultRatio(defaultEcommercePlatform, defaultCloneOutput));
  const [status, setStatus] = useState<ProductCloneStatus>("idle");
  const [results, setResults] = useState<CloneResult[]>([]);
  const [ecommerceHistoryRecords, setEcommerceHistoryRecords] = useState<EcommerceHistoryRecord[]>(() => readEcommerceHistoryRecords());
  const [activeHistoryRecordId, setActiveHistoryRecordId] = useState<string | null>(null);
  const [historyRefreshTick, setHistoryRefreshTick] = useState(0);
  const [isHistoryRefreshing, setIsHistoryRefreshing] = useState(false);
  const [historyRefreshMessage, setHistoryRefreshMessage] = useState("");
  const [historyRefreshStamp, setHistoryRefreshStamp] = useState(0);
  const historyRefreshLockRef = useRef(false);
  const lastSavedHistorySignatureRef = useRef("");
  const imageAbortRef = useRef({ current: false });
  const activeEcommerceTaskIdsRef = useRef<Set<string>>(new Set());
  const lastFailedActionRef = useRef<(() => void) | null>(null);
  const [garmentImages, setGarmentImages] = useState<CloneImageItem[]>([]);
  const [modelSource, setModelSource] = useState<TryOnModelSource>("ai");
  const [modelGender, setModelGender] = useState(tryOnModelOptions.gender[0]);
  const [modelAge, setModelAge] = useState(tryOnModelOptions.age[0]);
  const [modelEthnicity, setModelEthnicity] = useState(tryOnModelOptions.ethnicity[0]);
  const [modelBody, setModelBody] = useState(tryOnModelOptions.body[0]);
  const [appearance, setAppearance] = useState("");
  const [selectedScenes, setSelectedScenes] = useState<string[]>([]);

  useEffect(() => {
    if (status === "done") {
      setIsCommandComposerCompact(true);
    } else if (status === "generating" || status === "idle") {
      setIsCommandComposerCompact(false);
    }
  }, [status]);

  useEffect(() => {
    writeEcommerceHistoryRecords(ecommerceHistoryRecords);
  }, [ecommerceHistoryRecords]);
  const [customScene, setCustomScene] = useState("");
  const [smartScene, setSmartScene] = useState(false);
  const [tryOnRatio, setTryOnRatio] = useState(tryOnRatioOptions[0]);
  const [tryOnStatus, setTryOnStatus] = useState<TryOnStatus>("idle");
  const [tryOnResultImages, setTryOnResultImages] = useState<string[]>([]);
  const [detailProductImages, setDetailProductImages] = useState<CloneImageItem[]>([]);
  const [detailPlatform, setDetailPlatform] = useState(platformOptions[0]);
  const [detailMarket, setDetailMarket] = useState(marketOptions[0]);
  const [detailLanguage, setDetailLanguage] = useState(getPlatformDefaultLanguage(platformOptions[0], marketOptions[0]));
  const [detailRatio, setDetailRatio] = useState(getQuickSetRatioValue(getPlatformDefaultRatio(platformOptions[0], "detail")));
  const [detailType, setDetailType] = useState(detailTypeOptions[0]);
  const [detailRequirement, setDetailRequirement] = useState("");
  const [selectedDetailModules, setSelectedDetailModules] = useState<string[]>(defaultDetailModuleIds);
  const [detailStatus, setDetailStatus] = useState<DetailStatus>("idle");
  const [detailResultUrl, setDetailResultUrl] = useState<string | null>(null);
  const productSetRatioOptions = useMemo(
    () => getPlatformRatioOptions(productSetPlatform, productSetOutput),
    [productSetOutput, productSetPlatform],
  );
  const hotUploadedRatioOption = useMemo(
    () => cloneOutput === "hot" ? formatUploadedImageRatio(cloneReferenceImages[0]) : null,
    [cloneOutput, cloneReferenceImages],
  );
  const baseCloneRatioOptions = useMemo(
    () => getPlatformRatioOptions(platform, cloneOutput),
    [cloneOutput, platform],
  );
  const cloneRatioOptions = useMemo(
    () => hotUploadedRatioOption
      ? getUniqueRatioOptions([...baseCloneRatioOptions, hotUploadedRatioOption])
      : baseCloneRatioOptions,
    [baseCloneRatioOptions, hotUploadedRatioOption],
  );
  const productSetLanguageOptions = useMemo(
    () => getPlatformLanguageOptions(productSetPlatform, productSetMarket),
    [productSetMarket, productSetPlatform],
  );
  const cloneLanguageOptions = useMemo(
    () => getPlatformLanguageOptions(platform, market),
    [market, platform],
  );
  const detailLanguageOptions = useMemo(
    () => getPlatformLanguageOptions(detailPlatform, detailMarket),
    [detailMarket, detailPlatform],
  );
  const ecommerceMentionImages: MentionImageOption[] = [
    ...productImages.map((image, index) => ({ ...image, label: `商品图 ${index + 1}` })),
    ...cloneReferenceImages.map((image, index) => ({ ...image, label: `参考图 ${index + 1}` })),
  ];

  const ecommerceVideoImageDataUrls = useMemo(
    () => productImages.map((img) => img.src),
    [productImages],
  );
  const ecommerceVideoImageFiles = useMemo(
    () => productImages.map((img) => img.file),
    [productImages],
  );

  const selectedProductSetOutput =
    productSetOutputOptions.find((option) => option.key === productSetOutput) ?? productSetOutputOptions[0]!;
  const selectedCloneOutput = cloneOutputOptions.find((option) => option.key === cloneOutput) ?? cloneOutputOptions[1]!;
  const cloneRequirementPlaceholder =
    cloneOutput === "model"
      ? "建议包含以下信息：产品名称、核心卖点、期望场景、模特外貌描述（如小麦色皮肤、齐刘海、眼角有泪痣）、具体参数"
      : "建议包含以下信息：产品名称、核心卖点、期望场景、具体参数";
  const productSetPreviewReady = productSetStatus === "done";
  const cloneSetTotal = useMemo(
    () => Object.values(cloneSetCounts).reduce((sum, value) => sum + value, 0),
    [cloneSetCounts],
  );
  const canGenerateSet = setImages.length > 0 && productSetStatus !== "generating";
  const canGenerate = (cloneOutput === "video-outfit"
    ? Boolean(videoOutfitVideoFile && videoOutfitRefFile)
    : productImages.length > 0) && status !== "generating";
  const canGenerateTryOn = garmentImages.length > 0 && tryOnStatus !== "generating" && tryOnStatus !== "modeling";
  const canGenerateDetail = detailProductImages.length > 0 && detailStatus !== "generating";
  const cloneVideoDurationProgress =
    ((cloneVideoDuration - cloneVideoDurationMin) / (cloneVideoDurationMax - cloneVideoDurationMin)) * 100;
  const cloneVideoDurationStyle: CSSProperties = useMemo(
    () => ({
      "--clone-video-duration-progress": `${cloneVideoDurationProgress}%`,
    }) as CSSProperties,
    [cloneVideoDurationProgress],
  );

  const trackEcommerceTask = (taskId: string) => {
    activeEcommerceTaskIdsRef.current.add(taskId);
  };

  const untrackEcommerceTask = (taskId: string) => {
    activeEcommerceTaskIdsRef.current.delete(taskId);
  };

  const handleCancelGenerate = () => {
    imageAbortRef.current.current = true;
    const taskIds = Array.from(activeEcommerceTaskIdsRef.current);
    activeEcommerceTaskIdsRef.current.clear();
    taskIds.forEach((taskId) => {
      aiGenerationClient.cancelTask(taskId).catch(() => {});
    });
    lastFailedActionRef.current = null;
    if (productSetStatus === "generating") setProductSetStatus("idle");
    if (status === "generating") setStatus("idle");
    if (detailStatus === "generating") setDetailStatus("idle");
    if (tryOnStatus === "generating") setTryOnStatus("idle");
    if (tryOnStatus === "modeling") setTryOnStatus("ready");
    toast.info("\u5df2\u53d6\u6d88\u751f\u6210");
  };

  const syncRequirementMentionQuery = (value: string, selectionStart: number | null | undefined) => {
    setRequirementImageMentionQuery(ecommerceMentionImages.length ? getImageMentionQuery(value, selectionStart) : null);
  };

  const insertRequirementImageMention = (image: MentionImageOption) => {
    const textarea = requirementTextareaRef.current;
    const cursor = textarea?.selectionStart ?? requirement.length;
    const next = insertImageMentionValue(requirement, cursor, image.name, 500);
    setRequirement(next.value);
    setRequirementImageMentionQuery(null);
    window.requestAnimationFrame(() => {
      requirementTextareaRef.current?.focus();
      requirementTextareaRef.current?.setSelectionRange(next.selectionStart, next.selectionStart);
    });
  };

  const addSetImages = async (files: File[]) => {
    if (setImages.length >= 3) return;
    const imageFiles = notifyRejectedImages(files);
    if (!imageFiles.length) return;
    try {
      const nextImages = await createUploadedImageItems(imageFiles, 3 - setImages.length, "set");
      setSetImages((current) => {
        if (current.length >= 3) return current;
        return nextImages.length ? [...current, ...nextImages].slice(0, 3) : current;
      });
      setProductSetStatus("ready");
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "鍟嗗搧鍥句笂浼犲け璐?");
    }
  };

  const handleSetUpload = (event: ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (!files?.length) return;
    void addSetImages(Array.from(files));
    event.target.value = "";
  };

  const handleSetDrop = (event: DragEvent<HTMLButtonElement>) => {
    event.preventDefault();
    setIsSetUploadDragging(false);
    const files = Array.from(event.dataTransfer.files);
    if (files.length) void addSetImages(files);
  };

  const revokeSmartCutoutItem = (item: SmartCutoutImageItem | null) => {
    if (!item) return;
    URL.revokeObjectURL(item.src);
    if (item.originalSrc && item.originalSrc !== item.src) URL.revokeObjectURL(item.originalSrc);
  };

  const revokeSmartCutoutItems = (items: SmartCutoutImageItem[]) => {
    items.forEach(revokeSmartCutoutItem);
  };

  const clearSmartCutoutTransition = () => {
    if (smartCutoutTransitionTimeoutRef.current !== null) {
      window.clearTimeout(smartCutoutTransitionTimeoutRef.current);
      smartCutoutTransitionTimeoutRef.current = null;
    }
    if (smartCutoutPendingUrlsRef.current.length) {
      smartCutoutPendingUrlsRef.current.forEach((url) => URL.revokeObjectURL(url));
      smartCutoutPendingUrlsRef.current = [];
    }
    setIsSmartCutoutTransitioning(false);
  };

  const runSmartCutoutPageTransition = (message: { title: string; subtitle: string }, action: () => void, delay = 460) => {
    clearSmartCutoutTransition();
    setSmartCutoutTransitionMessage(message);
    setIsSmartCutoutTransitioning(true);
    smartCutoutTransitionTimeoutRef.current = window.setTimeout(() => {
      smartCutoutTransitionTimeoutRef.current = null;
      action();
      setIsSmartCutoutTransitioning(false);
    }, delay);
  };

  const openSmartCutoutUpload = () => {
    clearSmartCutoutTransition();
    setSmartCutoutTransitionMessage({
      title: "正在进入智能抠图",
      subtitle: "为你打开图片处理工具",
    });
    setActiveQuickTool("cutout");
    setSmartCutoutBatchImages((current) => {
      revokeSmartCutoutItems(current);
      return [];
    });
    setSmartCutoutImage((current) => {
      revokeSmartCutoutItem(current);
      return null;
    });
    setIsSmartCutoutComparing(false);
    setComposerMenu(null);
  };

  const openWatermarkRemovalPage = () => {
    clearSmartCutoutTransition();
    setActiveQuickTool("watermark");
    setComposerMenu(null);
    setIsCloneSettingsCollapsed(false);
  };

  const closeWatermarkRemovalPage = () => {
    if (watermarkProcessTimeoutRef.current !== null) {
      window.clearTimeout(watermarkProcessTimeoutRef.current);
      watermarkProcessTimeoutRef.current = null;
    }
    setActiveQuickTool(null);
    setWatermarkStatus("idle");
    setWatermarkImage((current) => {
      if (current?.src) URL.revokeObjectURL(current.src);
      return null;
    });
  };

  const addWatermarkImage = (file: File) => {
    const nextImage = {
      src: URL.createObjectURL(file),
      name: file.name,
      format: getImageFileFormat(file) || "PNG / JPG / WebP",
    };
    setWatermarkImage((current) => {
      if (current?.src) URL.revokeObjectURL(current.src);
      return nextImage;
    });
    setWatermarkStatus("idle");
    setActiveQuickTool("watermark");
  };

  const removeWatermarkImage = () => {
    if (watermarkProcessTimeoutRef.current !== null) {
      window.clearTimeout(watermarkProcessTimeoutRef.current);
      watermarkProcessTimeoutRef.current = null;
    }
    setWatermarkStatus("idle");
    setWatermarkImage((current) => {
      if (current?.src) URL.revokeObjectURL(current.src);
      return null;
    });
  };

  const handleWatermarkUpload = (event: ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;
    addWatermarkImage(file);
    event.target.value = "";
  };

  const handleWatermarkDrop = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    setIsWatermarkDragging(false);
    const file = Array.from(event.dataTransfer.files).find((item) => item.type.startsWith("image/"));
    if (file) addWatermarkImage(file);
  };

  const handleWatermarkGenerate = () => {
    if (!watermarkImage || watermarkStatus === "processing") return;
    if (watermarkProcessTimeoutRef.current !== null) window.clearTimeout(watermarkProcessTimeoutRef.current);
    setWatermarkStatus("processing");
    watermarkProcessTimeoutRef.current = window.setTimeout(() => {
      watermarkProcessTimeoutRef.current = null;
      setWatermarkStatus("done");
      toast.success("去水印处理完成");
    }, 900);
  };

  const handleWatermarkDownload = () => {
    if (!watermarkImage || watermarkStatus !== "done") {
      toast.info("请先完成去水印");
      return;
    }
    const link = document.createElement("a");
    const safeName = (watermarkImage.name || "watermark-result").replace(/\.[^.]+$/, "").replace(/[\\/:*?"<>|]+/g, "-");
    link.href = watermarkImage.src;
    link.download = `${safeName || "watermark-result"}-去水印.png`;
    document.body.appendChild(link);
    link.click();
    link.remove();
  };

  const openImageWorkbenchPage = () => {
    clearSmartCutoutTransition();
    setActiveQuickTool("image-edit");
    setComposerMenu(null);
    setIsCloneSettingsCollapsed(false);
    setImageWorkbenchStatus("idle");
  };

  const closeImageWorkbenchPage = () => {
    setActiveQuickTool(null);
    setImageWorkbenchStatus("idle");
    setImageWorkbenchPrompt("");
    setImageWorkbenchMaskStrokes([]);
    setImageWorkbenchBrushCursor(null);
    clearImageWorkbenchMaskCanvas();
    imageWorkbenchMaskPaintingRef.current = false;
    imageWorkbenchActiveStrokeIdRef.current = null;
    setImageWorkbenchImage((current) => {
      if (current?.src) URL.revokeObjectURL(current.src);
      return null;
    });
  };

  const addImageWorkbenchImage = (file: File) => {
    if (!file.type.startsWith("image/")) {
      toast.error("请上传图片文件");
      return;
    }
    const nextImage = {
      src: URL.createObjectURL(file),
      name: file.name,
      format: getImageFileFormat(file) || "PNG / JPG / WebP",
    };
    setImageWorkbenchImage((current) => {
      if (current?.src) URL.revokeObjectURL(current.src);
      return nextImage;
    });
    setImageWorkbenchStatus("idle");
    setImageWorkbenchMaskStrokes([]);
    setImageWorkbenchBrushCursor(null);
    clearImageWorkbenchMaskCanvas();
    imageWorkbenchActiveStrokeIdRef.current = null;
    setActiveQuickTool("image-edit");
  };

  const removeImageWorkbenchImage = () => {
    setImageWorkbenchStatus("idle");
    setImageWorkbenchMaskStrokes([]);
    setImageWorkbenchBrushCursor(null);
    clearImageWorkbenchMaskCanvas();
    imageWorkbenchMaskPaintingRef.current = false;
    imageWorkbenchActiveStrokeIdRef.current = null;
    setImageWorkbenchImage((current) => {
      if (current?.src) URL.revokeObjectURL(current.src);
      return null;
    });
  };

  const handleImageWorkbenchUpload = (event: ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;
    addImageWorkbenchImage(file);
    event.target.value = "";
  };

  const handleImageWorkbenchDrop = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    setIsImageWorkbenchDragging(false);
    const file = Array.from(event.dataTransfer.files).find((item) => item.type.startsWith("image/"));
    if (file) addImageWorkbenchImage(file);
  };

  const handleImageWorkbenchGenerate = () => {
    if (!imageWorkbenchImage) {
      toast.info("请先上传图片");
      return;
    }
    setImageWorkbenchStatus("processing");
    window.setTimeout(() => {
      setImageWorkbenchStatus("done");
      toast.success("局部重绘已完成");
    }, 900);
  };

  const syncImageWorkbenchMaskCanvas = () => {
    const canvas = imageWorkbenchMaskCanvasRef.current;
    if (!canvas) return null;
    const rect = canvas.getBoundingClientRect();
    const dpr = window.devicePixelRatio || 1;
    const width = Math.max(1, Math.round(rect.width * dpr));
    const height = Math.max(1, Math.round(rect.height * dpr));
    if (canvas.width !== width || canvas.height !== height) {
      canvas.width = width;
      canvas.height = height;
    }
    const context = canvas.getContext("2d");
    if (!context) return null;
    context.setTransform(dpr, 0, 0, dpr, 0, 0);
    context.lineCap = "round";
    context.lineJoin = "round";
    context.strokeStyle = "rgba(30, 189, 219, 0.52)";
    context.fillStyle = "rgba(30, 189, 219, 0.52)";
    context.lineWidth = imageWorkbenchBrushSize;
    return { canvas, context, rect };
  };

  const clearImageWorkbenchMaskCanvas = () => {
    const canvas = imageWorkbenchMaskCanvasRef.current;
    if (!canvas) return;
    const context = canvas.getContext("2d");
    if (!context) return;
    context.setTransform(1, 0, 0, 1, 0, 0);
    context.clearRect(0, 0, canvas.width, canvas.height);
  };

  const getImageWorkbenchMaskPoint = (event: ReactPointerEvent<HTMLElement>) => {
    const canvas = imageWorkbenchMaskCanvasRef.current;
    const rect = (canvas ?? event.currentTarget).getBoundingClientRect();
    const x = clampNumber(event.clientX - rect.left, 0, rect.width);
    const y = clampNumber(event.clientY - rect.top, 0, rect.height);
    return { x, y };
  };

  const appendImageWorkbenchMaskPoint = (event: ReactPointerEvent<HTMLElement>) => {
    const point = getImageWorkbenchMaskPoint(event);
    const rect = imageWorkbenchMaskCanvasRef.current?.getBoundingClientRect();
    setImageWorkbenchBrushCursor(rect ? { x: (point.x / Math.max(rect.width, 1)) * 100, y: (point.y / Math.max(rect.height, 1)) * 100 } : null);
    setImageWorkbenchStatus((current) => (current === "done" ? "idle" : current));
    const activeStrokeId = imageWorkbenchActiveStrokeIdRef.current;
    if (!activeStrokeId) return;
    const lastPoint = imageWorkbenchLastMaskPointRef.current;
    if (lastPoint && Math.hypot(point.x - lastPoint.x, point.y - lastPoint.y) < 1.5) return;
    const synced = syncImageWorkbenchMaskCanvas();
    if (!synced) return;
    synced.context.beginPath();
    synced.context.moveTo(lastPoint?.x ?? point.x, lastPoint?.y ?? point.y);
    synced.context.lineTo(point.x, point.y);
    synced.context.stroke();
    imageWorkbenchLastMaskPointRef.current = point;
  };

  const handleImageWorkbenchMaskPointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {
    if (!imageWorkbenchImage || event.button !== 0) return;
    event.preventDefault();
    event.currentTarget.setPointerCapture(event.pointerId);
    syncImageWorkbenchMaskCanvas();
    const point = getImageWorkbenchMaskPoint(event);
    const rect = imageWorkbenchMaskCanvasRef.current?.getBoundingClientRect();
    const strokeId = `mask-${Date.now()}`;
    imageWorkbenchMaskPaintingRef.current = true;
    imageWorkbenchActiveStrokeIdRef.current = strokeId;
    imageWorkbenchLastMaskPointRef.current = point;
    setImageWorkbenchBrushCursor(rect ? { x: (point.x / Math.max(rect.width, 1)) * 100, y: (point.y / Math.max(rect.height, 1)) * 100 } : null);
    setImageWorkbenchStatus((current) => (current === "done" ? "idle" : current));
    setImageWorkbenchMaskStrokes((current) => [...current, { id: strokeId, size: imageWorkbenchBrushSize, points: [] }]);
    const synced = syncImageWorkbenchMaskCanvas();
    if (synced) {
      synced.context.beginPath();
      synced.context.arc(point.x, point.y, imageWorkbenchBrushSize / 2, 0, Math.PI * 2);
      synced.context.fill();
    }
  };

  const handleImageWorkbenchMaskPointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {
    if (!imageWorkbenchImage) return;
    const point = getImageWorkbenchMaskPoint(event);
    const rect = imageWorkbenchMaskCanvasRef.current?.getBoundingClientRect();
    setImageWorkbenchBrushCursor(rect ? { x: (point.x / Math.max(rect.width, 1)) * 100, y: (point.y / Math.max(rect.height, 1)) * 100 } : null);
    if (!imageWorkbenchMaskPaintingRef.current) return;
    appendImageWorkbenchMaskPoint(event);
  };

  const stopImageWorkbenchMaskPainting = () => {
    imageWorkbenchMaskPaintingRef.current = false;
    imageWorkbenchActiveStrokeIdRef.current = null;
    imageWorkbenchLastMaskPointRef.current = null;
  };

  const clearQuickSetSelectTimer = () => {
    if (quickSetSelectTimerRef.current) {
      window.clearTimeout(quickSetSelectTimerRef.current);
      quickSetSelectTimerRef.current = null;
    }
  };

  const resetQuickSetSelectState = () => {
    clearQuickSetSelectTimer();
    openQuickSetSelectRef.current = null;
    visibleQuickSetSelectRef.current = null;
    setOpenQuickSetSelect(null);
    setVisibleQuickSetSelect(null);
    setIsQuickSetSelectClosing(false);
  };

  const closeQuickSetSelect = () => {
    if (!visibleQuickSetSelectRef.current) {
      openQuickSetSelectRef.current = null;
      setOpenQuickSetSelect(null);
      return;
    }
    clearQuickSetSelectTimer();
    openQuickSetSelectRef.current = null;
    setOpenQuickSetSelect(null);
    setIsQuickSetSelectClosing(true);
    quickSetSelectTimerRef.current = window.setTimeout(() => {
      visibleQuickSetSelectRef.current = null;
      setVisibleQuickSetSelect(null);
      setIsQuickSetSelectClosing(false);
      quickSetSelectTimerRef.current = null;
    }, 420);
  };

  const showQuickSetSelect = (key: CloneBasicSelectKey) => {
    clearQuickSetSelectTimer();
    if (visibleQuickSetSelectRef.current && visibleQuickSetSelectRef.current !== key && openQuickSetSelectRef.current) {
      openQuickSetSelectRef.current = null;
      setOpenQuickSetSelect(null);
      setIsQuickSetSelectClosing(true);
      quickSetSelectTimerRef.current = window.setTimeout(() => {
        visibleQuickSetSelectRef.current = key;
        openQuickSetSelectRef.current = key;
        setVisibleQuickSetSelect(key);
        setOpenQuickSetSelect(key);
        setIsQuickSetSelectClosing(false);
        quickSetSelectTimerRef.current = null;
      }, 180);
      return;
    }
    visibleQuickSetSelectRef.current = key;
    openQuickSetSelectRef.current = key;
    setVisibleQuickSetSelect(key);
    setOpenQuickSetSelect(key);
    setIsQuickSetSelectClosing(false);
  };

  const toggleQuickSetSelect = (key: CloneBasicSelectKey) => {
    if (openQuickSetSelectRef.current === key) {
      closeQuickSetSelect();
      return;
    }
    showQuickSetSelect(key);
  };

  const openQuickProductSetPage = () => {
    clearSmartCutoutTransition();
    setActiveQuickTool("set");
    setComposerMenu(null);
    setIsCloneSettingsCollapsed(false);
    setIsQuickPanelCollapsed(false);
    setPreviewZoom(1);
    resetQuickSetSelectState();
  };

  const openQuickDetailPage = () => {
    clearSmartCutoutTransition();
    setActiveQuickTool("detail");
    setComposerMenu(null);
    setIsCloneSettingsCollapsed(false);
    setIsQuickPanelCollapsed(false);
    setPreviewZoom(1);
    resetQuickSetSelectState();
    if (!selectedDetailModules.length) setSelectedDetailModules(defaultCloneDetailModuleIds);
  };

  const openHotVideoPage = () => {
    clearSmartCutoutTransition();
    setActiveQuickTool("hot-video");
    setComposerMenu(null);
    setIsCloneSettingsCollapsed(true);
    setIsCommandHistoryCollapsed(true);
  };

  const closeHotVideoPage = () => {
    setActiveQuickTool(null);
    setComposerMenu(null);
    setIsCloneSettingsCollapsed(false);
    setIsCommandHistoryCollapsed(false);
  };

  const closeSmartCutoutTool = () => {
    runSmartCutoutPageTransition(
      {
        title: "正在返回首页",
        subtitle: "回到电商智能体",
      },
      () => {
        setSmartCutoutBatchImages((current) => {
          revokeSmartCutoutItems(current);
          return [];
        });
        setSmartCutoutImage((current) => {
          revokeSmartCutoutItem(current);
          return null;
        });
        setIsSmartCutoutComparing(false);
        setActiveQuickTool(null);
        setComposerMenu(null);
      },
    );
  };

  const goSmartCutoutPrevious = () => {
    if (!smartCutoutImage) {
      closeSmartCutoutTool();
      return;
    }
    runSmartCutoutPageTransition(
      {
        title: "正在返回上一页",
        subtitle: "回到图片上传页",
      },
      () => {
        setSmartCutoutBatchImages((current) => {
          revokeSmartCutoutItems(current);
          return [];
        });
        setSmartCutoutImage((current) => {
          revokeSmartCutoutItem(current);
          return null;
        });
        setIsSmartCutoutComparing(false);
      },
    );
  };

  const addSmartCutoutImage = (files: File[]) => {
    const imageFiles = files.filter((file) => file.type.startsWith("image/"));
    if (!imageFiles.length) {
      toast.error("请上传图片文件");
      return;
    }
    clearSmartCutoutTransition();
    setSmartCutoutBatchImages((current) => {
      revokeSmartCutoutItems(current);
      return [];
    });
    setSmartCutoutImage((current) => {
      revokeSmartCutoutItem(current);
      return null;
    });
    setIsSmartCutoutComparing(false);
    const nextImages = imageFiles.map((file) => {
      const originalSrc = URL.createObjectURL(file);
      return { src: originalSrc, originalSrc, name: file.name };
    });
    smartCutoutPendingUrlsRef.current = nextImages.map((item) => item.src);
    setActiveQuickTool("cutout");
    setSmartCutoutSizeKey("original");
    setSmartCutoutTransitionMessage({
      title: imageFiles.length > 1 ? "正在批量抠图" : "正在智能抠图",
      subtitle: imageFiles.length > 1 ? `正在处理 ${imageFiles.length} 张图片` : "即将进入图片编辑室",
    });
    setIsSmartCutoutTransitioning(true);
    smartCutoutTransitionTimeoutRef.current = window.setTimeout(() => {
      smartCutoutTransitionTimeoutRef.current = null;
      smartCutoutPendingUrlsRef.current = [];
      setSmartCutoutBatchImages(nextImages);
      setSmartCutoutImage(nextImages[0]);
      setIsSmartCutoutTransitioning(false);
    }, 620);
  };

  const handleSmartCutoutUpload = (event: ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (!files?.length) return;
    addSmartCutoutImage(Array.from(files));
    event.target.value = "";
  };

  const handleSmartCutoutDrop = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    setIsSmartCutoutDragging(false);
    const files = Array.from(event.dataTransfer.files);
    if (files.length) addSmartCutoutImage(files);
  };

  const smartCutoutBackgroundValue = useMemo(() => {
    const rgb = hexToRgb(smartCutoutBackgroundColor) ?? { r: 255, g: 255, b: 255 };
    if (smartCutoutBackgroundAlpha >= 100) return smartCutoutBackgroundColor;
    return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${Math.round(smartCutoutBackgroundAlpha) / 100})`;
  }, [smartCutoutBackgroundAlpha, smartCutoutBackgroundColor]);

  const smartCutoutColorHsv = useMemo(() => hexToHsv(smartCutoutBackgroundColor), [smartCutoutBackgroundColor]);

  const selectedSmartCutoutSize = useMemo(
    () => smartCutoutSizeOptions.find((option) => option.key === smartCutoutSizeKey) ?? smartCutoutSizeOptions[0],
    [smartCutoutSizeKey],
  );

  const previewSmartCutoutSize = isSmartCutoutComparing ? smartCutoutSizeOptions[0] : selectedSmartCutoutSize;
  const previewSmartCutoutSizeKey = isSmartCutoutComparing ? "original" : smartCutoutSizeKey;
  const previewSmartCutoutImageSrc = isSmartCutoutComparing ? smartCutoutImage?.originalSrc ?? smartCutoutImage?.src : smartCutoutImage?.src;

  const smartCutoutFrameStyle = useMemo<CSSProperties>(
    () => ({
      "--smart-cutout-bg": smartCutoutBackgroundValue,
      "--smart-cutout-frame-width": previewSmartCutoutSize.frameWidth,
      "--smart-cutout-frame-aspect": previewSmartCutoutSize.frameAspect,
      "--smart-cutout-image-max-width": previewSmartCutoutSize.imageMaxWidth,
      "--smart-cutout-image-max-height": previewSmartCutoutSize.imageMaxHeight,
    } as CSSProperties),
    [previewSmartCutoutSize, smartCutoutBackgroundValue],
  );

  const showSmartCutoutOriginalCompare = (event: ReactPointerEvent<HTMLButtonElement>) => {
    event.currentTarget.setPointerCapture(event.pointerId);
    setIsSmartCutoutComparing(true);
  };

  const hideSmartCutoutOriginalCompare = () => {
    setIsSmartCutoutComparing(false);
  };

  const applySmartCutoutHsv = (h: number, s: number, v: number) => {
    const rgb = hsvToRgb(h, s, v);
    setSmartCutoutBackgroundColor(rgbToHex(rgb.r, rgb.g, rgb.b));
  };

  const updateSmartCutoutColorFromPoint = (element: HTMLElement, clientX: number, clientY: number) => {
    const rect = element.getBoundingClientRect();
    const saturation = clampNumber(((clientX - rect.left) / rect.width) * 100, 0, 100);
    const value = clampNumber(100 - ((clientY - rect.top) / rect.height) * 100, 0, 100);
    applySmartCutoutHsv(smartCutoutColorHsv.h, saturation, value);
  };

  const handleSmartCutoutColorPlanePointer = (event: ReactPointerEvent<HTMLButtonElement>) => {
    event.preventDefault();
    event.currentTarget.setPointerCapture(event.pointerId);
    updateSmartCutoutColorFromPoint(event.currentTarget, event.clientX, event.clientY);
  };

  const handleSmartCutoutColorPlaneMove = (event: ReactPointerEvent<HTMLButtonElement>) => {
    if (event.buttons !== 1) return;
    updateSmartCutoutColorFromPoint(event.currentTarget, event.clientX, event.clientY);
  };

  const handleSmartCutoutHexChange = (value: string) => {
    const nextValue = value.startsWith("#") ? value : `#${value}`;
    if (!/^#[0-9a-fA-F]{0,6}$/.test(nextValue)) return;
    setSmartCutoutHexDraft(nextValue);
    const normalized = normalizeHexColor(nextValue);
    if (normalized) setSmartCutoutBackgroundColor(normalized);
  };

  const scrollSmartCutoutTools = (direction: -1 | 1) => {
    smartCutoutToolsRef.current?.scrollBy({
      left: direction * 340,
      behavior: "smooth",
    });
  };

  const handleSmartCutoutDownload = async () => {
    if (!smartCutoutImage) {
      toast.error("请先上传图片");
      return;
    }
    try {
      const image = new Image();
      image.decoding = "async";
      const imageLoaded = new Promise<void>((resolve, reject) => {
        image.onload = () => resolve();
        image.onerror = () => reject(new Error("图片加载失败"));
      });
      image.src = smartCutoutImage.src;
      await imageLoaded;

      const aspect = parseSmartCutoutAspect(selectedSmartCutoutSize.frameAspect);
      const naturalWidth = Math.max(1, image.naturalWidth || image.width || 1200);
      const naturalHeight = Math.max(1, image.naturalHeight || image.height || 900);
      const outputWidth = "outputWidth" in selectedSmartCutoutSize ? selectedSmartCutoutSize.outputWidth : undefined;
      const outputHeight = "outputHeight" in selectedSmartCutoutSize ? selectedSmartCutoutSize.outputHeight : undefined;
      let canvasWidth = naturalWidth;
      let canvasHeight = naturalHeight;
      if (outputWidth && outputHeight) {
        canvasWidth = outputWidth;
        canvasHeight = outputHeight;
      } else if (aspect) {
        const longSide = 1600;
        if (aspect >= 1) {
          canvasWidth = longSide;
          canvasHeight = Math.round(longSide / aspect);
        } else {
          canvasHeight = longSide;
          canvasWidth = Math.round(longSide * aspect);
        }
      } else {
        const maxSide = 1600;
        const scale = Math.min(1, maxSide / Math.max(naturalWidth, naturalHeight));
        canvasWidth = Math.max(1, Math.round(naturalWidth * scale));
        canvasHeight = Math.max(1, Math.round(naturalHeight * scale));
      }

      const canvas = document.createElement("canvas");
      canvas.width = canvasWidth;
      canvas.height = canvasHeight;
      const context = canvas.getContext("2d");
      if (!context) throw new Error("无法生成图片");

      context.clearRect(0, 0, canvasWidth, canvasHeight);
      context.fillStyle = smartCutoutBackgroundValue;
      context.fillRect(0, 0, canvasWidth, canvasHeight);

      const maxWidthRatio = parseSmartCutoutPercent(selectedSmartCutoutSize.imageMaxWidth, 0.82);
      const maxHeightRatio = parseSmartCutoutPercent(selectedSmartCutoutSize.imageMaxHeight, 0.82);
      const fitScale = Math.min((canvasWidth * maxWidthRatio) / naturalWidth, (canvasHeight * maxHeightRatio) / naturalHeight, 1);
      const drawWidth = Math.round(naturalWidth * fitScale);
      const drawHeight = Math.round(naturalHeight * fitScale);
      const drawX = Math.round((canvasWidth - drawWidth) / 2);
      const drawY = Math.round((canvasHeight - drawHeight) / 2);
      context.drawImage(image, drawX, drawY, drawWidth, drawHeight);

      const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, "image/png"));
      if (!blob) throw new Error("图片导出失败");

      const objectUrl = URL.createObjectURL(blob);
      const link = document.createElement("a");
      const safeName = (smartCutoutImage.name || "smart-cutout").replace(/\.[^.]+$/, "").replace(/[\\/:*?"<>|]+/g, "-");
      link.href = objectUrl;
      link.download = `${safeName || "smart-cutout"}-${selectedSmartCutoutSize.label}.png`;
      document.body.appendChild(link);
      link.click();
      link.remove();
      window.setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
      toast.success("已下载图片");
    } catch (error) {
      toast.error(error instanceof Error ? error.message : "下载图片失败");
    }
  };

  useEffect(() => {
    setSmartCutoutHexDraft(smartCutoutBackgroundColor);
  }, [smartCutoutBackgroundColor]);

  useEffect(() => {
    if (!isSmartCutoutPaletteOpen) return undefined;
    const handlePointerDown = (event: PointerEvent) => {
      if (!smartCutoutPaletteRef.current?.contains(event.target as Node)) {
        setIsSmartCutoutPaletteOpen(false);
      }
    };
    document.addEventListener("pointerdown", handlePointerDown);
    return () => document.removeEventListener("pointerdown", handlePointerDown);
  }, [isSmartCutoutPaletteOpen]);

  const removeSetImage = (imageId: string) => {
    setSetImages((current) => {
      const next = current.filter((item) => item.id !== imageId);
      if (next.length === 0) setProductSetStatus("idle");
      return next;
    });
  };

  const addProductImages = async (files: File[]) => {
    const imageFiles = notifyRejectedImages(files);
    if (!imageFiles.length) return;
    try {
      const nextImages = await createUploadedImageItems(imageFiles, maxCloneProductImages - productImages.length, "product");
      setProductImages((current) => {
        if (current.length >= maxCloneProductImages) return current;
        return nextImages.length ? [...current, ...nextImages].slice(0, maxCloneProductImages) : current;
      });
      setStatus("ready");
      setResults([]);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "鍟嗗搧鍥句笂浼犲け璐?");
    }
  };

  const handleProductUpload = (event: ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (!files?.length) return;
    void addProductImages(Array.from(files));
    event.target.value = "";
  };

  const addComposerAssets = (files: File[]) => {
    const imageFiles = files.filter((file) => file.type.startsWith("image/"));
    const videoFiles = files.filter((file) => file.type.startsWith("video/"));
    const unsupportedCount = files.length - imageFiles.length - videoFiles.length;
    if (imageFiles.length) void addProductImages(imageFiles);
    if (videoFiles[0]) {
      setVideoOutfitVideoFile(videoFiles[0]);
      setStatus("ready");
      setResults([]);
    }
    if (unsupportedCount > 0) toast.info("浠呮敮鎸佷笂浼犲浘鐗囨垨瑙嗛鏂囦欢");
  };

  const handleComposerAssetUpload = (event: ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (!files?.length) return;
    addComposerAssets(Array.from(files));
    event.target.value = "";
  };

  const handleProductDrop = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    setIsProductUploadDragging(false);
    const files = Array.from(event.dataTransfer.files);
    if (files.length) void addProductImages(files);
  };

  const handleQuickProductDrop = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    const files = Array.from(event.dataTransfer.files);
    if (files.length) void addProductImages(files);
  };

  const removeProductImage = (imageId: string) => {
    setProductImages((current) => {
      const next = current.filter((item) => item.id !== imageId);
      if (next.length === 0) {
        setStatus("idle");
        setResults([]);
      }
      return next;
    });
  };

  const hydrateCloneReferenceImageMeta = (items: CloneImageItem[]) => {
    items.forEach((item) => {
      readImageDimensions(item.src)
        .then(({ width, height }) => {
          setCloneReferenceImages((current) =>
            current.map((currentItem) => (currentItem.id === item.id ? { ...currentItem, width, height } : currentItem)),
          );
        })
        .catch(() => undefined);
    });
  };

  const addCloneReferenceImages = async (files: File[]) => {
    const imageFiles = notifyRejectedImages(files);
    if (!imageFiles.length) return;
    const remainingSlots = maxCloneReferenceImages - cloneReferenceImages.length;
    if (remainingSlots <= 0) return;
    try {
      const nextImages = await createUploadedImageItems(imageFiles, remainingSlots, "reference");
      if (!nextImages.length) return;
      setCloneReferenceImages((current) => {
        if (current.length >= maxCloneReferenceImages) return current;
        return nextImages.length ? [...current, ...nextImages].slice(0, maxCloneReferenceImages) : current;
      });
      hydrateCloneReferenceImageMeta(nextImages);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "鍙傝€冨浘涓婁紶澶辫触");
    }
  };

  const handleCloneReferenceUpload = (event: ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (!files?.length) return;
    void addCloneReferenceImages(Array.from(files));
    event.target.value = "";
  };

  const [isCloneReferenceDragging, setIsCloneReferenceDragging] = useState(false);

  const handleCloneReferenceDragOver = (event: DragEvent<HTMLButtonElement>) => {
    event.preventDefault();
    event.stopPropagation();
    if (event.dataTransfer.types.includes("Files")) {
      setIsCloneReferenceDragging(true);
    }
  };

  const handleCloneReferenceDragLeave = (event: DragEvent<HTMLButtonElement>) => {
    event.preventDefault();
    event.stopPropagation();
    if (event.currentTarget === event.target || !event.currentTarget.contains(event.relatedTarget as Node)) {
      setIsCloneReferenceDragging(false);
    }
  };

  const handleCloneReferenceDrop = (event: DragEvent<HTMLButtonElement>) => {
    event.preventDefault();
    event.stopPropagation();
    setIsCloneReferenceDragging(false);
    const files = Array.from(event.dataTransfer.files);
    if (files.length) addCloneReferenceImages(files);
  };

  const updateCloneSetCount = (key: CloneSetCountKey, delta: -1 | 1) => {
    setCloneSetCounts((current) => {
      const total = Object.values(current).reduce((sum, value) => sum + value, 0);
      const nextValue = current[key] + delta;
      if (delta < 0 && (current[key] <= 0 || total <= minCloneSetTotal)) return current;
      if (delta > 0 && total >= maxCloneSetTotal) return current;
      return { ...current, [key]: Math.max(0, Math.min(maxCloneSetTotal, nextValue)) };
    });
  };

  const clearCloneSetCountHold = () => {
    window.removeEventListener("pointerup", clearCloneSetCountHold);
    window.removeEventListener("pointercancel", clearCloneSetCountHold);
    if (countHoldTimeoutRef.current !== null) {
      window.clearTimeout(countHoldTimeoutRef.current);
      countHoldTimeoutRef.current = null;
    }
    if (countHoldIntervalRef.current !== null) {
      window.clearInterval(countHoldIntervalRef.current);
      countHoldIntervalRef.current = null;
    }
  };

  const startCloneSetCountHold = (key: CloneSetCountKey, delta: -1 | 1, disabled: boolean) => {
    if (disabled) return;
    clearCloneSetCountHold();
    updateCloneSetCount(key, delta);
    window.addEventListener("pointerup", clearCloneSetCountHold, { once: true });
    window.addEventListener("pointercancel", clearCloneSetCountHold, { once: true });
    countHoldTimeoutRef.current = window.setTimeout(() => {
      countHoldIntervalRef.current = window.setInterval(() => updateCloneSetCount(key, delta), 110);
    }, 320);
  };

  const toggleCloneDetailModule = (moduleId: string) => {
    setSelectedCloneDetailModules((current) => {
      if (current.includes(moduleId)) return current.filter((item) => item !== moduleId);
      if (current.length >= maxDetailModuleSelection) {
        toast.info(`最多选择 ${maxDetailModuleSelection} 个模块`);
        return current;
      }
      return [...current, moduleId];
    });
  };

  const toggleCloneModelScene = (scene: string) => {
    setSelectedCloneModelScenes((current) => (current[0] === scene ? [] : [scene]));
  };

  const handleProductSetPlatformChange = (nextPlatform: string) => {
    const normalizedPlatform = normalizePlatform(nextPlatform);
    setProductSetPlatform(normalizedPlatform);
    setProductSetRatio((current) => normalizeRatioForPlatform(normalizedPlatform, current, productSetOutput));
    setProductSetLanguage(getPlatformDefaultLanguage(normalizedPlatform, productSetMarket));
  };

  const handleProductSetOutputChange = (nextOutput: ProductSetOutputKey) => {
    setProductSetOutput(nextOutput);
    setProductSetRatio((current) => normalizeRatioForPlatform(productSetPlatform, current, nextOutput));
  };

  const handleProductSetMarketChange = (nextMarket: string) => {
    const normalizedMarket = normalizeMarket(nextMarket);
    setProductSetMarket(normalizedMarket);
    setProductSetLanguage(getPlatformDefaultLanguage(productSetPlatform, normalizedMarket));
  };

  const handleClonePlatformChange = (nextPlatform: string) => {
    const normalizedPlatform = normalizePlatform(nextPlatform);
    setPlatform(normalizedPlatform);
    setRatio((current) =>
      cloneOutput === "hot" && current.startsWith("涓婁紶鍥剧墖") && hotUploadedRatioOption
        ? hotUploadedRatioOption
        : normalizeRatioForPlatform(normalizedPlatform, current, cloneOutput),
    );
    setLanguage(getPlatformDefaultLanguage(normalizedPlatform, market));
  };

  const handleCloneOutputChange = (nextOutput: CloneOutputKey) => {
    setCloneOutput(nextOutput);
    setRatio((current) =>
      nextOutput === "hot" && current.startsWith("涓婁紶鍥剧墖") && hotUploadedRatioOption
        ? hotUploadedRatioOption
        : normalizeRatioForPlatform(platform, current, nextOutput),
    );
  };

  const handleCloneMarketChange = (nextMarket: string) => {
    const normalizedMarket = normalizeMarket(nextMarket);
    setMarket(normalizedMarket);
    setLanguage(getPlatformDefaultLanguage(platform, normalizedMarket));
  };

  const handleDetailPlatformChange = (nextPlatform: string) => {
    const normalizedPlatform = normalizePlatform(nextPlatform);
    setDetailPlatform(normalizedPlatform);
    setDetailLanguage(getPlatformDefaultLanguage(normalizedPlatform, detailMarket));
    setDetailRatio((current) => getQuickSetRatioValue(current));
  };

  const handleDetailMarketChange = (nextMarket: string) => {
    const normalizedMarket = normalizeMarket(nextMarket);
    setDetailMarket(normalizedMarket);
    setDetailLanguage(getPlatformDefaultLanguage(detailPlatform, normalizedMarket));
  };

  const createCloneSettingSnapshot = (name: string, id = `clone-setting-${Date.now()}`): CloneSavedSetting => ({
    id,
    name,
    savedAt: new Date().toISOString(),
    output: cloneOutput,
    platform,
    market,
    language,
    ratio,
    setCounts: { ...cloneSetCounts },
    detailModules: [...selectedCloneDetailModules],
    modelPanelTab: cloneModelPanelTab,
    modelScenes: [...selectedCloneModelScenes],
    modelCustomScene: cloneModelCustomScene,
    modelGender: cloneModelGender,
    modelAge: cloneModelAge,
    modelEthnicity: cloneModelEthnicity,
    modelBody: cloneModelBody,
    modelAppearance: cloneModelAppearance,
    videoQuality: cloneVideoQuality,
    videoDurationSeconds: cloneVideoDuration,
    videoSmart: cloneVideoSmart,
    referenceMode: cloneReferenceMode,
    replicateLevel: cloneReplicateLevel,
    requirement,
  });

  const latestCloneSettingSnapshot = useMemo(
    () => createCloneSettingSnapshot(cloneSettingName, "clone-setting-latest"),
    [
      cloneOutput,
      platform,
      market,
      language,
      ratio,
      cloneSetCounts,
      selectedCloneDetailModules,
      cloneModelPanelTab,
      selectedCloneModelScenes,
      cloneModelCustomScene,
      cloneModelGender,
      cloneModelAge,
      cloneModelEthnicity,
      cloneModelBody,
      cloneModelAppearance,
      cloneVideoQuality,
      cloneVideoDuration,
      cloneVideoSmart,
      cloneReferenceMode,
      cloneReplicateLevel,
      requirement,
      cloneSettingName,
    ],
  );

  const persistLatestCloneSetting = () => {
    const snapshot = createCloneSettingSnapshot(cloneSettingName, "clone-setting-latest");
    latestCloneSettingRef.current = snapshot;
    writeCloneLatestSetting(snapshot);
    return snapshot;
  };

  const applyCloneSavedSetting = (setting: CloneSavedSetting) => {
    const nextCounts = {
      selling: Number.isFinite(setting.setCounts?.selling) ? setting.setCounts.selling : defaultCloneSetCounts.selling,
      white: Number.isFinite(setting.setCounts?.white) ? setting.setCounts.white : defaultCloneSetCounts.white,
      scene: Number.isFinite(setting.setCounts?.scene) ? setting.setCounts.scene : defaultCloneSetCounts.scene,
    };
    const nextPlatform = normalizePlatform(setting.platform);
    const nextMarket = normalizeMarket(setting.market);
    const nextOutput = cloneOutputOptions.some((option) => option.key === setting.output) ? setting.output : defaultCloneOutput;
    setCloneOutput(nextOutput);
    setPlatform(nextPlatform);
    setMarket(nextMarket);
    setLanguage(normalizeLanguageForPlatform(nextPlatform, nextMarket, setting.language));
    setRatio(normalizeRatioForPlatform(nextPlatform, setting.ratio, nextOutput));
    setCloneSetCounts(nextCounts);
    setSelectedCloneDetailModules((setting.detailModules?.length ? setting.detailModules : defaultCloneDetailModuleIds).slice(0, maxDetailModuleSelection));
    setCloneModelPanelTab(setting.modelPanelTab === "model" ? "model" : "scene");
    setSelectedCloneModelScenes(normalizeCloneModelSceneSelection(setting.modelScenes));
    setCloneModelCustomScene(setting.modelCustomScene ?? "");
    setCloneModelGender(tryOnModelOptions.gender.includes(setting.modelGender) ? setting.modelGender : tryOnModelOptions.gender[0]);
    setCloneModelAge(tryOnModelOptions.age.includes(setting.modelAge) ? setting.modelAge : tryOnModelOptions.age[0]);
    setCloneModelEthnicity(
      tryOnModelOptions.ethnicity.includes(setting.modelEthnicity) ? setting.modelEthnicity : tryOnModelOptions.ethnicity[0],
    );
    setCloneModelBody(tryOnModelOptions.body.includes(setting.modelBody) ? setting.modelBody : tryOnModelOptions.body[0]);
    setCloneModelAppearance(setting.modelAppearance ?? "");
    setCloneVideoQuality(
      cloneVideoQualityOptions.some((option) => option.key === setting.videoQuality) ? setting.videoQuality : "high",
    );
    setCloneVideoDuration(clampCloneVideoDuration(setting.videoDurationSeconds));
    setCloneVideoSmart(Boolean(setting.videoSmart));
    setCloneReferenceMode(setting.referenceMode === "link" ? "link" : "upload");
    setCloneReplicateLevel(setting.replicateLevel === "style" ? "style" : "high");
    setRequirement((setting.requirement ?? "").slice(0, 500));
    setCloneSettingName(setting.name);
    latestCloneSettingRef.current = setting;
    writeCloneLatestSetting(setting);
  };

  useEffect(() => {
    latestCloneSettingRef.current = latestCloneSettingSnapshot;
  }, [latestCloneSettingSnapshot]);

  useEffect(() => {
    window.localStorage.removeItem(cloneLatestSettingStorageKey);
  }, []);

  useEffect(() => {
    setProductSetRatio((current) => normalizeRatioForPlatform(productSetPlatform, current, productSetOutput));
  }, [productSetOutput, productSetPlatform]);

  useEffect(() => {
    if (activeQuickTool === "set") {
      setRatio((current) => getQuickSetRatioValue(current));
      return;
    }
    setRatio((current) => {
      const platformRatios = getPlatformRatioOptions(platform, cloneOutput);
      const availableRatios = hotUploadedRatioOption ? getUniqueRatioOptions([...platformRatios, hotUploadedRatioOption]) : platformRatios;
      if (current.startsWith("涓婁紶鍥剧墖") && hotUploadedRatioOption) return hotUploadedRatioOption;
      if (availableRatios.includes(current)) return current;
      const normalizedRatio = normalizeRatioToken(current);
      const matchedRatio = availableRatios.find((option) => normalizeRatioToken(option).includes(normalizedRatio));
      return matchedRatio ?? getPlatformDefaultRatio(platform, cloneOutput);
    });
  }, [activeQuickTool, cloneOutput, hotUploadedRatioOption, platform]);

  useEffect(() => {
    if (skipInitialCloneAutoSaveRef.current) {
      skipInitialCloneAutoSaveRef.current = false;
      return undefined;
    }
    if (skipNextCloneAutoSaveRef.current) {
      skipNextCloneAutoSaveRef.current = false;
      return undefined;
    }

    const timeoutId = window.setTimeout(() => {
      persistLatestCloneSetting();
    }, 300);

    return () => window.clearTimeout(timeoutId);
  }, [
    activeTool,
    cloneOutput,
    platform,
    market,
    language,
    ratio,
    cloneSetCounts,
    selectedCloneDetailModules,
    cloneModelPanelTab,
    selectedCloneModelScenes,
    cloneModelCustomScene,
    cloneModelGender,
    cloneModelAge,
    cloneModelEthnicity,
    cloneModelBody,
    cloneModelAppearance,
    cloneVideoQuality,
    cloneVideoDuration,
    cloneVideoSmart,
    cloneReferenceMode,
    cloneReplicateLevel,
    requirement,
    cloneSettingName,
  ]);

  useEffect(() => {
    const persistSnapshot = () => {
      if (latestCloneSettingRef.current) writeCloneLatestSetting(latestCloneSettingRef.current);
    };
    const handleVisibilityChange = () => {
      if (document.visibilityState === "hidden") persistSnapshot();
    };

    window.addEventListener("pagehide", persistSnapshot);
    document.addEventListener("visibilitychange", handleVisibilityChange);

    return () => {
      persistSnapshot();
      window.removeEventListener("pagehide", persistSnapshot);
      document.removeEventListener("visibilitychange", handleVisibilityChange);
    };
  }, []);

  useEffect(() => clearCloneSetCountHold, []);

  useEffect(() => {
    if (!openCloneBasicSelect) return undefined;

    const handlePointerDown = (event: PointerEvent) => {
      const target = event.target;
      if (!(target instanceof Element) || target.closest("[data-clone-basic-select]")) return;
      setOpenCloneBasicSelect(null);
    };
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") setOpenCloneBasicSelect(null);
    };

    document.addEventListener("pointerdown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);
    return () => {
      document.removeEventListener("pointerdown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [openCloneBasicSelect]);

  useEffect(() => {
    if (!composerMenu && !(status === "done" && !isCommandComposerCompact)) return undefined;

    const handlePointerDown = (event: PointerEvent) => {
      const target = event.target;
      if (!(target instanceof Node)) return;
      const composer = commandComposerWrapRef.current;
      if (composer?.contains(target)) return;

      if (composerMenu) setComposerMenu(null);
      if (status === "done" && !isCommandComposerCompact) setIsCommandComposerCompact(true);
    };

    document.addEventListener("pointerdown", handlePointerDown);
    return () => document.removeEventListener("pointerdown", handlePointerDown);
  }, [composerMenu, isCommandComposerCompact, status]);

  useEffect(() => {
    if (composerMenuCloseTimeoutRef.current !== null) {
      window.clearTimeout(composerMenuCloseTimeoutRef.current);
      composerMenuCloseTimeoutRef.current = null;
    }
    if (composerMenu) {
      setVisibleComposerMenu(composerMenu);
      setIsComposerMenuClosing(false);
      return;
    }
    if (!visibleComposerMenu) return;
    setIsComposerMenuClosing(true);
    composerMenuCloseTimeoutRef.current = window.setTimeout(() => {
      composerMenuCloseTimeoutRef.current = null;
      setVisibleComposerMenu(null);
      setIsComposerMenuClosing(false);
    }, 220);
  }, [composerMenu, visibleComposerMenu]);

  useEffect(
    () => () => {
      if (composerMenuCloseTimeoutRef.current !== null) {
        window.clearTimeout(composerMenuCloseTimeoutRef.current);
      }
    },
    [],
  );

  useEffect(() => {
    if (!openCloneModelSelect) return undefined;

    const handlePointerDown = (event: PointerEvent) => {
      const target = event.target;
      if (!(target instanceof Element) || target.closest("[data-clone-model-select]")) return;
      setOpenCloneModelSelect(null);
      setCloneModelSelectDropUp(false);
    };
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setOpenCloneModelSelect(null);
        setCloneModelSelectDropUp(false);
      }
    };

    document.addEventListener("pointerdown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);
    return () => {
      document.removeEventListener("pointerdown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [openCloneModelSelect]);

  const handleGarmentUpload = (event: ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (!files?.length) return;
    const uploadedFiles = notifyRejectedImages(Array.from(files));
    if (!uploadedFiles.length) {
      event.target.value = "";
      return;
    }
    void (async () => {
      try {
        const nextImages = await createUploadedImageItems(uploadedFiles, 5 - garmentImages.length, "garment");
        setGarmentImages((current) => [...current, ...nextImages].slice(0, 5));
        setTryOnStatus("ready");
      } catch (err) {
        toast.error(err instanceof Error ? err.message : "鏈嶉グ鍥句笂浼犲け璐?");
      }
    })();
    event.target.value = "";
  };

  const handleDetailUpload = (event: ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (!files?.length) return;
    void addDetailImages(Array.from(files));
    event.target.value = "";
  };

  const handleDetailDrop = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    const files = Array.from(event.dataTransfer.files);
    if (files.length) void addDetailImages(files);
  };

  const removeDetailImage = (imageId: string) => {
    setDetailProductImages((current) => {
      const next = current.filter((item) => item.id !== imageId);
      if (next.length === 0) {
        setDetailStatus("idle");
        setDetailResultUrl(null);
      }
      return next;
    });
  };

  const blobToDataUrl = (blob: Blob): Promise<string> =>
    new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(String(reader.result || ""));
      reader.onerror = () => reject(reader.error || new Error("鏂囦欢璇诲彇澶辫触"));
      reader.readAsDataURL(blob);
    });

  const addDetailImages = async (files: File[]) => {
    const uploadedFiles = notifyRejectedImages(files);
    if (!uploadedFiles.length) return;
    try {
      const nextImages = await createUploadedImageItems(uploadedFiles, 3 - detailProductImages.length, "detail");
      setDetailProductImages((current) => {
        if (current.length >= 3) return current;
        return nextImages.length ? [...current, ...nextImages].slice(0, 3) : current;
      });
      setDetailStatus("ready");
      setDetailResultUrl(null);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "璇︽儏鍥句笂浼犲け璐?");
    }
  };

  const uploadCloneImages = async (images: CloneImageItem[]): Promise<string[]> => {
    const urls: string[] = [];
    for (const item of images) {
      try {
        if (!item.file && item.src.startsWith("blob:")) {
          throw new Error("鏈湴棰勮鍥剧己灏戝師濮嬫枃浠讹紝鏃犳硶涓婁紶");
        }
        const rawBlob = item.file ?? (item.src.startsWith("data:") ? null : await (await fetch(item.src)).blob());
        const mimeType = normalizeEcommerceImageMime(
          rawBlob?.type || item.src.match(/^data:([^;,]+)/)?.[1] || "image/png",
        );
        const blob = rawBlob ? (rawBlob.type === mimeType ? rawBlob : new Blob([rawBlob], { type: mimeType })) : null;
        const dataUrl = item.src.startsWith("data:") ? item.src : await blobToDataUrl(blob!);
        const { url } = await aiGenerationClient.uploadAsset({ dataUrl, name: item.name, mimeType, scope: "ecommerce-product" });
        urls.push(url);
      } catch {
        // skip images that fail to upload
      }
    }
    return urls;
  };

  const IMAGE_MODEL = "gpt-image-2";

  const setCountLabels: Record<CloneSetCountKey, { label: string; promptDesc: string }> = {
    selling: { label: "鍗栫偣鍥?", promptDesc: "selling-point infographic image highlighting core product advantages and detail close-ups" },
    white:   { label: "鐧藉簳鍥?", promptDesc: "clean white-background product photo showing the item from its best angle, studio lighting, no props" },
    scene:   { label: "鍦烘櫙鍥?", promptDesc: "lifestyle scene image showing the product in a realistic usage environment with natural surroundings" },
  };

  const buildDetailModulePrompt = (moduleIds: string[]): string => {
    if (!moduleIds.length) {
      return "Generate a complete A+ detail layout with hero, selling points, usage scene, product detail, and specification modules.";
    }

    const selectedModules = cloneDetailModules.filter((module) => moduleIds.includes(module.id));
    if (!selectedModules.length) return "";

    const moduleList = selectedModules.map((module) => `${module.title}: ${module.desc}`).join("; ");
    return `Only generate these selected A+ detail modules, no extra modules: ${moduleList}. Keep the output focused even if only one or two modules are selected.`;
  };

  const buildSetSubPrompt = (countKey: CloneSetCountKey, index: number, totalCount: number, pPlatform: string, pRatio: string, pLanguage: string, pMarket: string): string => {
    const info = setCountLabels[countKey];
    const parts: string[] = [];
    parts.push(`Generate an e-commerce ${info.label.toLowerCase()} for a product listing.`);
    parts.push(info.promptDesc);
    if (countKey === "white") {
      parts.push("The output must be a clean white-background product image. Do not use lifestyle backgrounds, props, text overlays, or people.");
    }
    if (countKey === "scene") {
      parts.push("The output must be a realistic usage scene image. Keep the product clearly visible and preserve its shape, color, and key details.");
    }
    if (countKey === "selling") {
      parts.push("The output must be a selling-point graphic with clear hierarchy, concise copy, and product detail callouts.");
    }
    if (totalCount > 1) {
      parts.push(`This is variant ${index + 1} of ${totalCount} 鈥?vary the angle, composition, or emphasis to make each distinct.`);
    }
    parts.push(`Platform: ${pPlatform}. Aspect ratio: ${pRatio}. Language/copy: ${pLanguage}. Market: ${pMarket}.`);
    parts.push("Must comply with platform image guidelines 鈥?proper margins, no watermark, professional quality.");
    return parts.join(" ");
  };

  const buildEcommerceImagePrompt = (
    outputKey: CloneOutputKey, userText: string,
    pPlatform: string, pRatio: string, pLanguage: string, pMarket: string,
    tryOnOptions?: EcommerceImagePromptOptions,
  ): string => {
    const parts: string[] = [];
    if (outputKey === "detail") {
      parts.push("Generate a professional A+ detail page hero image for an e-commerce product listing.");
      parts.push("Create a high-impact first-screen visual that combines the product photo with key selling points, usage scenes, and detailed specifications in a cohesive layout.");
      parts.push(`Platform: ${pPlatform}. Aspect ratio: ${pRatio}. Language/copy: ${pLanguage}. Market: ${pMarket}.`);
      if (outputKey === "detail" && tryOnOptions?.detailModules) parts.push(buildDetailModulePrompt(tryOnOptions.detailModules));
      parts.push("Follow platform A+ page best practices 鈥?clear hierarchy, professional typography, high visual impact.");
    } else if (outputKey === "model") {
      parts.push("Generate model/try-on lifestyle images for an e-commerce product listing.");
      parts.push("Show the product being used or worn by a model in attractive lifestyle settings.");
      parts.push(`Platform: ${pPlatform}. Aspect ratio: ${pRatio}. Language/copy: ${pLanguage}. Market: ${pMarket}.`);
      if (tryOnOptions) {
        if (tryOnOptions.gender) parts.push(`Model gender: ${tryOnOptions.gender}.`);
        if (tryOnOptions.age) parts.push(`Model age: ${tryOnOptions.age}.`);
        if (tryOnOptions.ethnicity) parts.push(`Model ethnicity: ${tryOnOptions.ethnicity}.`);
        if (tryOnOptions.body) parts.push(`Model body type: ${tryOnOptions.body}.`);
        if (tryOnOptions.appearance) parts.push(`Model appearance details: ${tryOnOptions.appearance}.`);
        if (tryOnOptions.scenes?.length) parts.push(`Background scenes: ${tryOnOptions.scenes.join(", ")}.`);
        if (tryOnOptions.customScene) parts.push(`Custom background scene: ${tryOnOptions.customScene}.`);
        if (tryOnOptions.smartScene) parts.push("Use smart scene matching to select the best background context.");
      }
      parts.push("Model should appear natural and appealing. Background should complement the product. Image must meet platform standards.");
    } else if (outputKey === "hot") {
      parts.push("Generate a high-conversion e-commerce product image that closely replicates the style and composition of the reference image while adapting it to the target platform.");
      parts.push(`Replicate the visual style, color palette, and layout feel of the source product image, then adapt it for ${pPlatform} marketplace standards.`);
      parts.push(`Platform: ${pPlatform}. Aspect ratio: ${pRatio}. Language/copy: ${pLanguage}. Market: ${pMarket}.`);
      parts.push("The result must look professional and optimized for high click-through rate and conversion on the specified platform.");
    }
    if (userText.trim()) {
      parts.push(`Additional user requirements: ${userText.trim()}`);
    }
    return parts.join(" ");
  };

  const generateSetImages = async (
    images: CloneImageItem[],
    counts: Record<CloneSetCountKey, number>,
    userText: string,
    pPlatform: string,
    pRatio: string,
    pLanguage: string,
    pMarket: string,
    setStatusFn: (status: "generating" | "done" | "idle" | "failed") => void,
    setResultFn: (urls: string[]) => void,
  ): Promise<void> => {
    setStatusFn("generating");
    try {
      const referenceUrls = await uploadCloneImages(images);
      if (!referenceUrls.length) {
        setStatusFn("idle");
        return;
      }
      if (imageAbortRef.current.current) {
        setStatusFn("idle");
        return;
      }

      const generatedUrls: string[] = [];
      const stamp = Date.now();

      for (const countKey of cloneSetCountKeys) {
        if (imageAbortRef.current.current) break;
        const count = counts[countKey];
        for (let i = 0; i < count; i++) {
          if (imageAbortRef.current.current) break;
          const subPrompt = buildSetSubPrompt(countKey, i, count, pPlatform, pRatio, pLanguage, pMarket);
          const fullPrompt = userText.trim() ? `${subPrompt} Additional user requirements: ${userText.trim()}` : subPrompt;

          const { taskId } = await aiGenerationClient.createImageTask({
            model: IMAGE_MODEL,
            prompt: fullPrompt,
            ratio: normalizeRatioForApi(pRatio),
            quality: pRatio.includes("720") ? "720P" : "1080P",
            gridMode: "single",
            referenceUrls,
          });
          trackEcommerceTask(taskId);

          const storeId = imageGen.submitTask({ title: `${setCountLabels[countKey].label} ${i + 1}`, type: "image", status: "running", progress: 5, prompt: fullPrompt, sourceView: "ecommerce", taskId });

          let resultUrl: string | null = null;
          try {
            resultUrl = await waitForTask(taskId, {
              abortRef: imageAbortRef.current,
              onProgress: () => {},
            });
          } finally {
            untrackEcommerceTask(taskId);
          }

          if (imageAbortRef.current.current) break;

          if (resultUrl) {
            const persistedUrl = await persistGeneratedImageUrl(resultUrl, "ecommerce-generated", `ecommerce-${countKey}-${i + 1}`);
            generatedUrls.push(persistedUrl);
            imageGen.updateTask(storeId, { status: "completed", progress: 100, resultUrl: persistedUrl });
          } else {
            generatedUrls.push("");
            imageGen.updateTask(storeId, { status: "failed", error: "鐢熸垚鏈繑鍥炵粨鏋?" });
          }
        }
      }

      if (imageAbortRef.current.current) {
        setStatusFn("idle");
        return;
      }
      setResultFn(generatedUrls);
      setStatusFn(generatedUrls.some(Boolean) ? "done" : "failed");
    } catch (err) {
      if (imageAbortRef.current.current) {
        setStatusFn("idle");
        return;
      }
      if (err instanceof ServerRequestError && err.status === 402) {
        setResultFn([]);
        toast.error("浣欓涓嶈冻锛岃鍏呭€煎悗缁х画");
      } else {
        const msg = err instanceof Error ? err.message : "鐢熸垚澶辫触";
        toast.error(msg);
      }
      setStatusFn("failed");
    }
  };

  const generateEcommerceImage = async (
    outputKey: CloneOutputKey,
    images: CloneImageItem[],
    userText: string,
    pPlatform: string,
    pRatio: string,
    pLanguage: string,
    pMarket: string,
    tryOnOptions?: EcommerceImagePromptOptions,
    statusFn?: (status: "generating" | "done" | "idle" | "failed") => void,
    resultFn?: (results: CloneResult[]) => void,
  ): Promise<void> => {
    statusFn?.("generating");
    try {
      const referenceUrls = await uploadCloneImages(images);
      if (!referenceUrls.length) {
        statusFn?.("idle");
        return;
      }
      if (imageAbortRef.current.current) {
        statusFn?.("idle");
        return;
      }

      const prompt = buildEcommerceImagePrompt(outputKey, userText, pPlatform, pRatio, pLanguage, pMarket, tryOnOptions);
      const stamp = Date.now();

      const { taskId } = await aiGenerationClient.createImageTask({
        model: IMAGE_MODEL,
        prompt,
        ratio: normalizeRatioForApi(pRatio),
        quality: pRatio.includes("720") ? "720P" : "1080P",
        gridMode: "single",
        referenceUrls,
      });
      trackEcommerceTask(taskId);

      const storeId = imageGen.submitTask({ title: `电商${outputKey}图`, type: "image", status: "running", progress: 5, prompt, sourceView: "ecommerce", taskId });

      let resultUrl: string | null = null;
      try {
        resultUrl = await waitForTask(taskId, {
          abortRef: imageAbortRef.current,
          onProgress: () => {},
        });
      } finally {
        untrackEcommerceTask(taskId);
      }

      if (imageAbortRef.current.current) {
        statusFn?.("idle");
        return;
      }

      if (resultUrl) {
        const persistedUrl = await persistGeneratedImageUrl(resultUrl, "ecommerce-generated", `ecommerce-${outputKey}`);
        resultFn?.([{ id: `ecommerce-${stamp}`, src: persistedUrl, label: selectedCloneOutput.label }]);
        statusFn?.("done");
        imageGen.updateTask(storeId, { status: "completed", progress: 100, resultUrl: persistedUrl });
      } else {
        statusFn?.("idle");
        imageGen.updateTask(storeId, { status: "failed", error: "鐢熸垚鏈繑鍥炵粨鏋?" });
      }
    } catch (err) {
      if (imageAbortRef.current.current) {
        statusFn?.("idle");
        return;
      }
      if (err instanceof ServerRequestError && err.status === 402) {
        resultFn?.([{ id: `ecommerce-error-402`, src: "", label: "浣欓涓嶈冻锛岃鍏呭€煎悗缁х画" }]);
        toast.error("浣欓涓嶈冻锛岃鍏呭€煎悗缁х画");
      } else {
        const msg = err instanceof Error ? err.message : "鐢熸垚澶辫触";
        toast.error(msg);
      }
      statusFn?.("failed");
    }
  };

  const handleVideoOutfitGenerate = async () => {
    if (!videoOutfitVideoFile || !videoOutfitRefFile) return;
    setStatus("generating");
    try {
      const readAsDataUrl = (file: File): Promise<string> => new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result as string);
        reader.onerror = () => reject(new Error("鏂囦欢璇诲彇澶辫触"));
        reader.readAsDataURL(file);
      });

      const videoDataUrl = await readAsDataUrl(videoOutfitVideoFile);
      const refDataUrl = await readAsDataUrl(videoOutfitRefFile);

      const videoAsset = await aiGenerationClient.uploadAsset({
        dataUrl: videoDataUrl, name: videoOutfitVideoFile.name,
        mimeType: videoOutfitVideoFile.type || "video/mp4", scope: "video-outfit",
      });
      const refAsset = await aiGenerationClient.uploadAsset({
        dataUrl: refDataUrl, name: videoOutfitRefFile.name,
        mimeType: videoOutfitRefFile.type || "image/png", scope: "video-outfit",
      });
      if (imageAbortRef.current.current) {
        setStatus("idle");
        return;
      }

      const { taskId } = await aiGenerationClient.createVideoEditTask({
        videoUrl: videoAsset.url,
        referenceUrls: [refAsset.url],
        prompt: requirement || undefined,
      });
      trackEcommerceTask(taskId);

      const { waitForTask } = await import("../../api/taskSubscription");
      let resultUrl: string | null = null;
      try {
        resultUrl = await waitForTask(taskId, { abortRef: imageAbortRef.current });
      } finally {
        untrackEcommerceTask(taskId);
      }
      if (imageAbortRef.current.current) {
        setStatus("idle");
        return;
      }
      if (resultUrl) {
        setResults([{ id: crypto.randomUUID(), src: resultUrl, label: "鎹㈣瑙嗛" }]);
      }
      setStatus("done");
    } catch (err) {
      if (imageAbortRef.current.current) {
        setStatus("idle");
        return;
      }
      setStatus("failed");
      toast.error(err instanceof Error ? err.message : "瑙嗛鎹㈣鐢熸垚澶辫触");
    }
  };

  const handleGenerate = () => {
    if (!canGenerate) return;

    if ((appUsage?.balanceCents ?? 0) <= 0) {
      toast.error("绉垎涓嶈冻锛岃鍏呭€煎悗缁х画");
      return;
    }

    if (cloneOutput === "set" && cloneSetTotal > 5) {
      if (!window.confirm("将生成 " + String(cloneSetTotal) + " 张图片，可能消耗较多积分，是否继续？")) return;
    }

    imageAbortRef.current = { current: false };
    lastFailedActionRef.current = null;
    if (cloneOutput === "video-outfit") {
      void handleVideoOutfitGenerate();
    } else if (cloneOutput === "set") {
      void generateSetImages(
        productImages, cloneSetCounts, requirement,
        platform, ratio, language, market,
        (s) => setStatus(s as ProductCloneStatus),
        (urls) => setProductSetResultImages(urls),
      );
    } else {
      const clonePromptOptions: EcommerceImagePromptOptions | undefined =
        cloneOutput === "model"
          ? {
              gender: cloneModelGender,
              age: cloneModelAge,
              ethnicity: cloneModelEthnicity,
              body: cloneModelBody,
              scenes: selectedCloneModelScenes,
              customScene: cloneModelCustomScene,
            }
          : cloneOutput === "detail"
            ? { detailModules: selectedCloneDetailModules }
            : undefined;
      void generateEcommerceImage(
        cloneOutput, productImages, requirement,
        platform, ratio, language, market,
        clonePromptOptions,
        (s: string) => setStatus(s as ProductCloneStatus), setResults,
      );
      lastFailedActionRef.current = () => handleGenerate();
    }
  };

  const handleGenerateModel = () => {
    imageAbortRef.current = { current: false };
    lastFailedActionRef.current = null;
    setTryOnStatus("modeling");
    void generateEcommerceImage(
      "model", garmentImages, requirement,
      platform, ratio, language, market,
      { gender: modelGender, age: modelAge, ethnicity: modelEthnicity, body: modelBody, appearance, scenes: selectedScenes, smartScene },
      (s) => {
        if (s === "done") setTryOnStatus("ready");
        else setTryOnStatus(s as TryOnStatus);
      },
      () => { setTryOnStatus("ready"); },
    );
    lastFailedActionRef.current = () => handleGenerateModel();
  };

  const handleTryOnGenerate = () => {
    if (!canGenerateTryOn) return;
    imageAbortRef.current = { current: false };
    lastFailedActionRef.current = null;
    void generateEcommerceImage(
      "model", garmentImages, requirement,
      platform, ratio, language, market,
      { gender: modelGender, age: modelAge, ethnicity: modelEthnicity, body: modelBody, appearance, scenes: selectedScenes, smartScene },
      (s) => setTryOnStatus(s as TryOnStatus),
      (res) => {
        const urls: string[] = [];
        for (const item of res) {
          if (item.src) urls.push(item.src);
        }
        setTryOnResultImages(urls);
      },
    );
    lastFailedActionRef.current = () => handleTryOnGenerate();
  };

  const toggleScene = (scene: string) => {
    setSelectedScenes((current) =>
      current.includes(scene) ? current.filter((item) => item !== scene) : [...current, scene],
    );
  };

  const toggleDetailModule = (moduleId: string) => {
    setSelectedDetailModules((current) => {
      if (current.includes(moduleId)) return current.filter((item) => item !== moduleId);
      if (current.length >= maxDetailModuleSelection) {
        toast.info(`最多选择 ${maxDetailModuleSelection} 个模块`);
        return current;
      }
      return [...current, moduleId];
    });
  };

  const handleSetGenerate = () => {
    if (!canGenerateSet) return;
    imageAbortRef.current = { current: false };
    lastFailedActionRef.current = null;
    void generateSetImages(
      setImages, cloneSetCounts, productSetRequirement,
      productSetPlatform, productSetRatio, productSetLanguage, productSetMarket,
      (s) => setProductSetStatus(s as ProductSetStatus),
      (urls) => setProductSetResultImages(urls),
    );
    lastFailedActionRef.current = () => handleSetGenerate();
  };

  const openProductSetPreview = (card: { src: string; label: string }) => {
    setSelectedProductSetPreview(card);
  };

  const handleDetailAiWrite = () => {
    setDetailRequirement(
      "1.浜у搧鍚嶇О锛氭棤绾块檷鍣摑鐗欒€虫満\n2.鏍稿績鍗栫偣锛氫富鍔ㄩ檷鍣€?4H缁埅銆佷綆寤惰繜杩炴帴銆佽垝閫備僵鎴碶n3.閫傜敤浜虹兢锛氶€氬嫟銆佸姙鍏€佽繍鍔ㄥ拰鏃呰鐢ㄦ埛\n4.鏈熸湜鍦烘櫙锛氬湴閾侀€氬嫟銆佸眳瀹跺姙鍏€佹埛澶栬繍鍔╘n5.鍏蜂綋鍙傛暟锛氳摑鐗?.3銆両PX4闃叉按銆佸揩鍏?0鍒嗛挓浣跨敤2灏忔椂",
    );
  };

  const handleDetailGenerate = () => {
    if (!canGenerateDetail) return;
    imageAbortRef.current = { current: false };
    lastFailedActionRef.current = null;
    void generateEcommerceImage(
      "detail", detailProductImages, detailRequirement,
      detailPlatform, detailRatio, detailLanguage, detailMarket,
      { detailModules: selectedDetailModules },
      (s: string) => setDetailStatus(s as DetailStatus),
      (res) => setDetailResultUrl(res[0]?.src ?? null),
    );
  };

  const resetTask = () => {
    setSetImages([]);
    setProductSetRequirement("");
    setProductSetPlatform(defaultEcommercePlatform);
    setProductSetLanguage(getPlatformDefaultLanguage(defaultEcommercePlatform, productSetMarket));
    setProductSetOutput(defaultProductSetOutput);
    setProductSetRatio(getPlatformDefaultRatio(defaultEcommercePlatform, defaultProductSetOutput));
    setProductSetStatus("idle");
    setProductSetResultImages([]);
    setIsSetUploadDragging(false);
    setSelectedProductSetPreview(null);
    setShowHostingModal(false);
    setProductImages([]);
    setIsProductUploadDragging(false);
    setCloneOutput(defaultCloneOutput);
    setPlatform(defaultEcommercePlatform);
    setLanguage(getPlatformDefaultLanguage(defaultEcommercePlatform, market));
    setRatio(getPlatformDefaultRatio(defaultEcommercePlatform, defaultCloneOutput));
    setCloneSetCounts(defaultCloneSetCounts);
    setSelectedCloneDetailModules(defaultCloneDetailModuleIds);
    setCloneModelPanelTab("scene");
    setSelectedCloneModelScenes([]);
    setCloneModelCustomScene("");
    setCloneModelGender(tryOnModelOptions.gender[0]);
    setCloneModelAge(tryOnModelOptions.age[0]);
    setCloneModelEthnicity(tryOnModelOptions.ethnicity[0]);
    setCloneModelBody(tryOnModelOptions.body[0]);
    setCloneModelAppearance("");
    setCloneVideoQuality("high");
    setCloneVideoDuration(10);
    setCloneVideoSmart(true);
    setCloneReferenceMode("upload");
    setCloneReferenceImages([]);
    setCloneReplicateLevel("high");
    setRequirement("");
    setCloneSettingName("鏂板缓鍒涗綔");
    setResults([]);
    setStatus("idle");
    setGarmentImages([]);
    setAppearance("");
    setSelectedScenes([]);
    setCustomScene("");
    setSmartScene(false);
    setTryOnRatio(tryOnRatioOptions[0]);
    setTryOnStatus("idle");
    setTryOnResultImages([]);
    setDetailProductImages([]);
    setDetailRequirement("");
    setSelectedDetailModules(defaultDetailModuleIds);
    setDetailStatus("idle");
  };

  const activeToolMeta = sideTools.find((tool) => tool.key === activeTool);
  const isSetTool = activeTool === "set";
  const isDetail = activeTool === "detail";
  const isTryOn = activeTool === "wear";
  const isCloneTool = activeTool === "clone";
  const isSmartCutoutTool = isCloneTool && activeQuickTool === "cutout";
  const isQuickSetTool = isCloneTool && activeQuickTool === "set";
  const isQuickDetailTool = isCloneTool && activeQuickTool === "detail";
  const isWatermarkTool = isCloneTool && activeQuickTool === "watermark";
  const isImageEditTool = isCloneTool && activeQuickTool === "image-edit";
  const isHotVideoTool = isCloneTool && activeQuickTool === "hot-video";
  const pageLabel = isSetTool ? "鍟嗗搧濂楀浘" : isDetail ? "A+/璇︽儏椤?" : isTryOn ? "AI鏈嶉グ绌挎埓" : activeToolMeta?.label || "鍟嗗搧宸ュ叿";
  const setPrimaryLabel =
    setImages.length === 0
      ? "璇峰厛涓婁紶鍟嗗搧鍘熷浘"
    : productSetStatus === "generating"
      ? "鐢熸垚涓?.."
      : "鐢熸垚" + selectedProductSetOutput.label;
  const tryOnPrimaryLabel =
    garmentImages.length === 0 ? "璇峰厛涓婁紶鏈嶈鍥剧墖" : tryOnStatus === "generating" ? "鐢熸垚涓?.." : "鐢熸垚鏈嶉グ绌挎埓鍥?";
  const detailPrimaryLabel =
    detailProductImages.length === 0 ? "璇蜂笂浼犱骇鍝佸浘" : detailStatus === "generating" ? "鐢熸垚涓?.." : "鐢熸垚A+璇︽儏椤?";
  const clonePrimaryLabel =
    productImages.length === 0 ? "璇峰厛涓婁紶鍟嗗搧鍘熷浘" : status === "generating" ? "鐢熸垚涓?.." : "鐢熸垚" + selectedCloneOutput.label;
  const setPreviewCards: CloneResult[] = [];
  let setIndex = 0;
  for (const countKey of cloneSetCountKeys) {
    const count = cloneSetCounts[countKey];
    const info = setCountLabels[countKey];
    for (let i = 0; i < count; i++) {
      setPreviewCards.push({
        id: String(countKey) + "-" + String(i),
        src: productSetResultImages[setIndex] || productSetPreviewCards[setIndex % productSetPreviewCards.length]?.src || "",
        label: info.label + (count > 1 ? " " + String(i + 1) : ""),
      });
      setIndex++;
    }
  }

  const clonePreviewCards: CloneResult[] = [];
  let cloneIndex = 0;
  for (const countKey of cloneSetCountKeys) {
    const count = cloneSetCounts[countKey];
    const info = setCountLabels[countKey];
    for (let i = 0; i < count; i++) {
      clonePreviewCards.push({
        id: String(countKey) + "-" + String(i),
        src: productSetResultImages[cloneIndex] || productSetPreviewCards[cloneIndex % productSetPreviewCards.length]?.src || "",
        label: info.label + (count > 1 ? " " + String(i + 1) : ""),
      });
      cloneIndex++;
    }
  }

  const getCurrentHistoryResults = () =>
    cloneOutput === "set"
      ? productSetResultImages
          .filter(Boolean)
            .map((src, index) => ({ id: "history-set-" + String(index), src, label: clonePreviewCards[index]?.label || "濂楀浘 " + String(index + 1) }))
      : results.filter((item) => item.src);

  const buildHistorySignature = (output: CloneOutputKey, prompt: string, historyResults: CloneResult[], sourceImages: CloneImageItem[]) =>
    [
      output,
      prompt.trim(),
      historyResults.map((item) => item.src).join("|"),
      sourceImages.map((item) => item.src).join("|"),
    ].join("::");

  const formatHistoryTime = (timestamp: number) => {
    const diff = Math.max(0, Date.now() - timestamp);
    const minute = 60 * 1000;
    const hour = 60 * minute;
    const day = 24 * hour;
    if (diff < minute) return "鍒氬垰";
    if (diff < hour) return String(Math.floor(diff / minute)) + " 分钟前";
    if (diff < day) return String(Math.floor(diff / hour)) + " 小时前";
    return String(Math.floor(diff / day)) + " 天前";
  };

  const saveCurrentEcommerceHistory = () => {
    const historyResults = getCurrentHistoryResults();
    if (!historyResults.length) return null;
    const signature = buildHistorySignature(cloneOutput, requirement, historyResults, productImages);
    if (lastSavedHistorySignatureRef.current === signature && activeHistoryRecordId) return activeHistoryRecordId;

    const createdAt = Date.now();
    const outputLabel = cloneOutputOptions.find((option) => option.key === cloneOutput)?.label || "鐢熸垚璁板綍";
      const title = requirement.trim() || outputLabel + " " + new Date(createdAt).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
    const record: EcommerceHistoryRecord = {
      id: crypto.randomUUID(),
      title,
      createdAt,
      output: cloneOutput,
      platform,
      market,
      language,
      ratio,
      requirement,
      productImages,
      results: historyResults,
      setResultImages: cloneOutput === "set" ? historyResults.map((item) => item.src) : [],
      setCounts: cloneSetCounts,
      detailModules: selectedCloneDetailModules,
      modelScenes: selectedCloneModelScenes,
      referenceImages: cloneReferenceImages,
      replicateLevel: cloneReplicateLevel,
    };
    lastSavedHistorySignatureRef.current = signature;
    setEcommerceHistoryRecords((current) => {
      const nextRecords = [record, ...current.filter((item) => item.id !== record.id)].slice(0, 30);
      writeEcommerceHistoryRecords(nextRecords);
      return nextRecords;
    });
    setActiveHistoryRecordId(record.id);
    return record.id;
  };

  const openEcommerceHistoryRecord = (record: EcommerceHistoryRecord) => {
    setActiveTool("clone");
    setCloneOutput(record.output);
    setPlatform(record.platform);
    setMarket(record.market);
    setLanguage(record.language);
    setRatio(record.ratio);
    setRequirement(record.requirement);
    setProductImages(record.productImages);
    setCloneSetCounts(record.setCounts);
    setSelectedCloneDetailModules(record.detailModules.slice(0, maxDetailModuleSelection));
    setSelectedCloneModelScenes(record.modelScenes);
    setCloneReferenceImages(record.referenceImages);
    setCloneReplicateLevel(record.replicateLevel);
    setProductSetResultImages(record.setResultImages);
    setResults(record.output === "set" ? [] : record.results);
    setStatus("done");
    setPreviewZoom(1);
    setComposerMenu(null);
    setActiveHistoryRecordId(record.id);
    lastSavedHistorySignatureRef.current = buildHistorySignature(record.output, record.requirement, record.results, record.productImages);
    setIsCommandComposerCompact(true);
  };

  const handleNewEcommerceConversation = () => {
    saveCurrentEcommerceHistory();
    resetTask();
    setPreviewZoom(1);
    setPreviewOffset({ x: 0, y: 0 });
    setComposerMenu(null);
    setIsCommandComposerCompact(false);
    setActiveHistoryRecordId(null);
    lastSavedHistorySignatureRef.current = "";
  };

  const refreshEcommerceHistory = () => {
    if (historyRefreshLockRef.current) return;
    historyRefreshLockRef.current = true;
    setIsHistoryRefreshing(true);
    setHistoryRefreshMessage("鍒锋柊涓?..");
    setHistoryRefreshStamp(Date.now());

    window.setTimeout(() => {
      const storedRecords = readEcommerceHistoryRecords();
      const mergedRecords = [...ecommerceHistoryRecords, ...storedRecords]
        .reduce<EcommerceHistoryRecord[]>((records, record) => {
          if (!records.some((item) => item.id === record.id)) records.push(record);
          return records;
        }, [])
        .sort((a, b) => b.createdAt - a.createdAt)
        .slice(0, 30);
      writeEcommerceHistoryRecords(mergedRecords);
      setHistoryRefreshTick((tick) => tick + 1);
      setEcommerceHistoryRecords(mergedRecords);
        setHistoryRefreshMessage(mergedRecords.length ? "已刷新 " + String(mergedRecords.length) + " 条记录" : "暂无可刷新记录");
      setHistoryRefreshStamp(Date.now());
      setIsHistoryRefreshing(false);
      historyRefreshLockRef.current = false;
    }, 180);

    window.setTimeout(() => setHistoryRefreshMessage(""), 3000);
  };

  useEffect(() => {
    if (status === "done") saveCurrentEcommerceHistory();
  }, [status, results, productSetResultImages]);

  const detailSourcePreviewImages = detailProductImages.length
    ? detailProductImages.reduce<string[]>((urls, item) => {
        urls.push(item.src);
        return urls;
      }, [])
    : detailProductSamples;
  const cloneBasicSelects: Array<{
    key: CloneBasicSelectKey;
    label: string;
    value: string;
    options: string[];
    onChange: (value: string) => void;
  }> = [
    { key: "platform", label: "平台", value: platform, options: platformOptions, onChange: handleClonePlatformChange },
    { key: "market", label: "国家", value: market, options: marketOptions, onChange: handleCloneMarketChange },
    { key: "language", label: "语种", value: language, options: cloneLanguageOptions, onChange: setLanguage },
    { key: "ratio", label: "尺寸/比例", value: ratio, options: cloneRatioOptions, onChange: setRatio },
  ];
  const handleQuickSetPlatformChange = (nextPlatform: string) => {
    const normalizedPlatform = normalizePlatform(nextPlatform);
    setPlatform(normalizedPlatform);
    setLanguage(getPlatformDefaultLanguage(normalizedPlatform, market));
  };
  const quickSetBasicSelects: Array<{
    key: CloneBasicSelectKey;
    label: string;
    value: string;
    options: string[];
    onChange: (value: string) => void;
  }> = [
    { key: "platform", label: "平台", value: platform, options: platformOptions, onChange: handleQuickSetPlatformChange },
    { key: "market", label: "国家", value: market, options: marketOptions, onChange: handleCloneMarketChange },
    { key: "language", label: "语种", value: language, options: cloneLanguageOptions, onChange: setLanguage },
    { key: "ratio", label: "尺寸/比例", value: getQuickSetRatioValue(ratio), options: quickSetRatioOptions, onChange: setRatio },
  ];
  const quickDetailBasicSelects: Array<{
    key: CloneBasicSelectKey;
    label: string;
    value: string;
    options: string[];
    onChange: (value: string) => void;
  }> = [
    { key: "platform", label: "平台", value: detailPlatform, options: platformOptions, onChange: handleDetailPlatformChange },
    { key: "market", label: "国家", value: detailMarket, options: marketOptions, onChange: handleDetailMarketChange },
    { key: "language", label: "语种", value: detailLanguage, options: detailLanguageOptions, onChange: setDetailLanguage },
    { key: "ratio", label: "尺寸/比例", value: getQuickSetRatioValue(detailRatio), options: quickSetRatioOptions, onChange: setDetailRatio },
  ];

  const cloneModelSelects: Array<{
    key: CloneModelSelectKey;
    label: string;
    value: string;
    options: string[];
    onChange: (value: string) => void;
  }> = [
    { key: "gender", label: "性别", value: cloneModelGender, options: tryOnModelOptions.gender, onChange: setCloneModelGender },
    { key: "age", label: "年龄", value: cloneModelAge, options: tryOnModelOptions.age, onChange: setCloneModelAge },
    {
      key: "ethnicity",
      label: "人种",
      value: cloneModelEthnicity,
      options: tryOnModelOptions.ethnicity,
      onChange: setCloneModelEthnicity,
    },
    { key: "body", label: "体型", value: cloneModelBody, options: tryOnModelOptions.body, onChange: setCloneModelBody },
  ];

  const setPanel = (
    <EcommerceSetPanel
      setInputRef={setInputRef}
      setImages={setImages}
      isSetUploadDragging={isSetUploadDragging}
      productSetOutputOptions={productSetOutputOptions}
      productSetOutput={productSetOutput}
      platformOptions={platformOptions}
      marketOptions={marketOptions}
      productSetLanguageOptions={productSetLanguageOptions}
      productSetRatioOptions={productSetRatioOptions}
      productSetPlatform={productSetPlatform}
      productSetMarket={productSetMarket}
      productSetLanguage={productSetLanguage}
      productSetRatio={productSetRatio}
      setIsSetUploadDragging={setIsSetUploadDragging}
      handleSetDrop={handleSetDrop}
      handleSetUpload={handleSetUpload}
      removeSetImage={removeSetImage}
      handleProductSetOutputChange={handleProductSetOutputChange}
      handleProductSetPlatformChange={handleProductSetPlatformChange}
      handleProductSetMarketChange={handleProductSetMarketChange}
      setProductSetLanguage={setProductSetLanguage}
      setProductSetRatio={setProductSetRatio}
      formatRatioDisplayValue={formatRatioDisplayValue}
    />
  );

  const clonePanel = (
    <EcommerceClonePanel
      productInputRef={productInputRef}
      cloneReferenceInputRef={cloneReferenceInputRef}
      productImages={productImages}
      isProductUploadDragging={isProductUploadDragging}
      cloneOutput={cloneOutput}
      cloneOutputOptions={cloneOutputOptions}
      cloneBasicSelects={cloneBasicSelects}
      openCloneBasicSelect={openCloneBasicSelect}
      cloneReferenceMode={cloneReferenceMode}
      cloneReferenceImages={cloneReferenceImages}
      maxCloneReferenceImages={maxCloneReferenceImages}
      cloneReplicateLevel={cloneReplicateLevel}
      cloneReplicateLevelOptions={cloneReplicateLevelOptions}
      cloneSetCounts={cloneSetCounts}
      cloneSetCountOptions={cloneSetCountOptions}
      cloneSetTotal={cloneSetTotal}
      minCloneSetTotal={minCloneSetTotal}
      maxCloneSetTotal={maxCloneSetTotal}
      selectedCloneDetailModules={selectedCloneDetailModules}
      cloneDetailModules={cloneDetailModules}
      cloneModelPanelTab={cloneModelPanelTab}
      tryOnScenes={tryOnScenes}
      selectedCloneModelScenes={selectedCloneModelScenes}
      cloneModelCustomScene={cloneModelCustomScene}
      cloneModelSelects={cloneModelSelects}
      openCloneModelSelect={openCloneModelSelect}
      cloneModelSelectDropUp={cloneModelSelectDropUp}
      cloneVideoQuality={cloneVideoQuality}
      cloneVideoQualityOptions={cloneVideoQualityOptions}
      cloneVideoDuration={cloneVideoDuration}
      cloneVideoDurationMin={cloneVideoDurationMin}
      cloneVideoDurationMax={cloneVideoDurationMax}
      cloneVideoDurationStyle={cloneVideoDurationStyle}
      cloneVideoSmart={cloneVideoSmart}
      canGenerate={canGenerate}
      status={status}
      lastFailedActionRef={lastFailedActionRef}
      setIsProductUploadDragging={setIsProductUploadDragging}
      handleProductDrop={handleProductDrop}
      removeProductImage={removeProductImage}
      handleProductUpload={handleProductUpload}
      handleCloneOutputChange={handleCloneOutputChange}
      setOpenCloneBasicSelect={setOpenCloneBasicSelect}
      setCloneReferenceMode={setCloneReferenceMode}
      handleCloneReferenceUpload={handleCloneReferenceUpload}
      isCloneReferenceDragging={isCloneReferenceDragging}
      handleCloneReferenceDragOver={handleCloneReferenceDragOver}
      handleCloneReferenceDragLeave={handleCloneReferenceDragLeave}
      handleCloneReferenceDrop={handleCloneReferenceDrop}
      setCloneReplicateLevel={setCloneReplicateLevel}
      startCloneSetCountHold={startCloneSetCountHold}
      clearCloneSetCountHold={clearCloneSetCountHold}
      toggleCloneDetailModule={toggleCloneDetailModule}
      setCloneModelPanelTab={setCloneModelPanelTab}
      toggleCloneModelScene={toggleCloneModelScene}
      setCloneModelCustomScene={setCloneModelCustomScene}
      setOpenCloneModelSelect={setOpenCloneModelSelect}
      setCloneModelSelectDropUp={setCloneModelSelectDropUp}
      setCloneVideoQuality={setCloneVideoQuality}
      setCloneVideoDuration={setCloneVideoDuration}
      clampCloneVideoDuration={clampCloneVideoDuration}
      setCloneVideoSmart={setCloneVideoSmart}
      handleGenerate={handleGenerate}
      onCancelGenerate={handleCancelGenerate}
      formatRatioDisplayValue={formatRatioDisplayValue}
      setVideoOutfitFiles={(video, ref) => { setVideoOutfitVideoFile(video); setVideoOutfitRefFile(ref); }}
      onStartVideoPlan={() => setVideoPlanTrigger((n) => n + 1)}
    />
  );

  const detailPanel = (
    <EcommerceDetailPanel
      detailInputRef={detailInputRef}
      detailProductImages={detailProductImages}
      detailPlatform={detailPlatform}
      detailMarket={detailMarket}
      detailLanguage={detailLanguage}
      detailType={detailType}
      detailRequirement={detailRequirement}
      selectedDetailModules={selectedDetailModules}
      detailStatus={detailStatus}
      canGenerateDetail={canGenerateDetail}
      detailPrimaryLabel={detailPrimaryLabel}
      platformOptions={platformOptions}
      marketOptions={marketOptions}
      detailLanguageOptions={detailLanguageOptions}
      detailTypeOptions={detailTypeOptions}
      detailModules={detailModules}
      handleDetailUpload={handleDetailUpload}
      handleDetailPlatformChange={handleDetailPlatformChange}
      handleDetailMarketChange={handleDetailMarketChange}
      setDetailLanguage={setDetailLanguage}
      setDetailType={setDetailType}
      setDetailRequirement={setDetailRequirement}
      handleDetailAiWrite={handleDetailAiWrite}
      toggleDetailModule={toggleDetailModule}
      handleDetailGenerate={handleDetailGenerate}
      onCancelGenerate={handleCancelGenerate}
    />
  );

  const tryOnPanel = (
    <EcommerceTryOnPanel
      garmentInputRef={garmentInputRef}
      garmentImages={garmentImages}
      modelSource={modelSource}
      modelGender={modelGender}
      modelAge={modelAge}
      modelEthnicity={modelEthnicity}
      modelBody={modelBody}
      appearance={appearance}
      selectedScenes={selectedScenes}
      customScene={customScene}
      smartScene={smartScene}
      tryOnRatio={tryOnRatio}
      tryOnStatus={tryOnStatus}
      canGenerateTryOn={canGenerateTryOn}
      tryOnPrimaryLabel={tryOnPrimaryLabel}
      tryOnModelOptions={tryOnModelOptions}
      tryOnAssets={tryOnAssets}
      tryOnScenes={tryOnScenes}
      tryOnRatioOptions={tryOnRatioOptions}
      handleGarmentUpload={handleGarmentUpload}
      setModelSource={setModelSource}
      setModelGender={setModelGender}
      setModelAge={setModelAge}
      setModelEthnicity={setModelEthnicity}
      setModelBody={setModelBody}
      setAppearance={setAppearance}
      handleGenerateModel={handleGenerateModel}
      toggleScene={toggleScene}
      setCustomScene={setCustomScene}
      setSmartScene={setSmartScene}
      setTryOnRatio={setTryOnRatio}
      handleTryOnGenerate={handleTryOnGenerate}
      onCancelGenerate={handleCancelGenerate}
    />
  );

  const placeholderPanel = (
    <>
      <div className="product-clone-panel__scroll">
        <section className="product-clone-empty-panel">
          <span>{activeToolMeta?.icon}</span>
          <h2>{activeToolMeta?.label}</h2>
          <p>该工具页面正在接入，当前可使用电商 AI 作图、商品套图、A+ 详情与服饰穿搭。</p>
        </section>
      </div>
      <footer className="product-clone-panel__footer">
        <button type="button" className="product-clone-primary" disabled>
          暂未开放
        </button>
      </footer>
    </>
  );

  const setPreview = (
    <main className="product-clone-preview product-clone-preview--set" aria-label="AI商品套图预览" onWheel={handlePreviewWheel}>
      <div className="product-clone-preview__headline">
        <h1>预览</h1>
        <p>
          上传商品图，AI 即刻生成 <span>符合多电商平台规范</span> 的高转化率商品套图。
        </p>
      </div>

      {productSetPreviewReady ? (
        <section className="product-set-demo-board">
          <button
            type="button"
            className="product-set-main-card"
            onClick={() => openProductSetPreview(setPreviewCards[0] ?? productSetPreviewCards[0])}
          >
            <img src={setImages[0]?.src ?? (setPreviewCards[0]?.src ?? productSetPreviewCards[0].src)} alt="商品原图" />
            <span>原图素材</span>
          </button>
          <div className="product-set-flow-arrow" aria-hidden="true" />
          <div className="product-set-card-grid result-reveal">
            {setPreviewCards.map((card) => (
              <button key={card.id} type="button" onClick={() => openProductSetPreview(card)}>
                <img src={card.src} alt={card.label} />
                <span>{card.label}</span>
              </button>
            ))}
          </div>
        </section>
      ) : (
        <section className="product-set-empty-preview" aria-live="polite">
          {productSetStatus === "generating" ? <LoadingOutlined /> : <FileImageOutlined />}
          <strong>{productSetStatus === "generating" ? "正在生成" : "等待生成"}</strong>
          {productSetStatus === "generating" ? <EcommerceProgressBar status="generating" label="商品套图" /> : null}
          <span>{productSetStatus === "generating" ? "AI 正在整理主图、场景、细节与卖点图。" : "上传商品原图并填写信息后，AI 将为您生成专业的电商商品图。"}</span>
        </section>
      )}

      {productSetStatus === "done" ? <p className="product-set-generated-note">已生成{selectedProductSetOutput.label}预览</p> : null}

      <section className="product-set-floating-detail" aria-label="淇℃伅璇︽儏">
        <div className="product-set-floating-detail__head">
          <strong>淇℃伅璇︽儏</strong>
          <span>{productSetRequirement.length}/500</span>
        </div>
        <textarea
          value={productSetRequirement}
          onChange={(event) => setProductSetRequirement(event.target.value)}
          maxLength={500}
          placeholder="寤鸿鍖呭惈浠ヤ笅淇℃伅锛氫骇鍝佸悕绉般€佹牳蹇冨崠鐐广€佹湡鏈涘満鏅€佸叿浣撳弬鏁?"
        />
        <button type="button" className="product-set-floating-submit" disabled={!canGenerateSet} onClick={handleSetGenerate}>
          {productSetStatus === "generating" ? <LoadingOutlined /> : null}
          {setPrimaryLabel}
        </button>
        {productSetStatus === "generating" ? (
          <button type="button" className="product-set-floating-submit product-set-floating-submit--cancel" onClick={handleCancelGenerate}>
            {"\u53d6\u6d88\u751f\u6210"}
          </button>
        ) : null}
      </section>

      <button type="button" className="product-clone-help" aria-label="甯姪">
        <QuestionCircleOutlined />
      </button>
    </main>
  );

  const composerSettingLabel =
    cloneOutput === "set"
      ? "套图 " + String(cloneSetTotal) + "张"
      : cloneOutput === "detail"
        ? "详情 " + String(selectedCloneDetailModules.length) + "项"
      : cloneOutput === "model"
        ? cloneModelPanelTab === "scene" ? "场景设置" : "模特设置"
      : cloneOutput === "video"
        ? String(cloneVideoDuration) + "秒 " + (cloneVideoQuality === "standard" ? "720P" : "1080P")
      : cloneOutput === "hot"
        ? cloneReplicateLevel === "style" ? "风格复刻" : "高度复刻"
        : "换装素材";

  const renderComposerMenu = () => {
    const visibleCloneOutputOptions = cloneOutputOptions.filter((option) => option.key !== "video-outfit");
    const composerLanguageOptions = Array.from(new Set(marketLanguageOptions.flatMap((option) => option.languages))).map((item) => ({
      language: item,
      countries: marketLanguageOptions.filter((option) => option.languages.includes(item)).map((option) => option.country),
    }));
    const composerPopoverStyle: CSSProperties = { left: composerPopoverLeft };
    const menuToRender = composerMenu ?? visibleComposerMenu;
    if (!menuToRender) return null;
    const popoverClosingClass = !composerMenu && isComposerMenuClosing ? " is-closing" : "";
    const composerPopoverKey = `${menuToRender}-${cloneOutput}-${popoverClosingClass ? "closing" : "open"}`;
    if (menuToRender === "mode") {
      return (
        <div key={composerPopoverKey} className={`ecom-command-popover ecom-command-popover--grid${popoverClosingClass}`} style={composerPopoverStyle}>
          {visibleCloneOutputOptions.map((option) => (
            <button key={option.key} type="button" className={cloneOutput === option.key ? "is-active" : ""} onClick={() => { handleCloneOutputChange(option.key); setComposerMenu(null); }}>
              {option.label}
            </button>
          ))}
        </div>
      );
    }
    if (menuToRender === "platform") {
      return (
        <div key={composerPopoverKey} className={`ecom-command-popover ecom-command-popover--list ecom-command-popover--ratio ecom-command-popover--platform${popoverClosingClass}`} style={composerPopoverStyle}>
          {platformOptions.map((option) => (
            <button key={option} type="button" className={platform === option ? "is-active" : ""} onClick={() => { handleClonePlatformChange(option); setComposerMenu(null); }}>
              {renderPlatformLogo(option)}
              <span className="ecom-platform-name">{option}</span>
            </button>
          ))}
        </div>
      );
    }
    if (menuToRender === "language") {
      return (
        <div key={composerPopoverKey} className={`ecom-command-popover ecom-command-popover--languages${popoverClosingClass}`} style={composerPopoverStyle}>
          {composerLanguageOptions.map((option) => (
            <button key={option.language} type="button" className={language === option.language ? "is-active" : ""} onClick={() => { setLanguage(option.language); setComposerMenu(null); }}>
              <strong>{option.language}</strong>
              <span>({option.countries.slice(0, 3).join(" / ")}{option.countries.length > 3 ? " +" + String(option.countries.length - 3) : ""})</span>
            </button>
          ))}
        </div>
      );
    }
    if (menuToRender === "ratio") {
      return (
        <div key={composerPopoverKey} className={`ecom-command-popover ecom-command-popover--list${popoverClosingClass}`} style={composerPopoverStyle}>
          {cloneRatioOptions.map((option) => (
            <button key={option} type="button" className={ratio === option ? "is-active" : ""} onClick={() => { setRatio(option); setComposerMenu(null); }}>
              <span className="ecom-command-button-text">{formatRatioDisplayValue(option)}</span>
            </button>
          ))}
        </div>
      );
    }
    return (
      <div key={composerPopoverKey} className={`ecom-command-popover ecom-command-popover--settings ecom-command-popover--settings-${cloneOutput}${popoverClosingClass}`} style={composerPopoverStyle}>
        {cloneOutput === "set" ? (
          <>
            <header><strong>套图分类设置</strong><span>总数 1-16 张</span></header>
            {cloneSetCountOptions.map((item) => (
              <div key={item.key} className="ecom-command-count-row">
                <span><strong>{item.title}</strong><em>{item.desc}</em></span>
                <div>
                  <button type="button" onClick={() => updateCloneSetCount(item.key, -1)} disabled={cloneSetCounts[item.key] <= 0 || cloneSetTotal <= minCloneSetTotal}>-</button>
                  <b>{cloneSetCounts[item.key]}</b>
                  <button type="button" onClick={() => updateCloneSetCount(item.key, 1)} disabled={cloneSetTotal >= maxCloneSetTotal}>+</button>
                </div>
              </div>
            ))}
          </>
        ) : cloneOutput === "detail" ? (
          <>
            <header><strong>详情模块设置</strong><span>多选</span></header>
            <div className="ecom-command-module-grid ecom-command-module-grid--detail">
              {cloneDetailModules.map((module) => (
                <button key={module.id} type="button" className={selectedCloneDetailModules.includes(module.id) ? "is-active" : ""} onClick={() => toggleCloneDetailModule(module.id)}>
                  <strong>{module.title}</strong><span>{module.desc}</span>
                </button>
              ))}
            </div>
          </>
        ) : cloneOutput === "model" ? (
          <>
            <header><strong>场景设置</strong><span>场景 / 形象</span></header>
            <div className="ecom-command-module-grid ecom-command-module-grid--model">
              {tryOnScenes.map((scene) => (
                <button key={scene} type="button" className={selectedCloneModelScenes.includes(scene) ? "is-active" : ""} onClick={() => toggleCloneModelScene(scene)}>{scene}</button>
              ))}
            </div>
            <div className="ecom-command-model-profile">
              <strong className="ecom-command-model-profile__title">模特形象</strong>
              {cloneModelSelects.map((item) => (
                <section key={item.key}>
                  <strong>{item.label}</strong>
                  <div>
                    {item.options.map((option) => (
                      <button key={option} type="button" className={item.value === option ? "is-active" : ""} onClick={() => item.onChange(option)}>{option}</button>
                    ))}
                  </div>
                </section>
              ))}
            </div>
          </>
        ) : cloneOutput === "video" ? (
          <>
            <header><strong>短视频设置</strong><span>{cloneVideoDuration}秒</span></header>
            <div className="ecom-command-module-grid ecom-command-module-grid--video">
              {cloneVideoQualityOptions.map((option) => (
                <button key={option.key} type="button" className={cloneVideoQuality === option.key ? "is-active" : ""} onClick={() => setCloneVideoQuality(option.key)}>
                  <strong>{option.label}</strong><span>{option.desc}</span>
                </button>
              ))}
            </div>
            <label className="ecom-command-range">
              <span>视频时长 {cloneVideoDuration}秒</span>
              <input type="range" min={cloneVideoDurationMin} max={cloneVideoDurationMax} step={5} value={cloneVideoDuration} onChange={(event) => setCloneVideoDuration(clampCloneVideoDuration(Number(event.target.value)))} />
            </label>
          </>
        ) : cloneOutput === "hot" ? (
          <>
            <header><strong>爆款复刻设置</strong><span>{cloneReferenceImages.length}/{maxCloneReferenceImages}</span></header>
            <div className="ecom-command-hot-layout">
              <button
                type="button"
                className={`ecom-command-hot-upload${isCloneReferenceDragging ? " is-dragging" : ""}${cloneReferenceImages.length ? " has-image" : ""}`}
                onClick={() => cloneReferenceInputRef.current?.click()}
                onDragOver={handleCloneReferenceDragOver}
                onDragLeave={handleCloneReferenceDragLeave}
                onDrop={handleCloneReferenceDrop}
              >
                {cloneReferenceImages[0]?.src ? (
                  <>
                    <span className="ecom-command-hot-thumb-grid">
                      {cloneReferenceImages.map((image, index) => (
                        <span key={image.id} className="ecom-command-hot-thumb">
                          <img src={image.src} alt={`参考图 ${index + 1}`} />
                          <span className="ecom-command-hot-zoom" aria-hidden="true">
                            <img src={image.src} alt="" />
                          </span>
                        </span>
                      ))}
                    </span>
                    <span>已上传 {cloneReferenceImages.length} 张，点击继续上传</span>
                  </>
                ) : (
                  <>
                    <strong>+ 上传参考图片</strong>
                    <span>支持拖拽上传</span>
                  </>
                )}
              </button>
              <div className="ecom-command-hot-levels">
                {cloneReplicateLevelOptions.map((option) => (
                  <button key={option.key} type="button" className={cloneReplicateLevel === option.key ? "is-active" : ""} onClick={() => setCloneReplicateLevel(option.key)}>
                    <strong>{option.title}</strong><span>{option.desc}</span>
                  </button>
                ))}
              </div>
            </div>
          </>
        ) : (
          <>
            <header><strong>视频换装设置</strong><span>上传视频和服装参考</span></header>
            <p className="ecom-command-popover-note">请在提示词中描述换装目标，并上传参考商品图。</p>
          </>
        )}
      </div>
    );
  };

  const toggleComposerMenu = (menuKey: ComposerMenuKey, event: ReactMouseEvent<HTMLButtonElement>) => {
    const composerRect = event.currentTarget.closest(".clone-ai-input-wrapper.ecom-command-composer")?.getBoundingClientRect();
    const buttonRect = event.currentTarget.getBoundingClientRect();
    setComposerPopoverLeft(Math.max(0, buttonRect.left - (composerRect?.left ?? 0)));
    setComposerMenu((menu) => (menu === menuKey ? null : menuKey));
  };

  const commandGenerateDisabled =
    cloneOutput === "video"
      ? !isAuthenticated || (!productImages.length && !requirement.trim())
      : !canGenerate;

  const handleCommandGenerate = () => {
    if (cloneOutput === "video") {
      if (!isAuthenticated) {
        requestLogin();
        return;
      }
      setVideoPlanTrigger((value) => value + 1);
      return;
    }
    handleGenerate();
  };

  const clonePreview = (
    <main className="product-clone-preview clone-ai-preview" data-status={status} aria-label="电商 AI 作图预览" {...getPreviewSurfaceProps()}>
      <header className="clone-ai-preview-header">
        <strong>预览</strong>
        <span>
          上传商品图，AI 即刻生成 <b>符合多电商平台规范</b> 的高转化率商品素材。
        </span>
        <div className="clone-ai-preview-zoom">
          <button type="button" onClick={() => setPreviewZoom((z) => Math.max(0.25, z - 0.1))} disabled={previewZoom <= 0.25} aria-label="缩小">-</button>
          <span>{Math.round(previewZoom * 100)}%</span>
          <button type="button" onClick={() => setPreviewZoom((z) => Math.min(2, z + 0.1))} disabled={previewZoom >= 2} aria-label="放大">+</button>
        </div>
      </header>

      {cloneOutput === "video" ? (
        <>
          <section className="clone-ai-flow-pipeline" aria-label="生成流程">
            {/* Source Node 鈥?鍘熷浘绱犳潗 */}
            <div className="clone-ai-flow-source">
              <div className="clone-ai-flow-node clone-ai-flow-node--source">
                {productImages[0]?.src ? (
                  <img src={productImages[0].src} alt="商品原图" />
                ) : (
                  <div className="clone-ai-flow-node__placeholder">
                    <FileImageOutlined />
                  </div>
                )}
              </div>
              <span className="clone-ai-flow-node__label">附件原图</span>
            </div>

            {/* Connector 鈥?鍒嗘敮杩炴帴绾?*/}
            <div className="clone-ai-flow-connector" aria-hidden="true">
              <div className="clone-ai-flow-connector__trunk" />
              <div className="clone-ai-flow-connector__branches">
                <div className="clone-ai-flow-connector__branch" />
                <div className="clone-ai-flow-connector__branch" />
                <div className="clone-ai-flow-connector__branch" />
              </div>
            </div>

            {/* Branches 鈥?鐢熸垚璺緞鍒嗘敮 */}
            {status === "done" ? (
              <div className="clone-ai-flow-branches">
                {results[0]?.src ? (
                  <div className="clone-ai-flow-branch">
                    <div className="clone-ai-flow-node clone-ai-flow-node--text">
                      <div className="clone-ai-flow-node__text-content">
                        <span className="clone-ai-flow-node__text-title">{selectedCloneOutput.label}</span>
                        <span className="clone-ai-flow-node__text-desc">{requirement || "AI 智能生成"}</span>
                      </div>
                    </div>
                    <div className="clone-ai-flow-arrow" aria-hidden="true" />
                    <button
                      type="button"
                      className="clone-ai-flow-node clone-ai-flow-node--result"
                      onClick={() => openProductSetPreview(results[0])}
                    >
                      <img src={results[0].src} alt={selectedCloneOutput.label} />
                      <span className="clone-ai-flow-node__tag">{selectedCloneOutput.label}</span>
                    </button>
                    <div className="clone-ai-flow-arrow" aria-hidden="true" />
                    <div className="clone-ai-flow-node clone-ai-flow-node--video">
                      <img src={results[0].src} alt="分镜视频" />
                      <span className="clone-ai-flow-node__tag clone-ai-flow-node__tag--accent">分镜视频</span>
                    </div>
                  </div>
                ) : null}
              </div>
            ) : (
              <div className="clone-ai-flow-branches clone-ai-flow-branches--empty">
                {[1, 2, 3].map((branchIndex) => (
                  <div
                    key={branchIndex}
                    className={`clone-ai-flow-branch${status === "generating" ? " is-generating" : ""}${status === "failed" ? " is-failed" : ""}`}
                  >
                    <div className="clone-ai-flow-node clone-ai-flow-node--text">
                      <div className="clone-ai-flow-node__text-content">
                        <span className="clone-ai-flow-node__text-title">分镜文本{branchIndex}</span>
                        <span className="clone-ai-flow-node__text-desc">
                          {status === "generating" ? "AI 解析中..." : "等待生成"}
                        </span>
                      </div>
                    </div>
                    <div className="clone-ai-flow-arrow" aria-hidden="true" />
                    <div className="clone-ai-flow-node clone-ai-flow-node--result">
                      <div className="clone-ai-flow-node__placeholder">
                        {status === "generating" ? <LoadingOutlined /> : <FileImageOutlined />}
                      </div>
                      <span className="clone-ai-flow-node__tag">分镜图{branchIndex}</span>
                    </div>
                    <div className="clone-ai-flow-arrow" aria-hidden="true" />
                    <div className="clone-ai-flow-node clone-ai-flow-node--video">
                      <div className="clone-ai-flow-node__placeholder">
                        {status === "generating" ? <LoadingOutlined /> : <FileImageOutlined />}
                      </div>
                      <span className="clone-ai-flow-node__tag">分镜视频{branchIndex}</span>
                    </div>
                  </div>
                ))}
              </div>
            )}
          </section>

          {/* Status Overlay 鈥?鐢熸垚鐘舵€佽鐩栧眰 */}
          {status === "generating" || status === "failed" ? (
            <section className="clone-ai-flow-status" aria-live="polite">
              {status === "generating" ? (
                <>
                  <LoadingOutlined style={{ fontSize: 28 }} />
                  <strong>正在生成</strong>
                  <EcommerceProgressBar status="generating" label={`${selectedCloneOutput.label}生成`} />
                  <span>AI 正在为 {platform} / {market} 整理{selectedCloneOutput.label}。</span>
                </>
              ) : status === "failed" ? (
                <>
                  <FrownOutlined style={{ fontSize: 28 }} />
                  <strong>生成失败</strong>
                  <span>请检查网络后点击下方重试</span>
                  {lastFailedActionRef.current ? (
                    <button type="button" className="clone-ai-retry-btn" onClick={lastFailedActionRef.current}>
                      <ReloadOutlined /> 閲嶈瘯
                    </button>
                  ) : null}
                </>
              ) : null}
            </section>
          ) : null}
        </>
      ) : (
        <>
          {status === "done" ? (
            <div className="clone-ai-preview-zoom-wrap" style={previewTransformStyle}>
            <section className="clone-ai-preview-showcase" aria-label="生成结果">
              <button
                type="button"
                className="clone-ai-main-result"
                style={{ aspectRatio: parseRatioToAspectCss(ratio) }}
                onClick={() => {
                  const sourceImage = productImages[0];
                  if (sourceImage?.src) openProductSetPreview({ src: sourceImage.src, label: "原图素材" });
                }}
              >
                <img src={productImages[0]?.src ?? (cloneOutput === "set" ? clonePreviewCards[0].src : results[0]?.src ?? "")} alt="上传商品原图" />
                <span>原图素材</span>
              </button>
              <div className="clone-ai-flow-arrow" aria-hidden="true" />
              <div className="clone-ai-result-grid result-reveal">
                {cloneOutput === "set" ? (
                  clonePreviewCards.map((card) => (
                    <button key={card.id} type="button" style={{ aspectRatio: parseRatioToAspectCss(ratio) }} onClick={() => openProductSetPreview(card)}>
                      <img src={card.src} alt={card.label} />
                      <span>{card.label}</span>
                    </button>
                  ))
                ) : results[0]?.src ? (
                  <button type="button" style={{ aspectRatio: parseRatioToAspectCss(ratio) }} onClick={() => openProductSetPreview(results[0])}>
                    <img src={results[0].src} alt={selectedCloneOutput.label} />
                    <span>{selectedCloneOutput.label}</span>
                  </button>
                ) : null}
              </div>
            </section>
            </div>
          ) : status === "idle" || status === "ready" ? null : (
            <section className="clone-ai-empty-state" aria-live="polite">
              {status === "generating" ? <LoadingOutlined /> : status === "failed" ? <FrownOutlined /> : <FileImageOutlined />}
              <strong>{status === "generating" ? "正在生成" : status === "failed" ? "生成失败" : "等待生成"}</strong>
              {status === "generating" ? <EcommerceProgressBar status="generating" label={`${selectedCloneOutput.label}生成`} /> : null}
              <span>
                {status === "generating"
                  ? "AI 正在为 " + platform + " / " + market + " 整理" + selectedCloneOutput.label + "。"
                  : status === "failed"
                  ? "请检查网络后点击下方重试"
                  : "上传商品原图并填写信息后，AI 将在这里展示生成结果。"}
              </span>
              {status === "failed" && lastFailedActionRef.current ? (
                <button type="button" className="clone-ai-retry-btn" onClick={lastFailedActionRef.current}>
                  <ReloadOutlined /> 重试
                </button>
              ) : null}
            </section>
          )}
        </>
      )}

      <section
        ref={commandComposerWrapRef}
        className={`clone-ai-bottom-input ecom-command-composer-wrap${status === "done" ? " has-generated" : " is-before-generate"}${isCommandComposerCompact && status === "done" ? " is-compact" : ""}`}
        aria-label="生成指令"
        onClick={() => {
          if (isCommandComposerCompact) setIsCommandComposerCompact(false);
        }}
      >
        <h1 className={`ecom-command-title${status === "done" ? " is-after-generate" : ""}`}>
          {typewriterText}
          <span className="typewriter-cursor" aria-hidden="true">|</span>
        </h1>
        <input
          ref={cloneReferenceInputRef}
          type="file"
          accept="image/*"
          multiple
          className="ecom-command-hidden-file"
          onChange={handleCloneReferenceUpload}
        />
        <input
          ref={productInputRef}
          type="file"
          accept="image/*,video/*"
          multiple
          className="ecom-command-hidden-file"
          onChange={handleComposerAssetUpload}
        />
        <input
          ref={smartCutoutInputRef}
          type="file"
          accept="image/*"
          multiple
          className="ecom-command-hidden-file"
          onChange={handleSmartCutoutUpload}
        />
        <div className="clone-ai-input-wrapper ecom-command-composer">
          <button
            type="button"
            className={`ecom-command-reference${productImages.length || videoOutfitVideoFile ? " has-images" : ""}${isProductUploadDragging ? " is-dragging" : ""}`}
            onClick={() => productInputRef.current?.click()}
            onDragEnter={(event) => {
              event.preventDefault();
              setIsProductUploadDragging(true);
            }}
            onDragOver={(event) => event.preventDefault()}
            onDragLeave={() => setIsProductUploadDragging(false)}
            onDrop={(event) => {
              event.preventDefault();
              setIsProductUploadDragging(false);
              const files = Array.from(event.dataTransfer.files);
              if (files.length) addComposerAssets(files);
            }}
          >
            <span aria-hidden="true"><CloudUploadOutlined /></span>
            <strong>上传商品图</strong>
          </button>
          {productImages.length || videoOutfitVideoFile ? (
            <div className="ecom-command-asset-popover" aria-label="宸蹭笂浼犵礌鏉?">
              {productImages.map((image) => (
                <figure key={image.id} className="ecom-command-asset-thumb">
                  <img src={image.src} alt={image.name || "上传图片"} />
                  <span className="ecom-command-asset-zoom" aria-hidden="true">
                    <img src={image.src} alt="" />
                  </span>
                  <button type="button" onClick={() => removeProductImage(image.id)} aria-label="删除图片">
                    <DeleteOutlined />
                  </button>
                </figure>
              ))}
              {videoOutfitVideoFile && videoOutfitPreviewUrl ? (
                <figure className="ecom-command-asset-thumb ecom-command-asset-thumb--video">
                  <video src={videoOutfitPreviewUrl} muted playsInline />
                  <span className="ecom-command-asset-zoom" aria-hidden="true">
                    <video src={videoOutfitPreviewUrl} muted playsInline />
                  </span>
                  <button type="button" onClick={() => setVideoOutfitVideoFile(null)} aria-label="删除视频">
                    <DeleteOutlined />
                  </button>
                </figure>
              ) : null}
              <button type="button" className="ecom-command-asset-add" onClick={() => productInputRef.current?.click()} aria-label="继续上传">+</button>
            </div>
          ) : null}
          <textarea
            ref={requirementTextareaRef}
            value={requirement}
            onChange={(event) => {
              const nextValue = event.target.value.slice(0, 500);
              setRequirement(nextValue);
              syncRequirementMentionQuery(nextValue, event.target.selectionStart);
            }}
            onClick={(event) => syncRequirementMentionQuery(requirement, event.currentTarget.selectionStart)}
            onKeyUp={(event) => syncRequirementMentionQuery(requirement, event.currentTarget.selectionStart)}
            onKeyDown={(event) => {
              if (event.key === "Escape") setRequirementImageMentionQuery(null);
            }}
            maxLength={500}
            placeholder={cloneRequirementPlaceholder}
          />
          {requirementImageMentionQuery !== null && ecommerceMentionImages.length ? (
            <ImageMentionMenu images={ecommerceMentionImages} query={requirementImageMentionQuery} onSelect={insertRequirementImageMention} />
          ) : null}
          <div className="ecom-command-toolbar" aria-label="生成设置">
            <div className="ecom-command-option-row">
              <button
                type="button"
                className={`ecom-command-reference ecom-command-reference--inline${productImages.length || videoOutfitVideoFile ? " has-images" : ""}${isProductUploadDragging ? " is-dragging" : ""}`}
                onClick={() => productInputRef.current?.click()}
                onDragEnter={(event) => {
                  event.preventDefault();
                  setIsProductUploadDragging(true);
                }}
                onDragOver={(event) => event.preventDefault()}
                onDragLeave={() => setIsProductUploadDragging(false)}
                onDrop={(event) => {
                  event.preventDefault();
                  setIsProductUploadDragging(false);
                  const files = Array.from(event.dataTransfer.files);
                  if (files.length) addComposerAssets(files);
                }}
              >
                <span aria-hidden="true"><CloudUploadOutlined /></span>
                <strong>上传素材</strong>
              </button>
              <button type="button" className={composerMenu === "mode" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("mode", event)}>
                <span className="ecom-command-option-icon" aria-hidden="true"><AppstoreOutlined /></span>
                {selectedCloneOutput.label}<span>模式</span>
              </button>
              <button type="button" className={composerMenu === "platform" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("platform", event)}>
                <span className="ecom-command-option-icon" aria-hidden="true"><GlobalOutlined /></span>
                <span>平台</span>{platform}
              </button>
              <button type="button" className={composerMenu === "language" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("language", event)}>
                <span className="ecom-command-option-icon" aria-hidden="true"><FileImageOutlined /></span>
                <span>语种</span>{language}
              </button>
              <button type="button" className={composerMenu === "ratio" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("ratio", event)}>
                <span className="ecom-command-option-icon" aria-hidden="true"><TableOutlined /></span>
                <span>比例</span>{formatRatioDisplayValue(ratio)}
              </button>
              <button type="button" className={composerMenu === "settings" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("settings", event)}>
                <span className="ecom-command-option-icon" aria-hidden="true"><SettingOutlined /></span>
                <span>设置</span>{composerSettingLabel}
              </button>
            </div>
            <div className="ecom-command-submit-row">
              <button type="button" className="clone-ai-send-button ecom-command-send" disabled={commandGenerateDisabled} onClick={handleCommandGenerate} aria-label={clonePrimaryLabel}>
                {status === "generating" ? <LoadingOutlined /> : "➤"}
              </button>
            </div>
          </div>
          {renderComposerMenu()}
        </div>
        {status !== "done" ? (
          <section className="ecom-command-quick-board" aria-label="快捷功能">
            {[
              { label: "智能抠图", icon: <FileImageOutlined />, onClick: openSmartCutoutUpload },
              { label: "商品套图", icon: <AppstoreOutlined />, onClick: openQuickProductSetPage },
              { label: "图片修改", icon: <SettingOutlined />, onClick: openImageWorkbenchPage },
              { label: "增/去水印", icon: <FileImageOutlined />, onClick: openWatermarkRemovalPage },
              { label: "图片批处理", icon: <FolderOpenOutlined /> },
              { label: "一键翻译", icon: <FileImageOutlined /> },
              { label: "A+/详情页", icon: <FileImageOutlined />, onClick: openQuickDetailPage },
              { label: "变清晰", icon: <FileImageOutlined /> },
              { label: "AI消除", icon: <SettingOutlined /> },
              { label: "证件照", icon: <SkinOutlined /> },
              { label: "爆款视频", icon: <CloudUploadOutlined />, onClick: openHotVideoPage },
              { label: "拼图", icon: <TableOutlined /> },
            ].map((item) => (
              <button
                key={item.label}
                type="button"
                onClick={(event) => {
                  event.preventDefault();
                  event.stopPropagation();
                  item.onClick?.();
                }}
              >
                <span aria-hidden="true">{item.icon}</span>
                <strong>{item.label}</strong>
              </button>
            ))}
          </section>
        ) : null}
        <span className="clone-ai-char-count">{requirement.length}/500</span>
      </section>
    </main>
  );

  const smartCutoutPreview = (
    <main className={`ecom-smart-cutout-page${smartCutoutImage ? " is-editor" : " is-upload"}${isSmartCutoutTransitioning ? " is-transitioning" : ""}`} aria-label="智能抠图">
      <input
        ref={smartCutoutInputRef}
        type="file"
        accept="image/*"
        multiple
        className="ecom-command-hidden-file"
        onChange={handleSmartCutoutUpload}
      />
      <nav className="ecom-smart-cutout-nav" aria-label="智能抠图导航">
        <button type="button" onClick={closeSmartCutoutTool}>
          首页
        </button>
        <button type="button" onClick={goSmartCutoutPrevious}>
          上一页
        </button>
      </nav>
      {isSmartCutoutTransitioning ? (
        <div className="ecom-smart-cutout-transition" role="status" aria-live="polite">
          <span aria-hidden="true" />
          <strong>{smartCutoutTransitionMessage.title}</strong>
          <em>{smartCutoutTransitionMessage.subtitle}</em>
        </div>
      ) : null}
      {!smartCutoutImage ? (
        <section className="ecom-smart-cutout-upload">
          <div className="ecom-smart-cutout-head">
            <strong>智能抠图</strong>
            <span>3s 一键抠图，快速去除背景</span>
          </div>
          <div className="ecom-smart-cutout-upload__body">
            <div className="ecom-smart-cutout-demo" aria-hidden="true">
              <div className="ecom-smart-cutout-demo__tile ecom-smart-cutout-demo__tile--flower" />
              <div className="ecom-smart-cutout-demo__tile ecom-smart-cutout-demo__tile--product" />
              <div className="ecom-smart-cutout-demo__tile ecom-smart-cutout-demo__tile--poster" />
              <div className="ecom-smart-cutout-demo__tile ecom-smart-cutout-demo__tile--object" />
            </div>
            <div
              className={`ecom-smart-cutout-upload-box${isSmartCutoutDragging ? " is-dragging" : ""}`}
              role="button"
              tabIndex={0}
              onClick={() => smartCutoutInputRef.current?.click()}
              onKeyDown={(event) => {
                if (event.key === "Enter" || event.key === " ") {
                  event.preventDefault();
                  smartCutoutInputRef.current?.click();
                }
              }}
              onDragEnter={(event) => {
                event.preventDefault();
                setIsSmartCutoutDragging(true);
              }}
              onDragOver={(event) => event.preventDefault()}
              onDragLeave={() => setIsSmartCutoutDragging(false)}
              onDrop={handleSmartCutoutDrop}
            >
              <button type="button" className="ecom-smart-cutout-upload__primary">
                <CloudUploadOutlined />
                上传图片
              </button>
              <button type="button" className="ecom-smart-cutout-upload__secondary">
                <FolderOpenOutlined />
                批量上传
              </button>
              <span>上传文件夹 &gt;</span>
            </div>
          </div>
        </section>
      ) : (
        <section className="ecom-smart-editor">
          <div className="ecom-smart-editor__workspace">
            <div className="ecom-smart-editor__canvas">
              <div className={`ecom-smart-editor__checker is-size-${previewSmartCutoutSizeKey}`} style={smartCutoutFrameStyle}>
                <div className="ecom-smart-editor__background-layer" aria-hidden="true" />
                <img src={previewSmartCutoutImageSrc ?? smartCutoutImage.src} alt={smartCutoutImage.name} />
              </div>
              <div className="ecom-smart-editor__canvas-actions">
                <button
                  type="button"
                  onPointerDown={showSmartCutoutOriginalCompare}
                  onPointerUp={hideSmartCutoutOriginalCompare}
                  onPointerCancel={hideSmartCutoutOriginalCompare}
                  onBlur={hideSmartCutoutOriginalCompare}
                  onKeyDown={(event) => {
                    if (event.key === " " || event.key === "Enter") setIsSmartCutoutComparing(true);
                  }}
                  onKeyUp={hideSmartCutoutOriginalCompare}
                >
                  对比
                </button>
              </div>
            </div>
            <div className="ecom-smart-editor__tools-shell">
              <strong className="ecom-smart-editor__tools-title">功能</strong>
              <button type="button" className="ecom-smart-editor__tools-nav" onClick={() => scrollSmartCutoutTools(-1)} aria-label="查看上一组尺寸">
                ‹
              </button>
              <div className="ecom-smart-editor__tools" ref={smartCutoutToolsRef}>
                {smartCutoutSizeOptions.map((item) => (
                  <div className="ecom-smart-editor__tool-item" key={item.key}>
                    <button
                      type="button"
                      className={smartCutoutSizeKey === item.key ? "is-active" : ""}
                      onClick={() => setSmartCutoutSizeKey(item.key)}
                      aria-label={`${item.label}${"sizeLabel" in item ? ` ${item.sizeLabel}` : ""}`}
                      aria-pressed={smartCutoutSizeKey === item.key}
                    >
                      <span className={`ecom-smart-editor__tool-icon ecom-smart-editor__tool-icon--${item.icon}`} aria-hidden="true" />
                    </button>
                    <span className="ecom-smart-editor__tool-text">
                      <span>{item.label}</span>
                      {"sizeLabel" in item ? <span>{item.sizeLabel}</span> : null}
                    </span>
                  </div>
                ))}
              </div>
              <button type="button" className="ecom-smart-editor__tools-nav" onClick={() => scrollSmartCutoutTools(1)} aria-label="查看更多尺寸">
                ›
              </button>
            </div>
            {smartCutoutBatchImages.length > 1 ? (
              <section className="ecom-smart-editor__batch" aria-label="批量图片">
                <header>
                  <strong>批量图片</strong>
                  <span>{smartCutoutBatchImages.length} 张</span>
                </header>
                <div>
                  {smartCutoutBatchImages.map((image, index) => (
                    <button
                      key={`${image.src}-${index}`}
                      type="button"
                      className={smartCutoutImage.src === image.src ? "is-active" : ""}
                      onClick={() => setSmartCutoutImage(image)}
                    >
                      <img src={image.src} alt={image.name || `上传图片 ${index + 1}`} />
                      <span>{index + 1}</span>
                    </button>
                  ))}
                </div>
              </section>
            ) : null}
            <section className="ecom-smart-editor__gallery">
              <header><strong>纯色</strong><button type="button">查看全部 &gt;</button></header>
              <div className="ecom-smart-editor__swatches">
                {["#ffffff", "#eeeae3", "#f2e3cf", "#000000", "#a89682", "#c9c9c9"].map((color) => (
                  <button
                    key={color}
                    type="button"
                    className={smartCutoutBackgroundColor.toLowerCase() === color ? "is-active" : ""}
                    style={{ "--smart-cutout-swatch-bg": color } as CSSProperties}
                    onClick={() => {
                      setSmartCutoutBackgroundColor(color);
                      setSmartCutoutBackgroundAlpha(100);
                    }}
                  >
                    <span className="ecom-smart-editor__swatch-bg" aria-hidden="true" />
                    <img src={smartCutoutImage.src} alt="" />
                  </button>
                ))}
              </div>
              <header><strong>AI换背景</strong><button type="button">查看全部 &gt;</button></header>
              <div className="ecom-smart-editor__scenes">
                <button type="button" className="ecom-smart-editor__generate"><SettingOutlined />立即生成</button>
                {["客厅陈列", "桌面日光", "香氛产品", "绿植窗边", "居家空间"].map((item) => (
                  <button key={item} type="button"><span>{item}</span></button>
                ))}
              </div>
            </section>
          </div>
          <aside className="ecom-smart-editor__side">
            <strong>背景颜色</strong>
            <div className="ecom-smart-editor__color-row">
              <div className="ecom-smart-editor__color-wrap" ref={smartCutoutPaletteRef} style={{ "--smart-cutout-bg": smartCutoutBackgroundValue } as CSSProperties}>
                <button
                  type="button"
                  className={`ecom-smart-editor__custom-color${isSmartCutoutPaletteOpen ? " is-active" : ""}`}
                  style={{ background: smartCutoutBackgroundColor }}
                  onClick={() => setIsSmartCutoutPaletteOpen((open) => !open)}
                  aria-label="打开背景调色盘"
                >
                  <span>色</span>
                </button>
                {isSmartCutoutPaletteOpen ? (
                  <div className="ecom-smart-color-picker" role="dialog" aria-label="背景调色盘">
                    <button
                      type="button"
                      className="ecom-smart-color-picker__plane"
                      style={{ background: `linear-gradient(to top, #000000, transparent), linear-gradient(to right, #ffffff, hsl(${smartCutoutColorHsv.h} 100% 50%))` }}
                      onPointerDown={handleSmartCutoutColorPlanePointer}
                      onPointerMove={handleSmartCutoutColorPlaneMove}
                      aria-label="选择颜色明度和饱和度"
                    >
                      <span style={{ left: `${smartCutoutColorHsv.s}%`, top: `${100 - smartCutoutColorHsv.v}%` }} />
                    </button>
                    <div className="ecom-smart-color-picker__slider ecom-smart-color-picker__slider--hue">
                      <span style={{ background: `hsl(${smartCutoutColorHsv.h} 100% 50%)` }} />
                      <input
                        type="range"
                        min={0}
                        max={360}
                        value={smartCutoutColorHsv.h}
                        onChange={(event) => applySmartCutoutHsv(Number(event.target.value), smartCutoutColorHsv.s, smartCutoutColorHsv.v)}
                        aria-label="色相"
                      />
                    </div>
                    <div className="ecom-smart-color-picker__slider ecom-smart-color-picker__slider--alpha">
                      <span style={{ background: smartCutoutBackgroundValue }} />
                      <input
                        type="range"
                        min={0}
                        max={100}
                        value={smartCutoutBackgroundAlpha}
                        onChange={(event) => setSmartCutoutBackgroundAlpha(Number(event.target.value))}
                        aria-label="透明度"
                      />
                    </div>
                    <div className="ecom-smart-color-picker__fields">
                      <span aria-hidden="true">↙</span>
                      <input
                        value={smartCutoutHexDraft}
                        onChange={(event) => handleSmartCutoutHexChange(event.target.value)}
                        onBlur={() => setSmartCutoutHexDraft(smartCutoutBackgroundColor)}
                        aria-label="HEX 色值"
                      />
                      <input
                        value={Math.round(smartCutoutBackgroundAlpha)}
                        onChange={(event) => setSmartCutoutBackgroundAlpha(clampNumber(Number(event.target.value) || 0, 0, 100))}
                        aria-label="透明度百分比"
                      />
                      <strong>%</strong>
                    </div>
                    <p>纯色预设</p>
                    <div className="ecom-smart-color-picker__presets">
                      {smartCutoutColorPresets.map((color) => (
                        <button
                          key={color}
                          type="button"
                          className={smartCutoutBackgroundColor.toLowerCase() === color ? "is-active" : ""}
                          style={{ background: color }}
                          onClick={() => {
                            setSmartCutoutBackgroundColor(color);
                            setSmartCutoutBackgroundAlpha(100);
                          }}
                          aria-label={`选择颜色 ${color}`}
                        />
                      ))}
                    </div>
                  </div>
                ) : null}
              </div>
              {["#ffffff", "#f8f9fa", "#000000", "#bdbdbd"].map((color) => (
                <button
                  key={color}
                  type="button"
                  className={smartCutoutBackgroundColor.toLowerCase() === color ? "is-active" : ""}
                  style={{ background: color }}
                  onClick={() => {
                    setSmartCutoutBackgroundColor(color);
                    setSmartCutoutBackgroundAlpha(100);
                  }}
                  aria-label={`背景颜色 ${color}`}
                />
              ))}
            </div>
            <div className="ecom-smart-editor__side-actions">
              <button type="button" className="ecom-smart-editor__download" onClick={handleSmartCutoutDownload}>下载图片</button>
              <button type="button" onClick={() => smartCutoutInputRef.current?.click()}>再次上传</button>
            </div>
          </aside>
        </section>
      )}
    </main>
  );

  const imageWorkbenchPreview = (
    <main className="ecom-image-workbench-page" aria-label="图片修改局部重绘">
      <input
        ref={imageWorkbenchInputRef}
        type="file"
        accept="image/*"
        className="ecom-command-hidden-file"
        onChange={handleImageWorkbenchUpload}
      />
      <nav className="ecom-image-workbench-nav" aria-label="图片修改导航">
        <button type="button" onClick={closeImageWorkbenchPage}>首页</button>
        <button type="button" onClick={closeImageWorkbenchPage}>上一页</button>
      </nav>
      <aside className="ecom-image-workbench-side">
        <div className="ecom-image-workbench-heading">
          <span>图片修改</span>
          <strong>图片修改</strong>
          <p>涂抹需要修改的区域，输入提示词后让 AI 替换局部内容。</p>
        </div>

        <section className="ecom-image-workbench-panel">
          <header>
            <strong>素材</strong>
            <span>{imageWorkbenchImage ? "已上传" : "待上传"}</span>
          </header>
          <div
            className={`ecom-image-workbench-upload${isImageWorkbenchDragging ? " is-dragging" : ""}${imageWorkbenchImage ? " has-image" : ""}`}
            role="button"
            tabIndex={0}
            onClick={() => imageWorkbenchInputRef.current?.click()}
            onKeyDown={(event) => {
              if (event.key === "Enter" || event.key === " ") {
                event.preventDefault();
                imageWorkbenchInputRef.current?.click();
              }
            }}
            onDragEnter={(event) => {
              event.preventDefault();
              setIsImageWorkbenchDragging(true);
            }}
            onDragOver={(event) => event.preventDefault()}
            onDragLeave={() => setIsImageWorkbenchDragging(false)}
            onDrop={handleImageWorkbenchDrop}
          >
            {imageWorkbenchImage ? (
              <>
                <button
                  type="button"
                  className="ecom-image-workbench-remove"
                  onClick={(event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    removeImageWorkbenchImage();
                  }}
                  aria-label="删除素材"
                >
                  ×
                </button>
                <figure>
                  <img src={imageWorkbenchImage.src} alt={imageWorkbenchImage.name} />
                </figure>
                <div>
                  <strong>{imageWorkbenchImage.name}</strong>
                  <span>{imageWorkbenchImage.format || "PNG / JPG / WebP"}</span>
                </div>
              </>
            ) : (
              <>
                <FileImageOutlined />
                <strong>选择图片</strong>
                <span>PNG / JPG / WebP</span>
              </>
            )}
          </div>
          <div className="ecom-image-workbench-url-row">
            <input placeholder="粘贴图片 URL" aria-label="粘贴图片 URL" />
            <button type="button" onClick={() => toast.info("请先使用本地上传")}>添加</button>
          </div>
        </section>

        <section className="ecom-image-workbench-panel">
          <header>
            <strong>笔刷</strong>
            <span>{imageWorkbenchMaskStrokes.length ? `已遮罩 ${imageWorkbenchMaskStrokes.length}` : "未遮罩"}</span>
          </header>
          <label className="ecom-image-workbench-slider">
            <span>大小</span>
            <input
              type="range"
              min={10}
              max={120}
              value={imageWorkbenchBrushSize}
              onChange={(event) => setImageWorkbenchBrushSize(Number(event.target.value))}
            />
            <em>{imageWorkbenchBrushSize}px</em>
          </label>
          <button
            type="button"
            className="ecom-image-workbench-clear"
            onClick={() => {
              setImageWorkbenchMaskStrokes([]);
              setImageWorkbenchBrushCursor(null);
              clearImageWorkbenchMaskCanvas();
              setImageWorkbenchStatus("idle");
            }}
            disabled={!imageWorkbenchMaskStrokes.length}
          >
            清空遮罩
          </button>
        </section>

        <section className="ecom-image-workbench-panel">
          <strong>参数</strong>
          <div className="ecom-image-workbench-ratios" aria-label="输出分辨率">
            {["9:16", "16:9", "4:3", "3:4", "1:1"].map((item) => (
              <button
                key={item}
                type="button"
                className={imageWorkbenchRatio === item ? "is-active" : ""}
                onClick={() => setImageWorkbenchRatio(item)}
              >
                {item}
              </button>
            ))}
          </div>
        </section>

        <section className="ecom-image-workbench-panel">
          <strong>提示词</strong>
          <textarea
            value={imageWorkbenchPrompt}
            onChange={(event) => setImageWorkbenchPrompt(event.target.value.slice(0, 300))}
            placeholder="描述需要重绘的内容，例如：将背景替换为森林"
          />
        </section>

        <button
          type="button"
          className="ecom-image-workbench-primary"
          onClick={handleImageWorkbenchGenerate}
          disabled={!imageWorkbenchImage || imageWorkbenchStatus === "processing"}
        >
          {imageWorkbenchStatus === "processing" ? <LoadingOutlined /> : <SettingOutlined />}
          {imageWorkbenchStatus === "processing" ? "重绘中" : "开始重绘"}
        </button>
      </aside>

      <section className="ecom-image-workbench-stage">
        <div
          className={`ecom-image-workbench-canvas${isImageWorkbenchDragging ? " is-dragging" : ""}${imageWorkbenchImage ? " has-image" : ""}`}
          role="button"
          tabIndex={0}
          onClick={() => {
            if (!imageWorkbenchImage) imageWorkbenchInputRef.current?.click();
          }}
          onKeyDown={(event) => {
            if (!imageWorkbenchImage && (event.key === "Enter" || event.key === " ")) {
              event.preventDefault();
              imageWorkbenchInputRef.current?.click();
            }
          }}
          onDragEnter={(event) => {
            event.preventDefault();
            setIsImageWorkbenchDragging(true);
          }}
          onDragOver={(event) => event.preventDefault()}
          onDragLeave={() => setIsImageWorkbenchDragging(false)}
          onDrop={handleImageWorkbenchDrop}
        >
          {imageWorkbenchImage ? (
            <div className="ecom-image-workbench-preview">
              <div
                className="ecom-image-workbench-image-frame"
                onPointerDown={handleImageWorkbenchMaskPointerDown}
                onPointerMove={handleImageWorkbenchMaskPointerMove}
                onPointerUp={stopImageWorkbenchMaskPainting}
                onPointerCancel={stopImageWorkbenchMaskPainting}
                onPointerLeave={() => {
                  stopImageWorkbenchMaskPainting();
                  setImageWorkbenchBrushCursor(null);
                }}
              >
                <img src={imageWorkbenchImage.src} alt="局部重绘素材" draggable={false} />
                <canvas ref={imageWorkbenchMaskCanvasRef} className="ecom-image-workbench-mask-layer" aria-label="遮罩区域" />
                <span
                  className="ecom-image-workbench-brush"
                  style={{
                    left: `${imageWorkbenchBrushCursor?.x ?? 50}%`,
                    top: `${imageWorkbenchBrushCursor?.y ?? 50}%`,
                    width: imageWorkbenchBrushSize,
                    height: imageWorkbenchBrushSize,
                  }}
                />
              </div>
              <em>{imageWorkbenchStatus === "done" ? "重绘预览" : imageWorkbenchStatus === "processing" ? "正在生成重绘结果" : "按住鼠标涂抹需要修改的区域"}</em>
            </div>
          ) : (
            <div className="ecom-image-workbench-empty">
              <FileImageOutlined />
              <strong>点击或拖拽上传图片</strong>
              <span>支持 PNG / JPG / WebP，上传后使用画笔标记需要重绘的区域</span>
            </div>
          )}
        </div>
      </section>
    </main>
  );

  const watermarkPreview = (
    <main className="ecom-watermark-page" aria-label="去水印">
      <input
        ref={watermarkInputRef}
        type="file"
        accept="image/*"
        className="ecom-command-hidden-file"
        onChange={handleWatermarkUpload}
      />
      <nav className="ecom-watermark-nav" aria-label="去水印导航">
        <button type="button" onClick={closeWatermarkRemovalPage}>首页</button>
        <button type="button" onClick={closeWatermarkRemovalPage}>上一页</button>
      </nav>
      <aside className="ecom-watermark-side">
        <div className="ecom-watermark-heading">
          <span>AI 电商工具</span>
          <strong>增/去水印</strong>
          <p>上传商品素材，快速清理画面中的水印、文字和瑕疵。</p>
        </div>
        <section className="ecom-watermark-panel">
          <header>
            <strong>上传素材</strong>
            <span>{watermarkImage ? "已上传" : "待上传"}</span>
          </header>
          <div
            className={`ecom-watermark-upload-card${isWatermarkDragging ? " is-dragging" : ""}${watermarkImage ? " has-image" : ""}`}
            role="button"
            tabIndex={0}
            onClick={() => watermarkInputRef.current?.click()}
            onKeyDown={(event) => {
              if (event.key === "Enter" || event.key === " ") {
                event.preventDefault();
                watermarkInputRef.current?.click();
              }
            }}
            onDragEnter={(event) => {
              event.preventDefault();
              setIsWatermarkDragging(true);
            }}
            onDragOver={(event) => event.preventDefault()}
            onDragLeave={() => setIsWatermarkDragging(false)}
            onDrop={handleWatermarkDrop}
          >
            {watermarkImage ? (
              <>
                <button
                  type="button"
                  className="ecom-watermark-remove"
                  onClick={(event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    removeWatermarkImage();
                  }}
                  aria-label="删除素材"
                >
                  ×
                </button>
                <figure>
                  <img src={watermarkImage.src} alt={watermarkImage.name} />
                </figure>
                <div>
                  <strong>{watermarkImage.name}</strong>
                  <span>{watermarkImage.format || "PNG / JPG / WebP"}</span>
                </div>
              </>
            ) : (
              <>
                <CloudUploadOutlined />
                <strong>上传含水印图片</strong>
                <span>支持 PNG / JPG / WebP，拖拽或点击上传</span>
              </>
            )}
          </div>
          <div className="ecom-watermark-url-row">
            <input placeholder="粘贴图片 URL" aria-label="粘贴图片 URL" />
            <button type="button" onClick={() => toast.info("请先使用本地上传")}>导入</button>
          </div>
        </section>

        <section className="ecom-watermark-panel">
          <strong>处理说明</strong>
          <p>优先保留商品主体、材质和边缘细节，适合电商主图、详情图和社媒素材清理。</p>
        </section>

        <button
          type="button"
          className="ecom-watermark-primary"
          onClick={handleWatermarkGenerate}
          disabled={!watermarkImage || watermarkStatus === "processing"}
        >
          {watermarkStatus === "processing" ? <LoadingOutlined /> : <FileImageOutlined />}
          {watermarkStatus === "processing" ? "处理中" : "开始去水印"}
        </button>
      </aside>

      <section className="ecom-watermark-workspace">
        {!watermarkImage ? (
          <div
            className={`ecom-watermark-dropzone${isWatermarkDragging ? " is-dragging" : ""}`}
            role="button"
            tabIndex={0}
            onClick={() => watermarkInputRef.current?.click()}
            onKeyDown={(event) => {
              if (event.key === "Enter" || event.key === " ") {
                event.preventDefault();
                watermarkInputRef.current?.click();
              }
            }}
            onDragEnter={(event) => {
              event.preventDefault();
              setIsWatermarkDragging(true);
            }}
            onDragOver={(event) => event.preventDefault()}
            onDragLeave={() => setIsWatermarkDragging(false)}
            onDrop={handleWatermarkDrop}
          >
            <CloudUploadOutlined />
            <strong>点击或拖拽上传图片</strong>
            <span>支持 PNG / JPG / WebP，上传含水印图片后点击开始去水印</span>
          </div>
        ) : (
          <div className="ecom-watermark-grid">
            <article className="ecom-watermark-preview-card">
              <span>原图</span>
              <img src={watermarkImage.src} alt="原图" />
            </article>

            <article className="ecom-watermark-preview-card">
              <span>去水印结果</span>
              {watermarkStatus === "processing" ? (
                <div className="ecom-watermark-processing" role="status" aria-live="polite">
                  <LoadingOutlined />
                  <strong>正在去水印</strong>
                  <em>AI 正在清理图片中的水印和文字</em>
                </div>
              ) : watermarkStatus === "done" ? (
                <>
                  <img src={watermarkImage.src} alt="去水印结果" />
                  <button type="button" className="ecom-watermark-zoom" aria-label="查看大图">
                    <QuestionCircleOutlined />
                  </button>
                </>
              ) : (
                <div className="ecom-watermark-empty">
                  <FileImageOutlined />
                  <strong>等待处理</strong>
                  <em>点击开始去水印后显示结果</em>
                </div>
              )}
              <div className="ecom-watermark-actions">
                <button type="button" onClick={() => toast.success("已加入资产库")} disabled={watermarkStatus !== "done"}>
                  <FolderOpenOutlined />
                  加入资产库
                </button>
                <button type="button" onClick={handleWatermarkDownload} disabled={watermarkStatus !== "done"}>
                  <CloudUploadOutlined />
                  下载图片
                </button>
              </div>
            </article>
          </div>
        )}
      </section>
    </main>
  );

  const quickSetVisibleSelect = quickSetBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
  const openQuickUploadWithKeyboard = (
    event: ReactKeyboardEvent<HTMLDivElement>,
    inputRef: { current: HTMLInputElement | null },
  ) => {
    if (event.key !== "Enter" && event.key !== " ") return;
    event.preventDefault();
    inputRef.current?.click();
  };
  const renderQuickUploadThumbs = (items: CloneImageItem[], onRemove: (imageId: string) => void) => (
    <div className="ecom-quick-upload-thumbs" aria-label="已上传商品原图">
      {items.map((item) => (
        <figure key={item.id} className="ecom-command-asset-thumb ecom-quick-upload-thumb">
          <img src={item.src} alt={item.name} />
          <span className="ecom-command-asset-zoom" aria-hidden="true">
            <img src={item.src} alt="" />
          </span>
          <button
            type="button"
            aria-label="删除图片"
            onClick={(event) => {
              event.stopPropagation();
              onRemove(item.id);
            }}
          >
            ×
          </button>
        </figure>
      ))}
    </div>
  );

  const hotVideoPreview = (
    <main className="ecom-hot-video-page" aria-label="爆款视频">
      <nav className="ecom-hot-video-nav">
        <button type="button" className="ecom-hot-video-back" onClick={closeHotVideoPage}>
          ‹ 返回首页
        </button>
        <div className="ecom-hot-video-nav-title">
          <h1>爆款视频</h1>
          <span>AI智能策划 · 一键生成电商短视频</span>
        </div>
        <div className="ecom-hot-video-nav-meta">
          <span>{platform} / {formatRatioDisplayValue(ratio)} / {cloneVideoDuration}秒 / {cloneVideoQuality === "standard" ? "720P" : "1080P"}</span>
        </div>
      </nav>
      <div className="ecom-hot-video-body">
        <aside className="ecom-hot-video-settings" aria-label="视频设置">
          <section className="ecom-hot-video-section">
            <strong>上传商品图</strong>
            <div
              role="button"
              tabIndex={0}
              className={`ecom-hot-video-upload${productImages.length ? " has-images" : ""}${isProductUploadDragging ? " is-dragging" : ""}`}
              onClick={() => quickProductInputRef.current?.click()}
              onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") quickProductInputRef.current?.click(); }}
              onDragOver={(event) => { event.preventDefault(); setIsProductUploadDragging(true); }}
              onDragLeave={(event) => { event.preventDefault(); setIsProductUploadDragging(false); }}
              onDrop={handleProductDrop}
            >
              <CloudUploadOutlined />
              <span>拖拽或点击上传商品图片</span>
              <em>支持 JPG / PNG / WebP，建议清晰白底图</em>
              {productImages.length > 0 ? (
                <div className="ecom-hot-video-upload-thumbs">
                  {productImages.map((img) => (
                    <figure key={img.id}>
                      <img src={img.src} alt={img.name} />
                      <button
                        type="button"
                        aria-label="删除"
                        onClick={(event) => { event.stopPropagation(); removeProductImage(img.id); }}
                      >×</button>
                    </figure>
                  ))}
                </div>
              ) : null}
            </div>
            <input
              ref={quickProductInputRef}
              type="file"
              accept="image/*"
              multiple
              className="ecom-command-hidden-file"
              onChange={handleProductUpload}
            />
          </section>

          <section className="ecom-hot-video-section">
            <strong>商品描述</strong>
            <textarea
              className="ecom-hot-video-textarea"
              placeholder="描述你的商品特点、卖点、使用场景等，帮助AI更好地策划视频内容..."
              rows={4}
              value={requirement}
              onChange={(event) => setRequirement(event.target.value)}
              maxLength={500}
            />
          </section>

          <section className="ecom-hot-video-section">
            <strong>电商平台</strong>
            <div className="ecom-hot-video-options">
              {platformOptions.map((option) => (
                <button
                  key={option}
                  type="button"
                  className={platform === option ? "is-active" : ""}
                  onClick={() => setPlatform(option)}
                >
                  {renderPlatformLogo(option)}
                  <span>{option}</span>
                </button>
              ))}
            </div>
          </section>

          <section className="ecom-hot-video-section">
            <strong>视频比例</strong>
            <div className="ecom-hot-video-options ecom-hot-video-options--ratio">
              {cloneRatioOptions.map((option) => (
                <button
                  key={option}
                  type="button"
                  className={ratio === option ? "is-active" : ""}
                  onClick={() => setRatio(option)}
                >
                  {formatRatioDisplayValue(option)}
                </button>
              ))}
            </div>
          </section>

          <section className="ecom-hot-video-section">
            <strong>视频质量</strong>
            <div className="ecom-hot-video-options">
              {cloneVideoQualityOptions.map((option) => (
                <button
                  key={option.key}
                  type="button"
                  className={cloneVideoQuality === option.key ? "is-active" : ""}
                  onClick={() => setCloneVideoQuality(option.key)}
                >
                  <strong>{option.label}</strong>
                  <span>{option.desc}</span>
                </button>
              ))}
            </div>
          </section>

          <section className="ecom-hot-video-section">
            <strong>视频时长 · {cloneVideoDuration}秒</strong>
            <input
              type="range"
              className="ecom-hot-video-range"
              min={cloneVideoDurationMin}
              max={cloneVideoDurationMax}
              step={5}
              value={cloneVideoDuration}
              onChange={(event) => setCloneVideoDuration(clampCloneVideoDuration(Number(event.target.value)))}
              style={cloneVideoDurationStyle}
            />
            <div className="ecom-hot-video-range-labels">
              <span>{cloneVideoDurationMin}秒</span>
              <span>{cloneVideoDurationMax}秒</span>
            </div>
          </section>

          <button
            type="button"
            className="ecom-hot-video-start"
            disabled={!productImages.length && !requirement.trim()}
            onClick={() => setVideoPlanTrigger((prev) => prev + 1)}
          >
            一键策划
          </button>
        </aside>
        <section className="ecom-hot-video-workspace">
          <EcommerceVideoWorkspace
            isAuthenticated={isAuthenticated}
            productImageDataUrls={ecommerceVideoImageDataUrls}
            productImageFiles={ecommerceVideoImageFiles}
            requirement={requirement}
            platform={platform}
            aspectRatio={ratio.includes("9：16") || ratio.includes("9:16") ? "9:16" : ratio.includes("16：9") || ratio.includes("16:9") ? "16:9" : ratio.includes("3：4") || ratio.includes("3:4") ? "3:4" : "9:16"}
            durationSeconds={cloneVideoDuration}
            resolution={cloneVideoQuality === "standard" ? "720P" : "1080P"}
            onRequestLogin={() => (isAuthenticated ? undefined : requestLogin())}
            onOpenHistory={() => setVideoHistoryVisible(true)}
            triggerPlan={videoPlanTrigger}
          />
        </section>
      </div>
    </main>
  );

  const quickProductSetPreview = (
    <main className={`ecom-quick-set-page${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="AI商品套图">
      <div className="ecom-quick-set-body">
        <aside className="ecom-quick-set-panel" aria-label="商品套图设置" onWheel={handleQuickPanelWheel}>
          <header className="ecom-quick-set-panel-head">
            <button type="button" className="ecom-quick-set-back" onClick={() => setActiveQuickTool(null)}>
              首页
            </button>
            <button type="button" className="ecom-quick-set-back" onClick={() => setActiveQuickTool(null)}>
              上一页
            </button>
          </header>
          <section>
            <strong><CloudUploadOutlined /> 上传商品原图</strong>
            <div
              role="button"
              tabIndex={0}
              className={`ecom-quick-set-upload${productImages.length ? " has-images" : ""}`}
              onClick={() => quickProductInputRef.current?.click()}
              onKeyDown={(event) => openQuickUploadWithKeyboard(event, quickProductInputRef)}
              onDragOver={(event) => event.preventDefault()}
              onDrop={handleQuickProductDrop}
            >
              <FileImageOutlined />
              <span>拖拽或点击上传</span>
              <em>同一产品，最多 7 张</em>
              <b>+ 上传图片</b>
              {productImages.length ? renderQuickUploadThumbs(productImages, removeProductImage) : null}
            </div>
            <input
              ref={quickProductInputRef}
              type="file"
              accept="image/*"
              multiple
              className="ecom-command-hidden-file"
              onChange={handleProductUpload}
            />
          </section>
          <section className="ecom-quick-set-basic-section">
            <span className="ecom-quick-set-label">基础设置</span>
            <div className="ecom-quick-set-select-anchor">
              <div className="ecom-quick-set-selects">
                {quickSetBasicSelects.map((item) => (
                  <button
                    key={item.key}
                    type="button"
                    className={openQuickSetSelect === item.key ? "is-active" : ""}
                    onClick={() => toggleQuickSetSelect(item.key)}
                  >
                    <span>{item.label}</span><strong>{item.value}</strong><em>⌄</em>
                  </button>
                ))}
              </div>
              {quickSetVisibleSelect ? (
                <div
                  className={`ecom-quick-set-dropdown ecom-quick-set-dropdown--${quickSetVisibleSelect.key}${isQuickSetSelectClosing ? " is-closing" : ""}`}
                  role="listbox"
                  aria-label={quickSetVisibleSelect.label}
                >
                  {quickSetVisibleSelect.options.map((option) => (
                    <button
                      key={option}
                      type="button"
                      className={quickSetVisibleSelect.value === option ? "is-active" : ""}
                      onClick={() => {
                        quickSetVisibleSelect.onChange(option);
                        closeQuickSetSelect();
                      }}
                    >
                      {option}
                    </button>
                  ))}
                </div>
              ) : null}
            </div>
          </section>
          <section>
            <strong>可自由调整各类型图片数量，总数 1-16 张</strong>
            <div className="ecom-quick-set-counts">
              {cloneSetCountOptions.map((item) => (
                <article key={item.key}>
                  <div>
                    <strong>{item.title}</strong>
                    <span>{item.desc}</span>
                  </div>
                  <p>
                    <button type="button" onClick={() => updateCloneSetCount(item.key, -1)} disabled={cloneSetCounts[item.key] <= 0 || cloneSetTotal <= minCloneSetTotal}>-</button>
                    <b>{cloneSetCounts[item.key]}</b>
                    <button type="button" onClick={() => updateCloneSetCount(item.key, 1)} disabled={cloneSetTotal >= maxCloneSetTotal}>+</button>
                  </p>
                </article>
              ))}
            </div>
          </section>
          <button type="button" className="ecom-quick-set-primary" onClick={handleGenerate} disabled={!canGenerate}>
            ✦ 开始生成
          </button>
        </aside>
        <section className="ecom-quick-set-stage">
          <header className="ecom-quick-set-preview-head">
            <h1>预览</h1>
            <p>上传商品图，AI 即刻生成 <span>符合多电商平台规范</span> 的高转化率商品素材。</p>
            <div>
              <button type="button" onClick={() => setPreviewZoom((value) => Math.max(0.25, value - 0.1))}>-</button>
              <strong>{Math.round(previewZoom * 100)}%</strong>
              <button type="button" onClick={() => setPreviewZoom((value) => Math.min(2, value + 0.1))}>+</button>
            </div>
          </header>
          <div className="ecom-quick-set-canvas" onWheel={handleQuickPreviewWheel}>
            {productImages.length ? (
              <section className="ecom-quick-set-result-card" style={{ transform: `scale(${previewZoom})` }}>
                <figure>
                  <img src={productImages[0].src} alt="商品原图" />
                  <span>原图素材</span>
                </figure>
                <div>
                  {productSetPreviewCards.slice(1, 5).map((card) => (
                    <figure key={card.id}>
                      <img src={card.src} alt={card.label} />
                      <span>{card.id === "scene" ? "场景图" : card.id === "model" ? "模特图" : card.id === "detail" ? "细节图" : "卖点图"}</span>
                    </figure>
                  ))}
                </div>
              </section>
            ) : (
              <section className="ecom-quick-set-empty">
                <CloudUploadOutlined />
                <strong>等待生成</strong>
                <span>上传商品原图并填写信息后，AI 将在这里展示生成结果。</span>
              </section>
            )}
          </div>
          <section className="ecom-quick-set-prompt">
            <textarea
              value={requirement}
              onChange={(event) => setRequirement(event.target.value.slice(0, 500))}
              placeholder="建议包含以下信息，产品名称，核心卖点，期望场景，具体参数"
            />
            <button type="button" onClick={handleGenerate} disabled={!canGenerate}>↑</button>
            <span>{requirement.length}/500</span>
          </section>
        </section>
      </div>
      <button
        type="button"
        className="ecom-quick-set-collapse"
        aria-label={isQuickPanelCollapsed ? "展开设置" : "收起设置"}
        onClick={() => setIsQuickPanelCollapsed((value) => !value)}
      >
        {isQuickPanelCollapsed ? "›" : "‹"}
      </button>
      <button type="button" className="ecom-quick-set-help" aria-label="帮助" onClick={() => toast.info("上传商品原图后，可选择平台、语言、比例并开始生成。")}>?</button>
    </main>
  );

  const quickDetailVisibleSelect = quickDetailBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;

  const quickDetailPreview = (
    <main className={`ecom-quick-set-page ecom-quick-detail-page${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="A+详情页生成">
      <div className="ecom-quick-set-body">
        <aside className="ecom-quick-set-panel" aria-label="A+详情页设置" onWheel={handleQuickPanelWheel}>
          <header className="ecom-quick-set-panel-head">
            <button type="button" className="ecom-quick-set-back" onClick={() => setActiveQuickTool(null)}>
              首页
            </button>
            <button type="button" className="ecom-quick-set-back" onClick={() => setActiveQuickTool(null)}>
              上一页
            </button>
          </header>
          <section>
            <strong><CloudUploadOutlined /> 上传商品原图</strong>
            <div
              role="button"
              tabIndex={0}
              className={`ecom-quick-set-upload${detailProductImages.length ? " has-images" : ""}`}
              onClick={() => detailInputRef.current?.click()}
              onKeyDown={(event) => openQuickUploadWithKeyboard(event, detailInputRef)}
              onDragOver={(event) => event.preventDefault()}
              onDrop={handleDetailDrop}
            >
              <FileImageOutlined />
              <span>拖拽或点击上传</span>
              <em>同一产品，最多 3 张</em>
              <b>+ 上传图片</b>
              {detailProductImages.length ? renderQuickUploadThumbs(detailProductImages, removeDetailImage) : null}
            </div>
            <input
              ref={detailInputRef}
              type="file"
              accept="image/*"
              multiple
              className="ecom-command-hidden-file"
              onChange={handleDetailUpload}
            />
          </section>
          <section className="ecom-quick-set-basic-section">
            <span className="ecom-quick-set-label">生成内容</span>
            <div className="ecom-quick-detail-types">
              {detailTypeOptions.map((item) => (
                <button
                  key={item}
                  type="button"
                  className={detailType === item ? "is-active" : ""}
                  onClick={() => setDetailType(item)}
                >
                  {item}
                </button>
              ))}
            </div>
            <span className="ecom-quick-set-label">基础设置</span>
            <div className="ecom-quick-set-select-anchor">
              <div className="ecom-quick-set-selects">
                {quickDetailBasicSelects.map((item) => (
                  <button
                    key={item.key}
                    type="button"
                    className={openQuickSetSelect === item.key ? "is-active" : ""}
                    onClick={() => toggleQuickSetSelect(item.key)}
                  >
                    <span>{item.label}</span><strong>{formatRatioDisplayValue(item.value)}</strong><em>⌄</em>
                  </button>
                ))}
              </div>
              {quickDetailVisibleSelect ? (
                <div
                  className={`ecom-quick-set-dropdown ecom-quick-set-dropdown--${quickDetailVisibleSelect.key}${isQuickSetSelectClosing ? " is-closing" : ""}`}
                  role="listbox"
                  aria-label={quickDetailVisibleSelect.label}
                >
                  {quickDetailVisibleSelect.options.map((option) => (
                    <button
                      key={option}
                      type="button"
                      className={quickDetailVisibleSelect.value === option ? "is-active" : ""}
                      onClick={() => {
                        quickDetailVisibleSelect.onChange(option);
                        closeQuickSetSelect();
                      }}
                    >
                      {formatRatioDisplayValue(option)}
                    </button>
                  ))}
                </div>
              ) : null}
            </div>
          </section>
          <section>
            <strong>包含模块（多选）</strong>
            <div className="ecom-quick-detail-modules">
              {detailModules.map((module) => (
                <button
                  key={module.id}
                  type="button"
                  className={selectedDetailModules.includes(module.id) ? "is-active" : ""}
                  onClick={() => toggleDetailModule(module.id)}
                >
                  <strong>{module.title}</strong>
                  <span>{module.desc}</span>
                </button>
              ))}
            </div>
          </section>
          <button type="button" className="ecom-quick-set-primary" onClick={handleDetailGenerate} disabled={!canGenerateDetail}>
            {detailStatus === "generating" ? <LoadingOutlined /> : "✦"} 开始生成
          </button>
        </aside>
        <section className="ecom-quick-set-stage">
          <header className="ecom-quick-set-preview-head">
            <h1>预览</h1>
            <p>上传商品图，AI 即刻生成 <span>符合多电商平台规范</span> 的高转化率详情页素材。</p>
            <div>
              <button type="button" onClick={() => setPreviewZoom((value) => Math.max(0.25, value - 0.1))}>-</button>
              <strong>{Math.round(previewZoom * 100)}%</strong>
              <button type="button" onClick={() => setPreviewZoom((value) => Math.min(2, value + 0.1))}>+</button>
            </div>
          </header>
          <div className="ecom-quick-set-canvas" onWheel={handleQuickPreviewWheel}>
            {detailStatus === "done" && detailResultUrl ? (
              <section className="ecom-quick-detail-result" style={{ transform: `scale(${previewZoom})` }}>
                <img src={detailResultUrl} alt="A+详情页生成结果" />
              </section>
            ) : detailProductImages.length ? (
              <section className="ecom-quick-detail-preview-card" style={{ transform: `scale(${previewZoom})` }}>
                {detailGridSamples.slice(0, 6).map((src, index) => (
                  <figure key={src}>
                    <img src={src} alt={`详情页模块预览 ${index + 1}`} />
                    <span>{detailModules[index]?.title ?? "详情模块"}</span>
                  </figure>
                ))}
              </section>
            ) : (
              <section className="ecom-quick-set-empty">
                <FileImageOutlined />
                <strong>等待生成</strong>
                <span>上传商品原图并填写信息后，AI 将在这里展示生成结果。</span>
              </section>
            )}
          </div>
          <section className="ecom-quick-set-prompt">
            <textarea
              value={detailRequirement}
              onChange={(event) => setDetailRequirement(event.target.value.slice(0, 500))}
              placeholder="建议包含以下信息，产品名称，核心卖点，期望场景，具体参数"
            />
            <div className="ecom-quick-detail-prompt-actions">
              <button type="button" onClick={handleDetailAiWrite} aria-label="AI帮写">AI</button>
              <button type="button" onClick={handleDetailGenerate} disabled={!canGenerateDetail}>↑</button>
            </div>
            <span>{detailRequirement.length}/500</span>
          </section>
        </section>
      </div>
      <button
        type="button"
        className="ecom-quick-set-collapse"
        aria-label={isQuickPanelCollapsed ? "展开设置" : "收起设置"}
        onClick={() => setIsQuickPanelCollapsed((value) => !value)}
      >
        {isQuickPanelCollapsed ? "›" : "‹"}
      </button>
      <button type="button" className="ecom-quick-set-help" aria-label="帮助" onClick={() => toast.info("上传商品图后，选择平台和详情模块即可生成 A+ 详情页。")}>?</button>
    </main>
  );

  const detailPreview = (
    <main className="product-clone-preview product-clone-preview--detail" aria-label="A+详情预览" onWheel={handlePreviewWheel}>
      <div className="product-clone-preview__headline">
        <h1>A+/详情页</h1>
        <p>
          上传商品图，AI 即刻生成 <span>符合多电商平台规范</span> 的专业详情页。
        </p>
      </div>

      <section className="product-detail-demo-board">
        <div className="product-detail-source-stack">
          {detailSourcePreviewImages.map((src, index) => (
            <figure key={`${src}-${index}`}>
              <img src={src} alt={`商品原图 ${index + 1}`} />
            </figure>
          ))}
          <span>上传产品图</span>
        </div>
        <div className="product-detail-flow-arrow" aria-hidden="true" />
        <div className="product-detail-long-result">
          <img src={detailResultUrl ?? detailAssets.longPage} alt="生成电商长图" />
          <span>{detailStatus === "done" ? "已生成电商长图" : "生成电商长图"}</span>
        </div>
        <div className="product-detail-grid-result">
          {detailGridSamples.map((src, index) => (
            <img key={src} src={src} alt={`详情页模块 ${index + 1}`} />
          ))}
          <span>符合多电商平台规范</span>
        </div>
      </section>

      <button type="button" className="product-clone-help" aria-label="帮助">
        <QuestionCircleOutlined />
      </button>
    </main>
  );

  const tryOnPreview = (
    <main className="product-clone-preview product-clone-preview--try-on" aria-label="服饰穿搭预览" onWheel={handlePreviewWheel}>
      <div className="product-clone-preview__headline">
        <h1>AI服饰穿搭</h1>
        <p>上传服装图，定制专属模特，即刻生成多种场景不同姿势套图。</p>
      </div>

      {tryOnResultImages.length ? (
        <section className="product-try-on-generated" aria-label="生成结果">
          {tryOnResultImages.map((src, index) => (
            <figure key={src}>
              <img src={src} alt={`生成结果 ${index + 1}`} />
              <figcaption>{selectedScenes[index % Math.max(selectedScenes.length, 1)] || "智能场景"}</figcaption>
            </figure>
          ))}
        </section>
      ) : null}

      <section className="product-try-on-demo-board">
        {tryOnCards.map((card) => (
          <article key={card.title} className={`product-try-on-card product-try-on-card--${card.tone}`}>
            <h2>{card.title}</h2>
            <div className="product-try-on-inputs">
              {card.inputs.map((src, index) => (
                <div className="product-try-on-input-group" key={`${card.title}-${src}`}>
                  <img src={src} alt={`${card.title} 输入 ${index + 1}`} />
                  {index < card.inputs.length - 1 ? <span className="product-try-on-plus">+</span> : null}
                </div>
              ))}
            </div>
            <div className="product-try-on-arrow" aria-hidden="true" />
            <div className="product-try-on-results">
              {card.results.map((src, index) => (
                <img key={src} src={src} alt={`${card.title} 示例 ${index + 1}`} />
              ))}
            </div>
          </article>
        ))}
      </section>

      <button type="button" className="product-clone-help" aria-label="帮助">
        <QuestionCircleOutlined />
      </button>
    </main>
  );

  const placeholderPreview = (
    <main className="product-clone-preview product-clone-preview--placeholder" aria-label={`${pageLabel}预览`}>
      <div className="product-clone-preview__headline">
        <h1>{pageLabel}</h1>
        <p>选择左侧已接入的工具开始生成商品视觉内容。</p>
      </div>
      <button type="button" className="product-clone-help" aria-label="帮助">
        <QuestionCircleOutlined />
      </button>
    </main>
  );

  const activePreview = isSetTool
    ? setPreview
    : isDetail
      ? detailPreview
      : isTryOn
        ? tryOnPreview
        : isCloneTool
          ? isWatermarkTool
            ? watermarkPreview
            : isImageEditTool
              ? imageWorkbenchPreview
              : isSmartCutoutTool
                ? smartCutoutPreview
                : isQuickSetTool
                  ? quickProductSetPreview
                  : isQuickDetailTool
                    ? quickDetailPreview
                    : isHotVideoTool
                      ? hotVideoPreview
                      : cloneOutput === "video-outfit" && results.length > 0 && results[0].type === "video"
                      ? (
                        <main className="product-clone-preview product-clone-preview--video-outfit" style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
                          <div style={{ maxWidth: "100%", maxHeight: "100%" }}>
                            <video src={results[0].src} controls style={{ maxWidth: "100%", maxHeight: "70vh", borderRadius: "12px" }} />
                          </div>
                        </main>
                      )
                      : clonePreview
          : placeholderPreview;

  return (
    <section
      className={`product-clone-page page-motion${isCloneTool && isCloneSettingsCollapsed ? " is-settings-collapsed" : ""}${isCloneTool && isCommandHistoryCollapsed ? " is-history-collapsed" : ""}${isSmartCutoutTool ? " is-smart-cutout-page" : ""}${isQuickSetTool || isQuickDetailTool ? " is-quick-set-page" : ""}${isWatermarkTool ? " is-watermark-page" : ""}${isImageEditTool ? " is-image-workbench-page" : ""}${isHotVideoTool ? " is-hot-video-page" : ""}`}
      data-tool={activeTool}
      aria-label={pageLabel}
    >
      <div className="product-clone-shell">
        <aside className="product-clone-rail" aria-label="商品工具">
          {sideTools.map((tool) => (
            <button key={tool.key} type="button" className={activeTool === tool.key ? "is-active" : ""} onClick={() => setActiveTool(tool.key)}>
              {tool.icon}
              <span>{tool.label}</span>
            </button>
          ))}
        </aside>

        <aside
          id={isCloneTool ? "ecommerce-clone-settings-panel" : undefined}
          className={`product-clone-panel tool-panel-enter`}
          key={activeTool}
          aria-label={`${pageLabel}鍙傛暟`}
          aria-hidden={isCloneTool && isCloneSettingsCollapsed ? true : undefined}
        >
          {isSetTool ? setPanel : isDetail ? detailPanel : isTryOn ? tryOnPanel : isCloneTool ? clonePanel : placeholderPanel}
        </aside>

        {isCloneTool && !isSmartCutoutTool && !isQuickSetTool && !isQuickDetailTool && !isWatermarkTool && !isImageEditTool && !isHotVideoTool ? (
          <button
            type="button"
            className="clone-ai-settings-toggle"
            onClick={() => setIsCloneSettingsCollapsed((current) => !current)}
            aria-label={isCloneSettingsCollapsed ? "灞曞紑璁剧疆闈㈡澘" : "鏀惰捣璁剧疆闈㈡澘"}
            aria-controls="ecommerce-clone-settings-panel"
            aria-expanded={!isCloneSettingsCollapsed}
            title={isCloneSettingsCollapsed ? "灞曞紑璁剧疆闈㈡澘" : "鏀惰捣璁剧疆闈㈡澘"}
          >
            {isCloneSettingsCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
          </button>
        ) : null}

        {false ? (
          <main className="product-clone-preview product-clone-preview--video" style={{ padding: 0 }}>
            <EcommerceVideoWorkspace
              isAuthenticated={isAuthenticated}
              productImageDataUrls={ecommerceVideoImageDataUrls}
              productImageFiles={ecommerceVideoImageFiles}
              requirement={requirement}
              platform={platform}
              aspectRatio={ratio.includes("9锛?6") || ratio.includes("9:16") ? "9:16" : ratio.includes("16锛?") || ratio.includes("16:9") ? "16:9" : ratio.includes("3锛?") || ratio.includes("3:4") ? "3:4" : "9:16"}
              durationSeconds={cloneVideoDuration}
              resolution={cloneVideoQuality === "standard" ? "720P" : "1080P"}
              onRequestLogin={() => (isAuthenticated ? undefined : requestLogin())}
              onOpenHistory={() => setVideoHistoryVisible(true)}
              triggerPlan={videoPlanTrigger}
            />
          </main>
        ) : activePreview}
      </div>

      <aside className="ecom-command-history" aria-label="生成历史">
        <div className="ecom-command-history__tools">
          <button
            type="button"
            className="ecom-command-history__toggle"
            onClick={() => setIsCommandHistoryCollapsed((current) => !current)}
            title={isCommandHistoryCollapsed ? "展开记录" : "收起记录"}
            aria-label={isCommandHistoryCollapsed ? "展开记录" : "收起记录"}
            aria-expanded={!isCommandHistoryCollapsed}
          >
            {isCommandHistoryCollapsed ? "‹" : "›"}
          </button>
          <button type="button" className="ecom-command-history__new" onClick={handleNewEcommerceConversation}>新对话</button>
          <button
            type="button"
            className={`ecom-command-history__refresh${isHistoryRefreshing ? " is-refreshing" : ""}`}
            aria-label={isHistoryRefreshing ? "刷新中" : "刷新历史"}
            title={isHistoryRefreshing ? "刷新中" : "刷新历史"}
            onPointerDown={refreshEcommerceHistory}
            onClick={refreshEcommerceHistory}
            disabled={isHistoryRefreshing}
          >
            ↻
          </button>
        </div>
        <div className="ecom-command-history__heading">
          <strong>生成记录</strong>
          <span>{ecommerceHistoryRecords.length} 条</span>
        </div>
        {historyRefreshMessage ? (
          <p key={historyRefreshStamp} className="ecom-command-history__refresh-note" role="status">{historyRefreshMessage}</p>
        ) : null}
        <nav className="ecom-command-history__list" aria-label="历史对话">
          {ecommerceHistoryRecords.length ? (
            ecommerceHistoryRecords.map((record) => {
              const outputLabel = cloneOutputOptions.find((option) => option.key === record.output)?.label || "生成记录";
              return (
                <button
                  type="button"
                  key={`${record.id}-${historyRefreshTick}`}
                  className={activeHistoryRecordId === record.id ? "is-active" : ""}
                  onClick={() => openEcommerceHistoryRecord(record)}
                >
                  <strong>{record.title}</strong>
                  <span>{outputLabel} · {formatHistoryTime(record.createdAt)}</span>
                </button>
              );
            })
          ) : (
            <p className="ecom-command-history__empty">暂无生成记录</p>
          )}
        </nav>
      </aside>

      {selectedProductSetPreview ? (
        <div className="product-set-preview-backdrop" role="presentation" onClick={() => setSelectedProductSetPreview(null)}>
          <section
            className="product-set-preview-modal"
            role="dialog"
            aria-modal="true"
            aria-label={selectedProductSetPreview.label}
            onClick={(event) => event.stopPropagation()}
          >
            <button
              type="button"
              className="product-set-preview-close"
              onClick={() => setSelectedProductSetPreview(null)}
              aria-label="关闭预览"
            >
              <CloseOutlined />
            </button>
            <img src={selectedProductSetPreview.src} alt={selectedProductSetPreview.label} />
            <strong>{selectedProductSetPreview.label}</strong>
          </section>
        </div>
      ) : null}

      {showHostingModal ? (
        <div className="product-set-hosting-backdrop" role="presentation">
          <section className="product-set-hosting-modal" role="dialog" aria-modal="true" aria-label="批量托管上线">
            <img src={productSetAssets.hosting} alt="托管模式" />
            <div className="product-set-hosting-content">
              <button type="button" className="product-set-hosting-close" onClick={() => setShowHostingModal(false)} aria-label="关闭">
                ×
              </button>
              <h2>
                批量托管上线啦！
                <span>批量6折</span>
              </h2>
              <strong>睡一觉，图就做好了！</strong>
              <ul>
                <li>
                  <b>批量生产</b>
                  <span>支持多任务并行生成，效率直线提升。</span>
                </li>
                <li>
                  <b>成本立省40%</b>
                  <span>调度夜间闲置算力，享受专属离线点数折扣。</span>
                </li>
                <li>
                  <b>AI智能提取</b>
                  <span>自动识别图片卖点，生成高转化销售卖点。</span>
                </li>
              </ul>
              <button type="button" className="product-set-hosting-confirm" onClick={() => setShowHostingModal(false)}>
                我知道了
              </button>
            </div>
          </section>
        </div>
      ) : null}

      <EcommerceVideoHistoryPanel
        visible={videoHistoryVisible}
        onClose={() => setVideoHistoryVisible(false)}
      />
    </section>
  );
}

export default ProductClonePage;
