(() => {
  "use strict";

  const DB_KEY = "shandianmao_workflow_v1";
  const AUTH_KEY = "shandianmao_auth_v1";
  const REMEMBER_KEY = "shandianmao_remember_login_v1";
  const UI_STATE_KEY = "shandianmao_ui_state_v2";
  const ORDER_DAILY_SYNC_KEY = "yilian_order_daily_sync_v1";
  const LEGACY_DB_KEYS = ["wanneng_zhua_workflow_v3"];
  const CLOUD_API_BASE = String(globalThis.YILIAN_CLOUD_CONFIG?.apiBaseUrl || "").trim().replace(/\/$/, "");
  const API_BASE = location.protocol === "chrome-extension:" ? (CLOUD_API_BASE || "http://127.0.0.1:8001/api") : `${location.origin}/api`;
  const extensionDownloadUrl = () => `${API_BASE}/extension/download`;
  // 网页/WebView 也可能暴露 window.chrome，但只有扩展页面才允许直接调用 runtime。
  // 普通后台页面统一走 content-script 桥接，避免误判后访问不存在的 sendMessage。
  const HAS_EXTENSION_API = location.protocol === "chrome-extension:"
    && Boolean(globalThis.chrome?.runtime?.sendMessage && globalThis.chrome?.storage?.local);
  const MENU_PERMISSIONS = {
    collection: "采集库",
    drafts: "草稿库",
    products: "产品库",
    orders: "订单管理",
    settings: "默认设置",
    "compliance-label": "合规标签",
    logistics: "物流绑定",
    transparency: "透明计划",
    billing: "账号计费"
  };
  const CENTER_PERMISSIONS = { shops: "settings", logistics: "logistics", transparency: "transparency", audit: "accounts", "ui-components": "accounts" };
  const authState = {
    token: "",
    user: null,
    accounts: [],
    dataAccount: "",
    dataAccountType: "internal",
    dataAccessMode: "managed",
    dataAccessExpiresAt: "",
    registrationOpen: false,
    passwordResetEnabled: false,
    deploymentMode: "local",
    mode: "login",
    stores: [],
    activeStoreId: "",
    storeBindings: {},
    marketplaceBindings: {},
    apiSettings: null,
    apiSettingsLoading: false,
    aiServices: null,
    aiServicesLoading: false,
    aiServicesError: "",
    aiUsage: null,
    aiUsagePage: 1,
    aiUsagePageSize: 10,
    logistics: [],
    transparency: [],
    auditLogs: [],
    billing: { items: [], total: 0, page: 1, pageSize: 20, summary: {}, loading: false, error: "" },
    centerLoading: false
  };
  const COUNTRIES = [
    { code: "US", name: "美国", currency: "USD", symbol: "$", rateKey: "usdRate", language: "英语" },
    { code: "CA", name: "加拿大", currency: "CAD", symbol: "C$", rateKey: "cadRate", language: "英语" },
    { code: "MX", name: "墨西哥", currency: "MXN", symbol: "MX$", rateKey: "mxnRate", language: "西班牙语" },
    { code: "GB", name: "英国", currency: "GBP", symbol: "£", rateKey: "gbpRate", language: "英语" },
    { code: "DE", name: "德国", currency: "EUR", symbol: "€", rateKey: "eurRate", language: "德语" },
    { code: "ES", name: "西班牙", currency: "EUR", symbol: "€", rateKey: "eurRate", language: "西班牙语" },
    { code: "FR", name: "法国", currency: "EUR", symbol: "€", rateKey: "eurRate", language: "法语" },
    { code: "IT", name: "意大利", currency: "EUR", symbol: "€", rateKey: "eurRate", language: "意大利语" }
  ];
  const REGION_COUNTRIES = { americas: ["US", "CA", "MX"], europe: ["GB", "DE", "ES", "FR", "IT"] };
  const AI_TOOLS = {
    deepseek: { name: "DeepSeek", url: "https://chat.deepseek.com/" },
    doubao: { name: "豆包", url: "https://www.doubao.com/chat/" },
    chatgpt: { name: "ChatGPT", url: "https://chatgpt.com/" }
  };
  const DEFAULT_SETTINGS = {
    brand: "Generic",
    manufacturer: "Generic",
    stores: [],
    defaultStoreName: "",
    translationTools: [
      { id: "google", name: "Google 翻译", url: "https://translate.google.com/?sl=auto&tl=zh-CN&op=translate" }
    ],
    defaultTranslationToolId: "google",
    inventory: 30,
    handlingTime: 3,
    condition: "new",
    upcMode: "exempt",
    gbpRate: 9.2,
    eurRate: 7.85,
    usdRate: 7.2,
    cadRate: 5.25,
    mxnRate: 0.42,
    defaultCost: 1,
    defaultShipping: 1,
    defaultProfitRate: 1,
    aiTool: "deepseek",
    officialProductType: "",
    officialProductIdType: "GTIN Exempt",
    officialCountryOfOrigin: "CN",
    officialDangerousGoods: "Not Applicable",
    officialAssemblyRequired: "",
    officialBatteriesRequired: "",
    officialBaseMaterial: "",
    officialPackageLevel: "Unit",
    officialPackageContainsQuantity: 1,
    officialNumberOfItems: 1,
    officialUnitCountValue: 1,
    officialUnitCountUnit: "count",
    officialItemPackageQuantity: 1,
    officialVariationTheme: "COLOR/SIZE",
    officialTargetAudience: "Adult",
    officialMaterial: "",
    officialBrowseNode: "",
    officialShippingGroup: "",
    officialTaxCode: "",
    officialWarranty: "",
    officialModelName: "",
    officialModelNumber: "",
    officialPartNumber: "",
    officialSpecialFeature: "",
    officialIncludedComponents: "1 x product",
    officialItemWeightValue: 1,
    officialItemWeightUnit: "kilograms",
    officialFragile: "false",
    officialListPrice: 0,
    officialItemDepthValue: 0,
    officialItemDepthUnit: "centimeters",
    officialItemWidthValue: 0,
    officialItemHeightValue: 0,
    officialDimensionUnit: "centimeters",
    officialNetContentValue: 1,
    officialNetContentUnit: "count",
    officialDefaultSize: "",
    officialExtraFields: "",
    officialSchemaDefaults: {},
    complianceLabel: globalThis.YilianComplianceLabel?.defaults?.() || { enabled: false, autoAttach: true },
    storeProfiles: {}
  };
  const $ = id => document.getElementById(id);
  const clone = value => JSON.parse(JSON.stringify(value));
  const nowIso = () => new Date().toISOString();
  const uid = prefix => `${prefix}_${crypto.randomUUID ? crypto.randomUUID() : Date.now().toString(36) + Math.random().toString(36).slice(2)}`;
  const clean = value => String(value ?? "").replace(/\s+/g, " ").trim();
  const multiline = value => String(value ?? "").replace(/\r\n?/g, "\n").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
  const formatProductDescription = value => multiline(value)
    .replace(/[;；][ \t]*/g, match => `${match.trim()}\n`)
    .replace(/[ \t]+(?=(?:Product Features?|Material|Dimensions?|Installation|Suitable for|Care Instructions?|Package Contents?|Note|产品特点|产品特征|材质|尺寸|安装方式|适用场景|保养说明|包装清单|注意)\s*[:：])/gi, "\n")
    .replace(/\n[ \t]*(?=(?:Package Contents?|Note|包装清单|注意)\s*[:：])/gi, "\n\n")
    .replace(/\n{3,}/g, "\n\n")
    .trim();
  const escapeRegExp = value => String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const stripBrand = (value, brand, keepLines = false) => {
    const source = String(value || "");
    if (!brand) return keepLines ? multiline(source) : clean(source);
    const withoutBrand = source
      .replace(new RegExp(`\\b${escapeRegExp(brand)}\\b`, "gi"), "")
      .replace(/\b(?:brand|品牌)\s*[:：]\s*/gi, "")
      .replace(/\s+([,.;:，。；：])/g, "$1");
    return keepLines ? multiline(withoutBrand) : clean(withoutBrand);
  };
  const esc = value => clean(value).replace(/[&<>"']/g, char => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[char]));
  const escLines = value => multiline(value).replace(/[&<>"']/g, char => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[char]));
  const attr = value => esc(value);
  const num = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
  const safeImage = value => {
    const text = String(value || "").trim();
    return /^(https?:\/\/|data:image\/)/i.test(text) ? text : "";
  };
  const formatDate = value => {
    if (!value) return "—";
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return "—";
    return new Intl.DateTimeFormat("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }).format(date);
  };
  const formatOrderDate = value => {
    if (!value) return "—";
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return "—";
    return new Intl.DateTimeFormat("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit" }).format(date);
  };
  const countryName = code => COUNTRIES.find(item => item.code === code)?.name || code || "—";
  function normalizeCountryCode(value) {
    const original = clean(value);
    const normalized = original.toUpperCase().replace(/[\s._-]+/g, "");
    const aliases = {
      UK: "GB", UNITEDKINGDOM: "GB", 英国: "GB",
      US: "US", USA: "US", UNITEDSTATES: "US", 美国: "US",
      CA: "CA", CANADA: "CA", 加拿大: "CA",
      MX: "MX", MEXICO: "MX", 墨西哥: "MX",
      DE: "DE", GERMANY: "DE", 德国: "DE",
      ES: "ES", SPAIN: "ES", 西班牙: "ES",
      FR: "FR", FRANCE: "FR", 法国: "FR",
      IT: "IT", ITALY: "IT", 意大利: "IT"
    };
    return aliases[normalized] || COUNTRIES.find(item => item.code === normalized || item.name === original)?.code || "";
  }
  const unique = values => [...new Set(values.map(clean).filter(Boolean))];

  function dedupeCompleteEnglishWords(value) {
    const seen = new Set();
    let removed = 0;
    const next = String(value || "").replace(/\b[A-Za-z]+(?:['’-][A-Za-z]+)*\b/g, word => {
      const key = word.toLocaleLowerCase();
      if (!seen.has(key)) { seen.add(key); return word; }
      removed += 1;
      return "";
    }).replace(/[ \t]{2,}/g, " ").replace(/\s+([,.;:!?])/g, "$1").trim();
    return { value: next, removed };
  }

  function prepareChromeLayout() {
    const tabbar = document.querySelector(".workspace-tabbar");
    const actions = document.querySelector(".top-actions");
    if (tabbar && actions && actions.parentElement !== tabbar) tabbar.append(actions);
    document.querySelector(".topbar")?.classList.add("topbar-hidden");
    $("exportDataBtn")?.classList.add("hidden");
    $("collectProductBtn")?.classList.add("hidden");
  }

  function closeCustomSelects(except = null) {
    document.querySelectorAll(".sdm-select.open").forEach(wrapper => {
      if (wrapper !== except) wrapper.classList.remove("open");
    });
  }

  function syncCustomSelect(select) {
    const wrapper = select.closest(".sdm-select");
    const trigger = wrapper?.querySelector(".sdm-select-trigger");
    if (!trigger) return;
    const selected = select.options[select.selectedIndex];
    trigger.textContent = selected?.textContent || select.getAttribute("placeholder") || "请选择";
    trigger.disabled = select.disabled;
    trigger.classList.toggle("placeholder", !select.value);
  }

  function rebuildCustomSelectMenu(select) {
    const wrapper = select.closest(".sdm-select");
    const menu = wrapper?.querySelector(".sdm-select-menu");
    if (!menu) return;
    menu.innerHTML = [...select.options].map(option => `<button type="button" class="sdm-select-option ${option.selected ? "selected" : ""}" data-select-value="${attr(option.value)}" ${option.disabled ? "disabled" : ""}><span>${esc(option.textContent)}</span>${option.selected ? "<i>✓</i>" : ""}</button>`).join("");
  }

  function enhanceSelects(root = document) {
    root.querySelectorAll("select:not([data-native-select])").forEach(select => {
      if (!select.dataset.sdmEnhanced) {
        const wrapper = document.createElement("div");
        wrapper.className = `sdm-select${select.closest(".sidebar") ? " sdm-select-dark" : ""}`;
        select.parentNode.insertBefore(wrapper, select);
        wrapper.append(select);
        select.dataset.sdmEnhanced = "true";
        select.classList.add("sdm-native-select");
        const trigger = document.createElement("button");
        trigger.type = "button";
        trigger.className = "sdm-select-trigger";
        trigger.dataset.selectTrigger = "true";
        trigger.setAttribute("aria-haspopup", "listbox");
        const menu = document.createElement("div");
        menu.className = "sdm-select-menu";
        menu.setAttribute("role", "listbox");
        wrapper.append(trigger, menu);
        select.addEventListener("change", () => syncCustomSelect(select));
      }
      syncCustomSelect(select);
      if (select.closest(".sdm-select")?.classList.contains("open")) rebuildCustomSelectMenu(select);
    });
  }
  const normalizeTranslationTools = tools => {
    const rows = (Array.isArray(tools) ? tools : []).map((tool, index) => ({
      id: clean(tool?.id) || `translator_${index}_${Date.now().toString(36)}`,
      name: clean(tool?.name) || `翻译工具 ${index + 1}`,
      url: clean(tool?.url)
    })).filter(tool => /^https?:\/\//i.test(tool.url));
    return rows.length ? rows : clone(DEFAULT_SETTINGS.translationTools);
  };

  function normalizeSettings(raw = {}) {
    const settings = { ...clone(DEFAULT_SETTINGS), ...(raw || {}) };
    settings.stores = unique(Array.isArray(settings.stores) ? settings.stores : []);
    settings.defaultStoreName = settings.stores.includes(clean(settings.defaultStoreName))
      ? clean(settings.defaultStoreName)
      : (settings.stores[0] || "");
    settings.translationTools = normalizeTranslationTools(settings.translationTools);
    if (!["same", "replace", "exempt"].includes(settings.upcMode)) settings.upcMode = "exempt";
    if (!settings.translationTools.some(tool => tool.id === settings.defaultTranslationToolId)) {
      settings.defaultTranslationToolId = settings.translationTools[0].id;
    }
    settings.officialExtraFields = typeof settings.officialExtraFields === "string" ? settings.officialExtraFields : "";
    settings.officialSchemaDefaults = settings.officialSchemaDefaults && typeof settings.officialSchemaDefaults === "object" && !Array.isArray(settings.officialSchemaDefaults)
      ? settings.officialSchemaDefaults : {};
    settings.complianceLabel = globalThis.YilianComplianceLabel?.normalize?.(settings.complianceLabel)
      || { enabled: false, autoAttach: true };
    settings.storeProfiles = settings.storeProfiles && typeof settings.storeProfiles === "object" && !Array.isArray(settings.storeProfiles)
      ? settings.storeProfiles : {};
    return settings;
  }

  const activeStore = () => authState.stores.find(store => store.id === authState.activeStoreId) || null;
  const activeStoreCountries = () => activeStore()?.countries?.length ? activeStore().countries : [];
  const activeStoreRows = rows => {
    if (!authState.activeStoreId) return [];
    return (rows || []).filter(item => clean(item.storeId) === authState.activeStoreId);
  };
  function activeSettings() {
    const root = normalizeSettings(state.db.settings);
    const profile = root.storeProfiles?.[authState.activeStoreId];
    return normalizeSettings({ ...root, ...(profile && typeof profile === "object" ? profile : {}), storeProfiles: root.storeProfiles });
  }
  const countryMeta = code => COUNTRIES.find(country => country.code === code) || COUNTRIES[0];
  const emptyListing = raw => normalizeTranslations(raw || {});

  const state = {
    view: "collection",
    db: {
      collections: [],
      drafts: [],
      products: [],
      settings: clone(DEFAULT_SETTINGS)
    },
    selected: {
      collection: new Set(),
      drafts: new Set(),
      products: new Set()
    },
    filters: {
      collection: {},
      drafts: {},
      products: {},
      orders: {},
      billing: {}
    },
    editing: null,
    editingMode: "draft",
    variantSession: null,
    uploadContext: null,
    pendingDraftUploadIds: [],
    aiPendingByTab: {},
    detailSelection: null,
    editLocale: "",
    editSessions: {},
    editScrollByTab: {},
    activeEditTab: "",
    openTabs: ["collection"],
    pagination: {
      collection: { page: 1, pageSize: 10 },
      drafts: { page: 1, pageSize: 10 },
      products: { page: 1, pageSize: 10 },
      orders: { page: 1, pageSize: 20 },
      billing: { page: 1, pageSize: 20 }
    },
    orders: { items: [], total: 0, loading: false, error: "", financialData: true, warning: "", summary: [], performance: [], period: "month" }
  };
  let isImportingCapturedQueue = false;
  let serverSyncTimer = 0;
  let isHydratingServer = false;
  let serverRevision = null;
  let serverSnapshot = null;
  let serverSettingsVersion = "";
  let serverSyncInFlight = null;
  let serverSyncQueued = false;
  let localCacheTimer = 0;
  let uiPersistTimer = 0;
  let interactionRefreshPromise = null;
  let dataAccountSwitchInFlight = false;
  let automaticProductionConnectionKey = "";
  let amazonCategorySearch = { draftId: "", keyword: "", items: [], loading: false, error: "", open: false };
  let amazonProductTypeMatch = { draftId: "", country: "", query: "", mode: "title", items: [], loading: false, error: "", recommended: "", open: false, requestId: 0 };
  let amazonProductTypeRequestId = 0;
  let workspaceNavigationRequestId = 0;
  let amazonRequiredSchema = { country: "", productType: "", fields: [], loading: false, error: "" };
  let categorySearchTimer = 0;
  let draggedMainImageIndex = -1;
  let draggedVariantImage = null;
  const activeSandboxUploads = new Set();
  const pendingDraftUpserts = new Map();
  const activeDraftOptimizations = new Set();
  const queuedProductionUploads = new Set();
  const inflightGetRequests = new Map();
  let productionUploadQueue = Promise.resolve();
  const aiTaskAbortControllers = new Map();

  const accountDbKey = () => `${DB_KEY}:${authState.dataAccount || authState.user?.username || "anonymous"}`;
  const accountUiKey = () => `${UI_STATE_KEY}:${authState.dataAccount || authState.user?.username || "anonymous"}:${authState.activeStoreId || "no-store"}`;
  const can = permission => Boolean(authState.user?.role === "main" || authState.user?.permissions?.includes(permission));
  const isDelegatedReadOnly = () => Boolean(
    authState.user?.accountLevel === 1
    && clean(authState.dataAccount).toLowerCase() !== clean(authState.user?.username).toLowerCase()
    && authState.dataAccessMode === "read_only"
  );
  const accountTypeLabel = value => value === "external" ? "外部客户" : "自营账号";
  const ownerAccessLabel = value => value === "read_only" ? "只读查看" : "代运营";
  const canManageAmazonCredentials = () => {
    const level = Number(authState.user?.accountLevel || (authState.user?.role === "main" ? 1 : 2));
    const ownsScope = clean(authState.dataAccount).toLowerCase() === clean(authState.user?.username).toLowerCase();
    return level < 3 && ownsScope && Boolean(authState.activeStoreId) && can("settings");
  };

  function persistUiState() {
    if (!authState.user || !authState.dataAccount) return;
    try {
      if ((state.view === "edit" || state.view === "variants") && state.editing) saveActiveEditSession({ capture: false });
      const editTabs = Object.fromEntries(state.openTabs.filter(isEditTabKey).map(key => {
        const session = key === state.activeEditTab && state.editing
          ? { editing: state.editing, editingMode: state.editingMode, view: state.view, editLocale: state.editLocale }
          : state.editSessions[key];
        return [key, {
          id: session?.editing?.id || key.split(":").at(-1),
          mode: session?.editingMode === "product" ? "product" : "draft",
          view: session?.view === "variants" ? "variants" : "edit",
          editLocale: session?.editLocale || ""
        }];
      }));
      sessionStorage.setItem(accountUiKey(), JSON.stringify({
        view: state.view,
        openTabs: state.openTabs,
        pagination: state.pagination,
        filters: state.filters,
        editTabs,
        activeEditTab: state.activeEditTab,
        editLocale: state.editLocale
      }));
    } catch { /* 浏览器禁用会话存储时不影响业务 */ }
  }

  function scheduleUiStatePersist() {
    clearTimeout(uiPersistTimer);
    uiPersistTimer = setTimeout(persistUiState, 180);
  }

  function restoreUiState() {
    try {
      const saved = JSON.parse(sessionStorage.getItem(accountUiKey()) || "null");
      if (!saved || typeof saved !== "object") return false;
      const allowedViews = new Set(["collection", "drafts", "products", "orders", "settings", "compliance-label", "api-settings", "ai-services", "accounts", "billing", "edit", "variants"]);
      state.openTabs = Array.isArray(saved.openTabs) ? saved.openTabs.filter(key => typeof key === "string") : ["collection"];
      state.pagination = { ...state.pagination, ...(saved.pagination || {}) };
      state.filters = { ...state.filters, ...(saved.filters || {}) };
      state.editSessions = {};
      Object.entries(saved.editTabs || {}).forEach(([key, metadata]) => {
        const rows = metadata?.mode === "product" ? state.db.products : state.db.drafts;
        const source = rows.find(item => item.id === metadata?.id);
        if (!source) return;
        const editing = clone(source);
        initializeVariantPricingSession(editing);
        const requestedView = metadata?.view === "variants" ? "variants" : "edit";
        state.editSessions[key] = {
          editing, editingMode: metadata.mode === "product" ? "product" : "draft",
          originalEditing: clone(source),
          variantSession: requestedView === "variants" ? createVariantSession(editing, metadata.mode) : null,
          editLocale: metadata.editLocale || editing.countries?.[0] || "", view: requestedView
        };
      });
      state.openTabs = state.openTabs.filter(key => !isEditTabKey(key) || state.editSessions[key]);
      state.activeEditTab = clean(saved.activeEditTab);
      const activeSession = state.editSessions[state.activeEditTab];
      state.editing = activeSession?.editing ? clone(activeSession.editing) : null;
      state.editingMode = activeSession?.editingMode === "product" ? "product" : "draft";
      state.variantSession = activeSession?.variantSession ? clone(activeSession.variantSession) : null;
      state.editLocale = clean(saved.editLocale);
      let restoredView = allowedViews.has(saved.view) ? saved.view : firstAllowedView();
      if ((restoredView === "edit" || restoredView === "variants") && !state.editing) restoredView = firstAllowedView();
      if (restoredView === "variants" && !state.variantSession) restoredView = "edit";
      if (restoredView in MENU_PERMISSIONS && !can(restoredView)) restoredView = firstAllowedView();
      if (restoredView === "accounts" && !can("accounts")) restoredView = firstAllowedView();
      if (restoredView === "api-settings" && !canManageAmazonCredentials()) restoredView = firstAllowedView();
      if (restoredView === "ai-services" && authState.user?.role !== "main") restoredView = firstAllowedView();
      if (restoredView === "audit" && Number(authState.user?.accountLevel || 0) !== 1) restoredView = firstAllowedView();
      if (restoredView in CENTER_PERMISSIONS && !can(CENTER_PERMISSIONS[restoredView])) restoredView = firstAllowedView();
      state.view = restoredView;
      if (!state.openTabs.length) state.openTabs = [firstAllowedView()];
      return true;
    } catch {
      sessionStorage.removeItem(accountUiKey());
      return false;
    }
  }

  function authHeaders(headers = {}) {
    const result = new Headers(headers);
    if (authState.token) result.set("Authorization", `Bearer ${authState.token}`);
    if (authState.dataAccount) result.set("X-Data-Account", authState.dataAccount);
    if (authState.activeStoreId) result.set("X-Store-Id", authState.activeStoreId);
    return result;
  }

  async function authFetch(url, options = {}) {
    const response = await fetch(url, { ...options, headers: authHeaders(options.headers || {}) });
    if (response.status === 401 && authState.token && !String(url).includes("/auth/")) {
      await clearSession(false);
      renderAuth("login", "登录已过期，请重新登录", "error");
    }
    return response;
  }

  async function apiJson(path, options = {}, authenticated = true) {
    const method = String(options.method || "GET").toUpperCase();
    if (method === "GET" && !options.__skipDedupe) {
      const key = `${authenticated ? "auth" : "public"}:${authState.dataAccount}:${authState.activeStoreId}:${path}`;
      if (inflightGetRequests.has(key)) return inflightGetRequests.get(key);
      const request = apiJson(path, { ...options, __skipDedupe: true }, authenticated)
        .finally(() => inflightGetRequests.delete(key));
      inflightGetRequests.set(key, request);
      return request;
    }
    const { __skipDedupe, ...fetchOptions } = options;
    if (authenticated && isDelegatedReadOnly() && !String(path).startsWith("/auth/")) {
      if (["/db/backup", "/orders/export"].includes(String(path).split("?")[0])) {
        throw new Error("当前外部客户账号为只读查看，不能导出或下载完整数据");
      }
      if (!["GET", "HEAD", "OPTIONS"].includes(method) && String(path).split("?")[0] !== "/stores/active") {
        throw new Error("当前外部客户账号为只读查看，不能修改、删除、同步或上传数据");
      }
    }
    const response = authenticated
      ? await authFetch(`${API_BASE}${path}`, fetchOptions)
      : await fetch(`${API_BASE}${path}`, fetchOptions);
    const payload = await response.json().catch(() => ({}));
    if (!response.ok) {
      const detail = payload.detail;
      const error = new Error(typeof detail === "string" ? detail : (detail?.message || payload.msg || `HTTP ${response.status}`));
      error.detail = detail && typeof detail === "object" ? detail : null;
      throw error;
    }
    return payload;
  }

  async function syncExtensionContext() {
    if (HAS_EXTENSION_API) {
      await chrome.storage.local.set({
        workflowAuthToken: authState.token,
        workflowDataAccount: authState.dataAccount,
        workflowActiveStoreId: authState.activeStoreId
      });
      return { synced: true };
    }
    if (!authState.token) return { synced: false };
    const response = await sendExtensionMessage({
        type: "SYNC_WORKFLOW_CONTEXT",
        token: authState.token,
        account: authState.dataAccount,
        storeId: authState.activeStoreId
    });
    if (!response?.ok || response.synced !== true) throw new Error(response?.error || "插件未确认账号同步");
    return response;
  }

  function persistSession() {
    localStorage.setItem(AUTH_KEY, JSON.stringify({ token: authState.token }));
    syncExtensionContext().catch(() => {});
  }

  async function clearSession(revoke = true) {
    if (authState.dataAccount) flushLocalCache();
    if (revoke && authState.token) {
      await authFetch(`${API_BASE}/auth/logout`, { method: "POST" }).catch(() => {});
    }
    authState.token = "";
    authState.user = null;
    authState.accounts = [];
    authState.dataAccount = "";
    authState.dataAccountType = "internal";
    authState.dataAccessMode = "managed";
    authState.dataAccessExpiresAt = "";
    authState.stores = [];
    authState.activeStoreId = "";
    authState.storeBindings = {};
    authState.apiSettings = null;
    authState.apiSettingsLoading = false;
    authState.aiServices = null;
    authState.aiServicesLoading = false;
    authState.aiServicesError = "";
    authState.aiUsage = null;
    authState.aiUsagePage = 1;
    serverRevision = null;
    serverSnapshot = null;
    serverSettingsVersion = "";
    inflightGetRequests.clear();
    clearTimeout(serverSyncTimer);
    clearTimeout(uiPersistTimer);
    localStorage.removeItem(AUTH_KEY);
    if (HAS_EXTENSION_API) await chrome.storage.local.remove(["workflowAuthToken", "workflowDataAccount", "workflowActiveStoreId"]).catch(() => {});
    if ($("toastWrap")) $("toastWrap").innerHTML = "";
    $("appRoot")?.classList.add("hidden");
    document.body.classList.add("auth-active");
  }

  function emptyWorkflowState() {
    state.db = { collections: [], drafts: [], products: [], settings: clone(DEFAULT_SETTINGS) };
    pendingDraftUpserts.clear();
    activeDraftOptimizations.clear();
    Object.values(state.selected).forEach(selection => selection.clear());
    state.filters = { collection: {}, drafts: {}, products: {} };
    state.editing = null;
    state.variantSession = null;
    state.aiPendingByTab = {};
    state.editSessions = {};
    state.editScrollByTab = {};
    state.activeEditTab = "";
    state.openTabs = ["collection"];
    Object.values(state.pagination).forEach(pager => { pager.page = 1; });
  }

  function rememberedLogin() {
    if (authState.deploymentMode === "cloud") {
      localStorage.removeItem(REMEMBER_KEY);
      return null;
    }
    try {
      const stored = localStorage.getItem(REMEMBER_KEY);
      if (!stored) return null;
      const parsed = JSON.parse(stored);
      return parsed && typeof parsed.username === "string" ? { username: parsed.username } : null;
    } catch {
      localStorage.removeItem(REMEMBER_KEY);
      return null;
    }
  }

  function saveRememberedLogin(username, password, remember) {
    if (authState.deploymentMode === "cloud") {
      localStorage.removeItem(REMEMBER_KEY);
      return;
    }
    if (!remember) {
      localStorage.removeItem(REMEMBER_KEY);
      return;
    }
    localStorage.setItem(REMEMBER_KEY, JSON.stringify({ username }));
  }

  function renderAuth(mode = authState.mode, message = "", messageType = "") {
    if (mode === "forgot" && !authState.passwordResetEnabled) mode = "login";
    authState.mode = mode;
    document.body.classList.add("auth-active");
    $("appRoot")?.classList.add("hidden");
    const icon = location.protocol === "chrome-extension:" ? "assets/logo.png" : "/web/assets/logo.png";
    const bgUrl = location.protocol === "chrome-extension:" ? "assets/auth-bg.png" : "/web/assets/auth-bg.png";
    const isRegister = mode === "register";
    const isForgot = mode === "forgot";
    const remembered = !isRegister && !isForgot ? rememberedLogin() : null;
    const title = isRegister ? "注册主账号" : isForgot ? "找回密码" : "账号登录";
    const subtitle = isRegister
      ? "首次使用需验证手机号。注册成功后使用手机号登录。"
      : isForgot ? "仅主账号可通过注册手机号验证码重置密码。" : "登录后只会读取当前账号文件夹中的产品数据。";
    const statusClass = messageType === "error" ? "auth-error" : messageType === "success" ? "auth-success" : "auth-hint";
    const status = `<div id="authStatus" class="${message ? statusClass : "hidden"}">${esc(message)}</div>`;
    const accountField = isRegister || isForgot
      ? `<div class="field"><label>手机号码</label><input class="control" id="authPhone" inputmode="numeric" maxlength="11" placeholder="请输入 11 位手机号"></div>`
      : `<div class="field"><label>账号</label><input class="control" id="authUsername" autocomplete="username" value="${attr(remembered?.username || "")}" placeholder="主账号手机号或子账号"></div>`;
    const codeField = isRegister || isForgot
      ? `<div class="field"><label>手机验证码</label><div class="auth-code-row"><input class="control" id="authCode" inputmode="numeric" maxlength="6" placeholder="6 位验证码"><button class="btn" type="button" data-auth-action="send-code">获取验证码</button></div></div>`
      : "";
    $("authRoot").innerHTML = `<div class="auth-shell">
      <section class="auth-hero" style="background-image:url('${bgUrl}')"><div class="auth-brand"><img src="${icon}" alt="易联跨境" class="auth-brand-logo"><div><strong>易联跨境</strong><small>YILIAN CROSS-BORDER</small></div></div>
        <div class="auth-hero-content"><h2>易联全球<br>跨境无界</h2><p>让跨境生意更简单、更高效、更智能</p></div>
      </section>
      <section class="auth-card"><h1>${title}</h1><p class="auth-subtitle">${subtitle}</p>
        <form class="auth-form" id="authForm">${accountField}${codeField}
          <div class="field"><label>${isForgot ? "新密码" : "登录密码"}</label><div class="password-input-wrap"><input class="control" id="authPassword" type="password" autocomplete="${isRegister || isForgot ? "new-password" : "current-password"}" value="" placeholder="至少 8 位，包含字母和数字"><button type="button" class="password-eye" data-auth-action="toggle-password" aria-label="显示密码" title="显示密码"><span aria-hidden="true">◉</span></button></div><div class="password-rule">密码必须同时包含字母和数字，不能含空格。</div></div>
          ${!isRegister && !isForgot && authState.deploymentMode !== "cloud" ? `<label class="remember-login"><input type="checkbox" id="rememberPassword" ${remembered ? "checked" : ""}><span>记住登录账号</span></label>` : ""}
          ${status}<button class="btn btn-primary" type="submit">${isRegister ? "完成注册" : isForgot ? "确认修改密码" : "登录"}</button>
        </form>
        <div class="auth-links">
          ${mode !== "login" ? '<button class="auth-link" data-auth-action="mode" data-mode="login">← 返回登录</button>' : '<span></span>'}
          ${mode === "login" && authState.passwordResetEnabled ? '<button class="auth-link" data-auth-action="mode" data-mode="forgot">忘记密码</button>' : ""}
        </div>
      </section></div>`;
  }

  function setAuthMessage(message, type = "hint") {
    const node = $("authStatus");
    if (!node) return;
    node.textContent = message;
    node.className = type === "error" ? "auth-error" : type === "success" ? "auth-success" : "auth-hint";
  }

  function applySession(data) {
    authState.token = data.token || authState.token;
    authState.user = data.user;
    authState.accounts = Array.isArray(data.accounts) ? data.accounts : [data.user];
    authState.dataAccount = data.dataAccount || (data.user.accountLevel === 3 ? data.user.parentUsername : data.user.username);
    authState.dataAccountType = "internal";
    authState.dataAccessMode = "managed";
    authState.dataAccessExpiresAt = "";
    authState.activeStoreId = clean(data.activeStoreId);
    persistSession();
  }

  async function refreshStoreContext() {
    const result = await apiJson("/stores");
    authState.stores = Array.isArray(result.data?.stores) ? result.data.stores : [];
    authState.activeStoreId = clean(result.data?.activeStoreId);
    authState.storeBindings = result.data?.bindings && typeof result.data.bindings === "object" ? result.data.bindings : {};
    authState.marketplaceBindings = result.data?.marketplaceBindings && typeof result.data.marketplaceBindings === "object" ? result.data.marketplaceBindings : {};
    authState.dataAccountType = clean(result.data?.accountType) || "internal";
    authState.dataAccessMode = clean(result.data?.accessMode) || "managed";
    authState.dataAccessExpiresAt = clean(result.data?.accessExpiresAt);
    persistSession();
    refreshAccountChrome();
    return result.data;
  }

  async function switchActiveStore(storeId) {
    const value = clean(storeId);
    if (!value || value === authState.activeStoreId) return;
    if (state.view === "settings" && $("settingBrand")) {
      captureSettingsInputs();
      saveDb();
    }
    await apiJson("/stores/active", {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ storeId: value })
    });
    authState.activeStoreId = value;
    authState.apiSettings = null;
    automaticProductionConnectionKey = "";
    persistSession();
    state.selected.collection.clear();
    state.selected.drafts.clear();
    state.selected.products.clear();
    state.orders = { items: [], total: 0, loading: false, error: "", financialData: true, warning: "" };
    render();
    refreshAccountChrome();
    const settingsLoad = canManageAmazonCredentials() ? loadApiSettings(true) : loadApiRuntime();
    settingsLoad.then(() => {
      if (["settings", "compliance-label", "api-settings"].includes(state.view)) render();
      refreshAccountChrome();
    }).catch(() => {});
    toast(`已启用店铺：${activeStore()?.name || value}`, "success");
  }

  function firstAllowedView() {
    return Object.keys(MENU_PERMISSIONS).find(can) || (can("accounts") ? "accounts" : "no-access");
  }

  function refreshAccountChrome() {
    const user = authState.user;
    if (!user) return;
    document.querySelectorAll("[data-nav]").forEach(button => {
      const permission = button.dataset.nav;
      if (permission in MENU_PERMISSIONS) button.classList.toggle("hidden", !can(permission));
      if (permission in CENTER_PERMISSIONS) button.classList.toggle("hidden", !can(CENTER_PERMISSIONS[permission]));
    });
    document.querySelectorAll('[data-nav="audit"]').forEach(button => {
      button.classList.toggle("hidden", Number(user.accountLevel || 0) !== 1);
    });
    $("accountNav").classList.toggle("hidden", !can("accounts"));
    $("apiSettingsNav").classList.toggle("hidden", !canManageAmazonCredentials());
    $("aiServicesNav").classList.toggle("hidden", user.role !== "main");
    $("currentUserChip").textContent = `${user.username} · ${user.accountLevel || (user.role === "main" ? 1 : 2)}级账号`;
    const switchWrap = $("accountSwitchWrap");
    const selector = $("dataAccountSelect");
    const activeAccounts = authState.accounts.filter(account => account.active !== false && Number(account.accountLevel || (account.role === "main" ? 1 : 2)) !== 3);
    switchWrap.classList.toggle("hidden", user.accountLevel !== 1 || activeAccounts.length <= 1);
    selector.innerHTML = activeAccounts.map(account => {
      const mode = account.effectiveOwnerAccessMode || account.ownerAccessMode;
      const suffix = account.role === "main" ? "（主账号）" : account.accountType === "external" ? `（外部·${ownerAccessLabel(mode)}）` : "（自营）";
      return `<option value="${attr(account.username)}" ${account.username === authState.dataAccount ? "selected" : ""}>${esc(account.username)}${suffix}</option>`;
    }).join("");
    const accessBanner = $("tenantAccessBanner");
    if (accessBanner) {
      accessBanner.classList.toggle("hidden", !isDelegatedReadOnly());
      accessBanner.textContent = isDelegatedReadOnly()
        ? `正在只读查看外部客户 ${authState.dataAccount}：可浏览信息和打开产品链接，不能编辑、删除、同步、上传或导出完整数据。`
        : "";
    }
    const storeSelector = $("activeStoreSelect");
    if (storeSelector) {
      storeSelector.innerHTML = authState.stores.length
        ? authState.stores.map(store => `<option value="${attr(store.id)}" ${store.id === authState.activeStoreId ? "selected" : ""}>${esc(store.name)} · ${store.region === "americas" ? "美洲站" : "欧洲站"}</option>`).join("")
        : '<option value="">请选择店铺</option>';
      storeSelector.disabled = !authState.stores.length;
    }
    queueMicrotask(() => enhanceSelects(document));
  }

  async function startAuthenticatedApp({ serverWins = true } = {}) {
    document.body.classList.add("auth-active");
    $("appRoot").classList.add("hidden");
    emptyWorkflowState();
    await refreshStoreContext();
    loadDb();
    const restoredUi = restoreUiState();
    refreshAccountChrome();
    if (!restoredUi) state.view = firstAllowedView();
    setView(state.view);
    const optionalLoads = Promise.allSettled([
      canManageAmazonCredentials() ? loadApiSettings(true) : loadApiRuntime(),
      loadAiServices()
    ]);
    const hydrated = await hydrateFromServer({ serverWins });
    if (!authState.token || !authState.user) return false;
    document.body.classList.remove("auth-active");
    $("authRoot").innerHTML = "";
    $("appRoot").classList.remove("hidden");
    optionalLoads.then(() => {
      refreshAccountChrome();
      if (["settings", "api-settings", "ai-services"].includes(state.view)) render();
    });
    if (HAS_EXTENSION_API && can("collection")) queueMicrotask(() => importCapturedQueue());
    return hydrated;
  }

  async function switchDataAccount(account) {
    const target = clean(account);
    if (!target || target === authState.dataAccount || dataAccountSwitchInFlight) return;
    const targetAccount = authState.accounts.find(item => item.username === target);
    if (!targetAccount) {
      toast("目标账号不存在或已停用，请刷新账号列表后重试", "error");
      return;
    }
    const previous = {
      dataAccount: authState.dataAccount,
      dataAccountType: authState.dataAccountType,
      dataAccessMode: authState.dataAccessMode,
      dataAccessExpiresAt: authState.dataAccessExpiresAt,
      activeStoreId: authState.activeStoreId
    };
    dataAccountSwitchInFlight = true;
    const selector = $("dataAccountSelect");
    if (selector) selector.disabled = true;
    try {
      await syncToServer();
      flushLocalCache();
      authState.dataAccount = target;
      authState.dataAccountType = targetAccount.accountType || "internal";
      authState.dataAccessMode = targetAccount.effectiveOwnerAccessMode || targetAccount.ownerAccessMode || "managed";
      authState.dataAccessExpiresAt = targetAccount.ownerAccessExpiresAt || "";
      // Store IDs are scoped to the selected data account. Never send the previous
      // account's active store while resolving the new account's store context.
      authState.activeStoreId = "";
      authState.apiSettings = null;
      authState.aiServices = null;
      automaticProductionConnectionKey = "";
      persistSession();
      serverRevision = null;
      serverSnapshot = null;
      serverSettingsVersion = "";
      await startAuthenticatedApp({ serverWins: true });
      toast(`已切换到 ${target} 的产品数据`, "success");
    } catch (error) {
      authState.dataAccount = previous.dataAccount;
      authState.dataAccountType = previous.dataAccountType;
      authState.dataAccessMode = previous.dataAccessMode;
      authState.dataAccessExpiresAt = previous.dataAccessExpiresAt;
      authState.activeStoreId = previous.activeStoreId;
      authState.apiSettings = null;
      authState.aiServices = null;
      automaticProductionConnectionKey = "";
      persistSession();
      serverRevision = null;
      serverSnapshot = null;
      serverSettingsVersion = "";
      try {
        await startAuthenticatedApp({ serverWins: true });
      } catch {
        document.body.classList.remove("auth-active");
        $("authRoot").innerHTML = "";
        $("appRoot").classList.remove("hidden");
        state.view = firstAllowedView();
        setView(state.view);
        refreshAccountChrome();
      }
      toast(`账号切换失败，已恢复到 ${previous.dataAccount}：${error.message}`, "error");
    } finally {
      dataAccountSwitchInFlight = false;
      if (selector) selector.disabled = false;
      refreshAccountChrome();
    }
  }

  function loadDb() {
    try {
      const raw = localStorage.getItem(accountDbKey());
      const saved = JSON.parse(raw || "null");
      if (saved && typeof saved === "object") {
        state.db.collections = Array.isArray(saved.collections) ? saved.collections.map(normalizeCollected) : [];
        state.db.drafts = Array.isArray(saved.drafts) ? saved.drafts.map(normalizeDraft) : [];
        state.db.products = Array.isArray(saved.products) ? saved.products.map(normalizeDraft) : [];
        state.db.settings = normalizeSettings(saved.settings);
      }
    } catch {
      toast("本地数据读取失败，已使用空数据库", "error");
    }
  }

  function flushLocalCache() {
    clearTimeout(localCacheTimer);
    localCacheTimer = 0;
    try {
      localStorage.setItem(accountDbKey(), JSON.stringify(state.db));
    } catch (error) {
      setDbSyncStatus("本地缓存空间不足 · 云端数据不受影响", "error");
    }
  }

  function scheduleLocalCacheSave() {
    clearTimeout(localCacheTimer);
    localCacheTimer = setTimeout(flushLocalCache, 700);
  }

  function saveDb({ sync = true } = {}) {
    scheduleLocalCacheSave();
    updateBadges();
    if (sync && !isHydratingServer) scheduleServerSync();
  }

  function setDbSyncStatus(text, type = "") {
    const node = $("dbSyncStatus");
    if (!node) return;
    node.textContent = text;
    node.className = `db-sync ${type}`.trim();
  }

  function recordTime(item) {
    const time = new Date(item?.updatedAt || item?.collectedAt || item?.createdAt || 0).getTime();
    return Number.isFinite(time) ? time : 0;
  }

  function mergeRecords(serverRows, localRows, identity) {
    const merged = new Map();
    [...(Array.isArray(serverRows) ? serverRows : []), ...(Array.isArray(localRows) ? localRows : [])].forEach(item => {
      if (!item || typeof item !== "object") return;
      const key = identity(item) || item.id || uid("merge");
      const previous = merged.get(key);
      if (!previous || recordTime(item) >= recordTime(previous)) merged.set(key, item);
    });
    return [...merged.values()].sort((a, b) => recordTime(b) - recordTime(a));
  }

  function comparableRecord(item) {
    if (!item || typeof item !== "object") return item;
    const value = { ...item };
    delete value.__rowVersion;
    return value;
  }

  function recordsEqual(left, right) {
    return JSON.stringify(comparableRecord(left)) === JSON.stringify(comparableRecord(right));
  }

  function sectionChanges(previousRows, currentRows) {
    const previous = new Map((previousRows || []).filter(item => item?.id).map(item => [String(item.id), item]));
    const current = new Map((currentRows || []).filter(item => item?.id).map(item => [String(item.id), item]));
    const upsert = [];
    const deleted = [];
    current.forEach((item, id) => {
      const before = previous.get(id);
      if (!before || !recordsEqual(before, item)) upsert.push({ item, rowVersion: clean(before?.__rowVersion) });
    });
    previous.forEach((item, id) => {
      if (!current.has(id)) deleted.push({ id, rowVersion: clean(item?.__rowVersion) });
    });
    return { upsert, delete: deleted };
  }

  function buildWorkflowChanges(snapshot, current) {
    const changes = {
      collections: sectionChanges(snapshot?.collections, current.collections),
      drafts: sectionChanges(snapshot?.drafts, current.drafts),
      products: sectionChanges(snapshot?.products, current.products)
    };
    if (JSON.stringify(snapshot?.settings || {}) !== JSON.stringify(current.settings || {})) {
      changes.settings = { value: current.settings || {}, rowVersion: serverSettingsVersion };
    }
    return changes;
  }

  function workflowChangesCount(changes) {
    return ["collections", "drafts", "products"].reduce((total, section) => (
      total + (changes[section]?.upsert?.length || 0) + (changes[section]?.delete?.length || 0)
    ), changes.settings ? 1 : 0);
  }

  function applySyncVersions(snapshot, response) {
    const versions = response?.rowVersions || {};
    ["collections", "drafts", "products"].forEach(section => {
      const sectionVersions = versions[section] || {};
      (snapshot[section] || []).forEach(item => {
        const version = clean(sectionVersions[item.id]);
        if (!version) return;
        item.__rowVersion = version;
        if (!item.updatedAt) item.updatedAt = version;
      });
    });
  }

  function snapshotUpsert(section, item) {
    if (!serverSnapshot || !item?.id || !Array.isArray(serverSnapshot[section])) return;
    const saved = clone(item);
    saved.__rowVersion = clean(saved.__rowVersion || saved.updatedAt);
    serverSnapshot[section] = serverSnapshot[section].filter(row => row.id !== saved.id);
    serverSnapshot[section].unshift(saved);
  }

  function snapshotDelete(section, ids) {
    if (!serverSnapshot || !Array.isArray(serverSnapshot[section])) return;
    const removed = new Set((ids || []).map(String));
    serverSnapshot[section] = serverSnapshot[section].filter(item => !removed.has(String(item.id)));
  }

  async function hydrateFromServer({ serverWins = false } = {}) {
    setDbSyncStatus("正在连接数据库", "syncing");
    try {
      const response = await authFetch(`${API_BASE}/workflow/state`, { cache: "no-store" });
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const result = await response.json();
      const server = result?.data;
      if (!server || typeof server !== "object") throw new Error("数据库响应格式不正确");
      serverRevision = Number.isFinite(Number(server.revision)) ? Number(server.revision) : null;
      serverSettingsVersion = clean(server.settingsVersion);
      const local = clone(state.db);
      isHydratingServer = true;
      const serverCollections = (Array.isArray(server.collections) ? server.collections : []).map(normalizeCollected);
      const serverDrafts = (Array.isArray(server.drafts) ? server.drafts : []).map(normalizeDraft);
      const serverProducts = (Array.isArray(server.products) ? server.products : []).map(normalizeDraft);
      state.db.collections = serverWins
        ? serverCollections
        : mergeRecords(serverCollections, local.collections, item => item.asin ? `store:${clean(item.storeId) || authState.activeStoreId}|asin:${item.asin}` : `id:${item.id}`).map(normalizeCollected);
      const hydratedDrafts = serverWins
        ? serverDrafts
        : mergeRecords(serverDrafts, local.drafts, item => `id:${item.id}`).map(normalizeDraft);
      const pendingDrafts = [...pendingDraftUpserts.values()].map(normalizeDraft);
      state.db.drafts = pendingDrafts.length
        ? mergeRecords(hydratedDrafts, pendingDrafts, item => `id:${item.id}`).map(normalizeDraft)
        : hydratedDrafts;
      const hydratedProducts = serverWins ? serverProducts : mergeRecords(serverProducts, local.products, item => `id:${item.id}`).map(normalizeDraft);
      const activeLocalProducts = new Map(local.products.filter(item => activeSandboxUploads.has(item.id)).map(item => [item.id, normalizeDraft(item)]));
      state.db.products = hydratedProducts.map(item => activeLocalProducts.get(item.id) || item);
      activeLocalProducts.forEach((item, id) => {
        if (!state.db.products.some(product => product.id === id)) state.db.products.unshift(item);
      });
      const serverHasSettings = server.settings && Object.keys(server.settings).length;
      state.db.settings = normalizeSettings(serverHasSettings ? server.settings : local.settings);
      serverSnapshot = {
        collections: clone(serverCollections),
        drafts: clone(serverDrafts),
        products: clone(serverProducts),
        settings: serverHasSettings ? normalizeSettings(server.settings) : {}
      };
      saveDb({ sync: false });
      render();
      isHydratingServer = false;
      setDbSyncStatus("数据库已同步", "synced");
      const localHadData = local.collections.length || local.drafts.length || local.products.length;
      const serverHadData = (server.collections?.length || 0) + (server.drafts?.length || 0) + (server.products?.length || 0);
      if (localHadData && (!serverHadData || JSON.stringify(local) !== JSON.stringify(state.db))) scheduleServerSync(50);
      return true;
    } catch (error) {
      isHydratingServer = false;
      if (!authState.token || !authState.user) {
        renderAuth("login", "请先登录账号后使用桌面端", "error");
        return false;
      }
      setDbSyncStatus(HAS_EXTENSION_API ? "数据库离线 · 本地可用" : "数据库连接失败", "error");
      return false;
    }
  }

  function refreshWorkflowStateForInteraction() {
    if (interactionRefreshPromise) return interactionRefreshPromise;
    interactionRefreshPromise = (async () => {
      if (serverSyncInFlight || pendingDraftUpserts.size) {
        const synced = await syncToServer();
        if (synced) {
          [...pendingDraftUpserts.keys()].forEach(id => {
            if (!activeDraftOptimizations.has(id)) pendingDraftUpserts.delete(id);
          });
        }
      }
      return hydrateFromServer({ serverWins: true });
    })()
      .finally(() => { interactionRefreshPromise = null; });
    return interactionRefreshPromise;
  }

  async function performServerSync() {
    if (isDelegatedReadOnly()) {
      setDbSyncStatus("外部账号 · 只读查看", "synced");
      return true;
    }
    if (!serverSnapshot) return hydrateFromServer({ serverWins: false });
    const submittedSnapshot = clone(state.db);
    const changes = buildWorkflowChanges(serverSnapshot, submittedSnapshot);
    if (!workflowChangesCount(changes)) {
      setDbSyncStatus("数据库已同步", "synced");
      return true;
    }
    setDbSyncStatus("正在保存数据库", "syncing");
    try {
      let response = await authFetch(`${API_BASE}/workflow/changes`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(changes)
      });
      let usedLegacySync = false;
      if (response.status === 404 || response.status === 405) {
        usedLegacySync = true;
        response = await authFetch(`${API_BASE}/workflow/state`, {
          method: "PUT",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ ...submittedSnapshot, _revision: serverRevision })
        });
      }
      if (response.status === 409) {
        setDbSyncStatus("数据已更新 · 正在刷新", "syncing");
        await hydrateFromServer({ serverWins: true });
        toast("检测到其他窗口更新，已载入数据库最新数据", "info");
        return false;
      }
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const result = await response.json();
      if (Number.isFinite(Number(result?.data?.revision))) serverRevision = Number(result.data.revision);
      if (usedLegacySync) {
        serverSnapshot = submittedSnapshot;
      } else {
        applySyncVersions(submittedSnapshot, result?.data);
        serverSnapshot = submittedSnapshot;
        serverSettingsVersion = clean(result?.data?.settingsVersion) || serverSettingsVersion;
        ["collections", "drafts", "products"].forEach(section => {
          const currentById = new Map((state.db[section] || []).map(item => [String(item.id), item]));
          (submittedSnapshot[section] || []).forEach(sent => {
            const current = currentById.get(String(sent.id));
            if (current && recordsEqual(current, sent)) current.__rowVersion = sent.__rowVersion;
          });
        });
      }
      setDbSyncStatus("数据库已同步", "synced");
      if (workflowChangesCount(buildWorkflowChanges(serverSnapshot, state.db))) scheduleServerSync(40);
      return true;
    } catch {
      setDbSyncStatus(HAS_EXTENSION_API ? "数据库离线 · 已存本地" : "数据库保存失败", "error");
      return false;
    }
  }

  function scheduleServerSync(delay = 350) {
    if (isDelegatedReadOnly()) return;
    clearTimeout(serverSyncTimer);
    serverSyncTimer = setTimeout(syncToServer, delay);
  }

  async function sendExtensionMessage(message) {
    const runtime = globalThis.chrome?.runtime;
    if (location.protocol === "chrome-extension:" && typeof runtime?.sendMessage === "function") {
      return runtime.sendMessage(message);
    }
    const requestId = `sdm_bridge_${Date.now()}_${Math.random().toString(36).slice(2)}`;
    return new Promise((resolve, reject) => {
      let acknowledged = false;
      const cleanup = () => {
        window.removeEventListener("message", listener);
        clearTimeout(ackTimer);
        clearTimeout(responseTimer);
      };
      const listener = event => {
        if (event.source !== window || event.data?.requestId !== requestId) return;
        if (event.data.type === "SDM_EXTENSION_ACK") {
          acknowledged = true;
          clearTimeout(ackTimer);
          return;
        }
        if (event.data.type !== "SDM_EXTENSION_RESPONSE") return;
        cleanup();
        if (event.data.error) reject(new Error(event.data.error));
        else resolve(event.data.response);
      };
      window.addEventListener("message", listener);
      const ackTimer = setTimeout(() => {
        if (acknowledged) return;
        cleanup();
        reject(new Error("未检测到易联跨境扩展，请在 chrome://extensions 重新加载扩展"));
      }, 2500);
      const responseTimer = setTimeout(() => {
        cleanup();
        reject(new Error("易联跨境扩展处理超时，请重试"));
      }, 120000);
      window.postMessage({ type: "SDM_EXTENSION_REQUEST", requestId, message }, location.origin);
    });
  }

  function normalizeDimensionSkuValue(value) {
    const compact = String(value ?? "").trim().replace(/\s+/g, "").replace(/[×*X]/g, "x");
    const match = compact.match(/^(\d+(?:[.,]\d+)?(?:x\d+(?:[.,]\d+)?)+)([A-Za-z]+)?$/);
    if (!match) return compact;
    const parts = match[1].split("x");
    const decimalPlaces = Math.max(0, ...parts.map(part => (part.split(/[.,]/)[1] || "").length));
    if (!decimalPlaces) return `${parts.join("x")}${(match[2] || "").toLowerCase()}`;
    const factor = 10 ** decimalPlaces;
    const integers = parts.map(part => String(Math.round(Number(part.replace(",", ".")) * factor)));
    return `${integers.join("x")}${(match[2] || "").toLowerCase()}`;
  }

  function variantSkuPart(value, isSize = false) {
    const raw = isSize ? normalizeDimensionSkuValue(value) : clean(value).replace(/[×*X]/g, "x");
    const compact = isSize ? raw.replace(/\s+/g, "") : raw;
    return compact.replace(/\./g, "_").replace(/[^0-9A-Za-z_\u0080-\uffff]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 24);
  }

  function normalizeParentSku(value) {
    return clean(value)
      .replace(/[^0-9A-Za-z_\u0080-\uffff-]+/g, "-")
      .replace(/^-+|-+$/g, "")
      .slice(0, 40)
      .replace(/-+$/g, "");
  }

  function assignVariantSkus(parentSku, rows = []) {
    const seen = new Set();
    return rows.map((row, index) => {
      const existing = clean(row.sku);
      const color = variantSkuPart(row.color || row.color_name);
      const size = variantSkuPart(row.size || row.size_name, true);
      const parts = [normalizeParentSku(parentSku), color, size].filter(Boolean);
      const base = parts.join("-").slice(0, 40).replace(/-+$/g, "") || normalizeParentSku(parentSku);
      let sku = existing && existing.length <= 40 ? existing : base;
      if (seen.has(sku.toLowerCase())) {
        const suffix = `-${String(index + 1).padStart(2, "0")}`;
        sku = `${base.slice(0, 40 - suffix.length).replace(/-+$/g, "")}${suffix}`;
      }
      seen.add(sku.toLowerCase());
      return { ...row, sku };
    });
  }

  function sortVariantRows(rows = []) {
    const colorOrder = new Map();
    rows.forEach((row, index) => { const color = clean(row.color || row.color_name).toLocaleLowerCase(); if (!colorOrder.has(color)) colorOrder.set(color, index); });
    const parseSize = value => {
      const text = clean(value).toLowerCase().replace(/[×x]/g, "x");
      const matches = [...text.matchAll(/(\d+(?:\.\d+)?)\s*(mm|cm|m|in|inch|\")?/g)];
      if (!matches.length || !/(mm|cm|m|in|inch|\")/.test(text)) return null;
      const tailUnit = matches.at(-1)?.[2] || "";
      const factor = unit => ({ mm: 0.1, cm: 1, m: 100, in: 2.54, inch: 2.54, '"': 2.54 }[unit || tailUnit] || 0);
      const values = matches.map(match => Number(match[1]) * factor(match[2]));
      return values.every(Number.isFinite) && values.every(value => value > 0) ? values : null;
    };
    return rows.map((row, index) => ({ row, index, size: parseSize(row.size || row.size_name), color: clean(row.color || row.color_name).toLocaleLowerCase() }))
      .sort((left, right) => {
        const colorDiff = (colorOrder.get(left.color) || 0) - (colorOrder.get(right.color) || 0);
        if (colorDiff) return colorDiff;
        if (left.size && right.size) {
          const count = Math.max(left.size.length, right.size.length);
          for (let i = 0; i < count; i++) {
            const diff = (left.size[i] ?? -1) - (right.size[i] ?? -1);
            if (diff) return diff;
          }
        } else if (left.size) return -1;
        else if (right.size) return 1;
        return left.index - right.index;
      }).map(item => item.row);
  }

  function variantFactValues(rows, field) {
    const seen = new Set();
    return rows.map(row => clean(row[field] || row[`${field}_name`])).filter(value => {
      const key = value.toLocaleLowerCase();
      if (!key || seen.has(key)) return false;
      seen.add(key); return true;
    });
  }

  function applyVariantFactsToDescription(description, rows = []) {
    let next = multiline(description);
    const replaceFactLists = (labels, values) => {
      // Only colon-delimited labels at a line/semicolon boundary are structured
      // lists.  Ordinary prose such as "The black frame..." is never touched.
      const expression = new RegExp(`(^|[\\n;；]\\s*)((?:${labels.join("|")})\\s*[:：])[^\\n;；]*`, "ig");
      let found = false;
      const showList = values.length > 0 && values.length <= 10;
      next = next.replace(expression, (full, prefix, label) => {
        if (!found) {
          found = true;
          if (showList) return `${prefix}${label} ${values.join(", ")}`;
          // With no collected values, retain one existing list but never add one.
          return values.length === 0 ? full : prefix;
        }
        return prefix;
      });
    };
    replaceFactLists(["available\\s+colou?rs?", "可选颜色", "colou?rs?", "颜色"], variantFactValues(rows, "color"));
    replaceFactLists(["available\\s+sizes?", "size\\s+options?", "可选(?:尺寸|尺码)", "sizes?", "尺码", "尺寸"], variantFactValues(rows, "size"));
    let previous;
    do {
      previous = next;
      next = next.replace(/[;；]\s*[;；]\s*/g, "; ");
    } while (next !== previous);
    return next
      .replace(/(^|\\n)\s*[;；]\s*/g, "$1")
      .replace(/[;；]\s*$/g, "")
      .replace(/[ \\t]{2,}/g, " ")
      .replace(/\\n[ \\t]*\\n+/g, "\\n")
      .trim();
  }

  function normalizeSpecifications(value) {
    const rows = Array.isArray(value)
      ? value
      : (value && typeof value === "object" ? Object.entries(value).map(([label, item]) => ({ label, value: item })) : []);
    const seen = new Set();
    return rows.map(row => ({ label: clean(row?.label), value: clean(row?.value) })).filter(row => {
      const key = `${row.label.toLowerCase()}|${row.value.toLowerCase()}`;
      if (!row.label || !row.value || seen.has(key)) return false;
      seen.add(key);
      return true;
    }).slice(0, 100);
  }

  function specificationValue(rows, patterns) {
    return rows.find(row => patterns.some(pattern => pattern.test(row.label)))?.value || "";
  }

  function parseCollectedDimensions(value) {
    const text = clean(value);
    const unitText = clean(text.match(/\b(mm|millimet(?:er|re)s?|cm|centimet(?:er|re)s?|m|met(?:er|re)s?|in|inch(?:es)?|ft|foot|feet)\.?\b/i)?.[1]).toLowerCase();
    const unit = /^(?:mm|millimet)/.test(unitText) ? "millimeters"
      : /^(?:cm|centimet)/.test(unitText) ? "centimeters"
      : /^(?:m|met)/.test(unitText) ? "meters"
      : /^(?:in|inch)/.test(unitText) ? "inches"
      : /^(?:ft|foot|feet)/.test(unitText) ? "feet" : "";
    if (!unit) return null;
    const labeled = {};
    for (const match of text.matchAll(/(\d+(?:[.,]\d+)?)\s*([DLWH])(?=\s*(?:x|×|by|\b))/gi)) {
      const number = Number(match[1].replace(",", "."));
      const axis = match[2].toUpperCase();
      if (number > 0) labeled[axis === "D" || axis === "L" ? "depth" : axis === "W" ? "width" : "height"] = number;
    }
    if (labeled.depth && labeled.width && labeled.height) return { ...labeled, unit };
    const plain = text.match(/^\s*(\d+(?:[.,]\d+)?)\s*(?:x|×|by)\s*(\d+(?:[.,]\d+)?)\s*(?:x|×|by)\s*(\d+(?:[.,]\d+)?)/i);
    if (!plain) return null;
    const numbers = plain.slice(1).map(number => Number(number.replace(",", ".")));
    return numbers.every(number => number > 0) ? { depth: numbers[0], width: numbers[1], height: numbers[2], unit } : null;
  }

  function normalizeCollected(raw = {}) {
    const sourceImages = Array.isArray(raw.images) ? raw.images : [raw.image];
    const sourceBrand = clean(raw.brand);
    const specifications = normalizeSpecifications(raw.specifications);
    const productDimensions = clean(raw.productDimensions) || specificationValue(specifications, [
      /^product dimensions?$/i, /^item dimensions?$/i, /^dimensions?$/i,
      /^produktabmessungen$/i, /^dimensions? (?:du produit|de l['’]article)$/i,
      /^dimensiones? (?:del producto|del art[ií]culo)$/i, /^dimensioni (?:del prodotto|dell['’]articolo)$/i,
      /^商品尺寸$/i, /^产品尺寸$/i,
    ]) || clean(raw.productHighlight);
    const parsedDimensions = parseCollectedDimensions(productDimensions);
    const material = clean(raw.material || raw.baseMaterial) || specificationValue(specifications, [/^frame material$/i, /^base material$/i, /^material$/i, /^材质$/i, /^框架材质$/i]);
    const collectedColor = clean(raw.color || raw.color_name) || specificationValue(specifications, [/^colou?r$/i, /^farbe$/i, /^couleur$/i, /^colore$/i, /^颜色$/i]);
    const collectedStyle = clean(raw.style) || specificationValue(specifications, [/^style$/i, /^stil$/i, /^stile$/i, /^estilo$/i, /^风格$/i]);
    const highlightIsDimensions = Boolean(parsedDimensions && clean(raw.productHighlight) === productDimensions);
    return {
      ...raw,
      id: raw.id || uid("col"),
      site: clean(raw.site),
      country: clean(raw.country),
      domain: clean(raw.domain),
      asin: clean(raw.asin).toUpperCase(),
      rawTitle: clean(raw.rawTitle || raw.originalTitle || raw.title),
      cleanedTitle: stripBrand(raw.cleanedTitle || raw.title, sourceBrand),
      title: stripBrand(raw.title, sourceBrand) || `待完善 · ${clean(raw.asin) || "未知商品"}`,
      productHighlight: highlightIsDimensions ? "" : clean(raw.productHighlight),
      specifications,
      productDimensions: parsedDimensions ? productDimensions : clean(raw.productDimensions),
      itemDepth: num(raw.itemDepth || raw.itemLength || parsedDimensions?.depth),
      itemWidth: num(raw.itemWidth || parsedDimensions?.width),
      itemHeight: num(raw.itemHeight || parsedDimensions?.height),
      itemDimensionUnit: clean(raw.itemDimensionUnit || parsedDimensions?.unit),
      material,
      color: collectedColor,
      style: collectedStyle,
      price: raw.price === "" || raw.price == null ? "" : num(raw.price),
      currency: clean(raw.currency) || "GBP",
      image: safeImage(raw.image) || safeImage(sourceImages[0]),
      images: unique(sourceImages.map(safeImage)).slice(0, 20),
      rating: raw.rating === "" || raw.rating == null ? "" : num(raw.rating),
      reviews: raw.reviews === "" || raw.reviews == null ? "" : num(raw.reviews),
      seller: clean(raw.seller || raw.sellerName),
      sellerId: clean(raw.sellerId),
      sellerName: clean(raw.sellerName || raw.seller),
      sellerCount: raw.sellerCount === "" || raw.sellerCount == null ? "" : num(raw.sellerCount),
      brand: sourceBrand,
      category: clean(raw.category),
      categoryPath: clean(raw.categoryPath) || (Array.isArray(raw.breadcrumbs) ? raw.breadcrumbs.map(clean).filter(Boolean).join("/") : clean(raw.category)),
      categoryId: clean(raw.categoryId),
      breadcrumbs: Array.isArray(raw.breadcrumbs) ? raw.breadcrumbs.map(clean).filter(Boolean) : [],
      availability: clean(raw.availability),
      bulletPoints: Array.isArray(raw.bulletPoints) ? raw.bulletPoints.map(value => stripBrand(value, sourceBrand)).filter(Boolean) : [],
      description: stripBrand(raw.description, sourceBrand, true),
      aPlusDescription: multiline(raw.aPlusDescription),
      ranks: Array.isArray(raw.ranks) ? raw.ranks : [],
      mainRank: raw.mainRank || null,
      subRank: raw.subRank || null,
      firstAvailableDate: clean(raw.firstAvailableDate),
      fulfillmentType: clean(raw.fulfillmentType),
      currentColor: clean(raw.currentColor) || collectedColor,
      currentSize: clean(raw.currentSize),
      variants: Array.isArray(raw.variants) ? raw.variants : [],
      variantRows: Array.isArray(raw.variantRows) ? raw.variantRows : [],
      variantGroups: Array.isArray(raw.variantGroups) ? raw.variantGroups : [],
      parentAsin: clean(raw.parentAsin),
      upc: clean(raw.upc),
      url: clean(raw.url),
      storeId: clean(raw.storeId) || authState.activeStoreId,
      sku: "",
      countries: Array.isArray(raw.countries) && raw.countries.length ? raw.countries : [clean(raw.country) || "GB"],
      collectionStatus: raw.collectionStatus || "已采集",
      captureTaskId: clean(raw.captureTaskId),
      captureStatus: clean(raw.captureStatus),
      mainCaptureAsin: clean(raw.mainCaptureAsin),
      relationshipType: clean(raw.relationshipType),
      isPrimary: raw.isPrimary !== false,
      captureSource: clean(raw.captureSource),
      dataCompleteness: clean(raw.dataCompleteness),
      listingStatus: raw.listingStatus || "未上架",
      collectedAt: raw.collectedAt || raw.createdAt || nowIso(),
      updatedAt: raw.updatedAt || nowIso()
    };
  }

  function normalizeTranslations(raw = {}) {
    const productHighlight = clean(raw.productHighlight);
    return {
      title: clean(raw.title),
      productHighlight: parseCollectedDimensions(productHighlight) ? "" : productHighlight,
      keywords: clean(raw.keywords),
      bulletPoints: [...(Array.isArray(raw.bulletPoints) ? raw.bulletPoints.map(clean) : []), "", "", "", "", ""].slice(0, 5),
      description: formatProductDescription(raw.description)
    };
  }

  function normalizeDraft(raw = {}) {
    const images = Array.isArray(raw.images) ? raw.images.map(safeImage).filter(Boolean) : [];
    const countries = Array.isArray(raw.countries) && raw.countries.length ? unique(raw.countries) : ["GB"];
    const localizedContent = Object.fromEntries(Object.entries(raw.localizedContent || {}).map(([code, value]) => [code, emptyListing(value)]));
    const specifications = normalizeSpecifications(raw.specifications);
    const productDimensions = clean(raw.productDimensions) || specificationValue(specifications, [
      /^product dimensions?$/i, /^item dimensions?$/i, /^dimensions?$/i,
      /^produktabmessungen$/i, /^dimensions? (?:du produit|de l['’]article)$/i,
      /^dimensiones? (?:del producto|del art[ií]culo)$/i, /^dimensioni (?:del prodotto|dell['’]articolo)$/i,
      /^商品尺寸$/i, /^产品尺寸$/i,
    ]) || clean(raw.productHighlight);
    const parsedDimensions = parseCollectedDimensions(productDimensions);
    if (!localizedContent[countries[0]]) localizedContent[countries[0]] = emptyListing(raw);
    return {
      ...raw,
      productHighlight: parsedDimensions && clean(raw.productHighlight) === productDimensions ? "" : clean(raw.productHighlight),
      specifications,
      productDimensions: parsedDimensions ? productDimensions : clean(raw.productDimensions),
      itemDepth: num(raw.itemDepth || raw.itemLength || parsedDimensions?.depth),
      itemWidth: num(raw.itemWidth || parsedDimensions?.width),
      itemHeight: num(raw.itemHeight || parsedDimensions?.height),
      itemDimensionUnit: clean(raw.itemDimensionUnit || parsedDimensions?.unit),
      material: clean(raw.material || raw.baseMaterial) || specificationValue(specifications, [/^frame material$/i, /^base material$/i, /^material$/i, /^材质$/i, /^框架材质$/i]),
      color: clean(raw.color || raw.color_name) || specificationValue(specifications, [/^colou?r$/i, /^farbe$/i, /^couleur$/i, /^colore$/i, /^颜色$/i]),
      style: clean(raw.style) || specificationValue(specifications, [/^style$/i, /^stil$/i, /^stile$/i, /^estilo$/i, /^风格$/i]),
      storeId: clean(raw.storeId) || authState.activeStoreId,
      storeName: clean(raw.storeName),
      collectedCategoryPath: clean(raw.collectedCategoryPath || raw.categoryPath || raw.category),
      amazonProductType: clean(raw.amazonProductType || raw.productType).toUpperCase(),
      amazonProductTypes: raw.amazonProductTypes && typeof raw.amazonProductTypes === "object" && !Array.isArray(raw.amazonProductTypes)
        ? Object.fromEntries(Object.entries(raw.amazonProductTypes).map(([code, value]) => [code, clean(value).toUpperCase()]))
        : (clean(raw.amazonProductType || raw.productType) ? { [countries[0]]: clean(raw.amazonProductType || raw.productType).toUpperCase() } : {}),
      amazonProductTypeLabels: raw.amazonProductTypeLabels && typeof raw.amazonProductTypeLabels === "object" && !Array.isArray(raw.amazonProductTypeLabels)
        ? Object.fromEntries(Object.entries(raw.amazonProductTypeLabels).map(([code, value]) => [code, clean(value)]))
        : {},
      sourceClassification: raw.sourceClassification && typeof raw.sourceClassification === "object" && !Array.isArray(raw.sourceClassification)
        ? clone(raw.sourceClassification) : {},
      sourceMarketplace: clean(raw.sourceMarketplace || raw.sourceClassification?.marketplace).toUpperCase(),
      sourceProductType: clean(raw.sourceProductType || raw.sourceClassification?.productType).toUpperCase(),
      productTypeResolutions: raw.productTypeResolutions && typeof raw.productTypeResolutions === "object" && !Array.isArray(raw.productTypeResolutions)
        ? clone(raw.productTypeResolutions) : {},
      images: unique(images).slice(0, 20),
      countries,
      bulletPoints: [...(Array.isArray(raw.bulletPoints) ? raw.bulletPoints.map(clean) : []), "", "", "", "", ""].slice(0, 5),
      translations: normalizeTranslations(raw.translations),
      localizedContent,
      countryPrices: raw.countryPrices && typeof raw.countryPrices === "object" ? raw.countryPrices : {},
      listingStatus: raw.listingStatus === "未上架" ? "未上传" : (raw.listingStatus || "未上传"),
      uploadFailureReason: clean(raw.uploadFailureReason),
      uploadUpdatedAt: clean(raw.uploadUpdatedAt),
      sandboxUpload: raw.sandboxUpload && typeof raw.sandboxUpload === "object" ? raw.sandboxUpload : null,
      upcMode: ["same", "replace", "exempt"].includes(raw.upcMode) ? raw.upcMode : "exempt",
      variants: Array.isArray(raw.variants) ? raw.variants : [],
      variantRows: Array.isArray(raw.variantRows) ? raw.variantRows.map(row => ({
        ...row,
        id: row.id || uid("var"),
        color: clean(row.color || row.color_name),
        size: clean(row.size || row.size_name),
        originalPrice: row.originalPrice ?? row.price ?? "",
        priceCurrency: clean(row.priceCurrency || row.currency || raw.currency),
        priceDelta: num(row.priceDelta),
        images: unique((Array.isArray(row.images) ? row.images : [row.image]).map(safeImage)).slice(0, 20)
      })) : [],
      colors: Array.isArray(raw.colors) ? unique(raw.colors) : [],
      sizes: Array.isArray(raw.sizes) ? unique(raw.sizes) : []
    };
  }

  function upsertCollection(raw) {
    const incoming = normalizeCollected(raw);
    const index = state.db.collections.findIndex(item =>
      clean(item.storeId) === incoming.storeId && ((incoming.asin && item.asin === incoming.asin) || (incoming.url && item.url === incoming.url))
    );
    if (index >= 0) {
      const previous = state.db.collections[index];
      state.db.collections[index] = { ...previous, ...incoming, id: previous.id, sku: "", collectionStatus: previous.collectionStatus, collectedAt: previous.collectedAt, updatedAt: nowIso() };
      syncDraftFromCollection(state.db.collections[index]);
      saveDb();
      return { added: false, item: state.db.collections[index] };
    }
    state.db.collections.unshift(incoming);
    saveDb();
    return { added: true, item: incoming };
  }

  function collectedVariantRows(item) {
    return (Array.isArray(item.variantRows) && item.variantRows.length
      ? item.variantRows
      : item.variants.map(variant => ({
          asin: variant.asin,
          color: variant.color || (/color|colour|颜色/i.test(variant.dimension || "") ? variant.value : ""),
          size: variant.size || (/size|尺码|尺寸/i.test(variant.dimension || "") ? variant.value : ""),
          price: variant.price,
          originalPrice: variant.originalPrice,
          currency: variant.currency,
          priceCurrency: variant.priceCurrency,
          detailFetched: variant.detailFetched,
          images: variant.images || (variant.image ? [variant.image] : [])
        })))
      .map(row => ({
        id: uid("var"),
        asin: clean(row.asin),
        color: clean(row.color || row.color_name),
        size: clean(row.size || row.size_name),
        price: clean(row.price ?? row.originalPrice),
        originalPrice: clean(row.originalPrice ?? row.price),
        currency: clean(row.currency || row.priceCurrency || item.currency),
        priceCurrency: clean(row.priceCurrency || row.currency || item.currency),
        detailFetched: row.detailFetched === true,
        priceDelta: 0,
        images: unique((Array.isArray(row.images) ? row.images : [row.image]).map(safeImage)).slice(0, 20)
      }));
  }

  function syncDraftFromCollection(item) {
    const drafts = state.db.drafts.filter(candidate => candidate.sourceId === item.id);
    if (!drafts.length) return;
    const rows = collectedVariantRows(item);
    drafts.forEach(draft => {
      if (parseCollectedDimensions(draft.productHighlight)) draft.productHighlight = "";
      if (draft.creationStatus === "待编辑") {
        draft.images = clone(item.images || []);
        draft.variants = clone(item.variants || []);
        draft.variantRows = rows;
        draft.colors = unique(rows.map(row => row.color));
        draft.sizes = unique(rows.map(row => row.size));
        draft.parentAsin = item.parentAsin;
        draft.category = item.category;
        draft.collectedCategoryPath = item.categoryPath || item.category;
        if (!draft.amazonProductType) draft.categoryPath = draft.collectedCategoryPath;
        draft.categoryId = item.categoryId;
        draft.price = item.price;
        draft.currency = item.currency;
        draft.specifications = clone(item.specifications || []);
        draft.productDimensions = item.productDimensions;
        draft.itemDepth = item.itemDepth;
        draft.itemWidth = item.itemWidth;
        draft.itemHeight = item.itemHeight;
        draft.itemDimensionUnit = item.itemDimensionUnit;
        draft.material = item.material;
        draft.color = item.color;
        draft.style = item.style;
      } else {
        if (!draft.images?.length) draft.images = clone(item.images || []);
        if (!draft.variantRows?.length) {
          draft.variantRows = rows;
          draft.colors = unique(rows.map(row => row.color));
          draft.sizes = unique(rows.map(row => row.size));
        }
      }
      draft.updatedAt = nowIso();
    });
  }

  function draftFromCollection(item, allocatedSku = "") {
    const settings = activeSettings();
    const sku = clean(allocatedSku);
    if (!/^YL-[A-Za-z0-9]{6}$/.test(sku)) throw new Error("服务端未返回有效的唯一父 SKU");
    const collectedRows = sortVariantRows(assignVariantSkus(sku, collectedVariantRows(item)));
    const detectedColors = unique(collectedRows.map(row => row.color));
    const detectedSizes = unique(collectedRows.map(row => row.size));
    return {
      id: uid("draft"),
      sourceId: item.id,
      storeId: item.storeId || authState.activeStoreId,
      asin: item.asin,
      parentAsin: item.parentAsin,
      category: item.category,
      categoryPath: item.categoryPath || item.category,
      collectedCategoryPath: item.categoryPath || item.category,
      amazonProductType: "",
      categoryId: item.categoryId,
      price: item.price,
      currency: item.currency,
      sku,
      title: item.title,
      originalTitle: item.title,
      sourceBrand: item.brand,
      productHighlight: clean(item.productHighlight),
      specifications: clone(item.specifications || []),
      productDimensions: item.productDimensions,
      itemDepth: item.itemDepth,
      itemWidth: item.itemWidth,
      itemHeight: item.itemHeight,
      itemDimensionUnit: item.itemDimensionUnit,
      material: item.material,
      color: item.color,
      style: item.style,
      keywords: unique(item.title.split(/[\s,，/|]+/)).join(", ").slice(0, 500),
      bulletPoints: [...item.bulletPoints.slice(0, 5), "", "", "", "", ""].slice(0, 5),
      description: applyVariantFactsToDescription(item.description || item.bulletPoints.join("\n"), collectedRows),
      translations: normalizeTranslations(),
      images: item.images.length ? clone(item.images) : (item.image ? [item.image] : []),
      variants: clone(item.variants),
      variantRows: collectedRows,
      colors: detectedColors,
      sizes: detectedSizes,
      purchaseUrl: item.url,
      countries: [normalizeCountryCode(item.country || item.countries?.[0]) || activeStoreCountries()[0]].filter(Boolean),
      brand: settings.brand,
      manufacturer: settings.manufacturer,
      inventory: settings.inventory,
      handlingTime: settings.handlingTime,
      condition: ["new", "used_like_new", "used_very_good", "used_good", "used_acceptable"].includes(settings.condition) ? settings.condition : "new",
      upc: item.upc,
      upcMode: settings.upcMode || "exempt",
      cost: settings.defaultCost,
      shipping: settings.defaultShipping,
      profitRate: settings.defaultProfitRate,
      rmbPrice: 0,
      foreignPrice: 0,
      aiTool: settings.aiTool,
      storeName: activeStore()?.name || clean(settings.defaultStoreName || settings.stores?.[0]),
      localizedContent: {},
      countryPrices: {},
      pricingSource: num(item.price) > 0 ? "captured" : "manual",
      creationStatus: "待编辑",
      listingStatus: "未上架",
      createdAt: nowIso(),
      updatedAt: nowIso()
    };
  }

  function aiApiOptimizationAvailable() {
    const services = authState.aiServices || {};
    const provider = services.activeAiProvider || "deepseek";
    return services.aiApiEnabled === true && Boolean(services.ai?.[provider]?.hasApiKey);
  }

  function openDraftUploadModal(ids) {
    const selectedIds = unique(ids).filter(id => state.db.collections.some(item => item.id === id));
    if (!selectedIds.length) { toast("请先勾选需要上传到草稿库的产品", "info"); return; }
    if (!aiApiOptimizationAvailable()) { moveCollectionsToDraft(selectedIds); return; }
    const aiAvailable = aiApiOptimizationAvailable();
    state.pendingDraftUploadIds = selectedIds;
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal draft-upload-modal"><div class="modal-head"><div><h2>上传到草稿库</h2><p>已选择 ${selectedIds.length} 个产品；可以按模块自动优化文案，并统一调整采集价格。</p></div><button class="modal-close" data-action="close-modal">×</button></div>
      <div class="modal-body"><div class="draft-optimization-options">
        <label class="${aiAvailable ? "" : "disabled"}"><input type="checkbox" id="draftOptimizeTitle" ${aiAvailable ? "" : "disabled"}><span><strong>自动优化产品名称</strong><small>只把产品名称发送给 AI，名称限制为 75 个字符以内</small></span></label>
        <label class="${aiAvailable ? "" : "disabled"}"><input type="checkbox" id="draftOptimizeBullets" ${aiAvailable ? "" : "disabled"}><span><strong>自动优化产品五点</strong><small>只把产品五点发送给 AI，生成并保留最多 5 条</small></span></label>
        <label class="${aiAvailable ? "" : "disabled"}"><input type="checkbox" id="draftOptimizeDescription" ${aiAvailable ? "" : "disabled"}><span><strong>自动优化产品描述</strong><small>只把产品描述发送给 AI，并按段落和属性换行</small></span></label>
      </div><div class="field draft-price-adjustment"><label>批量加减价格倍数</label><input class="control" id="draftPriceMultiplier" type="number" min="0.01" step="0.01" placeholder="例如 1.1 或 0.9"><div class="meta">加价 10% 输入 1.1，减价 10% 输入 0.9；留空表示价格不变。</div></div>
      <div class="settings-note draft-ai-ready">${aiAvailable ? "当前 AI 大模型 API 和密钥已配置。只发送勾选模块的内容；未勾选时不会产生 AI 调用。" : "当前未开启 AI 大模型 API 或未保存有效密钥，文案优化选项已停用；提交后仍会正常创建草稿。"}</div></div>
      <div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="confirm-draft-upload">提交</button></div></div></div>`;
  }

  async function optimizeDraftAfterCreation(draft, fieldScopes) {
    const locale = draft.countries?.[0] || "";
    const response = await apiJson("/ai-services/optimize", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ mode: "optimize", prompt: buildAiPrompt("optimize", draft, locale, fieldScopes) })
    });
    const result = extractAiJson(response?.data?.text);
    assertAiTargetLocale(result, locale);
    const listing = result.listing || result;
    const scopes = normalizeAiFieldScopes(fieldScopes);
    if (scopes.includes("title") && !clean(listing.title)) throw new Error("AI没有返回优化后的产品名称");
    if (scopes.includes("bulletPoints") && !(Array.isArray(listing.bulletPoints) && listing.bulletPoints.some(clean))) throw new Error("AI没有返回优化后的产品五点");
    if (scopes.includes("description") && !clean(listing.description)) throw new Error("AI没有返回优化后的产品描述");
    applyListingResult(listing, draft, locale, scopes);
    draft.updatedAt = nowIso();
  }

  function applyDraftPriceMultiplier(draft, value) {
    const multiplier = Number(value);
    if (!Number.isFinite(multiplier) || multiplier <= 0 || Math.abs(multiplier - 1) < 0.000001) return;
    if (num(draft.price) > 0) draft.price = Math.round(num(draft.price) * multiplier * 100) / 100;
    draft.variantRows = (draft.variantRows || []).map(row => ({
      ...row,
      originalPrice: num(row.originalPrice) > 0
        ? Math.round(num(row.originalPrice) * multiplier * 100) / 100
        : row.originalPrice
    }));
    draft.priceAdjustmentMultiplier = multiplier;
  }

  async function persistCreatedDrafts(createdDrafts) {
    const localDrafts = createdDrafts.map(clone);
    state.db.drafts = mergeRecords(state.db.drafts, localDrafts, item => `id:${item.id}`).map(normalizeDraft);
    saveDb({ sync: false });
    let synced = await syncToServer();
    if (synced) return true;
    // A 409 refresh replaces the local arrays with the database snapshot. Re-merge
    // the just-created/optimized drafts, then retry against the refreshed revision.
    state.db.drafts = mergeRecords(state.db.drafts, localDrafts, item => `id:${item.id}`).map(normalizeDraft);
    saveDb({ sync: false });
    synced = await syncToServer();
    return synced;
  }

  async function moveCollectionsToDraft(ids, { optimizationScopes = [], priceMultiplier = 1 } = {}) {
    let created = 0;
    const createdDrafts = [];
    for (const id of ids) {
      const source = state.db.collections.find(item => item.id === id);
      if (!source) continue;
      let sku = "";
      try { sku = clean((await apiJson("/workflow/next-parent-sku")).data?.sku); } catch (error) { toast(`无法生成唯一 SKU：${error.message}`, "error"); continue; }
      const draft = draftFromCollection(source, sku);
      applyDraftPriceMultiplier(draft, priceMultiplier);
      state.db.drafts.unshift(draft);
      createdDrafts.push(draft);
      pendingDraftUpserts.set(draft.id, clone(draft));
      created++;
      source.collectionStatus = "已传草稿";
      source.updatedAt = nowIso();
    }
    state.selected.collection.clear();
    saveDb({ sync: false });
    render();
    if (!created) { toast("没有可上传的采集记录", "info"); return; }
    const scopes = normalizeAiFieldScopes(optimizationScopes);
    if (scopes.length && aiApiOptimizationAvailable()) {
      createdDrafts.forEach(draft => activeDraftOptimizations.add(draft.id));
    }
    const initiallySynced = await persistCreatedDrafts(createdDrafts);
    if (!scopes.length || !aiApiOptimizationAvailable()) {
      if (initiallySynced) createdDrafts.forEach(draft => pendingDraftUpserts.delete(draft.id));
      toast(`已新建 ${created} 个草稿，每个草稿均已生成全局唯一 SKU${initiallySynced ? "" : "；云端保存失败，内容已保留在当前浏览器"}`, initiallySynced ? "success" : "info");
      return;
    }
    toast(`已创建 ${created} 个草稿，正在逐个优化所选文案模块`, "info");
    let optimized = 0, failed = 0;
    const failureMessages = [];
    for (const draft of createdDrafts) {
      try { await optimizeDraftAfterCreation(draft, scopes); pendingDraftUpserts.set(draft.id, clone(draft)); optimized++; saveDb({ sync: false }); }
      catch (error) { failed++; failureMessages.push(clean(error?.message) || "未知错误"); }
    }
    saveDb({ sync: false });
    const synced = await persistCreatedDrafts(createdDrafts);
    createdDrafts.forEach(draft => activeDraftOptimizations.delete(draft.id));
    if (synced) createdDrafts.forEach(draft => pendingDraftUpserts.delete(draft.id));
    render();
    const failureReason = unique(failureMessages).slice(0, 2).join("；");
    toast(`草稿创建完成：AI优化成功 ${optimized} 个${failed ? `，${failed} 个保留原始内容${failureReason ? `（${failureReason}）` : ""}` : ""}${synced ? "" : "；云端保存失败，内容已保留在当前浏览器"}`, failed || !synced ? "info" : "success");
  }

  function updateBadges() {
    $("collectionBadge").textContent = activeStoreRows(state.db.collections).length;
    $("draftBadge").textContent = activeStoreRows(state.db.drafts).length;
    $("productBadge").textContent = activeStoreRows(state.db.products).length;
  }

  function toast(message, type = "info") {
    const node = document.createElement("div");
    node.className = `toast ${type}`;
    node.textContent = message;
    $("toastWrap").appendChild(node);
    setTimeout(() => node.remove(), 3500);
  }

  function navigationView() {
    if (state.view === "edit" || state.view === "variants") {
      return state.editingMode === "product" ? "products" : "drafts";
    }
    return state.view;
  }

  function syncNavigationActive() {
    const view = navigationView();
    document.querySelectorAll("[data-nav]").forEach(button => {
      const active = button.dataset.nav === view;
      button.classList.toggle("active", active);
      if (active) button.setAttribute("aria-current", "page");
      else button.removeAttribute("aria-current");
    });
  }

  function setView(view) {
    if (view === "audit" && Number(authState.user?.accountLevel || 0) !== 1) {
      toast("审计日志仅限一级账号查看", "error");
      return;
    }
    if (view in MENU_PERMISSIONS && !can(view)) {
      toast(`当前账号未开通“${MENU_PERMISSIONS[view]}”权限`, "error");
      return;
    }
    if (view === "accounts" && !can("accounts")) {
      toast("当前账号没有账号管理权限", "error");
      return;
    }
    if (view in CENTER_PERMISSIONS && !can(CENTER_PERMISSIONS[view])) {
      toast("当前账号没有进入该管理中心的权限", "error");
      return;
    }
    if (view === "api-settings" && !canManageAmazonCredentials()) {
      toast("只有主账号可以进入 API 设置", "error");
      return;
    }
    if (view === "ai-services" && authState.user?.role !== "main") {
      toast("只有主账号可以配置 AI 与翻译服务", "error");
      return;
    }
    state.view = view;
    ensureWorkspaceTab(view);
    syncNavigationActive();
    const titles = {
      collection: ["采集库", "商品工作流 / 采集库"],
      drafts: ["草稿库", "商品工作流 / 草稿库"],
      products: ["产品库", "商品工作流 / 产品库"],
      orders: ["订单管理", "商品工作流 / 订单管理"],
      settings: ["默认设置", "系统 / 默认设置"],
      "compliance-label": ["合规标签", "系统 / Amazon PS01 合规图片"],
      "api-settings": ["API设置", "系统 / Amazon SP-API 沙盒"],
      "ai-services": ["AI与翻译", "系统 / AI与翻译服务"],
      accounts: ["账号管理", "系统 / 账号与权限"],
      shops: ["店铺中心", "系统 / 店铺与授权"],
      logistics: ["物流绑定", "系统 / 店铺物流配置"],
      transparency: ["透明计划", "系统 / Transparency 授权"],
      billing: ["账号计费", "系统 / 局内 API 操作计费"],
      audit: ["审计日志", "系统 / 安全审计"],
      "ui-components": ["组件规范", "系统 / Design System"],
      "no-access": ["等待授权", "系统 / 权限"]
    };
    $("pageTitle").textContent = titles[view]?.[0] || "易联跨境";
    $("breadcrumb").textContent = titles[view]?.[1] || "商品工作流";
    $("collectProductBtn").classList.add("hidden");
    $("returnViewBtn").classList.add("hidden");
    $("topSubmitBtn").classList.add("hidden");
    $("exportDataBtn").classList.add("hidden");
    render();
    if (view === "orders") queueMicrotask(() => loadOrders());
    if (view === "billing") queueMicrotask(() => loadBilling());
    if (["logistics", "transparency", "audit"].includes(view)) queueMicrotask(() => loadAccountCenter(view));
    persistUiState();
  }

  function editTabKey(mode, id) {
    return `edit:${mode === "product" ? "product" : "draft"}:${id}`;
  }

  function isEditTabKey(key) {
    return typeof key === "string" && key.startsWith("edit:");
  }

  function currentEditTabKey() {
    if (state.activeEditTab) return state.activeEditTab;
    return state.editing?.id ? editTabKey(state.editingMode, state.editing.id) : "";
  }

  function rememberEditorScroll() {
    const shell = $("viewRoot")?.querySelector(".edit-shell[data-edit-tab-key]");
    const key = shell?.dataset.editTabKey;
    const viewName = shell?.dataset.editView || "edit";
    if (key) state.editScrollByTab[`${key}::${viewName}`] = shell.scrollTop;
  }

  function restoreEditorScroll(key = currentEditTabKey(), viewName = "edit") {
    if (!key) return;
    const scrollTop = Math.max(0, num(state.editScrollByTab[`${key}::${viewName}`]));
    requestAnimationFrame(() => {
      const shell = [...($("viewRoot")?.querySelectorAll(".edit-shell[data-edit-tab-key]") || [])]
        .find(node => node.dataset.editTabKey === key && (node.dataset.editView || "edit") === viewName);
      if (shell) shell.scrollTop = scrollTop;
    });
  }

  function clearEditorScroll(key) {
    Object.keys(state.editScrollByTab).forEach(item => {
      if (item.startsWith(`${key}::`)) delete state.editScrollByTab[item];
    });
  }

  function activeAiPending(key = currentEditTabKey()) {
    return key ? state.aiPendingByTab[key] || null : null;
  }

  function setAiPending(pending, key = currentEditTabKey()) {
    if (!key || !pending) return null;
    const next = { ...pending, tabKey: key };
    state.aiPendingByTab[key] = next;
    return next;
  }

  function clearAiPending(pendingOrKey) {
    const key = typeof pendingOrKey === "string" ? pendingOrKey : pendingOrKey?.tabKey;
    if (key) delete state.aiPendingByTab[key];
  }

  function createAiTaskAbortController(taskId) {
    const controller = new AbortController();
    aiTaskAbortControllers.set(taskId, controller);
    return controller;
  }

  function releaseAiTaskAbortController(taskId) {
    aiTaskAbortControllers.delete(taskId);
  }

  function cancelAiPendingForTab(key) {
    const pending = key ? state.aiPendingByTab[key] : null;
    if (!pending?.taskId) return false;
    const controller = aiTaskAbortControllers.get(pending.taskId);
    if (controller && !controller.signal.aborted) controller.abort();
    releaseAiTaskAbortController(pending.taskId);
    clearAiPending(key);
    sendExtensionMessage({ type: "CANCEL_EDITOR_TASK", taskId: pending.taskId }).catch(() => {});
    return true;
  }

  function findAiPending(taskId) {
    return Object.values(state.aiPendingByTab).find(item => item?.taskId === taskId) || null;
  }

  function pendingEditContext(pending) {
    if (!pending?.tabKey) return null;
    if (state.activeEditTab === pending.tabKey && state.editing?.id === pending.editingId) {
      return { editing: state.editing, active: true };
    }
    const session = state.editSessions[pending.tabKey];
    if (!session?.editing || session.editing.id !== pending.editingId) return null;
    return { editing: session.editing, active: false, session };
  }

  function renderPendingEdit(pending) {
    if (state.activeEditTab === pending?.tabKey && state.editing?.id === pending?.editingId && (state.view === "edit" || state.view === "variants")) {
      render();
    } else {
      renderWorkspaceTabs();
      persistUiState();
    }
  }

  function saveActiveEditSession({ capture = true } = {}) {
    const key = currentEditTabKey();
    if (!key || !state.editing) return;
    rememberEditorScroll();
    if (capture && state.view === "edit" && $("editTitle")) captureDraftForm();
    state.activeEditTab = key;
    state.editSessions[key] = {
      editing: clone(state.editing),
      editingMode: state.editingMode,
      originalEditing: clone(state.editSessions[key]?.originalEditing || state.editing),
      variantSession: state.variantSession ? clone(state.variantSession) : null,
      editLocale: state.editLocale,
      view: state.view === "variants" && state.variantSession ? "variants" : "edit"
    };
  }

  function activateEditTab(key) {
    if (!isEditTabKey(key)) return false;
    saveActiveEditSession();
    const session = state.editSessions[key];
    if (!session?.editing) return false;
    state.activeEditTab = key;
    state.editing = clone(session.editing);
    state.editingMode = session.editingMode === "product" ? "product" : "draft";
    state.variantSession = session.variantSession ? clone(session.variantSession) : null;
    state.editLocale = session.editLocale || state.editing.countries?.[0] || "";
    state.view = session.view === "variants" && state.variantSession ? "variants" : "edit";
    syncNavigationActive();
    requestAnimationFrame(() => {
      syncNavigationActive();
      document.querySelector(`[data-nav="${navigationView()}"]`)?.scrollIntoView({ block: "nearest" });
    });
    return true;
  }

  function openEditor(item, mode) {
    if (!item?.id) return;
    saveActiveEditSession();
    const key = editTabKey(mode, item.id);
    if (!state.editSessions[key]) {
      const editing = clone(item);
      initializeVariantPricingSession(editing);
      state.editSessions[key] = {
        editing,
        editingMode: mode === "product" ? "product" : "draft",
        originalEditing: clone(item),
        variantSession: null,
        editLocale: item.countries?.[0] || "",
        view: "edit"
      };
    }
    if (!state.openTabs.includes(key)) state.openTabs.push(key);
    activateEditTab(key);
    render();
  }

  function finishActiveEditSession(targetView) {
    const key = currentEditTabKey();
    if (key) {
      cancelAiPendingForTab(key);
      delete state.editSessions[key];
      clearEditorScroll(key);
      state.openTabs = state.openTabs.filter(item => item !== key);
    }
    state.activeEditTab = "";
    state.editing = null;
    state.variantSession = null;
    state.editingMode = "draft";
    state.editLocale = "";
    setView(targetView);
  }

  function workspaceTabKey(view = state.view) {
    return view === "edit" || view === "variants" ? currentEditTabKey() : view;
  }

  function ensureWorkspaceTab(view = state.view) {
    const key = workspaceTabKey(view);
    if (key === "no-access" || !key) return;
    if (!state.openTabs.includes(key)) state.openTabs.push(key);
  }

  function renderWorkspaceTabs() {
    const root = $("workspaceTabs");
    if (!root) return;
    ensureWorkspaceTab();
    const labels = { collection: "采集库", drafts: "产品草稿列表", products: "产品列表", orders: "订单管理", settings: "默认设置", "compliance-label": "合规标签", "api-settings": "API设置", "ai-services": "AI与翻译", accounts: "账号管理", shops: "店铺中心", logistics: "物流绑定", transparency: "透明计划", billing: "账号计费", audit: "审计日志", "ui-components": "组件规范" };
    state.openTabs = state.openTabs.filter(key => (!(key in MENU_PERMISSIONS) || can(key)) && (key !== "audit" || Number(authState.user?.accountLevel || 0) === 1));
    root.innerHTML = state.openTabs.filter(key => labels[key] || isEditTabKey(key)).map(key => {
      const active = workspaceTabKey() === key;
      const session = key === state.activeEditTab && state.editing ? { editing: state.editing, editingMode: state.editingMode } : state.editSessions[key];
      const editLabel = isEditTabKey(key)
        ? (session?.editingMode === "product" ? "编辑产品" : "编辑草稿")
        : "";
      const closeable = isEditTabKey(key) || state.openTabs.length > 1;
    const tabIcons = { collection: "◉", drafts: "▤", products: "▦", orders: "▣", settings: "⚙", "compliance-label": "▧", "ai-services": "✦", accounts: "♙", logistics: "L", transparency: "T", billing: "¥", audit: "A" };
      return `<button class="workspace-tab ${active ? "active" : ""}" data-workspace-tab="${key}"><span class="workspace-tab-icon" aria-hidden="true">${tabIcons[key] || (isEditTabKey(key) ? "✎" : "▦")}</span><span class="workspace-tab-label">${esc(labels[key] || editLabel)}</span>${closeable ? `<i data-close-workspace-tab="${key}" title="关闭">×</i>` : ""}</button>`;
    }).join("");
    requestAnimationFrame(() => {
      const active = root.querySelector(".workspace-tab.active");
      if (!active) return;
      const rootBounds = root.getBoundingClientRect();
      const activeBounds = active.getBoundingClientRect();
      if (activeBounds.left < rootBounds.left) root.scrollLeft -= rootBounds.left - activeBounds.left + 8;
      else if (activeBounds.right > rootBounds.right) root.scrollLeft += activeBounds.right - rootBounds.right + 8;
    });
  }

  function summaryCards(cards) {
    return `<div class="page-summary">${cards.map(card => `<div class="stat"><label>${esc(card.label)}</label><strong>${esc(card.value)}</strong><small>${esc(card.sub)}</small></div>`).join("")}</div>`;
  }

  function paginate(scope, rows) {
    const pager = state.pagination[scope] || (state.pagination[scope] = { page: 1, pageSize: 10 });
    pager.pageSize = [10, 20, 50, 100].includes(Number(pager.pageSize)) ? Number(pager.pageSize) : 10;
    const pages = Math.max(1, Math.ceil(rows.length / pager.pageSize));
    pager.page = Math.min(pages, Math.max(1, Number(pager.page) || 1));
    const start = (pager.page - 1) * pager.pageSize;
    return { rows: rows.slice(start, start + pager.pageSize), total: rows.length, page: pager.page, pageSize: pager.pageSize, pages };
  }

  function paginationNumbers(page, pages) {
    const values = new Set([1, pages, page - 2, page - 1, page, page + 1, page + 2]);
    return [...values].filter(value => value >= 1 && value <= pages).sort((a, b) => a - b);
  }

  function paginationBar(scope, total) {
    const pager = state.pagination[scope];
    const pages = Math.max(1, Math.ceil(total / pager.pageSize));
    const numbers = paginationNumbers(pager.page, pages);
    let previous = 0;
    const pageButtons = numbers.map(value => {
      const gap = previous && value - previous > 1 ? '<span class="pager-ellipsis">…</span>' : "";
      previous = value;
      return `${gap}<button class="pager-button ${value === pager.page ? "active" : ""}" data-action="set-page" data-scope="${scope}" data-page="${value}">${value}</button>`;
    }).join("");
    return `<div class="pager"><span>共 ${total} 条</span><select class="pager-size" data-page-size data-scope="${scope}" aria-label="每页数量">${[10,20,50,100].map(value => `<option value="${value}" ${value === pager.pageSize ? "selected" : ""}>${value}条/页</option>`).join("")}</select><div class="pager-pages"><button class="pager-button" data-action="set-page" data-scope="${scope}" data-page="${pager.page - 1}" ${pager.page <= 1 ? "disabled" : ""}>‹</button>${pageButtons}<button class="pager-button" data-action="set-page" data-scope="${scope}" data-page="${pager.page + 1}" ${pager.page >= pages ? "disabled" : ""}>›</button></div><label class="pager-jump">前往<input type="number" min="1" max="${pages}" value="${pager.page}" data-page-jump data-scope="${scope}">页</label></div>`;
  }

  const LIST_COLUMNS = {
    collection: [{ key: "product", label: "产品信息" }, { key: "country", label: "上架国家" }, { key: "status", label: "采集状态" }, { key: "draft", label: "草稿状态" }, { key: "variants", label: "变体数量" }, { key: "time", label: "采集时间" }],
    drafts: [{ key: "product", label: "产品信息" }, { key: "country", label: "上架国家" }, { key: "status", label: "创建状态" }, { key: "sku", label: "SKU" }, { key: "cost", label: "成本价" }, { key: "variants", label: "变体数量" }, { key: "upc", label: "UPC" }, { key: "time", label: "创建时间" }],
    products: [{ key: "product", label: "产品信息" }, { key: "country", label: "上架国家" }, { key: "sku", label: "SKU" }, { key: "cost", label: "成本价" }, { key: "variants", label: "变体数量" }, { key: "upc", label: "UPC" }, { key: "listing", label: "上传状态" }, { key: "time", label: "更新时间" }]
  };

  function listView(scope) {
    const root = normalizeSettings(state.db.settings);
    const saved = root.listViews?.[scope] || {};
    return { density: ["compact", "medium", "comfortable"].includes(saved.density) ? saved.density : "medium", hidden: Array.isArray(saved.hidden) ? saved.hidden : [] };
  }

  function listTableClass(scope) {
    const view = listView(scope);
    return `list-table list-${scope} density-${view.density} ${view.hidden.map(key => `hide-${key}`).join(" ")}`;
  }

  function listSettingsButton(scope) {
    return `<button class="btn btn-small" data-action="open-list-settings" data-scope="${scope}">⚙ 列表设置</button>`;
  }

  function openListSettings(scope) {
    const view = listView(scope), columns = LIST_COLUMNS[scope] || [];
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal list-settings-modal"><div class="modal-head"><div><h2>产品列表设置</h2><p>设置显示列和列表行距，选择框与操作列始终保留。</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body">
      <div class="field"><label>视图密度</label><div class="density-options">${[["compact","紧凑"],["medium","适中"],["comfortable","宽松"]].map(([value,label]) => `<label class="country-check"><input type="radio" name="listDensity" value="${value}" ${view.density === value ? "checked" : ""}>${label}</label>`).join("")}</div></div>
      <div class="field"><label>显示列</label><div class="column-options">${columns.map(column => `<label class="permission-check"><input type="checkbox" data-list-column value="${column.key}" ${view.hidden.includes(column.key) ? "" : "checked"}>${column.label}</label>`).join("")}</div></div>
      </div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="save-list-settings" data-scope="${scope}">保存设置</button></div></div></div>`;
  }

  function saveListSettings(scope) {
    const root = normalizeSettings(state.db.settings);
    const visible = [...document.querySelectorAll("[data-list-column]:checked")].map(input => input.value);
    const hidden = (LIST_COLUMNS[scope] || []).map(column => column.key).filter(key => !visible.includes(key));
    root.listViews = { ...(root.listViews || {}), [scope]: { density: document.querySelector("[name=listDensity]:checked")?.value || "medium", hidden } };
    state.db.settings = root;
    saveDb(); closeModal(); render();
    toast("列表显示设置已保存", "success");
  }

  function emptyState(title, message, button = "") {
    return `<div class="empty"><div class="empty-icon">⌁</div><h3>${esc(title)}</h3><p>${esc(message)}</p>${button}</div>`;
  }

  function thumb(item) {
    const image = safeImage(item.image || item.images?.[0]);
    return image
      ? `<button type="button" class="product-thumb-button" data-action="preview-list-image" data-image-url="${attr(image)}" aria-label="放大产品图片"><img class="thumb" src="${attr(image)}" alt="" data-image-fallback><span class="product-thumb-popover"><img src="${attr(image)}" alt=""></span></button>`
      : `<div class="thumb-fallback">A</div>`;
  }

  function productCell(item) {
    const url = clean(item.url || item.purchaseUrl);
    const title = url
      ? `<a class="product-title" href="${attr(url)}" target="_blank" rel="noopener noreferrer" title="${attr(item.title)}">${esc(item.title)}</a>`
      : `<strong class="product-title" title="${attr(item.title)}">${esc(item.title)}</strong>`;
    const completeness = item.dataCompleteness
      ? ` · <span class="${item.dataCompleteness === "待补全" ? "partial-hint" : ""}">${esc(item.dataCompleteness)}</span>`
      : "";
    return `<div class="product-info">${thumb(item)}<div class="product-copy">${title}<div class="meta">ASIN ${esc(item.asin || "—")}${completeness}</div></div></div>`;
  }

  function variantCount(item) {
    const rows = Array.isArray(item?.variantRows) && item.variantRows.length ? item.variantRows : (Array.isArray(item?.variants) ? item.variants : []);
    const identities = new Set(rows.map(row => clean(row?.asin) || `${clean(row?.color)}|${clean(row?.size)}|${clean(row?.value)}`).filter(Boolean));
    return Math.max(1, identities.size || rows.length || 0);
  }

  function statusClass(value) {
    if (/已入|已采集|完成|已创建|已上架|沙盒通过|上传成功|预检通过|上架成功/.test(value)) return "status-green";
    if (/采集中|上传中|预检中|提交中|已受理|处理中/.test(value)) return "status-blue";
    if (/失败|中断/.test(value)) return "status-red";
    if (/草稿|编辑/.test(value)) return "status-blue";
    if (/待|未/.test(value)) return "status-amber";
    return "status-gray";
  }

  function countryOptions(selected = "") {
    const allowed = activeStoreCountries();
    return `<option value="">全部国家</option>${COUNTRIES.filter(item => allowed.includes(item.code)).map(item => `<option value="${item.code}" ${selected === item.code ? "selected" : ""}>${item.name}</option>`).join("")}`;
  }

  function filterByDate(itemDate, start, end) {
    const time = new Date(itemDate).getTime();
    if (start && time < new Date(`${start}T00:00:00`).getTime()) return false;
    if (end && time > new Date(`${end}T23:59:59`).getTime()) return false;
    return true;
  }

  function collectionDraftStatus(item) {
    const draftCount = state.db.drafts.filter(row => row.sourceId === item.id).length;
    const productCount = state.db.products.filter(row => row.sourceId === item.id).length;
    if (productCount) return { text: `已入产品库 ${productCount} 份`, cls: "status-green" };
    if (draftCount) return { text: `已有草稿 ${draftCount} 份`, cls: "status-blue" };
    return { text: "未生成草稿", cls: "status-gray" };
  }

  function renderCollection() {
    const filters = state.filters.collection;
    const all = activeStoreRows(state.db.collections);
    const filtered = all.filter(item => {
      const query = clean(filters.query).toLowerCase();
      const haystack = `${item.title} ${item.asin}`.toLowerCase();
      return (!query || haystack.includes(query))
        && (!filters.country || item.countries.includes(filters.country))
        && (!filters.collectionStatus || item.collectionStatus === filters.collectionStatus)
        && filterByDate(item.collectedAt, filters.start, filters.end);
    });
    const { rows: list, total } = paginate("collection", filtered);
    const selected = state.selected.collection;
    const pageSelected = Boolean(list.length) && list.every(item => selected.has(item.id));
    $("viewRoot").innerHTML =
      `<div class="panel filter-panel"><div class="panel-body"><div class="filter-layout">
        <div class="filter-grid">
          <div class="field"><label>标题 / ASIN</label><input class="control" id="collectionQuery" value="${attr(filters.query || "")}" placeholder="搜索产品"></div>
          <div class="field"><label>国家</label><select class="control" id="collectionCountry">${countryOptions(filters.country)}</select></div>
          <div class="field"><label>采集状态</label><select class="control" id="collectionStatus"><option value="">全部状态</option><option ${filters.collectionStatus === "采集中" ? "selected" : ""}>采集中</option><option ${filters.collectionStatus === "已采集" ? "selected" : ""}>已采集</option><option ${filters.collectionStatus === "部分成功" ? "selected" : ""}>部分成功</option><option ${filters.collectionStatus === "采集失败" ? "selected" : ""}>采集失败</option><option ${filters.collectionStatus === "已传草稿" ? "selected" : ""}>已传草稿</option><option ${filters.collectionStatus === "已入产品库" ? "selected" : ""}>已入产品库</option></select></div>
          <div class="field"><label>开始时间</label><input class="control" type="date" id="collectionStart" value="${attr(filters.start || "")}"></div>
          <div class="field"><label>结束时间</label><input class="control" type="date" id="collectionEnd" value="${attr(filters.end || "")}"></div>
        </div>
        <div class="filter-actions"><button class="btn" data-action="reset-filters" data-scope="collection">重置</button><button class="btn btn-primary" data-action="apply-filters" data-scope="collection">查询</button></div>
      </div></div></div>
      <div class="panel">
        <div class="toolbar"><div class="toolbar-group">
          <button class="btn btn-primary" data-action="open-link-collect">＋ 链接采集</button>
          <button class="btn" data-action="upload-selected-drafts" ${selected.size ? "" : "disabled"}>上传到草稿库</button>
          <button class="btn" data-action="recollect-selected" ${selected.size === 1 ? "" : "disabled"}>重新采集</button>
          <button class="btn btn-danger" data-action="delete-selected" data-scope="collection" ${selected.size ? "" : "disabled"}>批量删除</button>
        </div><div class="toolbar-tail"><span class="selected-hint">已选择 ${selected.size} 项</span>${listSettingsButton("collection")}</div></div>
        ${list.length ? `<div class="table-wrap"><table class="${listTableClass("collection")}" data-list-scope="collection"><thead><tr>
          <th><input class="check" type="checkbox" data-select-all="collection" ${pageSelected ? "checked" : ""}></th><th>产品信息</th><th>上架国家</th><th>采集状态</th><th>草稿状态</th><th>变体数量</th><th>采集时间</th><th>操作</th>
        </tr></thead><tbody>${list.map(item => `<tr>
          <td><input class="check" type="checkbox" data-select="collection" value="${attr(item.id)}" ${selected.has(item.id) ? "checked" : ""}></td>
          <td>${productCell(item)}</td><td>${item.countries.map(countryName).join("、")}</td>
          <td><span class="status ${statusClass(item.collectionStatus)}">${esc(item.collectionStatus)}</span></td>
          ${(() => { const draftStatus = collectionDraftStatus(item); return `<td><span class="status ${draftStatus.cls}">${esc(draftStatus.text)}</span></td>`; })()}<td>${variantCount(item)}</td>
          <td>${formatDate(item.collectedAt)}</td><td><div class="row-actions">
            <button class="link-btn" data-action="view-collection-detail" data-id="${attr(item.id)}">查看详情</button>
            ${!["采集中", "采集失败"].includes(item.collectionStatus) ? `<button class="link-btn" data-action="upload-one-draft" data-id="${attr(item.id)}">新建草稿</button>` : ""}
            <button class="link-btn" data-action="recollect-one" data-id="${attr(item.id)}">重新采集</button>
            <button class="link-btn danger-link" data-action="delete-one" data-scope="collection" data-id="${attr(item.id)}">删除</button>
          </div></td>
        </tr>`).join("")}</tbody></table></div>${paginationBar("collection", total)}` : emptyState("采集库暂无产品", "点击“链接采集”，输入亚马逊商品链接即可开始采集。", `<button class="btn btn-primary" data-action="open-link-collect">＋ 链接采集</button>`)}
      </div>`;
  }

  function renderDrafts() {
    const filters = state.filters.drafts;
    const all = activeStoreRows(state.db.drafts);
    const filtered = all.filter(item => {
      const query = clean(filters.query).toLowerCase();
      return (!query || `${item.title} ${item.asin} ${item.sku}`.toLowerCase().includes(query))
        && (!filters.country || item.countries.includes(filters.country))
        && (!filters.creationStatus || item.creationStatus === filters.creationStatus);
    });
    const { rows: list, total } = paginate("drafts", filtered);
    const selected = state.selected.drafts;
    const pageSelected = Boolean(list.length) && list.every(item => selected.has(item.id));
    $("viewRoot").innerHTML =
      `<div class="panel filter-panel"><div class="panel-body"><div class="filter-layout"><div class="filter-grid">
        <div class="field"><label>编号 / SKU / 标题</label><input class="control" id="draftQuery" value="${attr(filters.query || "")}" placeholder="搜索草稿"></div>
        <div class="field"><label>国家</label><select class="control" id="draftCountry">${countryOptions(filters.country)}</select></div>
        <div class="field"><label>创建状态</label><select class="control" id="draftCreation"><option value="">全部状态</option><option ${filters.creationStatus === "待编辑" ? "selected" : ""}>待编辑</option><option ${filters.creationStatus === "编辑中" ? "selected" : ""}>编辑中</option></select></div>
      </div><div class="filter-actions"><button class="btn" data-action="reset-filters" data-scope="drafts">重置</button><button class="btn btn-primary" data-action="apply-filters" data-scope="drafts">查询</button></div></div></div></div>
      <div class="panel"><div class="toolbar"><div class="toolbar-group">
        <button class="btn btn-primary" data-action="submit-selected-products" ${selected.size ? "" : "disabled"}>提交到产品库</button>
        <button class="btn btn-danger" data-action="delete-selected" data-scope="drafts" ${selected.size ? "" : "disabled"}>批量删除</button>
      </div><div class="toolbar-tail"><span class="selected-hint">已选择 ${selected.size} 项</span>${listSettingsButton("drafts")}</div></div>
      ${list.length ? `<div class="table-wrap"><table class="${listTableClass("drafts")}" data-list-scope="drafts"><thead><tr>
        <th><input class="check" type="checkbox" data-select-all="drafts" ${pageSelected ? "checked" : ""}></th><th>产品信息</th><th>上架国家</th><th>创建状态</th><th>SKU</th><th>成本价</th><th>变体数量</th><th>UPC</th><th>创建时间</th><th>操作</th>
      </tr></thead><tbody>${list.map(item => `<tr>
        <td><input class="check" type="checkbox" data-select="drafts" value="${attr(item.id)}" ${selected.has(item.id) ? "checked" : ""}></td>
        <td>${productCell(item)}</td><td>${item.countries.map(countryName).join("、")}</td>
        <td><span class="status ${statusClass(item.creationStatus)}">${esc(item.creationStatus)}</span></td><td><strong>${esc(item.sku)}</strong></td>
        <td>¥${num(item.cost).toFixed(2)}</td><td>${variantCount(item)}</td><td>${item.upcMode === "exempt" ? "UPC豁免" : esc(item.upc || (item.upcMode === "replace" ? "待填写新UPC" : "使用原UPC"))}</td><td>${formatDate(item.createdAt)}</td>
        <td><div class="row-actions"><button class="link-btn" data-action="edit-draft" data-id="${attr(item.id)}">编辑产品</button><button class="link-btn danger-link" data-action="delete-one" data-scope="drafts" data-id="${attr(item.id)}">删除</button></div></td>
      </tr>`).join("")}</tbody></table></div>${paginationBar("drafts", total)}` : emptyState("草稿库为空", "请先在采集库选择产品并上传到草稿库。")} 
      </div>`;
  }

  function renderProducts() {
    const filters = state.filters.products;
    const all = activeStoreRows(state.db.products);
    const filtered = all.filter(item => {
      const query = clean(filters.query).toLowerCase();
      return (!query || `${item.title} ${item.asin} ${item.sku}`.toLowerCase().includes(query))
        && (!filters.country || item.countries.includes(filters.country))
        && (!filters.listingStatus || item.listingStatus === filters.listingStatus);
    });
    const { rows: list, total } = paginate("products", filtered);
    const selected = state.selected.products;
    const pageSelected = Boolean(list.length) && list.every(item => selected.has(item.id));
    $("viewRoot").innerHTML =
      `<div class="panel filter-panel"><div class="panel-body"><div class="filter-layout"><div class="filter-grid">
        <div class="field"><label>编号 / SKU / 标题</label><input class="control" id="productQuery" value="${attr(filters.query || "")}" placeholder="搜索产品"></div>
        <div class="field"><label>国家</label><select class="control" id="productCountry">${countryOptions(filters.country)}</select></div>
        <div class="field"><label>上传状态</label><select class="control" id="productListing"><option value="">全部状态</option>${["未上传", "上传中", "沙盒通过", "上传失败", "生产预检中", "生产预检通过", "提交中", "Amazon已受理", "处理中", "上架成功", "上架失败", "上传中断"].map(value => `<option ${filters.listingStatus === value ? "selected" : ""}>${value}</option>`).join("")}</select></div>
      </div><div class="filter-actions"><button class="btn" data-action="reset-filters" data-scope="products">重置</button><button class="btn btn-primary" data-action="apply-filters" data-scope="products">查询</button></div></div></div></div>
      <div class="panel"><div class="toolbar"><div class="toolbar-group">
        <button class="btn btn-primary" data-action="export-selected-products" ${selected.size ? "" : "disabled"}>批量导出</button>
        <button class="btn btn-danger" data-action="delete-selected" data-scope="products" ${selected.size ? "" : "disabled"}>批量删除</button>
        <button class="btn" data-action="${activeApiEnvironment() === "production" ? "batch-upload" : "batch-sandbox-upload"}" ${selected.size ? "" : "disabled"}>批量上传</button>
      </div><div class="toolbar-tail"><span class="selected-hint">已选择 ${selected.size} 项</span>${listSettingsButton("products")}</div></div>
      ${list.length ? `<div class="table-wrap"><table class="${listTableClass("products")}" data-list-scope="products"><thead><tr>
        <th><input class="check" type="checkbox" data-select-all="products" ${pageSelected ? "checked" : ""}></th><th>产品信息</th><th>上架国家</th><th>SKU</th><th>成本价</th><th>变体数量</th><th>UPC</th><th>上传状态</th><th>更新时间</th><th>操作</th>
      </tr></thead><tbody>${list.map(item => `<tr>
        <td><input class="check" type="checkbox" data-select="products" value="${attr(item.id)}" ${selected.has(item.id) ? "checked" : ""}></td>
        <td>${productCell(item)}</td><td>${item.countries.map(countryName).join("、")}</td>
        <td><strong>${esc(item.sku)}</strong></td>
        <td>¥${num(item.cost).toFixed(2)}</td><td>${variantCount(item)}</td><td>${item.upcMode === "exempt" ? "UPC豁免" : esc(item.upc || (item.upcMode === "replace" ? "待填写新UPC" : "使用原UPC"))}</td>
        <td><span class="status ${statusClass(item.listingStatus)}">${esc(item.listingStatus)}</span></td><td>${formatDate(item.updatedAt)}</td>
        <td><div class="row-actions"><button class="link-btn" data-action="view-product" data-id="${attr(item.id)}">查看</button><button class="link-btn" data-action="edit-product" data-id="${attr(item.id)}">编辑产品</button><button class="link-btn" data-action="${activeApiEnvironment() === "production" ? "upload-product" : "sandbox-upload-product"}" data-id="${attr(item.id)}" ${activeSandboxUploads.has(item.id) || queuedProductionUploads.has(item.id) ? "disabled" : ""}>${activeApiEnvironment() === "sandbox" && item.listingStatus === "上传中" ? "重试" : "上传"}</button><button class="link-btn" data-action="view-upload-failure" data-id="${attr(item.id)}" ${item.uploadFailureReason ? "" : "disabled"}>失败原因</button><button class="link-btn" data-action="delete-one" data-scope="products" data-id="${attr(item.id)}">删除</button></div></td>
      </tr>`).join("")}</tbody></table></div>${paginationBar("products", total)}` : emptyState("产品库为空", "草稿编辑完成并提交后，产品会出现在这里。")} 
      </div>`;
  }

  function uploadProductPayload(product) {
    const liveProduct = state.db.products.find(item => item.id === product?.id) || product;
    return { product: clone(liveProduct), settings: clone(activeSettings()) };
  }

  function applyProductTypeResolution(product, data = {}) {
    const liveProduct = state.db.products.find(item => item.id === product?.id) || product;
    if (!liveProduct) return null;
    const targets = [liveProduct];
    if (state.editing?.id === liveProduct.id && state.editing !== liveProduct) targets.push(state.editing);
    targets.forEach(target => {
      target.amazonProductTypes = { ...(target.amazonProductTypes || {}), ...(data.productTypes || {}) };
      target.productTypeResolutions = { ...(target.productTypeResolutions || {}), ...(data.resolutions || {}) };
      if (data.sourceClassification && typeof data.sourceClassification === "object") {
        target.sourceClassification = clone(data.sourceClassification);
        target.sourceMarketplace = clean(data.sourceClassification.marketplace).toUpperCase();
        target.sourceProductType = clean(data.sourceClassification.productType).toUpperCase();
      }
      target.amazonProductTypeLabels = target.amazonProductTypeLabels || {};
      Object.entries(data.resolutions || {}).forEach(([country, resolution]) => {
        const label = clean(resolution?.displayName || resolution?.productType);
        if (label) target.amazonProductTypeLabels[country] = label;
      });
      const primaryCountry = target.countries?.[0] || "";
      const primaryType = clean(target.amazonProductTypes?.[primaryCountry]).toUpperCase();
      if (primaryType) target.amazonProductType = primaryType;
      target.updatedAt = nowIso();
    });
    return liveProduct;
  }

  function positionProductThumbPopover(button) {
    const popover = button?.querySelector(".product-thumb-popover");
    if (!popover) return;
    const rect = button.getBoundingClientRect();
    const size = 220;
    const gap = 10;
    const viewportGap = 8;
    const left = rect.right + gap + size <= window.innerWidth - viewportGap
      ? rect.right + gap
      : Math.max(viewportGap, rect.left - gap - size);
    const top = Math.min(Math.max(viewportGap, rect.top + rect.height / 2 - size / 2), Math.max(viewportGap, window.innerHeight - size - viewportGap));
    popover.style.left = `${left}px`;
    popover.style.top = `${top}px`;
  }

  function setManualProductType(product, country, productType, label = "") {
    const code = clean(country).toUpperCase();
    const type = clean(productType).toUpperCase();
    if (!product || !code || !type) return;
    product.amazonProductTypes = product.amazonProductTypes || {};
    product.amazonProductTypeLabels = product.amazonProductTypeLabels || {};
    product.productTypeResolutions = product.productTypeResolutions || {};
    product.amazonProductTypes[code] = type;
    if (label) product.amazonProductTypeLabels[code] = clean(label);
    product.productTypeResolutions[code] = {
      productType: type, displayName: clean(label || type), confidence: 1,
      matchSource: "manual", sourceConsistency: "manual", semanticConsistency: "manual",
      needsManualReview: false, updatedAt: nowIso()
    };
    if (code === product.countries?.[0]) product.amazonProductType = type;
    const sourceCountry = clean(
      product.sourceClassification?.marketplace || product.sourceMarketplace || product.country || product.site || product.countries?.[0]
    ).toUpperCase();
    if (code === sourceCountry) {
      product.sourceClassification = { ...(product.sourceClassification || {}), marketplace: code, productType: type };
      product.sourceMarketplace = code;
      product.sourceProductType = type;
    }
  }

  async function syncToServer() {
    if (serverSyncInFlight) {
      serverSyncQueued = true;
      await serverSyncInFlight;
      return syncToServer();
    }
    serverSyncInFlight = performServerSync();
    try {
      return await serverSyncInFlight;
    } finally {
      serverSyncInFlight = null;
      serverSyncQueued = false;
    }
  }

  async function ensureAmazonProductType(product) {
    const liveProduct = state.db.products.find(item => item.id === product?.id) || product;
    const countries = (liveProduct?.countries || []).filter(code => activeStoreCountries().includes(code));
    const response = await apiJson("/api-settings/product-type-resolution", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ product: clone(liveProduct), countries })
    });
    const data = response?.data || {};
    applyProductTypeResolution(liveProduct, data);
    saveDb({ sync: false });
    await syncToServer().catch(() => false);
    if (data.needsManualReview || (data.missingFields || []).length) {
      const error = new Error("Product Type 无法从源站分类信息可靠确认，请在失败原因中人工选择");
      error.detail = { missingFields: data.missingFields || [] };
      throw error;
    }
    return liveProduct.amazonProductType;
  }

  function updateUploadState(product, status, reason = "", result = null) {
    const target = state.db.products.find(item => item.id === product?.id) || product;
    if (!target) return null;
    target.listingStatus = status;
    target.uploadFailureReason = clean(reason);
    if (result && typeof result === "object") {
      if (result.environment === "production") {
        if (result.mode === "JSON_LISTINGS_FEED") target.productionFeed = clone(result);
        else if (result.mode === "VALIDATION_PREVIEW") target.productionPreview = clone(result);
        else target.productionUpload = clone(result);
        if (result.browseNodes && typeof result.browseNodes === "object") target.amazonBrowseNodes = { ...(target.amazonBrowseNodes || {}), ...result.browseNodes };
        if (result.browseNodePaths && typeof result.browseNodePaths === "object") target.amazonBrowseNodePaths = { ...(target.amazonBrowseNodePaths || {}), ...result.browseNodePaths };
        if (result.browseNodeAttributes && typeof result.browseNodeAttributes === "object") target.amazonBrowseNodeAttributes = { ...(target.amazonBrowseNodeAttributes || {}), ...result.browseNodeAttributes };
      } else target.sandboxUpload = clone(result);
    }
    target.uploadUpdatedAt = nowIso();
    target.updatedAt = nowIso();
    saveDb({ sync: false });
    return target;
  }

  function localValidationResult(error, environment) {
    const missingFields = Array.isArray(error?.detail?.missingFields) ? error.detail.missingFields : [];
    return missingFields.length ? { environment, mode: "LOCAL_VALIDATION", missingFields, items: [] } : null;
  }

  async function sandboxUploadProduct(product, notify = true, persist = true) {
    if (!product) return false;
    if (activeSandboxUploads.has(product.id)) return false;
    try {
      await ensureAmazonProductType(product);
    } catch (error) {
      updateUploadState(product, "上传失败", error.message || "Amazon Product Type 匹配失败", localValidationResult(error, "sandbox"));
      render();
      if (notify) toast(`${product.sku} 上传失败：${error.message}`, "error");
      return false;
    }
    activeSandboxUploads.add(product.id);
    clearTimeout(serverSyncTimer);
    updateUploadState(product, "上传中", "");
    render();
    try {
      const response = await apiJson("/api-settings/sandbox-upload", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(uploadProductPayload(product))
      });
      if (Number.isFinite(Number(response?.data?.workflowRevision))) serverRevision = Number(response.data.workflowRevision);
      const liveProduct = updateUploadState(product, "沙盒通过", "", response.data || {});
      if (persist) {
        clearTimeout(serverSyncTimer);
        await hydrateFromServer({ serverWins: true }).catch(() => false);
      }
      render();
      if (notify) toast(`${liveProduct?.sku || product.sku} 沙盒试传通过`, "success");
      return true;
    } catch (error) {
      const liveProduct = updateUploadState(
        product, "上传失败", error.message || "沙盒试传失败", localValidationResult(error, "sandbox")
      );
      if (persist) {
        clearTimeout(serverSyncTimer);
        await hydrateFromServer({ serverWins: true }).catch(() => false);
      }
      render();
      if (notify) toast(`${liveProduct?.sku || product.sku} 上传失败：${error.message}`, "error");
      return false;
    } finally {
      activeSandboxUploads.delete(product.id);
      render();
    }
  }

  async function batchSandboxUpload(ids) {
    const products = ids.map(id => state.db.products.find(item => item.id === id)).filter(Boolean);
    if (!products.length) { toast("请先选择需要上传的产品", "info"); return; }
    state.selected.products.clear();
    let cursor = 0, success = 0, failed = 0;
    const worker = async () => {
      while (cursor < products.length) {
        const product = products[cursor++];
        if (await sandboxUploadProduct(product, false, false)) success++;
        else failed++;
      }
    };
    await Promise.all(Array.from({ length: Math.min(2, products.length) }, worker));
    clearTimeout(serverSyncTimer);
    await hydrateFromServer({ serverWins: true }).catch(() => false);
    toast(`批量沙盒试传完成：通过 ${success} 个，失败 ${failed} 个`, failed ? "info" : "success");
  }

  function activeApiEnvironment() {
    return authState.apiSettings?.activeEnvironment || authState.apiSettings?.environment || "sandbox";
  }

  async function productionUploadProduct(product, notify = true) {
    if (!product || activeSandboxUploads.has(product.id) || queuedProductionUploads.has(product.id)) return false;
    queuedProductionUploads.add(product.id);
    render();
    const queuedTask = productionUploadQueue.then(() => runProductionUploadProduct(product, notify));
    productionUploadQueue = queuedTask.catch(() => false);
    try {
      return await queuedTask;
    } finally {
      queuedProductionUploads.delete(product.id);
      render();
    }
  }

  async function runProductionUploadProduct(product, notify = true) {
    if (!product || activeSandboxUploads.has(product.id)) return false;
    try {
      await publishStoredProductImages(product);
      await ensureAmazonProductType(product);
    } catch (error) {
      updateUploadState(product, "上架失败", error.message || "Amazon Product Type 匹配失败", localValidationResult(error, "production"));
      render();
      if (notify) toast(`${product.sku} 上传失败：${error.message}`, "error");
      return false;
    }
    activeSandboxUploads.add(product.id);
    clearTimeout(serverSyncTimer);
    updateUploadState(product, "提交中", "");
    render();
    try {
      const payload = uploadProductPayload(product);
      const submitted = await apiJson("/api-settings/production-upload", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload)
      });
      if (Number.isFinite(Number(submitted?.data?.workflowRevision))) serverRevision = Number(submitted.data.workflowRevision);
      const offerError = String(submitted?.data?.offerError || "").trim();
      updateUploadState(
        product,
        "Amazon已受理",
        offerError ? `商品信息已由亚马逊受理，但报价更新失败：${offerError}` : "",
        submitted.data || {}
      );
      await hydrateFromServer({ serverWins: true }).catch(() => false);
      render();
      if (notify) toast(
        offerError
          ? `${product.sku} 商品已受理，但报价更新失败，请查看失败原因后重试`
          : `${product.sku} 商品和报价已由亚马逊受理，亚马逊将在后台继续处理`,
        offerError ? "error" : "success"
      );
      return true;
    } catch (error) {
      updateUploadState(
        product, "上架失败", error.message || "正式上传失败", localValidationResult(error, "production")
      );
      await hydrateFromServer({ serverWins: true }).catch(() => false);
      render();
      if (notify) toast(`${product.sku} 上传失败：${error.message}`, "error");
      return false;
    } finally {
      activeSandboxUploads.delete(product.id);
      render();
    }
  }

  async function uploadProduct(product, notify = true) {
    return activeApiEnvironment() === "production"
      ? productionUploadProduct(product, notify)
      : sandboxUploadProduct(product, notify);
  }

  async function batchUpload(ids) {
    const products = ids.map(id => state.db.products.find(item => item.id === id)).filter(Boolean);
    if (!products.length) { toast("请先选择需要上传的产品", "info"); return; }
    if (activeApiEnvironment() === "sandbox") return batchSandboxUpload(ids);
    const messageCount = products.reduce((total, product) => {
      const countryCount = Math.max(1, unique(product.countries || []).filter(code => COUNTRIES.some(country => country.code === code)).length);
      const variants = Array.isArray(product.variantRows) ? product.variantRows.filter(Boolean).length : 0;
      return total + countryCount * (variants > 1 ? variants + 1 : 1);
    }, 0);
    const useFeed = messageCount >= 1500;
    const routeLabel = useFeed ? "JSON_LISTINGS_FEED 异步批量通道" : "Listings Items 单条实时通道";
    if (useFeed) {
      products.forEach(product => updateUploadState(product, "提交中", ""));
      render();
      try {
        const response = await apiJson("/api-settings/production-feed", {
          method: "POST", headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ products: products.map(product => clone(product)), settings: clone(activeSettings()) })
        });
        products.forEach(product => updateUploadState(product, "处理中", "", response.data || {}));
        state.selected.products.clear();
        await hydrateFromServer({ serverWins: true }).catch(() => false);
        render();
        toast(response?.data?.message || "批量 Feed 已提交", "success");
      } catch (error) {
        products.forEach(product => updateUploadState(product, "上架失败", error.message || "批量 Feed 提交失败"));
        await hydrateFromServer({ serverWins: true }).catch(() => false);
        render();
        toast(`批量 Feed 提交失败：${error.message}`, "error");
      }
      return;
    }
    let success = 0, failed = 0;
    for (const product of products) {
      if (await productionUploadProduct(product, false)) success++; else failed++;
    }
    state.selected.products.clear();
    toast(`生产批量上传结束：已受理 ${success} 个，未提交或失败 ${failed} 个`, failed ? "info" : "success");
  }

  function uploadMissingValue(product, field) {
    if (field?.scope === "product_type") return field.value ?? "";
    if (field?.scope === "variant") {
      const row = (product?.variantRows || []).find(item => String(item.id || item.sku || "") === String(field.variantId || ""));
      return row?.[field.path] ?? "";
    }
    const key = `${field?.country || ""}:${field?.productType || ""}`;
    const raw = product?.amazonRequiredOverrides?.[key]?.[field?.attribute]?.fields?.[field?.path];
    return raw && typeof raw === "object" && "value" in raw ? raw.value : (raw ?? "");
  }

  function uploadMissingFieldHtml(product, field, index) {
    const value = String(uploadMissingValue(product, field) ?? "");
    const enums = Array.isArray(field.enum) ? field.enum : [];
    const enumNames = Array.isArray(field.enumNames) ? field.enumNames : [];
    const input = enums.length
      ? `<select class="control" data-upload-missing-index="${index}"><option value="">请选择</option>${enums.map((option, optionIndex) => `<option value="${attr(option)}" ${String(option) === value ? "selected" : ""}>${esc(enumNames[optionIndex] || option)}</option>`).join("")}</select>`
      : field.type === "boolean"
        ? `<select class="control" data-upload-missing-index="${index}"><option value="">请选择</option><option value="true" ${value === "true" ? "selected" : ""}>是</option><option value="false" ${value === "false" ? "selected" : ""}>否</option></select>`
        : `<input class="control" data-upload-missing-index="${index}" type="${["number", "integer"].includes(field.type) ? "number" : "text"}" ${field.minimum != null ? `min="${attr(field.minimum)}"` : ""} value="${attr(value)}">`;
    const label = field.scope === "product_type"
      ? "商品分类"
      : field.attributeTitle || field.label || field.attribute || "必填项";
    const variant = field.scope === "variant" && (field.variantLabel || field.variantId)
      ? `<span class="upload-required-variant">变体：${esc(field.variantLabel || field.variantId)}</span>`
      : "";
    return `<div class="field upload-required-field"><label>${esc(label)}</label>${variant}${input}${field.description ? `<div class="meta">${esc(field.description)}</div>` : ""}</div>`;
  }

  function uploadMissingFieldsHtml(product, missingFields) {
    const groups = [];
    const byCountry = new Map();
    missingFields.forEach((field, index) => {
      const code = clean(field.country).toUpperCase() || "COMMON";
      if (!byCountry.has(code)) {
        const group = { code, fields: [] };
        byCountry.set(code, group);
        groups.push(group);
      }
      byCountry.get(code).fields.push({ field, index });
    });
    return groups.map(group => `<section class="upload-required-country"><div class="upload-required-country-head"><strong>${esc(group.code === "COMMON" ? "通用信息" : countryName(group.code))}</strong><span>${group.fields.length} 项待补充</span></div><div class="upload-required-grid">${group.fields.map(item => uploadMissingFieldHtml(product, item.field, item.index)).join("")}</div></section>`).join("");
  }

  function displayUploadFailureReason(reason) {
    return String(reason || "暂无失败信息").replace(/\b(GB|DE|ES|FR|IT|US|CA|MX)\s*\/\s*[A-Z][A-Z0-9_]*\s*/g, (_match, code) => `${countryName(code)}：`);
  }

  function openUploadFailure(product) {
    const result = product?.productionUpload || product?.productionPreview || product?.sandboxUpload || {};
    const items = Array.isArray(result.items) ? result.items : [];
    const missingFields = Array.isArray(result.missingFields) ? result.missingFields : [];
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal ${missingFields.length ? "wide" : ""}"><div class="modal-head"><div><h2>上传详情 / 失败原因</h2><p>${esc(product?.sku || "")}</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body upload-failure-modal-body"><div class="upload-failure-box">${esc(displayUploadFailureReason(product?.uploadFailureReason))}</div>${product?.uploadUpdatedAt ? `<div class="upload-failure-time">最后尝试：${esc(formatDate(product.uploadUpdatedAt))}</div>` : ""}${missingFields.length ? `<div class="upload-required-panel"><div class="upload-required-title"><div><strong>补充必填信息</strong><span>填写完成后可直接重新上传</span></div><em>${missingFields.length} 项</em></div>${uploadMissingFieldsHtml(product, missingFields)}</div>` : ""}${items.length ? `<div class="upload-result-list">${items.map(item => `<div><strong>${esc(item.sku || "")}</strong><span>${esc(item.status || item.statuses?.join("、") || "")}</span></div>`).join("")}</div>` : ""}</div><div class="modal-foot"><button class="btn" data-action="close-modal">关闭</button>${missingFields.length ? `<button class="btn btn-primary" data-action="save-missing-upload-fields" data-id="${attr(product?.id || "")}">保存并重新上传</button>` : `<button class="btn btn-primary" data-action="${activeApiEnvironment() === "production" ? "upload-product" : "sandbox-upload-product"}" data-id="${attr(product?.id || "")}">重新上传</button>`}</div></div></div>`;
  }

  async function saveMissingUploadFields(product) {
    const result = product?.productionUpload || product?.productionPreview || product?.sandboxUpload || {};
    const missingFields = Array.isArray(result.missingFields) ? result.missingFields : [];
    if (!product || !missingFields.length) return;
    const values = missingFields.map((field, index) => ({
      field,
      value: document.querySelector(`[data-upload-missing-index="${index}"]`)?.value ?? ""
    }));
    const empty = values.find(item => String(item.value).trim() === "");
    if (empty) {
      toast(`请填写：${empty.field.attributeTitle || empty.field.variantLabel || empty.field.label}`, "error");
      return;
    }
    product.amazonRequiredOverrides = product.amazonRequiredOverrides || {};
    for (const { field, value } of values) {
      if (field.scope === "product_type") {
        const country = clean(field.country).toUpperCase();
        const productType = clean(value).toUpperCase();
        setManualProductType(product, country, productType);
        continue;
      }
      if (field.scope === "variant") {
        const row = (product.variantRows || []).find(item => String(item.id || item.sku || "") === String(field.variantId || ""));
        if (row) row[field.path] = ["number", "integer"].includes(field.type) ? Number(value) : value;
        continue;
      }
      const key = `${field.country || ""}:${field.productType || ""}`;
      const profile = product.amazonRequiredOverrides[key] ||= {};
      const configured = profile[field.attribute] ||= {
        title: field.attributeTitle || field.attribute,
        supportsMarketplaceId: Boolean(field.supportsMarketplaceId),
        supportsLanguageTag: Boolean(field.supportsLanguageTag),
        fields: {}
      };
      configured.fields[field.path || "value"] = { value, type: field.type || "string" };
    }
    product.updatedAt = nowIso();
    saveDb({ sync: false });
    await syncToServer().catch(() => false);
    closeModal();
    await uploadProduct(product);
  }

  function renderStoreManagement() {
    const current = activeStore();
    const verificationLabel = current?.status === "api_verified" ? "API 已验证" : current?.status === "verification_failed" ? "验证失败" : "仅保存资料";
    const verificationClass = current?.status === "api_verified" ? "status-green" : current?.status === "verification_failed" ? "status-red" : "status-yellow";
    return `<div class="edit-card"><div class="section-title">Amazon账号授权</div>
      <div class="store-toolbar"><div class="store-tabs">${authState.stores.map(store => `<button class="store-tab ${store.id === authState.activeStoreId ? "active" : ""}" data-action="activate-store" data-store="${attr(store.id)}">${esc(store.name)}</button>`).join("") || '<span class="meta">尚未添加 Amazon账号</span>'}</div>${Number(authState.user?.accountLevel || 1) < 3 ? '<button class="btn btn-primary" data-action="open-add-store">添加 Amazon账号</button>' : ""}</div>
      ${current ? `<div class="store-info-table"><div class="store-info-head"><span>Amazon账号</span><span>区域</span><span>国家站点</span><span>授权方式</span><span>Seller ID</span><span>授权状态</span><span>操作</span></div><div class="store-info-row"><strong>${esc(current.name)}</strong><span>${current.region === "americas" ? "美洲站" : "欧洲站"}</span><span>${current.countries.map(countryName).join("、")}</span><span>${current.authType === "amazon_oauth" ? "Amazon OAuth" : "手动 Token"}</span><span>${esc(current.sellerId)}</span><span class="status ${verificationClass}">${verificationLabel}</span><span>${Number(authState.user?.accountLevel || 1) < 3 ? `${canManageAmazonCredentials() ? `<button class="link-btn" data-action="start-store-oauth" data-store="${attr(current.id)}">OAuth授权</button>` : ""}<button class="link-btn danger-link" data-action="delete-store" data-store="${attr(current.id)}">删除</button>` : "—"}</span></div></div>` : '<div class="account-empty">请先添加 Amazon账号资料</div>'}
    </div>`;
  }

  function knownAmazonProductTypes() {
    const values = new Set();
    [...activeStoreRows(state.db.drafts), ...activeStoreRows(state.db.products)].forEach(item => {
      Object.values(item.amazonProductTypes || {}).forEach(value => { if (clean(value)) values.add(clean(value).toUpperCase()); });
      if (clean(item.amazonProductType)) values.add(clean(item.amazonProductType).toUpperCase());
    });
    Object.keys(activeSettings().officialSchemaDefaults || {}).forEach(key => {
      const type = clean(key.split(":").slice(1).join(":"));
      if (type) values.add(type.toUpperCase());
    });
    return [...values].sort();
  }

  function schemaDefaultProfile(settings, country, productType) {
    const key = `${country}:${productType}`;
    const value = settings.officialSchemaDefaults?.[key];
    return value && typeof value === "object" ? value : {};
  }

  function schemaFieldValue(profile, attribute, path) {
    const raw = profile?.[attribute]?.fields?.[path] ?? profile?.[attribute]?.[path];
    return raw && typeof raw === "object" ? raw.value ?? "" : raw ?? "";
  }

  function requiredSchemaInput(field, value, attribute) {
    const enums = Array.isArray(field.enum) ? field.enum : [];
    const enumNames = Array.isArray(field.enumNames) ? field.enumNames : [];
    const common = `data-schema-default data-schema-attribute="${attr(attribute)}" data-schema-path="${attr(field.path || "value")}" data-schema-type="${attr(field.type || "string")}"`;
    if (enums.length) {
      return `<select class="control" ${common}><option value="">请选择</option>${enums.map((item, index) => `<option value="${attr(item)}" ${String(value) === String(item) ? "selected" : ""}>${esc(enumNames[index] || item)}</option>`).join("")}</select>`;
    }
    if (field.type === "boolean") {
      return `<select class="control" ${common}><option value="">请选择</option><option value="true" ${String(value) === "true" ? "selected" : ""}>是</option><option value="false" ${String(value) === "false" ? "selected" : ""}>否</option></select>`;
    }
    const numeric = ["number", "integer"].includes(field.type);
    return `<input class="control" ${common} ${numeric ? `type="number" ${field.type === "integer" ? "step=\"1\"" : "step=\"any\""}` : ""} value="${attr(value)}" placeholder="${attr(field.description || field.label || field.path || "请输入真实值")}">`;
  }

  function amazonRequiredSchemaHtml() {
    const s = activeSettings();
    const countries = activeStoreCountries();
    const knownTypes = knownAmazonProductTypes();
    const selectedCountry = amazonRequiredSchema.country || countries[0] || "";
    const selectedType = amazonRequiredSchema.productType || knownTypes[0] || "";
    const profile = schemaDefaultProfile(s, selectedCountry, selectedType);
    const fields = Array.isArray(amazonRequiredSchema.fields) ? amazonRequiredSchema.fields : [];
    return `<div class="schema-default-panel">
      <div class="schema-default-toolbar">
        <div class="field"><label>站点国家</label><select class="control" id="schemaCountry">${countries.map(code => `<option value="${code}" ${code === selectedCountry ? "selected" : ""}>${countryName(code)}</option>`).join("")}</select></div>
        <div class="field"><label>产品类型</label><input class="control" id="schemaProductType" list="knownProductTypes" value="${attr(selectedType)}" placeholder="例如 TABLE"><datalist id="knownProductTypes">${knownTypes.map(value => `<option value="${attr(value)}"></option>`).join("")}</datalist></div>
        <button class="btn btn-primary" type="button" data-action="sync-required-schema" ${amazonRequiredSchema.loading ? "disabled" : ""}>${amazonRequiredSchema.loading ? "正在同步…" : "同步官方必填项"}</button>
      </div>
      ${amazonRequiredSchema.error ? `<div class="settings-note error-text">${esc(amazonRequiredSchema.error)}</div>` : ""}
      ${fields.length ? `<div class="settings-note">已从 Amazon Product Type Definitions API 读取 ${fields.length} 个店铺级必填属性。产品标题、价格、库存、图片等由系统自动提供；以下仅显示产品数据中没有的缺省项。</div><div class="settings-grid upload-defaults-grid schema-default-grid">${fields.flatMap(definition => (definition.fields || []).map(field => `<div class="field"><label title="${attr(definition.attribute)}">${esc(definition.title)} · ${esc(field.label || field.path)}</label>${requiredSchemaInput(field, schemaFieldValue(profile, definition.attribute, field.path || "value"), definition.attribute)}<small>${esc(definition.description || field.description || definition.attribute)}</small></div>`)).join("")}</div>` : `<div class="settings-note">选择国家和已识别的 Amazon 商品类型后点击“同步官方必填项”。相同国家、类型、关键词的官方结果会保存在本机缓存。</div>`}
    </div>`;
  }

  function renderAmazonRequiredSchemaPanel() {
    const root = $("amazonRequiredSchemaPanel");
    if (root) {
      root.innerHTML = amazonRequiredSchemaHtml();
      queueMicrotask(() => enhanceSelects(root));
    }
  }

  async function loadAmazonRequiredSchema() {
    const country = $("schemaCountry")?.value || amazonRequiredSchema.country || activeStoreCountries()[0] || "";
    const productType = clean($("schemaProductType")?.value || amazonRequiredSchema.productType).toUpperCase();
    if (!country || !productType) { toast("请先选择国家并填写产品类型", "error"); return; }
    amazonRequiredSchema = { country, productType, fields: [], loading: true, error: "" };
    renderAmazonRequiredSchemaPanel();
    try {
      const response = await apiJson(`/api-settings/product-type-required-fields?country=${encodeURIComponent(country)}&product_type=${encodeURIComponent(productType)}`);
      amazonRequiredSchema = {
        country, productType,
        fields: Array.isArray(response?.data?.missingDefaultFields) ? response.data.missingDefaultFields : [],
        loading: false, error: ""
      };
    } catch (error) {
      amazonRequiredSchema = { country, productType, fields: [], loading: false, error: error.message || "官方必填项同步失败" };
    }
    renderAmazonRequiredSchemaPanel();
  }

  function renderSettings() {
    const s = activeSettings();
    $("viewRoot").innerHTML = `<div class="edit-shell">${renderStoreManagement()}
      <div class="edit-card"><div class="section-title">产品默认信息</div>
        <div class="settings-grid">
          <div class="field"><label>默认品牌</label><input class="control" id="settingBrand" value="${attr(s.brand)}"></div>
          <div class="field"><label>默认制造商</label><input class="control" id="settingManufacturer" value="${attr(s.manufacturer)}"></div>
          <div class="field"><label>默认库存</label><input class="control" id="settingInventory" type="number" min="0" value="${attr(s.inventory)}"></div>
          <div class="field"><label>处理时间（天）</label><input class="control" id="settingHandling" type="number" min="0" value="${attr(s.handlingTime)}"></div>
          <div class="field"><label>物品状况</label><select class="control" id="settingCondition">${[["new","New"],["used_like_new","Used - Like New"],["used_very_good","Used - Very Good"],["used_good","Used - Good"],["used_acceptable","Used - Acceptable"]].map(([value,label]) => `<option value="${value}" ${String(s.condition || "new") === value ? "selected" : ""}>${label}</option>`).join("")}</select></div>
          <div class="field"><label>UPC 默认方式</label><select class="control" id="settingUpc"><option value="exempt" ${s.upcMode === "exempt" ? "selected" : ""}>UPC豁免</option><option value="same" ${s.upcMode === "same" ? "selected" : ""}>使用原UPC</option><option value="replace" ${s.upcMode === "replace" ? "selected" : ""}>更换UPC</option></select></div>
        </div>
      </div>
      <div class="edit-card"><div class="section-title">亚马逊上传缺省信息</div>
        <div class="settings-grid upload-defaults-grid">
          <div class="field"><label>商品编码类型</label><select class="control" id="settingOfficialProductIdType">${["GTIN Exempt", "UPC", "EAN", "GTIN", "ASIN"].map(value => `<option value="${value}" ${s.officialProductIdType === value ? "selected" : ""}>${value}</option>`).join("")}</select></div>
          <div class="field"><label>原产国</label><input class="control" id="settingOfficialCountry" value="${attr(s.officialCountryOfOrigin)}" placeholder="例如 CN、中国或 China"></div>
          <div class="field"><label>危险品法规</label><select class="control" id="settingOfficialDangerousGoods">${["Not Applicable", "GHS", "Storage", "Transportation"].map(value => `<option value="${value}" ${s.officialDangerousGoods === value ? "selected" : ""}>${value}</option>`).join("")}</select></div>
          <div class="field"><label>需要组装</label><select class="control" id="settingOfficialAssemblyRequired"><option value="" ${s.officialAssemblyRequired === "" ? "selected" : ""}>请选择</option><option value="false" ${String(s.officialAssemblyRequired) === "false" ? "selected" : ""}>否</option><option value="true" ${String(s.officialAssemblyRequired) === "true" ? "selected" : ""}>是</option></select></div>
          <div class="field"><label>需要电池</label><select class="control" id="settingOfficialBatteriesRequired"><option value="" ${s.officialBatteriesRequired === "" ? "selected" : ""}>请选择</option><option value="false" ${String(s.officialBatteriesRequired) === "false" ? "selected" : ""}>否</option><option value="true" ${String(s.officialBatteriesRequired) === "true" ? "selected" : ""}>是</option></select></div>
          <div class="field"><label>底座材料</label><input class="control" id="settingOfficialBaseMaterial" value="${attr(s.officialBaseMaterial)}" placeholder="例如 Metal、Wood"></div>
          <div class="field"><label>商品件数</label><input class="control" id="settingOfficialNumberOfItems" type="number" min="1" value="${attr(s.officialNumberOfItems)}"></div>
          <div class="field"><label>单位计数</label><div class="add-line"><input class="control" id="settingOfficialUnitCountValue" type="number" min="0.01" step="0.01" value="${attr(s.officialUnitCountValue)}"><select class="control" id="settingOfficialUnitCountUnit">${["count","piece","pair","set"].map(value => `<option value="${value}" ${s.officialUnitCountUnit === value ? "selected" : ""}>${value}</option>`).join("")}</select></div></div>
          <div class="field"><label>所包含组件</label><input class="control" id="settingOfficialIncludedComponents" value="${attr(s.officialIncludedComponents)}" placeholder="例如 1 x storage organizer"></div>
          <div class="field"><label>商品重量</label><div class="add-line"><input class="control" id="settingOfficialItemWeightValue" type="number" min="0" step="0.01" value="${attr(s.officialItemWeightValue)}"><select class="control" id="settingOfficialItemWeightUnit">${["kilograms","grams","pounds","ounces"].map(value => `<option value="${value}" ${s.officialItemWeightUnit === value ? "selected" : ""}>${value}</option>`).join("")}</select></div></div>
          <div class="field"><label>易碎商品</label><select class="control" id="settingOfficialFragile"><option value="false" ${String(s.officialFragile) !== "true" ? "selected" : ""}>否</option><option value="true" ${String(s.officialFragile) === "true" ? "selected" : ""}>是</option></select></div>
          <div class="field"><label>商品深度</label><input class="control" id="settingOfficialItemDepthValue" type="number" min="0" step="0.01" value="${attr(s.officialItemDepthValue)}"></div>
          <div class="field"><label>商品宽度</label><input class="control" id="settingOfficialItemWidthValue" type="number" min="0" step="0.01" value="${attr(s.officialItemWidthValue)}"></div>
          <div class="field"><label>商品高度</label><input class="control" id="settingOfficialItemHeightValue" type="number" min="0" step="0.01" value="${attr(s.officialItemHeightValue)}"></div>
          <div class="field"><label>尺寸单位</label><select class="control" id="settingOfficialDimensionUnit">${["centimeters","millimeters","meters","inches","feet"].map(value => `<option value="${value}" ${s.officialDimensionUnit === value ? "selected" : ""}>${value}</option>`).join("")}</select></div>
          <div class="field"><label>型号名称</label><input class="control" id="settingOfficialModelName" value="${attr(s.officialModelName)}" placeholder="例如 Storage Rack"></div>
          <div class="field"><label>净含量</label><div class="add-line"><input class="control" id="settingOfficialNetContentValue" type="number" min="0.01" step="0.01" value="${attr(s.officialNetContentValue)}"><input class="control" id="settingOfficialNetContentUnit" value="${attr(s.officialNetContentUnit)}" placeholder="例如 count"></div></div>
          <div class="field"><label>无变体时默认尺码</label><input class="control" id="settingOfficialDefaultSize" value="${attr(s.officialDefaultSize)}" placeholder="例如 Standard"></div>
        </div>
        <div class="filter-actions settings-save-actions"><button class="btn" data-action="import-backup">导入备份</button><button class="btn btn-primary" data-action="save-settings">保存设置</button></div>
      </div>
    </div>`;
  }


  let compliancePreviewTimer = null;

  function currentComplianceProfile() {
    const settings = activeSettings();
    const profile = globalThis.YilianComplianceLabel?.normalize?.(settings.complianceLabel)
      || { ...(settings.complianceLabel || {}) };
    if (!profile.manufacturerName) profile.manufacturerName = settings.manufacturer === "Generic" ? "" : settings.manufacturer;
    return profile;
  }

  function complianceProfileFromInputs() {
    const profile = currentComplianceProfile();
    document.querySelectorAll("[data-compliance-field]").forEach(input => {
      const key = input.dataset.complianceField;
      if (!key) return;
      profile[key] = input.type === "checkbox"
        ? input.checked
        : key === "contentFontSize" ? num(input.value, 26) : input.value;
    });
    return globalThis.YilianComplianceLabel?.normalize?.(profile) || profile;
  }

  function scheduleCompliancePreview(rawProfile = complianceProfileFromInputs()) {
    const profile = globalThis.YilianComplianceLabel?.normalize?.(rawProfile) || rawProfile;
    const fontSizeOutput = document.querySelector("[data-compliance-font-size-value]");
    if (fontSizeOutput) fontSizeOutput.textContent = `${num(profile.contentFontSize, 26)} px`;
    clearTimeout(compliancePreviewTimer);
    compliancePreviewTimer = setTimeout(async () => {
      const canvas = $("complianceLabelCanvas");
      if (!canvas || state.view !== "compliance-label") return;
      try {
        await globalThis.YilianComplianceLabel.draw(canvas, profile);
      } catch (error) {
        toast(error.message || "合规标签预览生成失败", "error");
      }
    }, 120);
  }

  function enableCompliancePreviewZoom() {
    const wrap = document.querySelector(".compliance-canvas-wrap");
    const canvas = wrap?.querySelector("canvas");
    if (!wrap || !canvas) return;
    wrap.addEventListener("pointermove", event => {
      const rect = wrap.getBoundingClientRect();
      const x = Math.max(0, Math.min(100, ((event.clientX - rect.left) / rect.width) * 100));
      const y = Math.max(0, Math.min(100, ((event.clientY - rect.top) / rect.height) * 100));
      canvas.style.transformOrigin = `${x}% ${y}%`;
    });
    wrap.addEventListener("pointerleave", () => { canvas.style.transformOrigin = "50% 50%"; });
  }

  function renderComplianceLabel() {
    if (!authState.activeStoreId) {
      $("viewRoot").innerHTML = emptyState("请先选择 Amazon账号", "每个 Amazon账号独立保存一套合规标签资料。");
      return;
    }
    const profile = currentComplianceProfile();
    const store = activeStore() || {};
    const checked = value => value ? "checked" : "";
    const saved = Boolean(profile.masterObjectKey);
    const fontBounds = globalThis.YilianComplianceLabel?.contentFontBounds || { minimum: 22, maximum: 34, defaultValue: 26 };
    $("viewRoot").innerHTML = `<div class="compliance-page">
      <div class="compliance-intro"><h2>Amazon PS01 合规标签</h2><p>当前账号只维护一套标签资料和一张纸箱贴标母图；正式上传产品时，系统会按单品或每个变体 ASIN 自动生成相同内容的 <code>ASIN.PS01.jpg</code>。</p></div>
      <div class="compliance-layout">
        <div class="compliance-form-card">
          <div class="compliance-preview-head"><div><h3>店铺标签资料</h3><p>资料仅作用于当前 Amazon账号。</p></div><span class="compliance-store-pill">${esc(store.name || store.sellerId || authState.activeStoreId)}</span></div>
          <div class="compliance-form-grid">
            <div class="field"><label>制造商名称</label><input class="control" data-compliance-field="manufacturerName" value="${attr(profile.manufacturerName)}" placeholder="Manufacturer"></div>
            <div class="field"><label>制造商邮箱</label><input class="control" type="email" data-compliance-field="manufacturerEmail" value="${attr(profile.manufacturerEmail)}" placeholder="name@example.com"></div>
            <div class="field full"><label>制造商地址</label><textarea class="control" data-compliance-field="manufacturerAddress" placeholder="完整英文地址">${escLines(profile.manufacturerAddress)}</textarea></div>
            <div class="field"><label>批次号</label><input class="control" data-compliance-field="batchNumber" value="${attr(profile.batchNumber)}" placeholder="例如 S2026"></div>
            <div class="field compliance-font-size-field"><label>填写内容文字大小 <output data-compliance-font-size-value>${num(profile.contentFontSize, fontBounds.defaultValue)} px</output></label><input class="compliance-font-slider" type="range" min="${fontBounds.minimum}" max="${fontBounds.maximum}" step="1" data-compliance-field="contentFontSize" value="${attr(profile.contentFontSize)}"><span class="field-help">统一调整全部填写内容，固定标题和标志不变</span></div>
            <div class="field"><label>欧代名称</label><input class="control" data-compliance-field="representativeName" value="${attr(profile.representativeName)}" placeholder="EU Responsible Person"></div>
            <div class="field"><label>欧代邮箱</label><input class="control" type="email" data-compliance-field="representativeEmail" value="${attr(profile.representativeEmail)}" placeholder="eu@example.com"></div>
            <div class="field full"><label>欧代地址</label><textarea class="control" data-compliance-field="representativeAddress" placeholder="欧盟境内完整地址">${escLines(profile.representativeAddress)}</textarea></div>
            <div class="field full"><label>安全警告（每行一条）</label><textarea class="control" rows="5" data-compliance-field="warnings">${escLines(profile.warnings)}</textarea></div>
            <div class="compliance-switches">
              <label><input type="checkbox" data-compliance-field="enabled" ${checked(profile.enabled)}>启用此店铺合规标签</label>
              <label><input type="checkbox" data-compliance-field="autoAttach" ${checked(profile.autoAttach)}>正式上传时自动附带 PS01</label>
            </div>
          </div>
          <div class="compliance-note">标签固定使用已确认的 EC REP、CE、七国语言警示、回收标识和 MADE IN CHINA 版式；请确保资料与实际发货包装一致。</div>
          <div class="compliance-actions"><button class="btn" data-action="download-compliance-label">下载母图预览</button><button class="btn btn-primary" data-action="save-compliance-label">保存并生成纸箱贴标图</button></div>
        </div>
        <div class="compliance-preview-card">
          <div class="compliance-preview-head"><div><h3>纸箱贴标效果</h3><p>输出为 2000 × 2000 JPG，无截图纹路、鼠标指针或网页边框。</p></div></div>
          <div class="compliance-canvas-wrap" title="移动鼠标可放大查看细节"><canvas id="complianceLabelCanvas" width="2000" height="2000"></canvas></div>
          <p class="compliance-zoom-hint">鼠标移入图片可放大查看细节</p>
          <div class="compliance-status"><span class="${saved ? "ready" : ""}">${saved ? "● 母图已保存" : "○ 尚未保存母图"}</span>${saved ? `<span>版本 ${num(profile.version, 1)}</span><span>更新于 ${esc(formatDate(profile.updatedAt))}</span>` : ""}</div>
          <div class="compliance-note">变体商品会按每个子 ASIN 生成 PS01；无变体商品使用产品 ASIN。Amazon 当前商品类型 Schema 不支持 PS01 时，系统会明确停止提交并提示使用图片管理器上传。</div>
        </div>
      </div>
    </div>`;
    enableCompliancePreviewZoom();
    scheduleCompliancePreview(profile);
  }

  function validateComplianceProfile(profile) {
    const required = [
      ["manufacturerName", "制造商名称"], ["manufacturerAddress", "制造商地址"],
      ["manufacturerEmail", "制造商邮箱"], ["batchNumber", "批次号"], ["warnings", "安全警告"],
      ["representativeName", "欧代名称"], ["representativeAddress", "欧代地址"], ["representativeEmail", "欧代邮箱"]
    ];
    const missing = required.filter(([key]) => !clean(profile[key])).map(([, label]) => label);
    if (missing.length) throw new Error(`请填写：${missing.join("、")}`);
    for (const key of ["manufacturerEmail", "representativeEmail"]) {
      if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(clean(profile[key]))) throw new Error("请输入有效的联系邮箱");
    }
  }

  async function complianceCanvasBlob(profile) {
    const canvas = $("complianceLabelCanvas");
    if (!canvas) throw new Error("合规标签预览尚未就绪");
    await globalThis.YilianComplianceLabel.draw(canvas, profile);
    return globalThis.YilianComplianceLabel.toJpegBlob(canvas);
  }

  async function saveComplianceLabel() {
    if (!authState.activeStoreId) { toast("请先选择 Amazon账号", "error"); return; }
    const profile = complianceProfileFromInputs();
    try {
      validateComplianceProfile(profile);
      const blob = await complianceCanvasBlob(profile);
      const form = new FormData();
      form.append("file", blob, "compliance-label-master.jpg");
      const response = await apiJson("/compliance-labels/master", { method: "POST", body: form });
      const saved = {
        ...profile,
        masterUrl: clean(response.data?.url),
        masterObjectKey: clean(response.data?.objectKey),
        version: num(profile.version, 0) + 1,
        updatedAt: nowIso()
      };
      const root = normalizeSettings(state.db.settings);
      const storeProfile = { ...activeSettings(), complianceLabel: saved };
      delete storeProfile.storeProfiles;
      root.storeProfiles = { ...root.storeProfiles, [authState.activeStoreId]: storeProfile };
      state.db.settings = root;
      saveDb({ sync: false });
      if (!await syncToServer()) throw new Error("标签已生成，但店铺配置未同步到服务器，请重试保存");
      renderComplianceLabel();
      toast("合规标签已保存；正式上传时会按 ASIN 自动附带 PS01", "success");
    } catch (error) {
      toast(error.message || "合规标签保存失败", "error");
    }
  }

  async function downloadComplianceLabel() {
    const profile = complianceProfileFromInputs();
    try {
      validateComplianceProfile(profile);
      const blob = await complianceCanvasBlob(profile);
      const url = URL.createObjectURL(blob);
      const link = document.createElement("a");
      const storeName = clean(activeStore()?.name || "store").replace(/[\\/:*?"<>|]+/g, "-");
      link.href = url;
      link.download = `${storeName}-PS01母图.jpg`;
      link.click();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
    } catch (error) {
      toast(error.message || "合规标签下载失败", "error");
    }
  }

  function permissionChoices(selected = [], account = "") {
    return Object.entries({ ...MENU_PERMISSIONS, accounts: "账号管理" }).filter(([key]) => can(key)).map(([key, label]) => `<label class="permission-check"><input type="checkbox" ${account ? `data-account-permission="${attr(account)}" data-permission="${key}"` : `data-child-permission="${key}"`} ${selected.includes(key) ? "checked" : ""}>${label}</label>`).join("");
  }

  function orderMoney(value, currency = "") {
    const symbols = { GBP: "£", EUR: "€", USD: "$", CAD: "C$", MXN: "MX$" };
    return `${symbols[currency] || `${currency} `}${num(value).toFixed(2)}`;
  }

  function orderCurrencyRate(currency = "") {
    const settings = activeSettings();
    return { GBP: num(settings.gbpRate, 9.2), EUR: num(settings.eurRate, 7.85), USD: num(settings.usdRate, 7.2), CAD: num(settings.cadRate, 5.25), MXN: num(settings.mxnRate, 0.42), CNY: 1 }[currency] || 1;
  }

  function orderProfit(row) {
    const rate = orderCurrencyRate(row.currency);
    const revenueRmb = num(row.orderTotal) * rate;
    const profit = (num(row.orderTotal) - num(row.deductionAmount) - num(row.commission) - num(row.vat)) * rate - num(row.procurementCost) - num(row.domesticShipping);
    return { value: profit, rate: revenueRmb ? profit / revenueRmb * 100 : 0 };
  }

  function orderSummary() {
    return (state.orders.summary || []).reduce((totals, row) => {
      const rate = orderCurrencyRate(row.currency);
      totals.sale += num(row.orderTotal) * rate;
      totals.commission += num(row.commission) * rate;
      totals.deduction += num(row.deductionAmount) * rate;
      totals.vat += num(row.vat) * rate;
      totals.procurement += num(row.procurementCost);
      totals.logistics += num(row.domesticShipping);
      return totals;
    }, { sale: 0, deduction: 0, commission: 0, vat: 0, procurement: 0, logistics: 0 });
  }

  function orderQueryString(includePage = true) {
    const filters = state.filters.orders || {};
    const pager = state.pagination.orders;
    const query = new URLSearchParams({
      keyword: clean(filters.query), status: clean(filters.status), country: clean(filters.country),
      start_date: clean(filters.startDate), end_date: clean(filters.endDate),
      store_id: clean(filters.storeId || authState.activeStoreId), period: clean(state.orders.period || "month")
    });
    if (includePage) {
      query.set("page", String(pager.page || 1));
      query.set("page_size", String(pager.pageSize || 20));
    }
    return query;
  }

  async function loadOrders(force = false) {
    if (state.view !== "orders" || state.orders.loading) return;
    if (!force && state.orders.items.length && !state.orders.error) {
      renderOrders();
      return;
    }
    const filters = state.filters.orders || {};
    const pager = state.pagination.orders;
    state.orders.loading = true;
    renderOrders();
    try {
      const query = orderQueryString(true);
      const result = await apiJson(`/orders?${query}`);
      state.orders = { ...state.orders, ...(result.data || {}), loading: false, error: "" };
    } catch (error) {
      state.orders = { ...state.orders, loading: false, error: error.message };
    }
    if (state.view === "orders") renderOrders();
  }

  function currentLocalDateKey() {
    const now = new Date();
    return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
  }

  function orderDailySyncStorage() {
    try { return JSON.parse(localStorage.getItem(ORDER_DAILY_SYNC_KEY) || "{}"); }
    catch { return {}; }
  }

  const dailyOrderSyncInFlight = new Set();
  async function autoSyncAmazonOrdersOnceDaily() {
    const storeId = clean(authState.activeStoreId);
    if (!authState.user || !can("orders") || isDelegatedReadOnly() || !storeId) return;
    const key = `${authState.dataAccount}:${storeId}`;
    const today = currentLocalDateKey();
    const completed = orderDailySyncStorage();
    if (completed[key] === today || dailyOrderSyncInFlight.has(key)) return;
    dailyOrderSyncInFlight.add(key);
    try {
      const result = await apiJson("/orders/sync", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ days: 365, storeId })
      });
      completed[key] = today;
      localStorage.setItem(ORDER_DAILY_SYNC_KEY, JSON.stringify(completed));
      state.orders.warning = result.data?.warning || "";
      state.orders.items = [];
      if (state.view === "orders") await loadOrders(true);
      toast(`今日订单已自动同步，共读取 ${num(result.data?.synced)} 条`, "success");
    } catch (error) {
      toast(`今日订单自动同步失败：${error.message}`, "error");
    } finally {
      dailyOrderSyncInFlight.delete(key);
    }
  }

  function ordersPagination() {
    const pager = state.pagination.orders;
    const total = Number(state.orders.total || 0);
    const pages = Math.max(1, Math.ceil(total / pager.pageSize));
    pager.page = Math.min(pages, Math.max(1, Number(pager.page) || 1));
    const numbers = paginationNumbers(pager.page, pages);
    return `<div class="pager"><span>共 ${total} 条</span><select class="pager-size" data-page-size data-scope="orders">${[10,20,50,100].map(value => `<option value="${value}" ${value === pager.pageSize ? "selected" : ""}>${value}条/页</option>`).join("")}</select><div class="pager-pages"><button class="pager-button" data-action="set-order-page" data-page="${pager.page - 1}" ${pager.page <= 1 ? "disabled" : ""}>‹</button>${numbers.map(value => `<button class="pager-button ${value === pager.page ? "active" : ""}" data-action="set-order-page" data-page="${value}">${value}</button>`).join("")}<button class="pager-button" data-action="set-order-page" data-page="${pager.page + 1}" ${pager.page >= pages ? "disabled" : ""}>›</button></div></div>`;
  }

  function renderOrders() {
    const filters = state.filters.orders || {};
    const rows = state.orders.items || [];
    const selectedStoreId = clean(filters.storeId || authState.activeStoreId);
    const selectedStore = authState.stores.find(store => store.id === selectedStoreId) || activeStore();
    const countries = selectedStore?.countries || [];
    const totals = orderSummary();
    const totalProfit = totals.sale - totals.deduction - totals.commission - totals.vat - totals.procurement - totals.logistics;
    const totalRate = totals.sale ? totalProfit / totals.sale * 100 : 0;
    const summary = `<div class="order-summary" title="销售额 − 平台扣点 − 亚马逊佣金（商品售价15%）− 税费 − 采购 − 物流"><span class="money-negative">¥${totals.sale.toFixed(2)}</span><b>− 扣点</b><span class="money-positive">¥${totals.deduction.toFixed(2)}</span><b>−</b><span class="money-positive">¥${totals.commission.toFixed(2)}</span><b>−</b><span class="money-positive">¥${totals.vat.toFixed(2)}</span><b>−</b><span class="money-positive">¥${totals.procurement.toFixed(2)}</span><b>−</b><span class="money-positive">¥${totals.logistics.toFixed(2)}</span><b>=</b><span class="money-negative">¥${totalProfit.toFixed(2)}</span><span class="money-positive">${totalRate.toFixed(2)}%</span></div>`;
    const body = state.orders.loading
      ? `<div class="orders-loading">正在读取当前店铺订单…</div>`
      : state.orders.error ? `<div class="orders-error">${esc(state.orders.error)}</div>`
      : rows.length ? `<div class="table-wrap"><table class="list-table orders-table"><thead><tr><th>订单日期</th><th>产品主图</th><th>订单编号 / SKU</th><th>销售站点</th><th>商品售价</th><th>平台扣点</th><th>亚马逊佣金（15%）</th><th>增值税</th><th>采购成本</th><th>物流成本</th><th>预估利润 / 利润率</th><th>订单状态</th><th>操作</th></tr></thead><tbody>${rows.map(row => { const profit = orderProfit(row); return `<tr><td class="order-date-cell">${formatOrderDate(row.createdAt)}</td><td>${row.imageUrl ? `<button class="product-thumb-button" data-action="preview-list-image" data-image-url="${attr(row.imageUrl)}"><img class="order-product-image" src="${attr(row.imageUrl)}" alt=""><span class="product-thumb-popover"><img src="${attr(row.imageUrl)}" alt=""></span></button>` : "—"}</td><td class="order-identity-cell"><strong class="order-id-line">${esc(row.amazonOrderId)}</strong><div class="meta order-sku-line" title="${attr(row.sku || "—")}">${esc(row.sku || "—")} × ${num(row.quantity)}</div></td><td>${esc(countryName(row.country))}</td><td class="money-cell money-negative">${esc(orderMoney(row.orderTotal, row.currency))}</td><td class="money-cell money-positive" title="快照比例 ${attr(row.deductionRateSnapshot || "0")}%">${esc(orderMoney(row.deductionAmount, row.currency))}</td><td class="money-cell money-positive" title="按商品售价的15%计算">${esc(orderMoney(row.commission, row.currency))}</td><td class="money-cell money-positive">${esc(orderMoney(row.vat, row.currency))}</td><td class="money-cell money-positive">¥${num(row.procurementCost).toFixed(2)}</td><td class="money-cell money-positive">¥${num(row.domesticShipping).toFixed(2)}</td><td class="money-cell"><strong class="money-negative">¥${profit.value.toFixed(2)}</strong><div class="money-positive">${profit.rate.toFixed(2)}%</div></td><td><span class="status ${statusClass(row.status)}">${esc(row.status || "—")}</span></td><td><button class="link-btn" data-action="edit-order-costs" data-id="${row.id}">录入成本</button></td></tr>`; }).join("")}</tbody></table></div>${ordersPagination()}`
      : emptyState("暂无订单", "点击“同步亚马逊订单”读取当前启用店铺最近的订单。") ;
    $("viewRoot").innerHTML = `<div class="panel filter-panel"><div class="panel-body"><div class="filter-layout"><div class="filter-grid orders-filter-grid"><div class="field"><label>订单号 / SKU</label><input class="control" id="orderQuery" value="${attr(filters.query || "")}" placeholder="搜索订单"></div><div class="field"><label>开始时间</label><input class="control" type="date" id="orderStartDate" value="${attr(filters.startDate || "")}"></div><div class="field"><label>结束时间</label><input class="control" type="date" id="orderEndDate" value="${attr(filters.endDate || "")}"></div><div class="field"><label>店铺</label><select class="control" id="orderStore">${authState.stores.map(store => `<option value="${attr(store.id)}" ${store.id === selectedStoreId ? "selected" : ""}>${esc(store.name)}</option>`).join("")}</select></div><div class="field"><label>国家</label><select class="control" id="orderCountry"><option value="">全部国家</option>${countries.map(code => `<option value="${code}" ${filters.country === code ? "selected" : ""}>${esc(countryName(code))}</option>`).join("")}</select></div><div class="field"><label>状态</label><select class="control" id="orderStatus"><option value="" ${!filters.status ? "selected" : ""}>全部</option><option value="unshipped" ${filters.status === "unshipped" ? "selected" : ""}>未发货</option><option value="shipped" ${filters.status === "shipped" ? "selected" : ""}>已发货</option></select></div></div><div class="filter-actions"><button class="btn" data-action="reset-order-filters">重置</button><button class="btn btn-primary" data-action="apply-order-filters">查询</button></div></div></div></div><div class="panel"><div class="toolbar orders-toolbar"><div class="toolbar-group"><button class="btn btn-primary" data-action="sync-amazon-orders" ${state.orders.loading ? "disabled" : ""}>同步亚马逊订单</button><button class="btn" data-action="export-amazon-orders">批量导出</button><button class="btn" data-action="open-order-performance">业绩对比</button></div>${state.orders.warning ? `<span class="selected-hint warning-text">${esc(state.orders.warning)}</span>` : ""}${summary}</div>${body}</div>`;
    enhanceSelects($("viewRoot"));
  }

  async function syncAmazonOrders() {
    state.orders.loading = true;
    renderOrders();
    try {
      const result = await apiJson("/orders/sync", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ days: 365, storeId: clean(state.filters.orders?.storeId || authState.activeStoreId) }) });
      state.orders.warning = result.data?.warning || "";
      state.orders.items = [];
      toast(`订单同步完成，共读取 ${num(result.data?.synced)} 条`, "success");
    } catch (error) {
      state.orders.error = error.message;
      toast(`订单同步失败：${error.message}`, "error");
    } finally {
      state.orders.loading = false;
      await loadOrders(true);
    }
  }

  async function exportAmazonOrders() {
    try {
      const response = await authFetch(`${API_BASE}/orders/export?${orderQueryString(false)}`);
      if (!response.ok) throw new Error(await response.text() || `HTTP ${response.status}`);
      const blob = await response.blob();
      const link = document.createElement("a");
      link.href = URL.createObjectURL(blob);
      link.download = `亚马逊订单-${new Date().toISOString().slice(0, 10)}.csv`;
      link.click();
      setTimeout(() => URL.revokeObjectURL(link.href), 1000);
    } catch (error) { toast(`订单导出失败：${error.message}`, "error"); }
  }

  function openOrderPerformanceModal() {
    const rows = state.orders.performance || [];
    const grouped = new Map();
    rows.forEach(row => {
      const rate = orderCurrencyRate(row.currency);
      const profit = (num(row.orderTotal) - num(row.commission) - num(row.vat)) * rate - num(row.procurementCost) - num(row.domesticShipping);
      grouped.set(row.label, (grouped.get(row.label) || 0) + profit);
    });
    const data = [...grouped.entries()].sort(([a], [b]) => a.localeCompare(b));
    const chartWidth = 900, chartHeight = 420;
    const margin = { top: 22, right: 28, bottom: 66, left: 78 };
    const plotWidth = chartWidth - margin.left - margin.right;
    const plotHeight = chartHeight - margin.top - margin.bottom;
    const values = data.map(([, value]) => value);
    const rawMin = Math.min(0, ...values), rawMax = Math.max(0, ...values, 1);
    const span = Math.max(1, rawMax - rawMin);
    const roughStep = span / 5;
    const power = 10 ** Math.floor(Math.log10(roughStep || 1));
    const fraction = roughStep / power;
    const step = (fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10) * power;
    const minValue = Math.floor(rawMin / step) * step;
    const maxValue = Math.ceil(rawMax / step) * step || step;
    const y = value => margin.top + (maxValue - value) / Math.max(step, maxValue - minValue) * plotHeight;
    const zeroY = y(0);
    const yTicks = Array.from({ length: Math.round((maxValue - minValue) / step) + 1 }, (_, index) => minValue + index * step);
    const slot = plotWidth / Math.max(1, data.length);
    const barWidth = Math.min(56, Math.max(18, slot * 0.46));
    const chart = data.length ? `<div class="performance-chart-frame"><svg class="performance-svg" viewBox="0 0 ${chartWidth} ${chartHeight}" role="img" aria-label="按时间统计预估利润柱状图">
      ${yTicks.map(value => `<line class="performance-grid-line" x1="${margin.left}" y1="${y(value)}" x2="${chartWidth - margin.right}" y2="${y(value)}"></line><text class="performance-axis-label performance-y-label" x="${margin.left - 12}" y="${y(value) + 4}" text-anchor="end">${value.toFixed(0)}</text>`).join("")}
      <line class="performance-axis-line" x1="${margin.left}" y1="${margin.top}" x2="${margin.left}" y2="${chartHeight - margin.bottom}"></line>
      <line class="performance-axis-line" x1="${margin.left}" y1="${zeroY}" x2="${chartWidth - margin.right}" y2="${zeroY}"></line>
      <text class="performance-axis-title" transform="translate(20 ${margin.top + plotHeight / 2}) rotate(-90)" text-anchor="middle">预估利润（人民币）</text>
      ${data.map(([label, value], index) => { const x = margin.left + slot * index + (slot - barWidth) / 2; const top = Math.min(zeroY, y(value)); const height = Math.max(2, Math.abs(y(value) - zeroY)); return `<g><rect class="performance-column ${value < 0 ? "negative" : ""}" x="${x}" y="${top}" width="${barWidth}" height="${height}"><title>${esc(label)}：¥${value.toFixed(2)}</title></rect><text class="performance-value-label" x="${x + barWidth / 2}" y="${value >= 0 ? top - 7 : top + height + 14}" text-anchor="middle">${value.toFixed(0)}</text><text class="performance-axis-label" x="${x + barWidth / 2}" y="${chartHeight - margin.bottom + 25}" text-anchor="middle">${esc(label)}</text></g>`; }).join("")}
      <text class="performance-axis-title" x="${margin.left + plotWidth / 2}" y="${chartHeight - 12}" text-anchor="middle">时间</text>
    </svg></div>` : emptyState("暂无业绩数据", "请先同步并筛选订单。");
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal performance-modal"><div class="modal-head"><div><h2>业绩对比</h2><p>按当前订单筛选条件统计预估利润</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="performance-periods">${[["week","周"],["month","月"],["quarter","季度"],["year","年"]].map(([key,label]) => `<button class="btn ${state.orders.period === key ? "btn-primary" : ""}" data-action="set-order-performance-period" data-period="${key}">${label}</button>`).join("")}</div>${chart}</div><div class="modal-foot"><button class="btn" data-action="close-modal">关闭</button></div></div></div>`;
  }

  function openOrderCostsModal(orderId) {
    const order = state.orders.items.find(item => Number(item.id) === Number(orderId));
    if (!order) return;
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal order-cost-modal"><div class="modal-head"><div><h2>录入订单成本</h2><p>${esc(order.amazonOrderId)}</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="account-form-grid"><div class="field"><label>采购成本（人民币）</label><input class="control" type="number" min="0" step="0.01" id="orderProcurementCost" value="${num(order.procurementCost)}"></div><div class="field"><label>物流成本（人民币）</label><input class="control" type="number" min="0" step="0.01" id="orderDomesticShipping" value="${num(order.domesticShipping)}"></div><div class="field span-2"><label>采购链接</label><input class="control" id="orderPurchaseUrl" value="${attr(order.purchaseUrl || "")}" placeholder="https://..."></div></div></div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="save-order-costs" data-id="${order.id}">保存并重算利润</button></div></div></div>`;
  }

  async function saveOrderCosts(orderId) {
    try {
      await apiJson(`/orders/${orderId}/costs`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ procurementCost: num($("orderProcurementCost")?.value), domesticShipping: num($("orderDomesticShipping")?.value), purchaseUrl: clean($("orderPurchaseUrl")?.value), storeId: clean(state.filters.orders?.storeId || authState.activeStoreId) }) });
      closeModal();
      state.orders.items = [];
      await loadOrders(true);
      toast("订单成本已保存，利润已重新计算", "success");
    } catch (error) { toast(error.message, "error"); }
  }

  function storePermissionChoices(account) {
    const selected = authState.storeBindings?.[account] || [];
    const marketplaceSelected = authState.marketplaceBindings?.[account] || {};
    return authState.stores.map(store => {
      const scoped = marketplaceSelected[store.id] || [];
      const authorized = selected.includes(store.id) || scoped.length > 0;
      return `<label class="permission-check"><input type="checkbox" data-account-store="${attr(account)}" value="${attr(store.id)}" ${authorized ? "checked" : ""}><strong>${esc(store.name)}</strong></label>`;
    }).join("") || '<span class="meta">请先添加 Amazon账号</span>';
  }

  async function loadBilling(force = false) {
    if (state.view !== "billing" || authState.billing.loading) return;
    if (!force && authState.billing.items.length && !authState.billing.error) { renderBilling(); return; }
    authState.billing.loading = true; renderBilling();
    const filters = state.filters.billing || {};
    const pager = state.pagination.billing || { page: 1, pageSize: 20 };
    const query = new URLSearchParams({ account: clean(filters.account), storeId: clean(filters.storeId), page: String(pager.page || 1), pageSize: String(pager.pageSize || 20) });
    try {
      const data = (await apiJson(`/billing/records?${query}`)).data || {};
      authState.billing = { ...authState.billing, ...data, loading: false, error: "" };
    } catch (error) { authState.billing = { ...authState.billing, loading: false, error: error.message }; }
    if (state.view === "billing") renderBilling();
  }

  function renderBilling() {
    const data = authState.billing || {}, filters = state.filters.billing || {};
    const pager = state.pagination.billing || { page: 1, pageSize: 20 };
    const totalPages = Math.max(1, Math.ceil(num(data.total) / pager.pageSize)), summary = data.summary || {};
    const billingAccounts = data.accounts?.length ? data.accounts : authState.accounts;
    const billingStores = data.stores?.length ? data.stores : authState.stores;
    const accountOptions = billingAccounts.map(account => `<option value="${attr(account.username)}" ${filters.account === account.username ? "selected" : ""}>${esc(account.displayName || account.username)} · ${esc(account.username)}</option>`).join("");
    const storeOptions = billingStores.map(store => `<option value="${attr(store.id)}" ${filters.storeId === store.id ? "selected" : ""}>${esc(store.name)}</option>`).join("");
    const rows = data.items || [];
    $("viewRoot").innerHTML = `<div class="edit-shell billing-shell"><div class="metric-grid"><div class="metric-card"><small>AI优化</small><strong>¥${esc(summary.aiOptimize || "0.00")}</strong></div><div class="metric-card"><small>AI翻译</small><strong>¥${esc(summary.aiTranslate || "0.00")}</strong></div><div class="metric-card"><small>中文翻译</small><strong>¥${esc(summary.chineseTranslate || "0.00")}</strong></div><div class="metric-card"><small>合计</small><strong>¥${esc(summary.total || "0.00")}</strong></div></div><div class="panel filter-panel billing-filter-panel"><div class="panel-body"><div class="filter-layout billing-filter-layout"><div class="filter-grid billing-filter-grid"><div class="field"><label>账号</label><select class="control" id="billingAccountFilter"><option value="">全部账号</option>${accountOptions}</select></div><div class="field"><label>店铺</label><select class="control" id="billingStoreFilter"><option value="">全部店铺</option>${storeOptions}</select></div></div><div class="filter-actions"><button class="btn btn-primary" data-action="apply-billing-filters">查询</button><button class="btn" data-action="reset-billing-filters">重置</button></div></div></div></div><div class="panel"><div class="toolbar"><span class="selected-hint">同一账号和 SKU 的三类计费在 1 分钟内合并；优化 ¥0.01，多国翻译 ¥0.04，中文翻译 ¥0.10</span><button class="btn" data-action="refresh-billing">刷新</button></div>${data.loading ? '<div class="center-empty">正在读取计费记录…</div>' : data.error ? `<div class="center-empty">${esc(data.error)}</div>` : rows.length ? `<div class="table-scroll"><table class="list-table billing-table"><thead><tr><th>账号</th><th>店铺名称</th><th>SKU</th><th>计费次数</th><th>AI优化</th><th>AI翻译</th><th>中文翻译</th><th>记录时间</th></tr></thead><tbody>${rows.map(row => `<tr><td>${esc(row.account)}</td><td>${esc(row.storeName)}</td><td>${esc(row.sku || "—")}</td><td>${num(row.count)}</td><td>¥${esc(row.aiOptimize)}</td><td>¥${esc(row.aiTranslate)}</td><td>¥${esc(row.chineseTranslate)}</td><td>${esc(formatDate(row.recordedAt))}</td></tr>`).join("")}</tbody></table></div><div class="pager"><span>共 ${num(data.total)} 条</span><div class="pager-pages"><button class="pager-button" data-action="billing-page" data-page="${pager.page - 1}" ${pager.page <= 1 ? "disabled" : ""}>‹</button><span class="pager-info">第 ${pager.page} / ${totalPages} 页</span><button class="pager-button" data-action="billing-page" data-page="${pager.page + 1}" ${pager.page >= totalPages ? "disabled" : ""}>›</button></div></div>` : '<div class="center-empty">暂无局内 API 计费记录</div>'}</div></div>`;
  }

  async function openAccountSettlement(account) {
    const target = accountByName(account);
    if (!target || Number(target.accountLevel) !== 2) return;
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal account-modal"><div class="modal-head"><div><h2>${esc(target.displayName || target.username)} · 月度结算</h2><p>订单扣点与 AI/翻译费用按月统计，金额折算为人民币</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="center-empty">正在读取月度费用…</div></div></div></div>`;
    try {
      const rows = (await apiJson(`/billing/settlements/${encodeURIComponent(account)}`)).data || [];
      const body = $("modalRoot")?.querySelector(".modal-body"); if (!body) return;
      body.innerHTML = rows.length ? `<div class="table-scroll"><table class="list-table"><thead><tr><th>月份</th><th>订单扣点费用</th><th>AI/翻译费用</th><th>状态</th><th>操作</th></tr></thead><tbody>${rows.map(row => `<tr><td>${esc(row.month)}</td><td>¥${esc(row.orderDeductionAmount)}</td><td>¥${esc(row.aiTranslationAmount)}</td><td><span class="status ${row.status === "settled" ? "status-green" : "status-orange"}">${row.status === "settled" ? "已结算" : "待结算"}</span></td><td>${row.status === "settled" ? esc(formatDate(row.settledAt)) : `<button class="link-btn" data-action="settle-account-month" data-account="${attr(account)}" data-month="${attr(row.month)}">结算</button>`}</td></tr>`).join("")}</tbody></table></div>` : '<div class="center-empty">当前账号暂无可结算费用</div>';
    } catch (error) { const body = $("modalRoot")?.querySelector(".modal-body"); if (body) body.innerHTML = `<div class="center-empty">${esc(error.message)}</div>`; }
  }

  async function settleAccountMonth(account, month) {
    try {
      await apiJson(`/billing/settlements/${encodeURIComponent(account)}/${encodeURIComponent(month)}`, { method: "POST" });
      toast(`${month} 费用已结算`, "success"); await refreshAccounts(); await openAccountSettlement(account);
    } catch (error) { toast(`结算失败：${error.message}`, "error"); }
  }

  function renderAccounts() {
    const level = Number(authState.user?.accountLevel || (authState.user?.role === "main" ? 1 : 2));
    const tenantView = level === 1 && authState.dataAccount !== authState.user?.username ? authState.dataAccount : "";
    const children = authState.accounts.filter(account => account.username !== authState.user?.username && (
      !tenantView || account.username === tenantView || account.parentUsername === tenantView
    ));
    $("viewRoot").innerHTML = `<div class="edit-shell account-list-shell">
      <div class="metric-grid"><div class="metric-card"><small>当前账号层级</small><strong>${level} 级</strong></div><div class="metric-card"><small>可管理下级</small><strong>${level < 3 ? `${children.length}` : "0"}</strong></div><div class="metric-card"><small>可见店铺</small><strong>${authState.stores.length}</strong></div><div class="metric-card"><small>数据账号</small><strong>${esc(authState.dataAccount)}</strong></div></div>
      <div class="panel account-list-panel">
        <div class="toolbar"><div>${level < 3 ? '<button class="btn btn-primary" data-action="open-create-child">新增下级账号</button>' : ""}</div><span class="selected-hint">共 ${children.length} 个可管理账号</span></div>
        ${children.length ? `<div class="table-scroll"><table class="list-table account-table"><thead><tr><th>公司名称</th><th>账号类型</th><th>账号</th><th>账号数量</th><th>店铺数量</th><th>订单扣点</th><th>AI/翻译计费</th><th>菜单权限</th><th>状态</th><th>操作</th></tr></thead><tbody>${children.map(account => {
          const fullStores = (authState.storeBindings?.[account.username] || []).map(id => authState.stores.find(item => item.id === id)?.name).filter(Boolean);
          const scopedStores = Object.entries(authState.marketplaceBindings?.[account.username] || {}).map(([storeId, marketplaceIds]) => {
            const store = authState.stores.find(item => item.id === storeId);
            const names = (store?.marketplaces || []).filter(item => marketplaceIds.includes(item.marketplaceId)).map(item => item.name || countryName(item.countryCode));
            return store ? `${store.name}（${names.join("、")}）` : "";
          }).filter(Boolean);
          const stores = [...fullStores, ...scopedStores];
          const parent = authState.accounts.find(item => item.username === account.parentUsername);
          const companyName = Number(account.accountLevel) === 2 ? (account.displayName || account.username) : (parent?.displayName || parent?.username || account.displayName || account.username);
          const accessSummary = Number(account.accountLevel) === 2
            ? `${accountTypeLabel(account.accountType)} · ${ownerAccessLabel(account.effectiveOwnerAccessMode || account.ownerAccessMode)}`
            : "操作员";
          return `<tr><td><div class="account-cell"><span class="account-cell-text account-name">${esc(companyName)}</span></div></td><td><div class="account-cell"><span class="account-cell-text">${esc(accessSummary)}</span></div></td><td><div class="account-cell"><span class="account-cell-text">${esc(account.username)}</span></div></td><td><div class="account-cell"><span class="account-cell-text">${Number(account.accountLevel) === 2 ? `${num(account.childAccountCount)}/${num(account.maxChildAccounts)}` : "—"}</span></div></td><td><div class="account-cell"><span class="account-cell-text">${Number(account.accountLevel) === 2 ? `${num(account.sellerAccountCount ?? account.shopCount)}/${num(account.maxSellerAccounts ?? account.maxShopCount)}` : "—"}</span></div></td><td><div class="account-cell"><span class="account-cell-text">${Number(account.accountLevel) === 2 ? `${esc(account.orderDeductionRate || "0")}%` : "—"}</span></div></td><td><div class="account-cell"><span class="account-cell-text">${Number(account.accountLevel) === 2 ? `¥${esc(account.aiTranslationBilling || "0.00")}` : "—"}</span></div></td><td class="account-perm-cell"><div class="account-cell account-cell-tags">${(account.permissions || []).map(key => `<span class="tag">${esc(MENU_PERMISSIONS[key] || (key === "accounts" ? "账号管理" : key))}</span>`).join(" ") || "—"}</div></td><td><div class="account-cell"><span class="status ${account.active ? "status-green" : "status-gray"}">${account.active ? "正常" : "已停用"}</span></div></td><td><div class="account-cell account-cell-actions"><div class="row-actions"><button class="link-btn" data-action="open-account-permissions" data-account="${attr(account.username)}">权限</button><button class="link-btn" data-action="open-account-edit" data-account="${attr(account.username)}">配置</button>${level === 1 && Number(account.accountLevel) === 2 ? `<button class="link-btn" data-action="open-account-settlement" data-account="${attr(account.username)}">结算</button>` : ""}<button class="link-btn" data-action="open-account-password" data-account="${attr(account.username)}">密码</button><button class="link-btn danger-link" data-action="delete-child-account" data-account="${attr(account.username)}">删除</button></div></div></td></tr>`;
        }).join("")}</tbody></table></div>` : emptyState("尚未创建子账号", "点击左上角“新增账号”录入账号、密码和初始权限。")}
      </div>
    </div>`;
  }

  function accountByName(username) {
    return authState.accounts.find(account => account.username === username && account.username !== authState.user?.username);
  }

  function openCreateChildModal() {
    const targetLevel = Number(authState.user?.accountLevel || 1) + 1;
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal account-modal"><div class="modal-head"><div><h2>新增 ${targetLevel} 级账号</h2>${targetLevel === 2 ? "" : "<p>操作员共享当前客户的数据数据库</p>"}</div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="account-form-grid"><div class="field"><label>登录账号</label><input class="control" id="newChildUsername" placeholder="3–32 位字母、数字、_ 或 -"></div><div class="field"><label>${targetLevel === 2 ? "公司名称" : "显示名称"}</label><input class="control" id="newChildDisplayName" placeholder="${targetLevel === 2 ? "公司名称" : "操作员名称"}"></div><div class="field"><label>初始密码</label><input class="control" id="newChildPassword" type="password" placeholder="至少 8 位，包含字母和数字"></div><div class="field"><label>AI与翻译功能</label><select class="control" id="newAiCallMode"><option value="internal" selected>局内调用（使用一级账号 API）</option><option value="tab">页签调用（打开网页页签）</option></select></div>${targetLevel === 2 ? '<div class="field"><label>账号类型</label><select class="control" id="newAccountType"><option value="external" selected>外部客户</option><option value="internal">自营账号</option></select></div><div class="field"><label>一级账号访问方式</label><select class="control" id="newOwnerAccessMode"><option value="read_only" selected>只读查看</option><option value="managed">代运营（可操作）</option></select></div><div class="field"><label>授权有效期</label><input class="control" id="newOwnerAccessExpiresAt" type="date"><small>留空表示长期有效；到期后自动转为只读。</small></div><div class="field"><label>三级账号上限</label><input class="control" id="newChildQuota" type="number" min="0" value="5"></div><div class="field"><label>Amazon账号上限</label><input class="control" id="newShopQuota" type="number" min="0" value="5"></div><div class="field"><label>订单扣点（%）</label><input class="control" id="newDeductionRate" type="number" min="0" max="100" step="0.0001" value="0"></div>' : ""}<div class="field span-2"><label>开通菜单权限</label><div class="permission-list">${permissionChoices()}</div></div></div></div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="create-child-account">创建账号</button></div></div></div>`;
    enhanceSelects($("modalRoot"));
  }

  function openAccountPermissionsModal(account) {
    const child = accountByName(account);
    if (!child) return;
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal account-modal"><div class="modal-head"><div><h2>${esc(account)} · 权限设置</h2><p>菜单与店铺权限同时保存；授权店铺即包含该店铺全部站点</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="field"><label>菜单权限</label><div class="permission-list">${permissionChoices(child.permissions || [], account)}</div></div><div class="field"><label>AI与翻译功能</label><select class="control" id="accountAiCallMode"><option value="internal" ${(child.aiCallMode || "internal") === "internal" ? "selected" : ""}>局内调用（使用一级账号 API）</option><option value="tab" ${child.aiCallMode === "tab" ? "selected" : ""}>页签调用（打开网页页签）</option></select></div><div class="field"><label>授权店铺</label><div class="permission-list store-permission-list">${storePermissionChoices(account)}</div></div></div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="save-account-access" data-account="${attr(account)}">保存权限</button></div></div></div>`;
  }

  function openAccountPasswordModal(account) {
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal account-modal"><div class="modal-head"><div><h2>${esc(account)} · 修改密码</h2><p>修改后该账号的原登录会话会退出</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="field"><label>新密码</label><input class="control" type="password" data-child-password="${attr(account)}" placeholder="至少 8 位，包含字母和数字"></div></div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="reset-child-password" data-account="${attr(account)}">保存新密码</button></div></div></div>`;
  }

  function openAccountEditModal(account) {
    const child = accountByName(account);
    if (!child) return;
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal account-modal"><div class="modal-head"><div><h2>${esc(account)} · 账号配置</h2><p>登录账号和数据目录创建后保持不变</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="account-form-grid"><div class="field"><label>账号</label><input class="control" value="${attr(account)}" readonly></div><div class="field"><label>${Number(child.accountLevel) === 2 ? "公司名称" : "显示名称"}</label><input class="control" id="editChildDisplayName" value="${attr(child.displayName || "")}"></div><div class="field"><label>账号状态</label><select class="control" id="editChildActive"><option value="1" ${child.active ? "selected" : ""}>启用</option><option value="0" ${!child.active ? "selected" : ""}>停用</option></select></div>${Number(child.accountLevel) === 2 ? `<div class="field"><label>账号类型</label><select class="control" id="editAccountType"><option value="external" ${child.accountType === "external" ? "selected" : ""}>外部客户</option><option value="internal" ${child.accountType !== "external" ? "selected" : ""}>自营账号</option></select></div><div class="field"><label>一级账号访问方式</label><select class="control" id="editOwnerAccessMode"><option value="read_only" ${(child.ownerAccessMode || "managed") === "read_only" ? "selected" : ""}>只读查看</option><option value="managed" ${(child.ownerAccessMode || "managed") !== "read_only" ? "selected" : ""}>代运营（可操作）</option></select></div><div class="field"><label>授权有效期</label><input class="control" id="editOwnerAccessExpiresAt" type="date" value="${attr(child.ownerAccessExpiresAt || "")}"><small>留空表示长期有效；到期后自动转为只读。</small></div><div class="field"><label>三级账号上限</label><input class="control" id="editChildQuota" type="number" min="0" value="${attr(child.maxChildAccounts)}"></div><div class="field"><label>Amazon账号上限</label><input class="control" id="editShopQuota" type="number" min="0" value="${attr(child.maxShopCount)}"></div><div class="field"><label>订单扣点（%）</label><input class="control" id="editDeductionRate" type="number" min="0" max="100" step="0.0001" value="${attr(child.orderDeductionRate || "0")}"></div>` : ""}</div></div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="save-account-profile" data-account="${attr(account)}">保存</button></div></div></div>`;
    enhanceSelects($("modalRoot"));
  }

  function apiConnectionStatus(settings = {}, environment = "sandbox") {
    if (settings.credentialError) {
      return `<div class="api-connection failed"><span class="api-connection-dot"></span><div><strong>旧凭证需要重新录入</strong><p>${esc(settings.credentialError)}</p><small>请完整填写本页三个凭证字段并保存一次，之后将使用稳定的本地加密格式。</small></div></div>`;
    }
    const status = settings.lastStatus || "untested";
    const tone = status === "success" ? "success" : status === "failed" ? "failed" : "untested";
    const envName = environment === "production" ? "生产" : "沙盒";
    const title = status === "success" ? `${envName}连接正常` : status === "failed" ? "上次连接失败" : `尚未测试${envName}连接`;
    const message = settings.lastMessage || `保存凭证后，点击“${envName}连接”验证 LWA 凭证与 SP-API 端点。`;
    return `<div class="api-connection ${tone}"><span class="api-connection-dot"></span><div><strong>${esc(title)}</strong><p>${esc(message)}</p>${settings.lastCheckedAt ? `<small>检测时间：${esc(formatDate(settings.lastCheckedAt))}</small>` : ""}</div></div>`;
  }

  async function loadApiSettings(force = false) {
    if (!canManageAmazonCredentials() || authState.apiSettingsLoading || (!force && authState.apiSettings && !authState.apiSettings.runtimeOnly)) return;
    authState.apiSettingsLoading = true;
    if (state.view === "api-settings") renderApiSettings();
    try {
      const result = await apiJson("/api-settings");
      authState.apiSettings = result.data || {};
    } catch (error) {
      authState.apiSettings = { activeEnvironment: "sandbox", profiles: { sandbox: { lastStatus: "failed", lastMessage: error.message } } };
      toast(error.message, "error");
    } finally {
      authState.apiSettingsLoading = false;
      if (state.view === "api-settings") renderApiSettings();
    }
  }

  async function loadApiRuntime() {
    try {
      const result = await apiJson("/api-settings/runtime");
      authState.apiSettings = result.data || { activeEnvironment: "sandbox", runtimeOnly: true };
    } catch {
      authState.apiSettings = { activeEnvironment: "sandbox", environment: "sandbox", runtimeOnly: true };
    }
  }

  function renderApiSettings() {
    const rootSettings = authState.apiSettings || { activeEnvironment: "sandbox", profiles: {} };
    const environment = rootSettings.activeEnvironment || rootSettings.environment || "sandbox";
    const settings = rootSettings.profiles?.[environment] || rootSettings;
    const production = environment === "production";
    const loading = authState.apiSettingsLoading;
    $("viewRoot").innerHTML = `<div class="edit-shell api-settings-shell">
      <div class="edit-card api-settings-card">
        <div class="section-title">Amazon SP-API 设置 · ${esc(activeStore()?.name || "当前店铺")}</div>
        <div class="api-environment-tabs"><button class="btn ${!production ? "btn-primary" : ""}" data-action="switch-api-environment" data-environment="sandbox">沙盒环境</button><button class="btn ${production ? "btn-danger" : ""}" data-action="switch-api-environment" data-environment="production">生产环境</button></div>
        <div class="settings-note ${production ? "api-production-warning" : ""}">${production ? "生产环境会读取真实店铺并可将商品写入亚马逊卖家后台。Client ID / Secret 属于应用，可供同一主账号下店铺复用；Refresh Token 属于当前卖家店铺，新增店铺必须单独授权并录入。上传仍须先通过生产 VALIDATION_PREVIEW，再进行二次确认。" : "沙盒只验证接口参数，不读取真实店铺，也不会创建真实商品。"} API 凭证使用本机密钥加密后保存在本地数据库。</div>
        <div class="api-environment-row"><div><span class="api-env-badge">${production ? "PRODUCTION" : "SANDBOX"}</span><strong>${production ? "生产环境" : "沙盒环境"}</strong><small>${production ? "真实店铺授权与正式商品上传" : "验证授权、接口参数和上传流程"}</small></div>${loading ? '<span class="api-loading">正在读取…</span>' : ""}</div>
        <div class="settings-grid api-settings-grid">
          <div class="field"><label>${production ? "生产" : "沙盒"}所属区域</label><select class="control" id="apiRegion"><option value="na" ${settings.region === "na" ? "selected" : ""}>美洲站（NA ${production ? "Production" : "Sandbox"}）</option><option value="eu" ${settings.region !== "na" ? "selected" : ""}>欧洲站（EU ${production ? "Production" : "Sandbox"}）</option></select></div>
          <div class="field span-2"><label>LWA Client ID</label><input class="control" id="apiClientId" autocomplete="off" placeholder="${settings.clientIdMasked ? `已保存：${attr(settings.clientIdMasked)}（留空不修改）` : "请输入 Client ID"}"></div>
          <div class="field span-3"><label>LWA Client Secret</label><div class="api-secret-wrap"><input class="control" id="apiClientSecret" type="password" autocomplete="new-password" placeholder="${settings.clientSecretMasked ? `已保存：${attr(settings.clientSecretMasked)}（留空不修改）` : "请输入 Client Secret"}"><button type="button" class="api-secret-eye" data-action="toggle-api-secret" data-target="apiClientSecret" aria-label="显示或隐藏 Client Secret">◉</button></div></div>
          <div class="field span-3"><label>${production ? "Production" : "Sandbox"} Refresh Token</label><div class="api-secret-wrap"><input class="control" id="apiRefreshToken" type="password" autocomplete="new-password" placeholder="${settings.refreshTokenMasked ? `已保存：${attr(settings.refreshTokenMasked)}（留空不修改）` : `请输入${production ? "店铺授权后获得的生产" : "沙盒"} Refresh Token`}"><button type="button" class="api-secret-eye" data-action="toggle-api-secret" data-target="apiRefreshToken" aria-label="显示或隐藏 Refresh Token">◉</button></div><small class="field-help">${production ? "这里填写卖家店铺授权生成的 Refresh Token，不是沙盒令牌。" : "创建的是沙盒令牌，不是卖家平台登录密码。"} 易联跨境不会显示或导出完整令牌。</small></div>
        </div>
        ${apiConnectionStatus(settings, environment)}
        <div class="api-actions"><button class="btn" data-action="save-api-settings" ${loading ? "disabled" : ""}>保存 API 信息</button><button class="btn ${production ? "btn-danger" : "btn-primary"}" data-action="${production ? "test-production-connection" : "test-sandbox-connection"}" ${loading ? "disabled" : ""}>${production ? "生产连接" : "沙盒连接"}</button></div>
      </div>
    </div>`;
    if (!authState.apiSettings && !authState.apiSettingsLoading) queueMicrotask(() => loadApiSettings());
  }

  function apiSettingsPayload() {
    return {
      environment: authState.apiSettings?.activeEnvironment || authState.apiSettings?.environment || "sandbox",
      region: $("apiRegion")?.value || "eu",
      clientId: clean($("apiClientId")?.value),
      clientSecret: clean($("apiClientSecret")?.value),
      refreshToken: clean($("apiRefreshToken")?.value)
    };
  }

  async function saveApiSettings(showToast = true) {
    const result = await apiJson("/api-settings", {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(apiSettingsPayload())
    });
    authState.apiSettings = result.data || {};
    render();
    if (showToast) toast("API 信息已加密保存", "success");
    return result.data;
  }

  async function testSandboxConnection() {
    const buttons = [...document.querySelectorAll('[data-action="save-api-settings"],[data-action="test-sandbox-connection"]')];
    buttons.forEach(button => { button.disabled = true; });
    try {
      await saveApiSettings(false);
      authState.apiSettingsLoading = true;
      render();
      const result = await apiJson("/api-settings/sandbox-test", { method: "POST" });
      authState.apiSettings = result.data || {};
      toast("亚马逊沙盒连接成功", "success");
    } catch (error) {
      const root = authState.apiSettings || { activeEnvironment: "sandbox", profiles: {} };
      root.activeEnvironment = "sandbox";
      root.profiles = root.profiles || {};
      root.profiles.sandbox = { ...(root.profiles.sandbox || {}), lastStatus: "failed", lastMessage: error.message, lastCheckedAt: new Date().toISOString() };
      authState.apiSettings = root;
      toast(error.message, "error");
    } finally {
      authState.apiSettingsLoading = false;
      render();
    }
  }

  async function testProductionConnection() {
    const buttons = [...document.querySelectorAll('[data-action="save-api-settings"],[data-action="test-production-connection"]')];
    buttons.forEach(button => { button.disabled = true; });
    try {
      await saveApiSettings(false);
      authState.apiSettingsLoading = true;
      render();
      const result = await apiJson("/api-settings/production-test", { method: "POST" });
      authState.apiSettings = result.data || {};
      await refreshStoreContext();
      toast("亚马逊生产连接成功，Refresh Token 与当前 Seller ID 已验证", "success");
    } catch (error) {
      const root = authState.apiSettings || { activeEnvironment: "production", profiles: {} };
      root.activeEnvironment = "production";
      root.profiles = root.profiles || {};
      root.profiles.production = { ...(root.profiles.production || {}), lastStatus: "failed", lastMessage: error.message, lastCheckedAt: new Date().toISOString() };
      authState.apiSettings = root;
      toast(error.message, "error");
    } finally {
      authState.apiSettingsLoading = false;
      render();
    }
  }

  async function setApiEnvironment(environment) {
    const selected = environment === "production" ? "production" : "sandbox";
    try {
      const result = await apiJson("/api-settings/environment", {
        method: "PUT", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ environment: selected })
      });
      authState.apiSettings = result.data || {};
      render();
    } catch (error) {
      toast(`API 环境保存失败：${error.message}`, "error");
      await loadApiSettings(true);
    }
  }

  async function autoConnectCurrentStoreProduction() {
    if (!canManageAmazonCredentials() || !authState.activeStoreId) return;
    const production = authState.apiSettings?.profiles?.production;
    if (!production?.hasCredentials) return;
    const key = `${authState.dataAccount}:${authState.activeStoreId}`;
    if (automaticProductionConnectionKey === key) return;
    automaticProductionConnectionKey = key;
    try {
      const result = await apiJson("/api-settings/production-test", { method: "POST" });
      authState.apiSettings = result.data || authState.apiSettings;
      if (state.view === "api-settings") renderApiSettings();
      refreshAccountChrome();
    } catch (error) {
      // 后端已持久化失败原因；重新读取，避免刷新后丢失真实连接状态。
      await loadApiSettings(true).catch(() => {});
    }
  }

  async function loadAiServices(force = false) {
    if (authState.aiServicesLoading || (!force && (authState.aiServices || authState.aiServicesError))) return;
    authState.aiServicesLoading = true;
    authState.aiServicesError = "";
    if (state.view === "ai-services") renderAiServices();
    try {
      const [settings, usage] = await Promise.all([
        apiJson("/ai-services"),
        Number(authState.user?.accountLevel || 0) === 1
          ? apiJson(`/ai-services/usage?page=${authState.aiUsagePage}&pageSize=${authState.aiUsagePageSize}`)
          : Promise.resolve(null)
      ]);
      authState.aiServices = settings.data || {};
      authState.aiUsage = usage?.data || null;
    } catch (error) {
      authState.aiServicesError = error.message || "服务配置读取失败";
      toast(`AI 与翻译服务读取失败：${error.message}`, "error");
    } finally {
      authState.aiServicesLoading = false;
      if (state.view === "ai-services") renderAiServices();
    }
  }

  async function loadAiUsage(page = 1, usageDate = "") {
    authState.aiUsagePage = Math.max(1, Number(page) || 1);
    const query = new URLSearchParams({
      page: String(authState.aiUsagePage),
      pageSize: String(authState.aiUsagePageSize)
    });
    if (usageDate) query.set("date", usageDate);
    const response = await apiJson(`/ai-services/usage?${query.toString()}`);
    authState.aiUsage = response.data || null;
    renderAiServices();
  }

  function providerSecretField(id, label, hasSecret, masked, placeholder = "录入后加密保存") {
    return `<div class="field"><label>${label}</label><div class="api-secret-wrap"><input class="control" id="${id}" type="password" autocomplete="new-password" placeholder="${hasSecret ? `已保存 ${attr(masked || "••••••")}；留空不修改` : attr(placeholder)}"><button class="api-secret-eye" type="button" data-action="toggle-api-secret" data-target="${id}" aria-label="显示或隐藏密钥">◉</button></div><small class="field-help">密钥只保存在后端加密数据库，页面不会读取原文。</small></div>`;
  }

  function aiProviderFields(provider, config = {}) {
    const accountLabel = provider === "doubao" ? "账号 ID（可选）" : provider === "chatgpt" ? "Project ID（可选）" : "账号 ID（可选）";
    return `<div class="provider-config-grid">
      ${providerSecretField("serviceAiKey", "API Key", config.hasApiKey, config.apiKeyMasked)}
      <div class="field"><label>${accountLabel}</label><input class="control" id="serviceAiAccount" value="${attr(config.accountId || "")}" placeholder="可选"></div>
      <div class="field"><label>${provider === "doubao" ? "推理接入点 ID / Model" : "模型"}</label><input class="control" id="serviceAiModel" value="${attr(config.model || "")}" placeholder="${provider === "doubao" ? "例如 ep-2026..." : "模型名称"}"></div>
      <div class="field span-2"><label>接口地址</label><input class="control" id="serviceAiEndpoint" value="${attr(config.endpoint || "")}" placeholder="https://..."></div>
      <div class="field"><label>缓存命中输入单价（元/百万 Token）</label><input class="control" id="serviceAiCachedInputRate" type="number" min="0" step="0.0001" value="${attr(config.cachedInputRate || 0)}"></div>
      <div class="field"><label>输入单价（元/百万 Token）</label><input class="control" id="serviceAiInputRate" type="number" min="0" step="0.0001" value="${attr(config.inputRate || 0)}"></div>
      <div class="field"><label>输出单价（元/百万 Token）</label><input class="control" id="serviceAiOutputRate" type="number" min="0" step="0.0001" value="${attr(config.outputRate || 0)}"></div>
    </div>`;
  }

  function translationProviderFields(provider, config = {}) {
    if (provider === "baidu") return `<div class="provider-config-grid">
      <div class="field"><label>APP ID</label><input class="control" id="serviceTranslationAccount" value="${attr(config.appId || "")}" placeholder="百度翻译 APP ID"></div>
      ${providerSecretField("serviceTranslationKey", "Secret Key", config.hasSecretKey, config.secretKeyMasked)}
      <div class="field span-2"><label>接口地址</label><input class="control" id="serviceTranslationEndpoint" value="${attr(config.endpoint || "")}"></div>
      <div class="field"><label>单价（元/百万字符）</label><input class="control" id="serviceTranslationRate" type="number" min="0" step="0.0001" value="${attr(config.charRate || 0)}"><small class="field-help">消费统计按全部实际字符直接计费，不抵扣服务商免费额度。</small></div>
    </div>`;
    return `<div class="provider-config-grid">
      <div class="field"><label>Google Cloud Project ID（可选）</label><input class="control" id="serviceTranslationAccount" value="${attr(config.accountId || "")}" placeholder="Project ID"></div>
      ${providerSecretField("serviceTranslationKey", "Google Translation API Key", config.hasApiKey, config.apiKeyMasked)}
      <div class="field span-2"><label>接口地址</label><input class="control" id="serviceTranslationEndpoint" value="${attr(config.endpoint || "")}"></div>
      <div class="field"><label>单价（元/百万字符）</label><input class="control" id="serviceTranslationRate" type="number" min="0" step="0.0001" value="${attr(config.charRate || 0)}"><small class="field-help">消费统计按全部实际字符直接计费，不抵扣服务商免费额度。</small></div>
    </div>`;
  }

  function renderAiServices() {
    if (!authState.aiServices && !authState.aiServicesLoading && !authState.aiServicesError) queueMicrotask(() => loadAiServices());
    const root = $("viewRoot");
    if (authState.aiServicesLoading && !authState.aiServices) {
      root.innerHTML = '<div class="edit-card"><div class="api-loading">正在读取 AI 与翻译服务配置…</div></div>';
      return;
    }
    if (authState.aiServicesError && !authState.aiServices) {
      root.innerHTML = `<div class="edit-card"><div class="service-load-error"><strong>AI 与翻译服务读取失败</strong><span>${esc(authState.aiServicesError)}</span><button class="btn btn-primary" data-action="retry-ai-services">重新读取</button></div></div>`;
      return;
    }
    const data = authState.aiServices || {};
    const activeAi = data.activeAiProvider || "deepseek";
    const activeTranslation = data.activeTranslationProvider || "google";
    const ai = data.ai?.[activeAi] || {};
    const translation = data.translation?.[activeTranslation] || {};
    const usage = authState.aiUsage || {};
    const summary = usage.summary || {};
    const rows = Array.isArray(usage.items) ? usage.items : [];
    const usagePagination = usage.pagination || { page: 1, pageSize: authState.aiUsagePageSize, total: rows.length, totalPages: 1 };
    const aiApiEnabled = data.aiApiEnabled !== false;
    const translationApiEnabled = data.translationApiEnabled !== false;
    root.innerHTML = `<div class="service-settings-shell">
      <div class="service-provider-grid">
      <div class="edit-card"><div class="section-title service-title-row"><span>AI 大模型</span><label class="service-toggle"><input id="aiApiEnabled" type="checkbox" ${aiApiEnabled ? "checked" : ""}><span></span><b>${aiApiEnabled ? "API 已开启" : "页签模式"}</b></label></div>
        <div class="settings-note">开启时由易联跨境后端调用 API；关闭时“一键优化”恢复为后台打开所选 AI 网页页签。</div>
        <div class="provider-header"><div class="field"><label>当前生效 AI</label><select class="control" id="activeAiProvider" data-service-provider="ai">${Object.entries(AI_TOOLS).map(([key, item]) => `<option value="${key}" ${key === activeAi ? "selected" : ""}>${item.name}</option>`).join("")}</select></div><span class="service-status ${ai.hasApiKey ? "ready" : "missing"}">${ai.hasApiKey ? "密钥已保存（未验证）" : "尚未配置密钥"}</span><button class="btn" data-action="renew-provider" data-url="${attr(ai.renewUrl || "")}">续费 / 充值</button></div>
        ${aiProviderFields(activeAi, ai)}
      </div>
      <div class="service-provider-column"><div class="edit-card"><div class="section-title service-title-row"><span>翻译引擎</span><label class="service-toggle"><input id="translationApiEnabled" type="checkbox" ${translationApiEnabled ? "checked" : ""}><span></span><b>${translationApiEnabled ? "API 已开启" : "页签模式"}</b></label></div>
        <div class="settings-note">开启时调用当前翻译 API；关闭时恢复为浏览器后台页签翻译。</div>
        <div class="provider-header"><div class="field"><label>当前生效翻译服务</label><select class="control" id="activeTranslationProvider" data-service-provider="translation"><option value="google" ${activeTranslation === "google" ? "selected" : ""}>Google 翻译</option><option value="baidu" ${activeTranslation === "baidu" ? "selected" : ""}>百度翻译</option></select></div><span class="service-status ${(translation.hasApiKey || translation.hasSecretKey) ? "ready" : "missing"}">${(translation.hasApiKey || translation.hasSecretKey) ? "密钥已保存（未验证）" : "尚未配置密钥"}</span><button class="btn" data-action="renew-provider" data-url="${attr(translation.renewUrl || "")}">续费 / 充值</button></div>
        ${translationProviderFields(activeTranslation, translation)}
      </div><div class="edit-card service-save-card"><div class="service-save-row"><span>留空密钥框会保留已经加密保存的密钥。</span><button class="btn btn-primary" data-action="save-ai-services">保存服务配置</button></div></div></div></div>
      <div class="edit-card"><div class="section-title">每日消耗统计</div>
        <div class="usage-toolbar"><div class="field"><label>统计日期</label><input class="control" id="serviceUsageDate" type="date" value="${attr(summary.date || new Date().toISOString().slice(0,10))}"></div><button class="btn" data-action="refresh-ai-usage">手动刷新</button><small>仅统计经易联跨境后端发起的业务调用，不接入服务商账单。</small></div>
        <div class="usage-summary"><div><span>AI 消耗金额</span><strong>¥${num(summary.ai_amount).toFixed(4)}</strong></div><div><span>翻译消耗金额</span><strong>¥${num(summary.translation_amount).toFixed(4)}</strong></div><div><span>AI 调用</span><strong>${num(summary.ai_calls)}</strong></div><div><span>翻译调用</span><strong>${num(summary.translation_calls)}</strong></div><div><span>成功调用</span><strong>${num(summary.success_calls)}</strong></div></div>
        <div class="usage-table-wrap"><table class="usage-table"><thead><tr><th>时间</th><th>类型</th><th>服务商</th><th>计量</th><th>金额</th><th>结果</th><th>说明</th></tr></thead><tbody>${rows.length ? rows.map(item => `<tr><td>${esc(formatDate(item.created_at))}</td><td>${item.service_type === "ai" ? "AI优化" : "翻译"}</td><td>${esc(item.provider)}</td><td>${item.service_type === "ai" ? `输入 ${num(item.input_units)}（缓存 ${num(item.cached_input_units)}）/ 输出 ${num(item.output_units)}` : `${num(item.units)} 字符（计费 ${num(item.billable_units)}）`}</td><td>¥${num(item.amount).toFixed(4)}</td><td><span class="status ${item.success ? "status-green" : "status-red"}">${item.success ? "成功" : "失败"}</span></td><td>${esc(item.message || "—")}</td></tr>`).join("") : '<tr><td colspan="7" class="meta">当日暂无调用记录</td></tr>'}</tbody></table></div>
        <div class="usage-pagination"><span>共 ${num(usagePagination.total)} 条 · 第 ${num(usagePagination.page, 1)} / ${num(usagePagination.totalPages, 1)} 页</span><div><button class="btn" data-action="ai-usage-page" data-page="${Math.max(1, num(usagePagination.page, 1) - 1)}" ${num(usagePagination.page, 1) <= 1 ? "disabled" : ""}>上一页</button><button class="btn" data-action="ai-usage-page" data-page="${Math.min(num(usagePagination.totalPages, 1), num(usagePagination.page, 1) + 1)}" ${num(usagePagination.page, 1) >= num(usagePagination.totalPages, 1) ? "disabled" : ""}>下一页</button></div></div>
      </div>
    </div>`;
    queueMicrotask(() => enhanceSelects(root));
  }

  async function saveAiServices() {
    const activeAi = $("activeAiProvider")?.value || "deepseek";
    const activeTranslation = $("activeTranslationProvider")?.value || "google";
    const aiConfig = {
      apiKey: $("serviceAiKey")?.value || "", accountId: $("serviceAiAccount")?.value || "",
      model: $("serviceAiModel")?.value || "", endpoint: $("serviceAiEndpoint")?.value || "",
      cachedInputRate: num($("serviceAiCachedInputRate")?.value),
      inputRate: num($("serviceAiInputRate")?.value), outputRate: num($("serviceAiOutputRate")?.value)
    };
    const translationConfig = {
      endpoint: $("serviceTranslationEndpoint")?.value || "", charRate: num($("serviceTranslationRate")?.value)
    };
    if (activeTranslation === "baidu") {
      translationConfig.appId = $("serviceTranslationAccount")?.value || "";
      translationConfig.secretKey = $("serviceTranslationKey")?.value || "";
    } else {
      translationConfig.accountId = $("serviceTranslationAccount")?.value || "";
      translationConfig.apiKey = $("serviceTranslationKey")?.value || "";
    }
    const response = await apiJson("/ai-services", {
      method: "PUT", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        activeAiProvider: activeAi,
        activeTranslationProvider: activeTranslation,
        aiApiEnabled: Boolean($("aiApiEnabled")?.checked),
        translationApiEnabled: Boolean($("translationApiEnabled")?.checked),
        ai: { [activeAi]: aiConfig },
        translation: { [activeTranslation]: translationConfig }
      })
    });
    authState.aiServices = response.data || {};
    toast("AI 与翻译服务配置已加密保存", "success");
    renderAiServices();
  }

  async function refreshAiUsage() {
    const selectedDate = $("serviceUsageDate")?.value || "";
    try {
      const response = await apiJson("/ai-services/usage/refresh", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ date: selectedDate, page: authState.aiUsagePage, pageSize: authState.aiUsagePageSize })
      });
      authState.aiUsage = response.data || null;
      renderAiServices();
      toast("调用用量已刷新", "success");
    } catch (error) { toast(`用量刷新失败：${error.message}`, "error"); }
  }

  async function saveServiceSwitches(changedInput) {
    const previous = authState.aiServices || {};
    const aiEnabled = Boolean($("aiApiEnabled")?.checked);
    const translationEnabled = Boolean($("translationApiEnabled")?.checked);
    authState.aiServices = { ...previous, aiApiEnabled: aiEnabled, translationApiEnabled: translationEnabled };
    try {
      const response = await apiJson("/ai-services", {
        method: "PUT", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          activeAiProvider: previous.activeAiProvider || "deepseek",
          activeTranslationProvider: previous.activeTranslationProvider || "google",
          aiApiEnabled: aiEnabled,
          translationApiEnabled: translationEnabled
        })
      });
      authState.aiServices = response.data || authState.aiServices;
      renderAiServices();
    } catch (error) {
      if (changedInput) changedInput.checked = !changedInput.checked;
      authState.aiServices = previous;
      toast(`开关保存失败：${error.message}`, "error");
      renderAiServices();
    }
  }

  async function loadAccountCenter(view = state.view) {
    if (authState.centerLoading) return;
    authState.centerLoading = true;
    try {
      if (view === "logistics") authState.logistics = (await apiJson("/account-center/logistics")).data || [];
      if (view === "transparency") authState.transparency = (await apiJson("/account-center/transparency")).data || [];
      if (view === "audit") authState.auditLogs = (await apiJson("/account-center/audit-logs?limit=200")).data || [];
    } catch (error) { toast(error.message, "error"); }
    finally { authState.centerLoading = false; if (state.view === view) render(); }
  }

  function renderShops() {
    const current = activeStore();
    $("viewRoot").innerHTML = `<div class="edit-shell"><div class="metric-grid"><div class="metric-card"><small>可见店铺</small><strong>${authState.stores.length}</strong></div><div class="metric-card"><small>当前店铺</small><strong>${esc(current?.name || "未选择")}</strong></div><div class="metric-card"><small>授权方式</small><strong>${current?.authType === "amazon_oauth" ? "OAuth" : "Token"}</strong></div><div class="metric-card"><small>连接状态</small><strong>${current?.status === "api_verified" ? "正常" : "待验证"}</strong></div></div>${renderStoreManagement()}<div class="panel"><div class="panel-body"><strong>统一授权说明</strong><p class="meta">手动 Token 与 Amazon OAuth 使用同一店铺、配额和权限范围。OAuth 采用一次性 state 校验；Refresh Token 只会加密保存，不会在页面或日志中回显。</p></div></div></div>`;
  }

  function renderLogistics() {
    const rows = authState.logistics || [];
    $("viewRoot").innerHTML = `<div class="panel"><div class="toolbar"><button class="btn btn-primary" data-action="open-logistics">新增物流绑定</button><button class="btn" data-action="refresh-center" data-center="logistics">刷新</button><span class="selected-hint">按店铺维护渠道、配送方式和默认项</span></div>${authState.centerLoading ? '<div class="center-empty">正在读取物流配置…</div>' : rows.length ? `<div class="table-scroll"><table class="list-table"><thead><tr><th>店铺</th><th>物流渠道</th><th>配送方式</th><th>默认</th><th>状态</th><th>更新时间</th><th>操作</th></tr></thead><tbody>${rows.map(row => `<tr><td>${esc(authState.stores.find(store => store.id === row.storeId)?.name || row.storeId)}</td><td>${esc(row.channelName)}</td><td>${esc(row.shippingMethod)}</td><td>${row.isDefault ? '<span class="status status-green">默认</span>' : "—"}</td><td>${row.active ? "启用" : "停用"}</td><td>${esc(formatDate(row.updatedAt))}</td><td><button class="link-btn danger-link" data-action="delete-logistics" data-id="${row.id}">删除</button></td></tr>`).join("")}</tbody></table></div>` : '<div class="center-empty">暂无物流绑定</div>'}</div>`;
  }

  function openLogisticsModal() {
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal"><div class="modal-head"><div><h2>新增物流绑定</h2><p>同一店铺只能有一个默认物流方案</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="field"><label>店铺</label><select class="control" id="logisticsStore">${authState.stores.map(store => `<option value="${attr(store.id)}">${esc(store.name)}</option>`).join("")}</select></div><div class="field"><label>物流渠道</label><input class="control" id="logisticsChannel" placeholder="例如 Amazon Shipping"></div><div class="field"><label>配送方式</label><input class="control" id="logisticsMethod" placeholder="例如 Standard"></div><label class="permission-check"><input type="checkbox" id="logisticsDefault">设为该店铺默认物流</label></div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="save-logistics">保存</button></div></div></div>`;
    enhanceSelects($("modalRoot"));
  }

  async function saveLogisticsBinding() {
    try {
      await apiJson("/account-center/logistics", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ storeId: $("logisticsStore")?.value, channelName: clean($("logisticsChannel")?.value), shippingMethod: clean($("logisticsMethod")?.value), isDefault: Boolean($("logisticsDefault")?.checked) }) });
      closeModal(); await loadAccountCenter("logistics"); toast("物流绑定已保存", "success");
    } catch (error) { toast(error.message, "error"); }
  }

  function renderTransparency() {
    const rows = authState.transparency || [];
    $("viewRoot").innerHTML = `<div class="panel"><div class="toolbar"><button class="btn btn-primary" data-action="open-transparency">新增授权</button><button class="btn" data-action="refresh-center" data-center="transparency">刷新</button><span class="selected-hint">Client Secret 采用当前 Windows 用户加密，仅显示是否已配置</span></div>${authState.centerLoading ? '<div class="center-empty">正在读取授权…</div>' : rows.length ? `<div class="table-scroll"><table class="list-table"><thead><tr><th>账号名称</th><th>Client ID</th><th>Client Secret</th><th>状态</th><th>更新时间</th></tr></thead><tbody>${rows.map(row => `<tr><td>${esc(row.accountName)}</td><td>${esc(row.clientId)}</td><td>${row.clientSecretConfigured ? "已加密配置" : "未配置"}</td><td>${row.active ? '<span class="status status-green">启用</span>' : '<span class="status status-gray">停用</span>'}</td><td>${esc(formatDate(row.updatedAt))}</td></tr>`).join("")}</tbody></table></div>` : '<div class="center-empty">暂无透明计划授权</div>'}</div>`;
  }

  function openTransparencyModal() {
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal"><div class="modal-head"><div><h2>透明计划授权</h2><p>密钥保存后不会再次回显</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="field"><label>账号名称</label><input class="control" id="transparencyName"></div><div class="field"><label>Client ID</label><input class="control" id="transparencyClientId"></div><div class="field"><label>Client Secret</label><input class="control" type="password" id="transparencySecret" autocomplete="new-password"></div></div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="save-transparency">加密保存</button></div></div></div>`;
  }

  async function saveTransparencyAuthorization() {
    try {
      await apiJson("/account-center/transparency", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ accountName: clean($("transparencyName")?.value), clientId: clean($("transparencyClientId")?.value), clientSecret: $("transparencySecret")?.value || "" }) });
      closeModal(); await loadAccountCenter("transparency"); toast("授权已加密保存", "success");
    } catch (error) { toast(error.message, "error"); }
  }

  function renderAudit() {
    const rows = authState.auditLogs || [];
    $("viewRoot").innerHTML = `<div class="panel"><div class="toolbar"><button class="btn" data-action="refresh-center" data-center="audit">刷新</button><span class="selected-hint">只记录操作元数据，不记录密码、Token、验证码或密钥</span></div>${authState.centerLoading ? '<div class="center-empty">正在读取审计日志…</div>' : rows.length ? `<div class="table-scroll"><table class="list-table"><thead><tr><th>时间</th><th>操作账号</th><th>动作</th><th>对象</th><th>摘要</th></tr></thead><tbody>${rows.map(row => `<tr><td>${esc(formatDate(row.createdAt))}</td><td>${esc(row.actorUsername)}</td><td>${esc(row.action)}</td><td>${esc(`${row.targetType}:${row.targetId}`)}</td><td><span class="meta">${esc(JSON.stringify(row.summary || {}))}</span></td></tr>`).join("")}</tbody></table></div>` : '<div class="center-empty">暂无审计记录</div>'}</div>`;
  }

  function renderUiComponents() {
    $("viewRoot").innerHTML = `<div class="panel"><div class="panel-body component-playground"><div><h2>易联跨境 Design System</h2><p class="meta">默认控件 32px，小型 24px，大型 40px；正文 14px，辅助文字 12px；间距遵循 4px 网格。</p></div><div class="component-row"><button class="btn btn-primary">主要按钮</button><button class="btn">次要按钮</button><button class="btn btn-danger">危险按钮</button><button class="btn" disabled>禁用按钮</button></div><div class="component-row"><input class="control" placeholder="Input 32px"><select class="control"><option>Select 32px</option></select><input class="control" type="number" value="32"><input class="control" type="date"></div><div class="component-row"><span class="status status-green">成功</span><span class="status status-orange">警告</span><span class="status status-red">失败</span><span class="status status-gray">禁用</span></div><div class="table-scroll"><table class="list-table"><thead><tr><th>组件</th><th>默认尺寸</th><th>圆角</th><th>说明</th></tr></thead><tbody><tr><td>Input / Select / Button</td><td>32px</td><td>6px</td><td>同一行基线对齐</td></tr><tr><td>Card / Modal</td><td>内容自适应</td><td>8px</td><td>统一表面容器</td></tr></tbody></table></div></div></div>`;
  }

  async function startStoreOauth(storeId) {
    try {
      const result = await apiJson(`/stores/${encodeURIComponent(storeId)}/oauth/start`, { method: "POST" });
      window.open(result.data.authorizationUrl, "_blank", "noopener,noreferrer");
      toast("已打开 Amazon 授权页面", "success");
    } catch (error) { toast(error.message, "error"); }
  }

  function renderNoAccess() {
    $("viewRoot").innerHTML = emptyState("尚未开通菜单权限", "请联系主账号，在“账号管理”中为当前子账号配置采集库、草稿库、产品库或默认设置权限。");
  }

  const READ_ONLY_SAFE_ACTIONS = new Set([
    "apply-filters", "apply-order-filters", "cancel-edit", "close-modal", "open-list-settings",
    "open-order-performance", "open-purchase", "preview-detail-image", "preview-image",
    "preview-list-image", "preview-variant-image", "copy-image", "copy-variant-image", "reset-filters", "reset-order-filters",
    "select-detail-variant", "set-audit-page", "set-order-page", "set-order-performance-period",
    "set-page", "switch-edit-locale", "view-collection-detail", "view-product",
    "view-upload-failure", "edit-draft", "edit-product"
  ]);

  function applyTenantAccessUi() {
    const readOnly = isDelegatedReadOnly();
    $("appRoot")?.classList.toggle("tenant-readonly", readOnly);
    ["topSubmitBtn", "collectProductBtn", "exportDataBtn"].forEach(id => {
      const button = $(id);
      if (button) button.disabled = readOnly;
    });
    if (!readOnly || state.view === "accounts") return;
    document.querySelectorAll("#viewRoot button[data-action]").forEach(button => {
      if (["edit-draft", "edit-product"].includes(button.dataset.action || "")) button.textContent = "查看产品";
      if (READ_ONLY_SAFE_ACTIONS.has(button.dataset.action || "")) return;
      button.disabled = true;
      button.title = "当前外部客户账号为只读查看";
    });
    if (["edit", "variants", "settings", "compliance-label", "api-settings", "shops", "logistics", "transparency"].includes(state.view)) {
      document.querySelectorAll("#viewRoot input, #viewRoot textarea, #viewRoot select").forEach(control => {
        control.disabled = true;
      });
    }
  }

  function render() {
    const editorActive = state.view === "edit" || state.view === "variants";
    $("appRoot")?.classList.toggle("editor-mode", editorActive);
    syncNavigationActive();
    updateBadges();
    renderWorkspaceTabs();
    if (state.view === "collection") renderCollection();
    else if (state.view === "drafts") renderDrafts();
    else if (state.view === "products") renderProducts();
    else if (state.view === "orders") renderOrders();
    else if (state.view === "settings") renderSettings();
    else if (state.view === "compliance-label") renderComplianceLabel();
    else if (state.view === "api-settings") renderApiSettings();
    else if (state.view === "ai-services") renderAiServices();
    else if (state.view === "accounts") renderAccounts();
    else if (state.view === "shops") renderShops();
    else if (state.view === "logistics") renderLogistics();
    else if (state.view === "transparency") renderTransparency();
    else if (state.view === "billing") renderBilling();
    else if (state.view === "audit") renderAudit();
    else if (state.view === "ui-components") renderUiComponents();
    else if (state.view === "no-access") renderNoAccess();
    else if (state.view === "edit") renderEdit();
    else if (state.view === "variants") renderVariants();
    applyTenantAccessUi();
    queueMicrotask(() => enhanceSelects($("viewRoot")));
    queueMicrotask(syncNavigationActive);
    scheduleUiStatePersist();
  }

  function categorySearchResultsHtml() {
    if (amazonCategorySearch.draftId && amazonCategorySearch.draftId !== state.editing?.id) return "";
    if (!amazonCategorySearch.open) return "";
    if (amazonCategorySearch.loading) return '<div class="category-search-message">正在从亚马逊官方接口查找分类路径…</div>';
    if (amazonCategorySearch.error) return `<div class="category-search-message error-text">${esc(amazonCategorySearch.error)}</div>`;
    if (!amazonCategorySearch.items.length) return '<div class="category-search-message">亚马逊官方接口暂未返回匹配分类，可继续保留采集分类。</div>';
    return amazonCategorySearch.items.map(item => `<button type="button" class="category-path-option" data-action="select-category-path" data-path="${attr(item.path)}" data-node-id="${attr(item.nodeId || "")}" data-product-type="${attr(item.productType || "")}"><span>${esc(item.path)}</span><small>${item.productType ? `Product Type：${esc(item.productType)}` : ""}${item.nodeId ? ` · 节点：${esc(item.nodeId)}` : ""}</small></button>`).join("");
  }

  function renderCategorySearchResults() {
    const root = $("categorySearchResults");
    if (root) root.innerHTML = categorySearchResultsHtml();
  }

  async function loadAmazonCategoryPaths(keyword, force = false) {
    const draft = state.editing;
    const leaf = clean(keyword).split(/[\/>＞]/).pop().trim();
    if (!draft || !leaf || amazonCategorySearch.loading) return;
    if (!force && amazonCategorySearch.draftId === draft.id && amazonCategorySearch.keyword === leaf && amazonCategorySearch.items.length) {
      amazonCategorySearch.open = true;
      renderCategorySearchResults();
      return;
    }
    amazonCategorySearch = { draftId: draft.id, keyword: leaf, items: [], loading: true, error: "", open: true };
    renderCategorySearchResults();
    try {
      const country = draft.countries?.[0] || activeStoreCountries()[0] || "";
      const response = await apiJson(`/api-settings/category-paths?country=${encodeURIComponent(country)}&query=${encodeURIComponent(leaf)}`);
      amazonCategorySearch = { draftId: draft.id, keyword: leaf, items: Array.isArray(response?.data?.categories) ? response.data.categories : [], loading: false, error: "", open: true };
    } catch (error) {
      amazonCategorySearch = { draftId: draft.id, keyword: leaf, items: [], loading: false, error: error.message || "亚马逊官方分类读取失败", open: true };
    }
    if (state.view === "edit" && state.editing?.id === draft.id) renderCategorySearchResults();
  }

  function productTypeMatchHtml() {
    const currentCountry = state.editLocale || state.editing?.countries?.[0] || "";
    if ((amazonProductTypeMatch.draftId && amazonProductTypeMatch.draftId !== state.editing?.id) || (amazonProductTypeMatch.country && amazonProductTypeMatch.country !== currentCountry)) return '<div class="category-source-meta" id="amazonProductTypeMatch">自动判断优先使用源站 Product Type、原始分类路径和商品语义；目标语言标题只作辅助。</div>';
    if (amazonProductTypeMatch.loading) return '<div class="product-type-dropdown open" id="amazonProductTypeMatch"><div class="product-type-message">正在按源站分类信息核对 Amazon 官方商品类型…</div></div>';
    if (amazonProductTypeMatch.error) return `<div class="product-type-dropdown open" id="amazonProductTypeMatch"><div class="product-type-message error-text">${esc(amazonProductTypeMatch.error)}</div></div>`;
    if (!amazonProductTypeMatch.open || !amazonProductTypeMatch.items.length) return '<div class="category-source-meta" id="amazonProductTypeMatch">已解析类型与手动搜索候选严格区分；输入关键词按回车只会展示候选，不会自动覆盖。</div>';
    return `<div class="product-type-dropdown open" id="amazonProductTypeMatch"><div class="product-type-result-count">找到 ${amazonProductTypeMatch.items.length} 个匹配类型 · ${esc(countryName(currentCountry))}</div>${amazonProductTypeMatch.items.slice(0, 100).map(item => `<button type="button" class="product-type-option" data-action="select-product-type" data-product-type="${attr(item.name)}" data-product-type-label="${attr(item.displayName || item.name)}"><span><strong>${esc(item.displayName || item.name)}</strong><small>上传代码：${esc(item.name)}</small></span><em>选择</em></button>`).join("")}</div>`;
  }

  function renderProductTypeMatch() {
    const current = $("amazonProductTypeMatch");
    if (!current) return;
    const holder = document.createElement("div");
    holder.innerHTML = productTypeMatchHtml();
    current.replaceWith(holder.firstElementChild);
  }

  async function matchAmazonProductType(title, mode = "title") {
    const draft = state.editing;
    const query = clean(title);
    if (!draft || (mode === "keyword" && query.length < 2)) return;
    const country = state.editLocale || draft.countries?.[0] || activeStoreCountries()[0] || "";
    const storedResolution = draft.productTypeResolutions?.[country];
    if (mode === "title" && storedResolution?.productType && !storedResolution?.needsManualReview) return;
    if (amazonProductTypeMatch.draftId === draft.id && amazonProductTypeMatch.country === country && amazonProductTypeMatch.query === query && amazonProductTypeMatch.mode === mode && amazonProductTypeMatch.items.length) {
      if (mode === "keyword") {
        amazonProductTypeMatch.open = true;
        renderProductTypeMatch();
      }
      return;
    }
    const requestId = ++amazonProductTypeRequestId;
    amazonProductTypeMatch = { draftId: draft.id, country, query, mode, items: [], loading: true, error: "", recommended: "", open: true, requestId };
    renderProductTypeMatch();
    try {
      const response = mode === "title"
        ? await apiJson("/api-settings/product-type-resolution", {
            method: "POST", headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ product: clone(draft), countries: [country] })
          })
        : await apiJson(`/api-settings/product-types?country=${encodeURIComponent(country)}&mode=keyword&query=${encodeURIComponent(query)}`);
      const data = response?.data || {};
      const responseCountry = clean(data.country).toUpperCase();
      if (responseCountry && responseCountry !== country) throw new Error(`亚马逊返回站点 ${responseCountry}，与当前国家 ${country} 不一致`);
      if (mode === "title") applyProductTypeResolution(draft, data);
      const resolution = data.resolutions?.[country] || {};
      const missing = (data.missingFields || []).find(field => field.country === country && field.scope === "product_type");
      const items = mode === "title"
        ? (Array.isArray(resolution.candidates) && resolution.candidates.length
            ? resolution.candidates
            : (missing?.enum || []).map((name, index) => ({ name, displayName: missing.enumNames?.[index] || name })))
        : (Array.isArray(data.productTypes) ? data.productTypes : []);
      const recommended = mode === "title" ? clean(data.productTypes?.[country]).toUpperCase() : "";
      const recommendedLabel = mode === "title"
        ? clean(resolution.displayName || resolution.productType || recommended)
        : "";
      if (amazonProductTypeMatch.requestId === requestId) {
        amazonProductTypeMatch = { draftId: draft.id, country, query, mode, items, loading: false, error: "", recommended, open: mode === "keyword" || Boolean(data.needsManualReview), requestId };
      }
      if (mode === "title" && recommended && state.editing?.id === draft.id) {
        state.editing.amazonProductTypes = state.editing.amazonProductTypes || {};
        state.editing.amazonProductTypeLabels = state.editing.amazonProductTypeLabels || {};
        state.editing.amazonProductTypes[country] = recommended;
        state.editing.amazonProductTypeLabels[country] = recommendedLabel;
        if (country === state.editing.countries?.[0]) state.editing.amazonProductType = recommended;
        const visibleCountry = state.editLocale || state.editing.countries?.[0] || "";
        if (country === visibleCountry && $("editAmazonProductType")) {
          $("editAmazonProductType").value = recommendedLabel || recommended;
          $("editAmazonProductType").dataset.selectedProductType = recommended;
          $("editAmazonProductType").dataset.selectedProductTypeLabel = recommendedLabel;
        }
      }
    } catch (error) {
      if (amazonProductTypeMatch.requestId === requestId) {
        amazonProductTypeMatch = { draftId: draft.id, country, query, mode, items: [], loading: false, error: error.message || "亚马逊产品类型匹配失败", recommended: "", open: true, requestId };
      }
    }
    if (amazonProductTypeMatch.requestId === requestId && state.view === "edit" && state.editing?.id === draft.id) renderProductTypeMatch();
  }

  function captureDraftForm() {
    const draft = state.editing;
    if (!draft) return null;
    const editingCountry = state.editLocale || draft.countries?.[0] || activeStoreCountries()[0];
    draft.localizedContent = draft.localizedContent && typeof draft.localizedContent === "object" ? draft.localizedContent : {};
    const listing = emptyListing({
      title: $("editTitle")?.value,
      productHighlight: $("editHighlight")?.value,
      keywords: $("editKeywords")?.value,
      bulletPoints: [...document.querySelectorAll("[data-bullet]")].map(input => input.value.trim()),
      description: applyVariantFactsToDescription($("editDescription")?.value, draft.variantRows || [])
    });
    if (editingCountry) draft.localizedContent[editingCountry] = listing;
    draft.amazonProductTypes = draft.amazonProductTypes && typeof draft.amazonProductTypes === "object" ? draft.amazonProductTypes : {};
    draft.amazonProductTypeLabels = draft.amazonProductTypeLabels && typeof draft.amazonProductTypeLabels === "object" ? draft.amazonProductTypeLabels : {};
    draft.productTypeResolutions = draft.productTypeResolutions && typeof draft.productTypeResolutions === "object" ? draft.productTypeResolutions : {};
    if (editingCountry) {
      const previousType = clean(draft.amazonProductTypes[editingCountry]).toUpperCase();
      const typeInput = $("editAmazonProductType");
      const typedValue = clean(typeInput?.value);
      const selectedCode = clean(typeInput?.dataset.selectedProductType).toUpperCase();
      const selectedLabel = clean(typeInput?.dataset.selectedProductTypeLabel);
      const usesSelectedType = Boolean(selectedCode && (typedValue === selectedLabel || typedValue.toUpperCase() === selectedCode));
      const nextType = usesSelectedType ? selectedCode : typedValue.toUpperCase();
      if (nextType && nextType !== previousType) setManualProductType(draft, editingCountry, nextType, usesSelectedType ? selectedLabel : nextType);
      else draft.amazonProductTypes[editingCountry] = nextType;
      if (usesSelectedType && selectedLabel) draft.amazonProductTypeLabels[editingCountry] = selectedLabel;
      else if (previousType !== nextType || typedValue !== clean(draft.amazonProductTypeLabels[editingCountry])) delete draft.amazonProductTypeLabels[editingCountry];
    }
    draft.countries = [...document.querySelectorAll("[data-country]:checked")].map(input => input.value);
    if (!draft.countries.length) draft.countries = [activeStoreCountries()[0]].filter(Boolean);
    Object.keys(draft.localizedContent).forEach(code => { if (!draft.countries.includes(code)) delete draft.localizedContent[code]; });
    draft.brand = $("editBrand").value;
    draft.manufacturer = $("editManufacturer").value;
    draft.storeName = activeStore()?.name || $("editStore")?.value || draft.storeName || "";
    draft.inventory = num($("editInventory").value);
    draft.handlingTime = num($("editHandling").value);
    draft.sku = normalizeParentSku($("editSku")?.value || draft.sku);
    draft.condition = $("editCondition")?.value || "new";
    draft.variantRows = assignVariantSkus(draft.sku, draft.variantRows || []);
    draft.upcMode = document.querySelector("[name=upcMode]:checked")?.value || "exempt";
    draft.upc = draft.upcMode === "exempt" ? "" : $("editUpc").value.trim();
    draft.cost = num($("editCost").value);
    draft.shipping = num($("editShipping").value);
    draft.profitRate = num($("editProfit").value, 1);
    draft.countryPrices = draft.countryPrices && typeof draft.countryPrices === "object" ? draft.countryPrices : {};
    document.querySelectorAll("[data-country-price]").forEach(input => {
      const code = input.dataset.countryPrice;
      if (!code) return;
      const country = countryMeta(code);
      const foreign = Math.max(0, num(input.value));
      const rate = num(activeSettings()[country.rateKey], 1);
      draft.countryPrices[code] = { ...(draft.countryPrices[code] || {}), foreign, rmb: foreign * rate, currency: country.currency, manual: true };
    });
    applyVariantBaseShift(draft);
    const primaryListing = draft.localizedContent[draft.countries[0]] || listing;
    draft.title = primaryListing.title;
    draft.categoryPath = draft.collectedCategoryPath || draft.categoryPath || draft.category || "";
    draft.amazonProductType = clean(draft.amazonProductTypes[draft.countries[0]] || draft.amazonProductType).toUpperCase();
    draft.productHighlight = primaryListing.productHighlight;
    draft.keywords = primaryListing.keywords;
    draft.bulletPoints = primaryListing.bulletPoints;
    draft.description = primaryListing.description;
    draft.aiTool = authState.aiServices?.activeAiProvider || draft.aiTool || "deepseek";
    draft.updatedAt = nowIso();
    return draft;
  }

  function calculatePrices(draft = state.editing, force = false, applyVariantShift = true) {
    if (!draft) return;
    const settings = activeSettings();
    const currencyRates = { USD: settings.usdRate, CAD: settings.cadRate, MXN: settings.mxnRate, GBP: settings.gbpRate, EUR: settings.eurRate };
    let rmbPrice = (num(draft.cost) + num(draft.shipping)) * num(draft.profitRate, 1);
    if (!force && draft.pricingSource === "captured" && num(draft.price) > 0 && currencyRates[String(draft.currency || "").toUpperCase()]) {
      rmbPrice = num(draft.price) * num(currencyRates[String(draft.currency).toUpperCase()], 1);
    }
    if (force) draft.pricingSource = "manual";
    draft.countryPrices = draft.countryPrices && typeof draft.countryPrices === "object" ? draft.countryPrices : {};
    const selected = draft.countries?.length ? draft.countries : [activeStoreCountries()[0]].filter(Boolean);
    Object.keys(draft.countryPrices).forEach(code => { if (!selected.includes(code)) delete draft.countryPrices[code]; });
    selected.forEach(code => {
      const country = countryMeta(code);
      const rate = num(settings[country.rateKey], 1);
      const existing = draft.countryPrices[code] || {};
      if (!force && existing.manual === true && num(existing.foreign) >= 0) {
        draft.countryPrices[code] = { ...existing, rmb: num(existing.foreign) * rate, currency: country.currency, manual: true };
      } else {
        draft.countryPrices[code] = { rmb: rmbPrice, foreign: rate > 0 ? rmbPrice / rate : 0, currency: country.currency, manual: false };
      }
    });
    const primary = countryMeta(selected[0]);
    draft.rmbPrice = num(draft.countryPrices[selected[0]]?.rmb, rmbPrice);
    draft.foreignPrice = num(draft.countryPrices[selected[0]]?.foreign);
    if (applyVariantShift) applyVariantBaseShift(draft);
  }

  function counter(value, limit) {
    const length = String(value || "").length;
    return `<div class="counter ${length > limit ? "over" : ""}" data-counter>${length}/${limit}</div>`;
  }

  function textFieldFooter(targetId, value, limit = 0) {
    return `<div class="text-field-footer">${limit ? counter(value, limit) : "<span></span>"}<div class="text-field-actions"><button type="button" title="回退到打开编辑页时的内容" data-action="restore-text-field" data-target="${attr(targetId)}">↶</button><button type="button" title="复制内容" data-action="copy-text-field" data-target="${attr(targetId)}">⧉</button><button type="button" title="一键清空" data-action="clear-text-field" data-target="${attr(targetId)}">×</button></div></div>`;
  }

  function fieldOptimizeButton(field, editPending) {
    return `<button type="button" class="field-optimize-button" data-action="ai-optimize-field" data-ai-field="${attr(field)}" ${editPending ? "disabled" : ""}>内容优化</button>`;
  }

  function originalEditListing(locale = state.editLocale) {
    const session = state.editSessions[currentEditTabKey()];
    const original = session?.originalEditing;
    if (!original) return emptyListing();
    const code = locale || original.countries?.[0] || "";
    const localized = original.localizedContent?.[code];
    if (localized) return emptyListing(localized);
    return code === original.countries?.[0] ? emptyListing(original) : emptyListing();
  }

  function restoreTextField(targetId) {
    const draft = captureDraftForm();
    if (!draft) return;
    const code = state.editLocale || draft.countries?.[0] || "";
    const current = draft.localizedContent?.[code] || emptyListing(draft);
    const original = originalEditListing(code);
    const mapping = { editTitle: "title", editHighlight: "productHighlight", editKeywords: "keywords", editDescription: "description" };
    const bulletMatch = /^editBullet([0-4])$/.exec(targetId || "");
    if (bulletMatch) {
      const index = Number(bulletMatch[1]);
      current.bulletPoints = [...current.bulletPoints];
      current.bulletPoints[index] = original.bulletPoints[index] || "";
    } else if (mapping[targetId]) current[mapping[targetId]] = original[mapping[targetId]] || "";
    else return;
    draft.localizedContent[code] = emptyListing(current);
    if (code === draft.countries?.[0]) Object.assign(draft, draft.localizedContent[code]);
    renderEdit();
    toast("已回退到打开编辑页时的内容", "success");
  }

  function renderEdit() {
    const d = state.editing;
    if (!d) { setView("drafts"); return; }
    rememberEditorScroll();
    const editTabKeyValue = currentEditTabKey();
    const editPending = activeAiPending();
    const editingProduct = state.editingMode === "product";
    const allowedCountries = activeStoreCountries();
    // A collected locale is source content, not disposable store UI state.
    // Never silently replace DE content with the first configured store locale.
    d.countries = unique(d.countries || []);
    if (!d.countries.length && allowedCountries.length) d.countries = [allowedCountries[0]];
    d.storeId = d.storeId || authState.activeStoreId;
    d.localizedContent = d.localizedContent && typeof d.localizedContent === "object" ? d.localizedContent : {};
    if (!d.localizedContent[d.countries[0]]) d.localizedContent[d.countries[0]] = emptyListing(d);
    d.countries.forEach(code => { if (!d.localizedContent[code]) d.localizedContent[code] = emptyListing(); });
    if (!d.countries.includes(state.editLocale)) state.editLocale = d.countries[0] || "";
    const editContent = d.localizedContent[state.editLocale] || emptyListing(d);
    d.translations = normalizeTranslations(d.translations);
    calculatePrices(d);
    const primary = countryMeta(d.countries?.[0]);
    const priceRows = d.countries.map(code => {
      const country = countryMeta(code), price = d.countryPrices?.[code] || {};
      const profitValue = num(price.rmb) - num(d.cost) - num(d.shipping);
      return `<tr><td>${country.name}</td><td data-country-rmb="${code}">¥${num(price.rmb).toFixed(2)}</td><td><div class="foreign-price-editor"><span>${country.symbol}</span><input class="control" type="number" min="0" step="0.01" data-country-price="${code}" value="${attr(num(price.foreign).toFixed(2))}" aria-label="${attr(country.name)}外币售价"></div></td><td data-country-profit="${code}">¥${profitValue.toFixed(2)}</td></tr>`;
    }).join("");
    $("pageTitle").textContent = editingProduct ? "编辑产品" : "编辑草稿";
    $("breadcrumb").textContent = `${editingProduct ? "产品库" : "草稿库"} / ${d.sku} / 编辑产品`;
    $("collectProductBtn").classList.add("hidden");
    $("exportDataBtn").classList.add("hidden");
    $("returnViewBtn").classList.add("hidden");
    $("topSubmitBtn").classList.add("hidden");
    const translated = d.translations;
    const editingCountry = state.editLocale || d.countries?.[0] || allowedCountries[0] || "";
    d.amazonProductTypes = d.amazonProductTypes && typeof d.amazonProductTypes === "object" ? d.amazonProductTypes : {};
    d.amazonProductTypeLabels = d.amazonProductTypeLabels && typeof d.amazonProductTypeLabels === "object" ? d.amazonProductTypeLabels : {};
    if (!d.amazonProductTypes[d.countries?.[0]] && d.amazonProductType) d.amazonProductTypes[d.countries[0]] = clean(d.amazonProductType).toUpperCase();
    const currentProductType = clean(d.amazonProductTypes?.[editingCountry]).toUpperCase();
    const currentProductTypeLabel = clean(d.amazonProductTypeLabels?.[editingCountry]);
    const translationBox = (value, className = "") => `<div class="translated-box ${className}">${value ? escLines(value) : '<span class="translation-empty">点击“翻译为中文”后显示</span>'}</div>`;
    $("viewRoot").innerHTML = `<div class="edit-shell" data-edit-tab-key="${attr(editTabKeyValue)}" data-edit-view="edit">
      <div class="edit-heading-row"><div class="edit-page-return"><button class="btn" data-action="cancel-edit">← 返回${editingProduct ? "产品库" : "草稿库"}</button><button class="btn btn-primary" data-action="next-variants">下一步</button></div><div class="stepper"><div class="step active">1　产品信息编辑</div><div class="step">2　编辑变体</div></div></div>
      <div class="edit-card"><div class="section-title">基本信息</div>
        <div class="country-link-line"><div class="field country-select-field"><label>上架国家</label><div class="country-row country-selector">${COUNTRIES.filter(country => allowedCountries.includes(country.code)).map(country => `<label class="country-check"><input type="checkbox" data-country value="${country.code}" ${d.countries.includes(country.code) ? "checked" : ""}><span>${country.name}</span></label>`).join("")}</div></div><div class="field product-link-field"><label>产品链接</label><div class="add-line purchase-link-row"><input class="control purchase-link-input" value="${attr(d.purchaseUrl)}" title="${attr(d.purchaseUrl)}" readonly><button class="btn purchase-link-button" data-action="open-purchase">打开链接</button></div></div><div class="field upc-mode-field"><label>UPC方式</label><div class="country-row compact-upc-row"><label class="country-check"><input type="radio" name="upcMode" value="same" ${d.upcMode === "same" ? "checked" : ""}>使用原UPC</label><label class="country-check"><input type="radio" name="upcMode" value="replace" ${d.upcMode === "replace" ? "checked" : ""}>更换UPC</label><label class="country-check"><input type="radio" name="upcMode" value="exempt" ${d.upcMode === "exempt" ? "checked" : ""}>UPC豁免</label></div></div></div>
        <div class="basic-info-layout">
          <div class="compact-basic-grid">
            <div class="field compact-field"><label>品牌（默认设置）</label><input class="control" id="editBrand" value="${attr(activeSettings().brand || "Generic")}" readonly title="上传时严格使用当前 Amazon账号的产品默认信息"></div>
            <div class="field compact-field"><label>制造商（默认设置）</label><input class="control" id="editManufacturer" value="${attr(activeSettings().manufacturer || "Generic")}" readonly title="上传时严格使用当前 Amazon账号的产品默认信息"></div>
            <div class="field compact-field"><label>库存</label><input class="control" id="editInventory" type="number" min="0" value="${attr(d.inventory)}"></div>
            <div class="field compact-field"><label>物品状况</label><select class="control" id="editCondition">${[["new","New"],["used_like_new","Used - Like New"],["used_very_good","Used - Very Good"],["used_good","Used - Good"],["used_acceptable","Used - Acceptable"]].map(([value,label]) => `<option value="${value}" ${String(d.condition || "new") === value ? "selected" : ""}>${label}</option>`).join("")}</select></div>
            <div class="field compact-field"><label>SKU</label><input class="control" id="editSku" maxlength="40" value="${attr(normalizeParentSku(d.sku))}"></div>
            <div class="field compact-field"><label>处理时间</label><input class="control" id="editHandling" type="number" min="0" value="${attr(d.handlingTime)}"></div>
            <div class="field compact-field"><label>UPC编码</label><input class="control" id="editUpc" value="${attr(d.upc)}" placeholder="${d.upcMode === "replace" ? "新UPC" : "留空"}" ${d.upcMode === "exempt" ? "disabled" : ""}></div>
          </div>
        </div>
      </div>
      <div class="edit-card"><div class="section-title">售价计算</div>
        <div class="form-grid">
          <div class="field"><label><em>*</em> 成本</label><input class="control" id="editCost" type="number" min="0" step="0.01" value="${attr(d.cost)}"></div>
          <div class="field"><label><em>*</em> 运费</label><input class="control" id="editShipping" type="number" min="0" step="0.01" value="${attr(d.shipping)}"></div>
          <div class="field"><label><em>*</em> 利润率</label><input class="control" id="editProfit" type="number" min="0" step="0.01" value="${attr(d.profitRate)}"></div>
          <div class="field"><label>&nbsp;</label><button class="btn btn-primary" data-action="calculate-price">计算售价</button></div>
        </div>
        <div class="country-price-table-wrap" id="countryPriceGrid"><table class="country-price-table"><thead><tr><th>国家</th><th>人民币售价</th><th>外币售价</th><th>利润</th></tr></thead><tbody>${priceRows}</tbody></table></div>
      </div>
      <div class="edit-card"><div class="section-title">产品描述</div>
        <div class="country-content-tabs">${d.countries.map(code => `<button class="country-content-tab ${code === state.editLocale ? "active" : ""}" data-action="switch-edit-locale" data-country-code="${code}">${countryName(code)}</button>`).join("")}</div>
        <div class="ai-toolbar">
          <button class="btn" data-action="translate" ${editPending ? "disabled" : ""}>翻译</button><button class="btn btn-primary" data-action="ai-optimize" ${editPending ? "disabled" : ""}>一键优化</button><button class="btn" data-action="translate-cn" ${editPending ? "disabled" : ""}>翻译为中文</button><button class="btn" data-action="dedupe-title" ${editPending ? "disabled" : ""}>字段去重</button>
          <div class="active-service-pills"><span>AI：${esc(AI_TOOLS[authState.aiServices?.activeAiProvider]?.name || "未配置")}</span><span>翻译：${esc(authState.aiServices?.translation?.[authState.aiServices?.activeTranslationProvider]?.name || "未配置")}</span></div>
        </div>
        ${editPending ? `<div class="ai-running"><span class="spinner"></span>${esc(editPending.toolName || "服务商")} 正在处理，请稍候。</div>` : ""}
        <div class="bilingual-head"><span>上架文案 / 原文</span><span>中文翻译</span></div>
        <div class="text-row"><div class="text-label"><span>产品名称</span>${fieldOptimizeButton("title", editPending)}</div><div class="bilingual-fields"><div><textarea class="control" id="editTitle" rows="2" maxlength="75">${esc(editContent.title)}</textarea>${textFieldFooter("editTitle", editContent.title, 75)}</div>${translationBox(translated.title)}</div></div>
        <div class="text-row"><div class="text-label">产品亮点</div><div class="bilingual-fields"><div><textarea class="control" id="editHighlight" rows="2" maxlength="125" placeholder="AI会将原长标题拆分后，把补充卖点回填到这里">${esc(editContent.productHighlight)}</textarea>${textFieldFooter("editHighlight", editContent.productHighlight, 125)}</div>${translationBox(translated.productHighlight)}</div></div>
        <div class="text-row"><div class="text-label">产品类型</div><div class="category-select-wrap product-type-combobox"><div class="product-type-search-box"><input class="control" id="editAmazonProductType" value="${attr(currentProductTypeLabel || currentProductType)}" data-selected-product-type="${attr(currentProductType)}" data-selected-product-type-label="${attr(currentProductTypeLabel)}" placeholder="输入产品类型或关键词，按回车搜索"><button class="product-type-search-button" type="button" data-action="search-product-type" title="搜索产品类型">⌕</button></div>${productTypeMatchHtml()}</div></div>
        <div class="text-row"><div class="text-label">关键词</div><div class="bilingual-fields"><div><textarea class="control" id="editKeywords" rows="3" maxlength="500">${esc(editContent.keywords)}</textarea>${textFieldFooter("editKeywords", editContent.keywords, 500)}</div>${translationBox(translated.keywords)}</div></div>
        <div class="text-row"><div class="text-label"><span>产品五点</span>${fieldOptimizeButton("bulletPoints", editPending)}</div><div class="bilingual-fields"><div class="five-points">${editContent.bulletPoints.slice(0,5).map((point, index) => `<div class="point-row"><div><textarea class="control" id="editBullet${index}" data-bullet rows="2" maxlength="500">${esc(point)}</textarea>${textFieldFooter(`editBullet${index}`, point, 500)}</div></div>`).join("")}</div><div class="translated-points">${translated.bulletPoints.map(point => `<div class="translated-point"><div>${point ? esc(point) : '<span class="translation-empty">等待翻译</span>'}</div></div>`).join("")}</div></div></div>
        <div class="text-row"><div class="text-label"><span>产品描述</span>${fieldOptimizeButton("description", editPending)}</div><div class="bilingual-fields"><div><textarea class="control" id="editDescription" rows="12" maxlength="2000">${escLines(editContent.description)}</textarea>${textFieldFooter("editDescription", editContent.description, 2000)}</div>${translationBox(translated.description, "translated-description")}</div></div>
      </div>
      <div class="edit-card"><div class="section-title">图片管理</div>
        <div class="image-grid" id="mainImageGrid">${renderImageCards(d.images)}<button class="upload-card" data-action="upload-main-images"><span>▧<br>请上传</span></button></div>
      </div>
      <div class="edit-footer"><button class="btn" data-action="cancel-edit">返回${editingProduct ? "产品库" : "草稿库"}</button><button class="btn btn-primary" data-action="next-variants">下一步【编辑变体】</button></div>
    </div>`;
    queueMicrotask(() => {
      enhanceSelects(document);
      if (!currentProductType || !currentProductTypeLabel) {
        matchAmazonProductType(editContent.title || d.title || d.originalTitle, "title");
      }
      restoreEditorScroll(editTabKeyValue, "edit");
    });
  }

  function renderImageCards(images) {
    return images.map((url, index) => `<div class="image-card" data-image-index="${index}" draggable="true" title="按住图片拖动可调整顺序"><div class="image-stage"><img src="${attr(safeImage(url))}" alt="" data-measure-image draggable="false"><div class="image-overlay"><button title="放大查看" data-action="preview-image" data-index="${index}">⌕</button><button title="复制图片" data-action="copy-image" data-index="${index}">⧉</button><button title="删除" data-action="delete-image" data-index="${index}">×</button></div></div><div class="image-meta"><span>图片 ${index + 1}</span><span data-image-size>读取尺寸…</span></div></div>`).join("");
  }

  function reorderMainImages(fromIndex, toIndex) {
    const images = state.editing?.images;
    if (!Array.isArray(images) || fromIndex < 0 || toIndex < 0 || fromIndex >= images.length || toIndex >= images.length || fromIndex === toIndex) return false;
    const [moved] = images.splice(fromIndex, 1);
    images.splice(toIndex, 0, moved);
    return true;
  }

  function reorderVariantImages(rowId, fromIndex, toIndex) {
    const row = state.variantSession?.rows?.find(item => String(item.id) === String(rowId));
    const images = row?.images;
    if (!Array.isArray(images) || fromIndex < 0 || toIndex < 0 || fromIndex >= images.length || toIndex >= images.length || fromIndex === toIndex) return false;
    const [moved] = images.splice(fromIndex, 1);
    images.splice(toIndex, 0, moved);
    return true;
  }

  function aiSourcePayload(draft, locale = state.editLocale, fieldScopes = []) {
    const code = locale || draft.countries?.[0];
    const listing = draft.localizedContent?.[code] || emptyListing(draft);
    const scopes = normalizeAiFieldScopes(fieldScopes);
    const allFields = !scopes.length;
    const source = {
      ...(allFields || scopes.includes("title") ? { title: listing.title || draft.originalTitle || "" } : {}),
      ...(allFields ? { productHighlight: listing.productHighlight || "", keywords: listing.keywords || "" } : {}),
      ...(allFields || scopes.includes("bulletPoints") ? { bulletPoints: (listing.bulletPoints || []).slice(0, 5).filter(Boolean) } : {}),
      ...(allFields || scopes.includes("description") ? { description: listing.description || "" } : {}),
      ...(allFields ? { colors: (draft.colors || []).filter(Boolean), sizes: (draft.sizes || []).filter(Boolean), category: draft.category || "" } : {}),
      brandNameToRemove: draft.sourceBrand || ""
    };
    return Object.fromEntries(Object.entries(source).filter(([, value]) => Array.isArray(value) ? value.length : Boolean(value)));
  }

  function normalizeAiFieldScopes(value) {
    const allowed = new Set(["title", "bulletPoints", "description"]);
    return [...new Set(Array.isArray(value) ? value : [value])].filter(field => allowed.has(field));
  }

  function buildAiPrompt(mode, draft, locale = state.editLocale, fieldScopes = []) {
    const code = locale || draft.countries?.[0] || "";
    const country = COUNTRIES.find(item => item.code === code) || COUNTRIES[0];
    const scopes = normalizeAiFieldScopes(fieldScopes);
    const source = JSON.stringify(aiSourcePayload(draft, country.code, scopes));
    const allFields = !scopes.length;
    const listingSchema = allFields
      ? { title: "", productHighlight: "", keywords: "", bulletPoints: ["", "", "", "", ""], description: "" }
      : Object.fromEntries(scopes.map(field => [field, field === "bulletPoints" ? ["", "", "", "", ""] : ""]));
    const schema = JSON.stringify({ targetLocale: country.code, listing: listingSchema });
    const scopeInstruction = allFields ? "优化全部文案字段" : `只优化这些字段：${scopes.join("、")}；未请求字段不要返回`;
    const common = `你是亚马逊商品文案专家。当前目标站点代码必须是 ${country.code}，目标国家是${country.name}，所有面向买家的字段必须使用${country.language}，绝不能改写为其他语言。${scopeInstruction}。仅依据source优化文案，不得虚构信息。删除brandNameToRemove品牌。title≤75字符；productHighlight≤125字符且不与标题重复；keywords≤500字符；bulletPoints固定5条且每条≤500字符；description≤2000字符，必须使用简短介绍段落，再把不同属性按“字段名: 内容;”逐行输出，字段之间必须使用换行符\\n，不要把全部内容连成一段，不要Markdown。只输出合法JSON，不要解释。JSON格式：${schema}。source=${source}`;
    return common;
  }

  function repairAiJsonText(jsonText) {
    let repaired = "";
    let inString = false;
    let escaped = false;
    for (let index = 0; index < jsonText.length; index++) {
      const char = jsonText[index];
      if (!inString) {
        if (char === '"') inString = true;
        repaired += char;
        continue;
      }
      if (escaped) {
        repaired += char;
        escaped = false;
        continue;
      }
      if (char === "\\") {
        repaired += char;
        escaped = true;
        continue;
      }
      if (char === "\n" || char === "\r") {
        repaired += "\\n";
        continue;
      }
      if (char === '"') {
        let cursor = index + 1;
        while (/\s/.test(jsonText[cursor] || "")) cursor++;
        const next = jsonText[cursor] || "";
        if (next && !/[,:}\]]/.test(next)) {
          repaired += '\\"';
          continue;
        }
        inString = false;
      }
      repaired += char;
    }
    return repaired.replace(/,\s*([}\]])/g, "$1");
  }

  function readBalancedAiJson(text, start) {
    let depth = 0;
    let inString = false;
    let escaped = false;
    for (let index = start; index < text.length; index++) {
      const char = text[index];
      if (inString) {
        if (escaped) {
          escaped = false;
        } else if (char === "\\") {
          escaped = true;
        } else if (char === '"') {
          inString = false;
        }
        continue;
      }
      if (char === '"') {
        inString = true;
      } else if (char === "{") {
        depth++;
      } else if (char === "}") {
        depth--;
        if (depth === 0) return { jsonText: text.slice(start, index + 1), end: index + 1 };
      }
    }
    return null;
  }

  function isExpectedAiPayload(value) {
    if (!value || typeof value !== "object" || Array.isArray(value)) return false;
    return ["listing", "locales", "chinese", "title", "productHighlight", "keywords", "bulletPoints", "description"]
      .some(key => Object.prototype.hasOwnProperty.call(value, key));
  }

  function extractAiJson(value) {
    if (value && typeof value === "object") return value;
    const text = String(value || "").replace(/```(?:json)?/gi, "").replace(/```/g, "").trim();
    if (!text.includes("{")) throw new Error("AI 返回内容中没有找到 JSON");

    try {
      return JSON.parse(text);
    } catch {
      // 服务商偶尔会在正确 JSON 后重复输出 JSON 或附加说明，下面逐段提取。
    }

    let cursor = 0;
    let firstParsed = null;
    let lastError = null;
    while (cursor < text.length) {
      const start = text.indexOf("{", cursor);
      if (start < 0) break;
      const candidate = readBalancedAiJson(text, start);
      if (!candidate) break;
      cursor = candidate.end;
      for (const jsonText of [candidate.jsonText, repairAiJsonText(candidate.jsonText)]) {
        try {
          const parsed = JSON.parse(jsonText);
          if (!firstParsed) firstParsed = parsed;
          if (isExpectedAiPayload(parsed)) return parsed;
          break;
        } catch (error) {
          lastError = error;
        }
      }
    }
    if (firstParsed) return firstParsed;

    // 兼容内部引号或换行损坏、导致括号扫描无法完整闭合的旧服务商响应。
    const start = text.indexOf("{");
    const end = text.lastIndexOf("}");
    if (start >= 0 && end > start) {
      try {
        return JSON.parse(repairAiJsonText(text.slice(start, end + 1)));
      } catch (error) {
        lastError = error;
      }
    }
    throw new Error(`AI返回格式不完整，已自动修复但仍无法解析：${lastError?.message || "未知格式错误"}`);
  }

  function applyListingResult(listing, draft = state.editing, locale = state.editLocale, fieldScopes = []) {
    if (!listing || typeof listing !== "object") return;
    const d = draft;
    if (!d) return;
    const code = locale || d.countries?.[0];
    const current = d.localizedContent?.[code] || emptyListing(d);
    const scopes = normalizeAiFieldScopes(fieldScopes);
    const includes = field => !scopes.length || scopes.includes(field);
    const currentPoints = Array.isArray(current.bulletPoints) ? current.bulletPoints : [];
    const returnedPoints = Array.isArray(listing.bulletPoints) ? listing.bulletPoints.map(clean).filter(Boolean) : [];
    // A tab bridge can observe a partial AI response.  Never allow that
    // incomplete response to erase previously captured product bullets.
    const points = Array.isArray(listing.bulletPoints) && returnedPoints.length >= currentPoints.filter(point => clean(point)).length
      ? listing.bulletPoints
      : currentPoints;
    const next = emptyListing(current);
    if (includes("title")) next.title = stripBrand(listing.title || current.title, d.sourceBrand).slice(0, 75);
    if (!scopes.length) {
      next.productHighlight = stripBrand(listing.productHighlight || current.productHighlight, d.sourceBrand).slice(0, 125);
      next.keywords = stripBrand(listing.keywords || current.keywords, d.sourceBrand).slice(0, 500);
    }
    if (includes("bulletPoints")) next.bulletPoints = emptyListing({ bulletPoints: points.map(point => stripBrand(point, d.sourceBrand).slice(0, 500)) }).bulletPoints;
    if (includes("description")) next.description = formatProductDescription(applyVariantFactsToDescription(stripBrand(listing.description || current.description, d.sourceBrand, true).slice(0, 2000), d.variantRows || []));
    d.localizedContent = d.localizedContent || {};
    d.localizedContent[code] = next;
    if (code === d.countries?.[0]) Object.assign(d, next);
  }

  function assertAiTargetLocale(result, expectedLocale) {
    const returned = clean(result?.targetLocale).toUpperCase();
    // Older page-tab providers may omit targetLocale. The pending task itself is
    // already bound to one editor locale, so only an explicit conflicting locale
    // is unsafe; a missing marker can still be filled into that bound locale.
    if (returned && expectedLocale && returned !== expectedLocale) {
      throw new Error(`AI 返回站点 ${returned} 与当前编辑站点 ${expectedLocale} 不一致，原文案未覆盖`);
    }
  }

  function applyChineseResult(chinese, draft = state.editing) {
    if (!chinese || typeof chinese !== "object") return;
    if (draft) draft.translations = normalizeTranslations(chinese);
  }

  function dedupeCurrentTitle() {
    const draft = captureDraftForm();
    if (!draft) return;
    const code = state.editLocale || draft.countries?.[0] || "";
    const listing = draft.localizedContent?.[code];
    if (!listing) return;
    const result = dedupeCompleteEnglishWords(listing.title);
    if (!result.removed) {
      toast("当前产品名称没有重复的完整单词", "info");
      return;
    }
    listing.title = result.value;
    if (code === draft.countries?.[0]) draft.title = listing.title;
    renderEdit();
    toast(`已从产品名称移除 ${result.removed} 个重复单词`, "success");
  }

  async function recordBillableAiClick(type, eventId, draft = state.editing) {
    if (authState.user?.aiCallMode !== "internal") return null;
    return (await apiJson("/billing/events", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ eventId, type, sku: clean(draft?.sku) })
    }))?.data || null;
  }

  async function runAiTask(mode, fieldScope = "") {
    captureDraftForm();
    const d = state.editing;
    if (!d || activeAiPending()) return;
    const tabKey = currentEditTabKey();
    const editLocale = state.editLocale || d.countries?.[0] || "";
    const taskId = uid("ai");
    const provider = authState.aiServices?.activeAiProvider || "deepseek";
    const tool = AI_TOOLS[provider] || AI_TOOLS.deepseek;
    const fieldScopes = normalizeAiFieldScopes(fieldScope);
    const pending = setAiPending({ taskId, mode, fieldScopes, tool: provider, toolName: tool.name, editingId: d.id, editLocale }, tabKey);
    renderEdit();
    if (authState.aiServices?.aiApiEnabled === false) {
      try {
        await sendExtensionMessage({
          type: "RUN_AI_TASK",
          task: { id: taskId, prompt: buildAiPrompt(mode, d, editLocale, fieldScopes), tool: provider, conversationTitle: "易联跨境商品优化", targetLocale: editLocale }
        });
        if (!findAiPending(taskId)) return;
        toast(`已交给 ${tool.name} 后台页签处理`, "info");
      } catch (error) {
        if (!findAiPending(taskId)) return;
        clearAiPending(pending);
        renderPendingEdit(pending);
        toast(`无法启动 AI 页签：${error.message}`, "error");
      }
      return;
    }
    try {
      await recordBillableAiClick("ai_optimize", taskId, d);
    } catch (error) {
      clearAiPending(pending); renderPendingEdit(pending);
      toast(`本次优化未开始：${error.message}`, "error"); return;
    }
    const controller = createAiTaskAbortController(taskId);
    try {
      const response = await apiJson("/ai-services/optimize", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ mode, prompt: buildAiPrompt(mode, d, editLocale, fieldScopes) }), signal: controller.signal
      });
      if (controller.signal.aborted || !findAiPending(taskId)) return;
      const result = extractAiJson(response?.data?.text);
      const context = pendingEditContext(pending);
      if (context) {
        assertAiTargetLocale(result, pending.editLocale);
        applyListingResult(result.listing || result, context.editing, pending.editLocale, pending.fieldScopes);
      }
      clearAiPending(pending);
      renderPendingEdit(pending);
      toast(`${response?.data?.providerName || tool.name} 优化完成并已回填`, "success");
    } catch (error) {
      if (controller.signal.aborted || !findAiPending(taskId)) return;
      clearAiPending(pending);
      renderPendingEdit(pending);
      toast(`AI调用失败：${error.message}`, "error");
    } finally {
      releaseAiTaskAbortController(taskId);
    }
  }

  function openTranslationConfirm() {
    const d = captureDraftForm();
    if (!d || activeAiPending() || d.countries.length < 2) {
      if (d?.countries?.length < 2) toast("请至少选择两个国家后再翻译填充", "info");
      return;
    }
    const source = state.editLocale && d.countries.includes(state.editLocale) ? state.editLocale : d.countries[0];
    const content = d.localizedContent[source] || emptyListing(d);
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal translation-confirm-modal"><div class="modal-head"><div><h2>翻译文本</h2><p>以一个国家的文案为原文，由“AI与翻译”页面当前生效的翻译 API 填充其他国家。</p></div><button class="modal-close" data-action="close-modal">×</button></div>
      <div class="modal-body translation-preview">
        <div class="translation-preview-row"><strong>产品名称</strong><textarea class="control" rows="2" readonly>${escLines(content.title)}</textarea></div>
        <div class="translation-preview-row"><strong>产品亮点</strong><textarea class="control" rows="2" readonly>${escLines(content.productHighlight)}</textarea></div>
        <div class="translation-preview-row"><strong>关键词</strong><textarea class="control" rows="3" readonly>${escLines(content.keywords)}</textarea></div>
        <div class="translation-preview-row"><strong>产品五点</strong><textarea class="control" rows="5" readonly>${escLines(content.bulletPoints.filter(Boolean).join("\n"))}</textarea></div>
        <div class="translation-preview-row"><strong>产品描述</strong><textarea class="control" rows="5" readonly>${escLines(content.description)}</textarea></div>
        <div class="field"><label>源语言 / 国家</label><select class="control" id="translationSource">${d.countries.map(code => `<option value="${code}" ${code === source ? "selected" : ""}>${countryName(code)} · ${countryMeta(code).language}</option>`).join("")}</select></div>
      </div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="confirm-locale-translation">翻译并填充</button></div></div></div>`;
  }

  function buildLocaleTranslationPrompt(draft, sourceCode, targetCodes) {
    const source = draft.localizedContent[sourceCode] || emptyListing(draft);
    const targets = targetCodes.map(code => `${code}=${countryName(code)}(${countryMeta(code).language})`).join("、");
    return `你是亚马逊多站点商品翻译专家。把source中的商品文案准确翻译为每个目标国家的语言，保持事实、规格和五点数量，不得虚构信息。标题最多75字符，productHighlight最多125字符，keywords最多500字符，每条bulletPoints最多500字符，description最多2000字符。只输出严格JSON，不要Markdown或解释。JSON结构必须为：{"locales":{"目标国家代码":{"title":"","productHighlight":"","keywords":"","bulletPoints":["","","","",""],"description":""}}}。源国家：${sourceCode}=${countryName(sourceCode)}(${countryMeta(sourceCode).language})。目标：${targets}。source=${JSON.stringify(source)}`;
  }

  async function runLocaleTranslation(sourceCode) {
    const d = captureDraftForm();
    const targets = d.countries.filter(code => code !== sourceCode);
    if (!targets.length || activeAiPending()) return;
    const tabKey = currentEditTabKey();
    const taskId = uid("translation");
    const useTranslationApi = authState.aiServices?.translationApiEnabled !== false;
    const aiProvider = authState.aiServices?.activeAiProvider || "deepseek";
    const aiTool = AI_TOOLS[aiProvider] || AI_TOOLS.deepseek;
    const translationProviderName = authState.aiServices?.translation?.[authState.aiServices?.activeTranslationProvider]?.name || "翻译 API";
    const providerName = useTranslationApi ? translationProviderName : aiTool.name;
    const requestedTargets = useTranslationApi ? targets : targets.slice(0, 1);
    const pending = setAiPending({
      taskId, mode: "translate-locales", tool: useTranslationApi ? "translation" : aiProvider,
      toolName: providerName, sourceCode, targets: requestedTargets,
      remainingTargets: useTranslationApi ? [] : targets.slice(1), completedTargets: [], editingId: d.id
    }, tabKey);
    closeModal();
    renderEdit();
    if (!useTranslationApi) {
      try {
        await sendExtensionMessage({
          type: "RUN_AI_TASK",
          task: {
            id: taskId, prompt: buildLocaleTranslationPrompt(d, sourceCode, requestedTargets),
            tool: aiProvider, conversationTitle: "易联跨境多站点翻译",
            keepAiTabActive: pending.remainingTargets.length > 0
          }
        });
        if (!findAiPending(taskId)) return;
        toast(`已交给 ${aiTool.name} 后台页签翻译`, "info");
      } catch (error) {
        if (!findAiPending(taskId)) return;
        clearAiPending(pending);
        renderPendingEdit(pending);
        toast(`无法启动翻译页签：${error.message}`, "error");
      }
      return;
    }
    try {
      await recordBillableAiClick("ai_translate", taskId, d);
    } catch (error) {
      clearAiPending(pending); renderPendingEdit(pending);
      toast(`本次翻译未开始：${error.message}`, "error"); return;
    }
    const controller = createAiTaskAbortController(taskId);
    try {
      const source = d.localizedContent[sourceCode] || emptyListing(d);
      const texts = [source.title, source.productHighlight, source.keywords, ...source.bulletPoints.slice(0, 5), source.description];
      const localizedResults = {};
      for (const targetCode of targets) {
        const response = await apiJson("/ai-services/translate", {
          method: "POST", headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ texts, sourceLanguage: sourceCode, targetLanguage: targetCode }), signal: controller.signal
        });
        if (controller.signal.aborted || !findAiPending(taskId)) return;
        const translated = response?.data?.translations || [];
        if (translated.length !== texts.length) throw new Error("翻译服务返回字段数量不完整");
        localizedResults[targetCode] = emptyListing({
          title: translated[0], productHighlight: translated[1], keywords: translated[2],
          bulletPoints: translated.slice(3, 8), description: translated[8]
        });
      }
      const context = pendingEditContext(pending);
      if (context) {
        context.editing.localizedContent = context.editing.localizedContent || {};
        Object.assign(context.editing.localizedContent, localizedResults);
        const primary = context.editing.localizedContent[context.editing.countries[0]];
        if (primary) Object.assign(context.editing, primary);
      }
      clearAiPending(pending);
      renderPendingEdit(pending);
      toast(`${providerName} 已翻译并填充 ${targets.length} 个国家`, "success");
    } catch (error) {
      if (controller.signal.aborted || !findAiPending(taskId)) return;
      clearAiPending(pending);
      renderPendingEdit(pending);
      toast(`翻译 API 调用失败：${error.message}`, "error");
    } finally {
      releaseAiTaskAbortController(taskId);
    }
  }

  async function runGoogleChineseTranslation() {
    captureDraftForm();
    const d = state.editing;
    if (!d || activeAiPending()) return;
    const tabKey = currentEditTabKey();
    const taskId = uid("translation");
    const sourceCode = state.editLocale || d.countries?.[0] || "auto";
    const source = d.localizedContent?.[sourceCode] || emptyListing(d);
    const providerName = authState.aiServices?.translation?.[authState.aiServices?.activeTranslationProvider]?.name || "翻译 API";
    const pending = setAiPending({ taskId, mode: "translate-cn", tool: "translation", toolName: providerName, editingId: d.id }, tabKey);
    renderEdit();
    if (authState.aiServices?.translationApiEnabled === false) {
      const settings = activeSettings();
      const translationTool = normalizeTranslationTools(settings.translationTools)
        .find(item => item.id === settings.defaultTranslationToolId) || normalizeTranslationTools(settings.translationTools)[0];
      try {
        await sendExtensionMessage({ type: "RUN_TRANSLATION_TAB", taskId, content: source, translationTool });
        if (!findAiPending(taskId)) return;
        toast(`已打开 ${translationTool?.name || "翻译"} 后台页签`, "info");
      } catch (error) {
        if (!findAiPending(taskId)) return;
        clearAiPending(pending);
        renderPendingEdit(pending);
        toast(`无法启动翻译页签：${error.message}`, "error");
      }
      return;
    }
    try {
      await recordBillableAiClick("translate_cn", taskId, d);
    } catch (error) {
      clearAiPending(pending); renderPendingEdit(pending);
      toast(`本次中文翻译未开始：${error.message}`, "error"); return;
    }
    const controller = createAiTaskAbortController(taskId);
    try {
      const texts = [source.title, source.productHighlight, source.keywords, ...source.bulletPoints.slice(0, 5), source.description];
      const response = await apiJson("/ai-services/translate", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ texts, sourceLanguage: sourceCode, targetLanguage: "zh-CN" }), signal: controller.signal
      });
      if (controller.signal.aborted || !findAiPending(taskId)) return;
      const translated = response?.data?.translations || [];
      if (translated.length !== texts.length) throw new Error("翻译服务返回字段数量不完整");
      const context = pendingEditContext(pending);
      if (context) applyChineseResult({ title: translated[0], productHighlight: translated[1], keywords: translated[2], bulletPoints: translated.slice(3, 8), description: translated[8] }, context.editing);
      clearAiPending(pending);
      renderPendingEdit(pending);
      toast(`${response?.data?.providerName || providerName} 中文翻译已回填`, "success");
    } catch (error) {
      if (controller.signal.aborted || !findAiPending(taskId)) return;
      clearAiPending(pending);
      renderPendingEdit(pending);
      toast(`翻译 API 调用失败：${error.message}`, "error");
    } finally {
      releaseAiTaskAbortController(taskId);
    }
  }

  function receiveGoogleTranslation(message) {
    const pending = findAiPending(message.taskId);
    if (!pending || pending.tool !== "translation" || message.taskId !== pending.taskId) return;
    const toolName = pending.toolName || "翻译软件";
    clearAiPending(pending);
    if (!message.ok) {
      renderPendingEdit(pending);
      toast(`${toolName}翻译失败：${message.error || "没有读取到译文"}`, "error");
      return;
    }
    const context = pendingEditContext(pending);
    if (context) applyChineseResult(message.result, context.editing);
    renderPendingEdit(pending);
    toast(`${toolName}中文翻译已自动回填到右侧`, "success");
  }

  async function receiveAiResult(message) {
    const pending = findAiPending(message.taskId);
    if (!pending || message.taskId !== pending.taskId) return;
    const mode = pending.mode;
    const context = pendingEditContext(pending);
    if (!context) {
      clearAiPending(pending);
      toast("原编辑页签已关闭，任务结果未回填", "error");
      return;
    }
    if (!message.ok) {
      clearAiPending(pending);
      renderPendingEdit(pending);
      toast(`AI处理失败：${message.error || "没有取得结果"}。AI页面已保留，可重试。`, "error");
      return;
    }
    try {
      const result = extractAiJson(message.result || message.text);
      if (mode === "translate-locales") {
        const locales = Object.fromEntries(Object.entries(result.locales || {}).map(([code, value]) => [clean(code).toUpperCase(), value]));
        const targets = pending.targets || Object.keys(locales);
        const missing = targets.filter(code => !locales[code]);
        if (missing.length) throw new Error(`AI没有返回${missing.map(countryName).join("、")}的翻译内容`);
        context.editing.localizedContent = context.editing.localizedContent || {};
        targets.forEach(code => {
          if (locales[code]) context.editing.localizedContent[code] = emptyListing(locales[code]);
        });
        const primary = context.editing.localizedContent[context.editing.countries[0]];
        if (primary) Object.assign(context.editing, primary);
        const completedTargets = unique([...(pending.completedTargets || []), ...targets]);
        const remainingTargets = pending.remainingTargets || [];
        if (remainingTargets.length) {
          const nextTarget = remainingTargets[0];
          const nextTaskId = uid("translation");
          const nextPending = setAiPending({
            ...pending, taskId: nextTaskId, targets: [nextTarget],
            remainingTargets: remainingTargets.slice(1), completedTargets
          }, pending.tabKey);
          renderPendingEdit(nextPending);
          await sendExtensionMessage({
            type: "RUN_AI_TASK",
            task: {
              id: nextTaskId,
              prompt: buildLocaleTranslationPrompt(context.editing, pending.sourceCode, [nextTarget]),
              tool: pending.tool, conversationTitle: "易联跨境多站点翻译",
              keepAiTabActive: nextPending.remainingTargets.length > 0
            }
          });
          return;
        }
        clearAiPending(pending);
        applyChineseResult(result.chinese, context.editing);
        renderPendingEdit(pending);
        toast(`AI已翻译并填充 ${completedTargets.length} 个国家`, "success");
        return;
      } else if (mode !== "translate-cn") {
        assertAiTargetLocale(result, pending.editLocale);
        applyListingResult(result.listing || result, context.editing, pending.editLocale, pending.fieldScopes);
      }
      clearAiPending(pending);
      applyChineseResult(result.chinese, context.editing);
      renderPendingEdit(pending);
      toast("AI内容已生成并自动回填", "success");
    } catch (error) {
      clearAiPending(pending);
      renderPendingEdit(pending);
      toast(`AI结果回填失败：${error.message}`, "error");
    }
  }

  function capturedCurrencyRate(currency) {
    const settings = activeSettings();
    return num({ USD: settings.usdRate, CAD: settings.cadRate, MXN: settings.mxnRate, GBP: settings.gbpRate, EUR: settings.eurRate }[clean(currency).toUpperCase()] || 1);
  }

  function hasVariantOriginalPrice(row) {
    return row?.originalPrice !== "" && row?.originalPrice != null && Number.isFinite(Number(row.originalPrice));
  }

  function primaryVariantCountry(draft) {
    return countryMeta(draft?.countries?.[0] || activeStoreCountries()[0]);
  }

  function productBaseRmb(draft) {
    const country = primaryVariantCountry(draft);
    const price = draft?.countryPrices?.[country.code];
    if (price && Number.isFinite(Number(price.rmb))) return num(price.rmb);
    if (price && Number.isFinite(Number(price.foreign))) return num(price.foreign) * capturedCurrencyRate(country.currency);
    if (Number.isFinite(Number(draft?.rmbPrice))) return num(draft.rmbPrice);
    return 0;
  }

  function variantOriginalRmb(draft, row) {
    if (!hasVariantOriginalPrice(row)) return null;
    const currency = clean(row.priceCurrency || row.currency).toUpperCase();
    if (!currency) return null;
    return num(row.originalPrice) * capturedCurrencyRate(currency);
  }

  function variantLatestRmb(draft, row) {
    const originalRmb = variantOriginalRmb(draft, row);
    return originalRmb == null ? null : originalRmb + num(row.priceDelta);
  }

  function variantPriceFromRmb(draft, value) {
    if (value == null) return null;
    const currency = primaryVariantCountry(draft).currency;
    return num(value) / (capturedCurrencyRate(currency) || 1);
  }

  function initializeVariantPricing(draft) {
    if (!draft || typeof draft !== "object") return;
    (draft.variantRows || []).forEach(row => {
      if (row.originalPrice == null && row.price != null) row.originalPrice = row.price;
      row.priceDelta = num(row.priceDelta);
    });
  }

  function initializeVariantPricingSession(draft) {
    if (!draft || typeof draft !== "object") return;
    initializeVariantPricing(draft);
    // 首次进入编辑器时，先由采集价/汇率计算实际基准，再建立本次会话锚点。
    // 不能把草稿初始化时的 rmbPrice: 0 当成用户调价前的价格。
    calculatePrices(draft, false, false);
    draft.variantBaseRmb = productBaseRmb(draft);
  }

  function applyVariantBaseShift(draft) {
    if (!draft || typeof draft !== "object") return 0;
    initializeVariantPricing(draft);
    const nextBase = productBaseRmb(draft);
    if (!Number.isFinite(Number(draft.variantBaseRmb))) {
      draft.variantBaseRmb = nextBase;
      return 0;
    }
    const previousBase = num(draft.variantBaseRmb, nextBase);
    const shift = Math.round((nextBase - previousBase) * 100) / 100;
    if (shift) {
      (draft.variantRows || []).forEach(row => { row.priceDelta = Math.round((num(row.priceDelta) + shift) * 100) / 100; });
      draft.variantPricingReviewed = true;
    }
    draft.variantBaseRmb = nextBase;
    return shift;
  }

  function shouldWriteVariantPricingV2(draft, rows) {
    if (num(draft?.variantPricingVersion) >= 2 || draft?.variantPricingReviewed) return true;
    return !(rows || []).some(row => num(row.priceDelta) !== 0);
  }

  function rebuildVariantRows(session) {
    const oldRows = new Map((session.rows || []).map(row => [`${row.color}|${row.size}`, row]));
    const colors = session.colors.length ? session.colors : [""];
    const sizes = session.sizes.length ? session.sizes : [""];
    session.rows = [];
    colors.forEach(color => sizes.forEach(size => {
      const key = `${color}|${size}`;
      session.rows.push(oldRows.get(key) || { id: uid("var"), color, size, originalPrice: 0, priceCurrency: "CNY", priceDelta: 0, images: [] });
    }));
    session.rows = sortVariantRows(session.rows);
  }

  function createVariantSession(draft, mode = "draft") {
    initializeVariantPricing(draft);
    const rows = Array.isArray(draft?.variantRows) ? sortVariantRows(clone(draft.variantRows)) : [];
    const session = {
      draftId: draft?.id || "",
      sourceMode: mode === "product" ? "product" : "draft",
      colors: clone(draft?.colors || []),
      sizes: clone(draft?.sizes || []),
      rows,
      batch: { type: "", option: "", increase: "", decrease: "" }
    };
    if (rows.length) {
      const seen = new Set();
      session.rows = rows.filter(row => {
        const key = row.asin || `${row.color || ""}|${row.size || ""}`;
        if (seen.has(key)) return false;
        seen.add(key);
        row.id = row.id || uid("var");
        row.images = Array.isArray(row.images) ? row.images.filter(Boolean) : [];
        if (row.originalPrice == null && row.price != null) row.originalPrice = row.price;
        row.priceCurrency = row.priceCurrency || row.currency || "";
        row.priceDelta = num(row.priceDelta);
        return true;
      });
      session.colors = [...new Set([...session.colors, ...session.rows.map(row => row.color)].filter(Boolean))];
      session.sizes = [...new Set([...session.sizes, ...session.rows.map(row => row.size)].filter(Boolean))];
    } else {
      const colors = session.colors.length ? session.colors : [""];
      const sizes = session.sizes.length ? session.sizes : [""];
      session.rows = [];
      colors.forEach(color => sizes.forEach(size => session.rows.push({
        id: uid("var"), color, size, originalPrice: 0,
        priceCurrency: "CNY", priceDelta: 0, images: []
      })));
    }
    return session;
  }

  function beginVariants() {
    const draft = captureDraftForm();
    calculatePrices(draft);
    // 编辑会话在最终“提交保存”前只能保留在内存中；关闭页签即可放弃本次修改。
    state.variantSession = createVariantSession(draft, state.editingMode);
    state.view = "variants";
    render();
  }

  function renderVariants() {
    const d = state.editing;
    const s = state.variantSession;
    if (!d || !s) { setView("drafts"); return; }
    rememberEditorScroll();
    const editTabKeyValue = currentEditTabKey();
    $("pageTitle").textContent = "编辑变体";
    $("breadcrumb").textContent = `${state.editingMode === "product" ? "产品库" : "草稿库"} / ${d.sku} / 编辑变体`;
    $("collectProductBtn").classList.add("hidden");
    $("exportDataBtn").classList.add("hidden");
    $("returnViewBtn").classList.add("hidden");
    $("topSubmitBtn").classList.add("hidden");
    const batch = s.batch || (s.batch = { type: "", option: "", increase: "", decrease: "" });
    const options = (values, selected = "") => `<option value="">请选择</option>${values.map(value => `<option value="${attr(value)}" ${selected === value ? "selected" : ""}>${esc(value)}</option>`).join("")}`;
    $("viewRoot").innerHTML = `<div class="edit-shell" data-edit-tab-key="${attr(editTabKeyValue)}" data-edit-view="variants">
      <div class="edit-heading-row"><div class="edit-page-return"><button class="btn" data-action="back-from-variants">← 上一步</button><button class="btn btn-primary" data-action="submit-product">提交保存</button></div><div class="stepper"><div class="step done">1　产品信息编辑</div><div class="step active">2　编辑变体</div></div></div>
      <div class="edit-card"><div class="section-title">产品变体</div>
        <div class="variant-builder compact-variant-builder">
          <div class="token-box"><h3>颜色</h3><div class="add-line"><input class="control" id="newColor" placeholder="输入颜色"><button class="btn btn-primary" data-action="add-token" data-type="color">添加</button></div><div class="tokens">${s.colors.map(value => `<span class="token">${esc(value)}<button data-action="remove-token" data-type="color" data-value="${attr(value)}">×</button></span>`).join("")}</div></div>
          <div class="token-box"><h3>尺寸</h3><div class="add-line"><input class="control" id="newSize" placeholder="输入尺寸"><button class="btn btn-primary" data-action="add-token" data-type="size">添加</button></div><div class="tokens">${s.sizes.map(value => `<span class="token">${esc(value)}<button data-action="remove-token" data-type="size" data-value="${attr(value)}">×</button></span>`).join("")}</div></div>
        </div>
        <div class="batch-bar">
          <div class="field"><label>类型</label><select class="control" id="batchType"><option value="">请选择类型</option><option value="color" ${batch.type === "color" ? "selected" : ""}>颜色</option><option value="size" ${batch.type === "size" ? "selected" : ""}>尺码</option></select></div>
          <div class="field"><label>对应信息</label><select class="control" id="batchOption">${batch.type ? options(batch.type === "color" ? s.colors : s.sizes, batch.option) : '<option value="">先选择类型</option>'}</select></div>
          <div class="field"><label>加价（人民币）</label><input class="control" id="batchIncrease" type="number" min="0" step="0.01" placeholder="例如 5" value="${attr(batch.increase)}"></div>
          <div class="field"><label>降价（人民币）</label><input class="control" id="batchDecrease" type="number" min="0" step="0.01" placeholder="例如 3" value="${attr(batch.decrease)}"></div>
          <button class="btn btn-primary" data-action="batch-price">批量设置</button>
        </div>
      </div>
      <div class="panel"><div class="table-wrap"><table class="variant-table"><thead><tr><th>颜色</th><th>尺寸</th><th>原价（采集币种）</th><th>加减价格（人民币）</th><th>最新价（${esc(primaryVariantCountry(d).currency)}）</th><th>图片</th><th>操作</th></tr></thead><tbody>
        ${s.rows.map(row => { const latestRmb = variantLatestRmb(d, row); const latest = variantPriceFromRmb(d, latestRmb); const sourceCurrency = clean(row.priceCurrency || row.currency).toUpperCase(); const original = hasVariantOriginalPrice(row) && sourceCurrency ? `${sourceCurrency} ${num(row.originalPrice).toFixed(2)}` : "待确认"; const latestText = latest == null ? "待确认" : `${primaryVariantCountry(d).symbol}${latest.toFixed(2)}`; return `<tr><td>${esc(row.color || "—")}</td><td>${esc(row.size || "—")}</td><td><span class="variant-price">${esc(original)}</span></td><td><input class="control variant-price adjust ${row.priceDelta > 0 ? "positive" : row.priceDelta < 0 ? "negative" : ""}" data-variant-price-delta data-row="${attr(row.id)}" type="number" step="0.01" value="${attr(num(row.priceDelta))}"></td><td><span class="variant-price latest" data-variant-latest="${attr(row.id)}">${esc(latestText)}</span></td>
          <td><div class="variant-images">${(row.images || []).slice(0,9).map((url, imageIndex) => `<div class="variant-image-card" data-row="${attr(row.id)}" data-variant-image-index="${imageIndex}" draggable="true" title="按住图片拖动可调整顺序"><img src="${attr(safeImage(url))}" alt="" draggable="false"><div class="variant-image-overlay"><button title="放大查看" data-action="preview-variant-image" data-row="${attr(row.id)}" data-index="${imageIndex}">⌕</button><button title="复制图片" data-action="copy-variant-image" data-row="${attr(row.id)}" data-index="${imageIndex}">⧉</button><button title="删除" data-action="delete-variant-image" data-row="${attr(row.id)}" data-index="${imageIndex}">×</button></div></div>`).join("")}<button class="upload-card btn-small" data-action="upload-variant-image" data-row="${attr(row.id)}">请上传</button></div></td>
          <td><div class="row-actions"><button class="link-btn" data-action="delete-variant" data-row="${attr(row.id)}">删除</button><button class="link-btn" data-action="copy-main-image" data-row="${attr(row.id)}">复制主图</button><button class="link-btn" data-action="copy-same-color" data-row="${attr(row.id)}">复制到同色图</button></div></td>
        </tr>`; }).join("")}
      </tbody></table></div></div>
    </div>`;
    restoreEditorScroll(editTabKeyValue, "variants");
  }

  function openCollectModal(prefill = "", force = false) {
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal"><div class="modal-head"><div><h2>${force ? "重新采集" : "链接采集"}</h2><p>输入亚马逊商品链接，直接创建后台采集任务</p></div><button class="modal-close" data-action="close-modal">×</button></div>
      <div class="modal-body"><div class="field"><label>产品链接</label><textarea class="control" id="collectUrls" rows="7" placeholder="每行输入一个亚马逊商品链接">${esc(prefill)}</textarea><div class="meta">插件会使用当前服务器账号和左侧所选店铺采集完整详情；支持一次输入多个链接，每行一个。</div></div><div id="collectProgress"></div></div>
      <div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="run-collection" data-force="${force ? "true" : "false"}">${force ? "重新采集" : "开始采集"}</button></div></div></div>`;
  }

  async function runCollection() {
    const urls = $("collectUrls").value.split(/\r?\n/).map(clean).filter(Boolean);
    if (!urls.length) { toast("请至少输入一个产品链接", "error"); return; }
    const button = document.querySelector('[data-action="run-collection"]');
    const force = button?.dataset.force === "true";
    button.disabled = true;
    let accepted = 0, duplicates = 0, failed = 0;
    try {
      await syncExtensionContext();
    } catch (error) {
      button.disabled = false;
      toast(`无法连接采集插件：${error.message}。请在扩展管理页面重新加载易联跨境插件。`, "error");
      return;
    }
    if (force) await sendExtensionMessage({ type: "CLEAR_CAPTURE_CACHE" });
    for (let index = 0; index < urls.length; index++) {
      $("collectProgress").innerHTML = `<div class="progress"><span class="spinner"></span>正在提交 ${index + 1} / ${urls.length} 个后台任务…</div>`;
      try {
        const response = await sendExtensionMessage({ type: force ? "RECOLLECT_URL" : "COLLECT_URL", url: urls[index], includeVariants: true });
        if (!response?.ok) throw new Error(response?.error || "扩展没有返回数据");
        response.duplicate ? duplicates++ : accepted++;
      } catch (error) {
        failed++;
        toast(`第 ${index + 1} 个商品失败：${error.message}`, "error");
      }
    }
    closeModal();
    render();
    toast(`任务已受理：新增 ${accepted}，重复 ${duplicates}${failed ? `，提交失败 ${failed}` : ""}；详情将在后台采集`, accepted || duplicates ? "success" : "error");
  }

  function closeModal() { $("modalRoot").innerHTML = ""; }

  function installLightboxControls() {
    const viewport = document.querySelector("[data-lightbox-content]");
    const image = viewport?.querySelector("[data-lightbox-image]");
    const zoomLabel = viewport?.querySelector("[data-lightbox-zoom]");
    if (!viewport || !image) return;
    let scale = 1, offsetX = 0, offsetY = 0, dragging = false, startX = 0, startY = 0;
    const apply = () => {
      image.style.transform = `translate3d(${offsetX}px, ${offsetY}px, 0) scale(${scale})`;
      image.dataset.zoom = scale.toFixed(2);
      viewport.classList.toggle("zoomed", scale !== 1);
      if (zoomLabel) zoomLabel.textContent = `${Math.round(scale * 100)}%`;
    };
    const setScale = next => {
      scale = Math.min(20, Math.max(.15, next));
      if (Math.abs(scale - 1) < .025) scale = 1;
      apply();
    };
    viewport.addEventListener("wheel", event => {
      event.preventDefault();
      setScale(scale * (event.deltaY < 0 ? 1.16 : 1 / 1.16));
    }, { passive: false });
    viewport.querySelector('[data-lightbox-command="out"]')?.addEventListener("click", event => { event.stopPropagation(); setScale(scale / 1.25); });
    viewport.querySelector('[data-lightbox-command="in"]')?.addEventListener("click", event => { event.stopPropagation(); setScale(scale * 1.25); });
    viewport.querySelector('[data-lightbox-command="fit"]')?.addEventListener("click", event => { event.stopPropagation(); scale = 1; offsetX = 0; offsetY = 0; apply(); });
    viewport.addEventListener("pointerdown", event => {
      if (event.button !== 0 || event.target.closest("button")) return;
      dragging = true;
      startX = event.clientX - offsetX;
      startY = event.clientY - offsetY;
      viewport.classList.add("dragging");
      viewport.setPointerCapture?.(event.pointerId);
    });
    viewport.addEventListener("pointermove", event => {
      if (!dragging) return;
      offsetX = event.clientX - startX;
      offsetY = event.clientY - startY;
      apply();
    });
    const stopDragging = event => {
      dragging = false;
      viewport.classList.remove("dragging");
      if (event?.pointerId != null) viewport.releasePointerCapture?.(event.pointerId);
    };
    viewport.addEventListener("pointerup", stopDragging);
    viewport.addEventListener("pointercancel", stopDragging);
    viewport.addEventListener("dblclick", event => {
      if (event.target.closest("button")) return;
      scale = 1; offsetX = 0; offsetY = 0; apply();
    });
    apply();
  }

  function previewImage(url) {
    $("modalRoot").innerHTML = `<div class="image-lightbox" data-action="close-modal"><div class="image-lightbox-inner" data-lightbox-content><img data-lightbox-image src="${attr(safeImage(url))}" alt=""><div class="image-lightbox-tools"><button type="button" data-lightbox-command="out" title="缩小">−</button><span data-lightbox-zoom>100%</span><button type="button" data-lightbox-command="in" title="放大">＋</button><button type="button" data-lightbox-command="fit" title="适应屏幕">适应</button></div><button class="image-lightbox-close" data-action="close-modal" aria-label="关闭">×</button></div></div>`;
    installLightboxControls();
  }

  async function imageBlobAsPng(blob) {
    if (blob.type === "image/png") return blob;
    const bitmap = await createImageBitmap(blob);
    const canvas = document.createElement("canvas");
    canvas.width = bitmap.width;
    canvas.height = bitmap.height;
    const context = canvas.getContext("2d");
    context.drawImage(bitmap, 0, 0);
    bitmap.close?.();
    return await new Promise((resolve, reject) => canvas.toBlob(result => result ? resolve(result) : reject(new Error("图片转换失败")), "image/png"));
  }

  async function copyRenderedImage(source) {
    const wrapper = document.createElement("div");
    wrapper.contentEditable = "true";
    wrapper.style.cssText = "position:fixed;left:-10000px;top:0;width:1px;height:1px;overflow:hidden";
    const image = document.createElement("img");
    image.src = source;
    wrapper.appendChild(image);
    document.body.appendChild(wrapper);
    try {
      await new Promise((resolve, reject) => {
        if (image.complete && image.naturalWidth) return resolve();
        const timer = setTimeout(() => reject(new Error("图片加载超时")), 4000);
        image.onload = () => { clearTimeout(timer); resolve(); };
        image.onerror = () => { clearTimeout(timer); reject(new Error("图片加载失败")); };
      });
      const selection = window.getSelection();
      const range = document.createRange();
      range.selectNode(image);
      selection.removeAllRanges();
      selection.addRange(range);
      const copied = document.execCommand("copy");
      selection.removeAllRanges();
      return copied;
    } finally {
      wrapper.remove();
    }
  }

  async function copyImageToClipboard(url) {
    const source = String(url || "").trim();
    if (!source) return;
    try {
      if (!navigator.clipboard?.write || typeof ClipboardItem === "undefined") throw new Error("当前环境不支持图片剪贴板");
      const response = await fetch(source);
      if (!response.ok) throw new Error(`图片读取失败（${response.status}）`);
      const blob = await response.blob();
      if (!blob.type.startsWith("image/")) throw new Error("链接内容不是图片");
      const png = await imageBlobAsPng(blob);
      await navigator.clipboard.write([new ClipboardItem({ "image/png": png })]);
      toast("图片已复制，可直接粘贴使用", "success");
    } catch (error) {
      let renderedCopied = false;
      try { renderedCopied = await copyRenderedImage(source); } catch {}
      if (renderedCopied) {
        toast("图片已复制，可直接粘贴使用", "success");
        return;
      }
      try {
        await navigator.clipboard.writeText(source);
        toast("图片来源限制了直接复制，已复制图片链接", "info");
      } catch {
        toast(error.message || "图片复制失败", "error");
      }
    }
  }

  async function aiEditImage(index) {
    if (state.view === "edit") captureDraftForm();
    const tool = AI_TOOLS[state.editing.aiTool] || AI_TOOLS.deepseek;
    const prompt = "优化图片质量";
    try { await navigator.clipboard.writeText(prompt); } catch {}
    window.open(tool.url, "_blank", "noopener,noreferrer");
    toast(`已打开 ${tool.name}，提示词“优化图片质量”已复制；浏览器不允许扩展自动替你粘贴本地图片`, "info");
  }

  async function uploadProductImageFile(file) {
    if (!file?.type?.startsWith("image/")) throw new Error("请选择 JPG、PNG 或 GIF 图片");
    if (file.size > 10 * 1024 * 1024) throw new Error("单张图片不能超过 10MB");
    const form = new FormData();
    form.append("file", file, file.name || `product-image-${Date.now()}`);
    const response = await apiJson("/product-images", { method: "POST", body: form });
    const url = clean(response?.data?.url);
    if (!url.toLowerCase().startsWith("https://")) throw new Error("服务器没有返回有效的 HTTPS 图片地址");
    return url;
  }

  async function publishStoredProductImages(product) {
    const cache = new Map();
    const publish = source => {
      const value = clean(source);
      if (!/^(?:data:image\/|blob:)/i.test(value)) return Promise.resolve(value);
      if (!cache.has(value)) {
        cache.set(value, (async () => {
          const response = await fetch(value);
          if (!response.ok) throw new Error(`本地图片读取失败（${response.status}）`);
          const blob = await response.blob();
          const extension = blob.type === "image/png" ? "png" : blob.type === "image/gif" ? "gif" : "jpg";
          return uploadProductImageFile(new File([blob], `product-image-${Date.now()}.${extension}`, { type: blob.type || "image/jpeg" }));
        })());
      }
      return cache.get(value);
    };
    const mainImages = Array.isArray(product.images) ? product.images : [];
    const variants = Array.isArray(product.variantRows) ? product.variantRows : [];
    const hasLocalImages = mainImages.some(value => /^(?:data:image\/|blob:)/i.test(clean(value)))
      || variants.some(row => (row.images || []).some(value => /^(?:data:image\/|blob:)/i.test(clean(value))));
    if (!hasLocalImages) return false;
    product.images = await Promise.all(mainImages.map(publish));
    for (const row of variants) row.images = await Promise.all((row.images || []).map(publish));
    saveDb({ sync: false });
    if (!await syncToServer()) throw new Error("公网图片地址已生成，但产品数据保存失败，请检查服务器连接后重试");
    return true;
  }

  async function readImages(files, context) {
    const images = [...files].filter(file => file.type.startsWith("image/"));
    if (!images.length) return;
    try {
      toast(`正在发布 ${images.length} 张图片…`, "info");
      const urls = await Promise.all(images.map(uploadProductImageFile));
      if (context.type === "main") {
        state.editing.images.push(...urls);
        renderEdit();
      } else {
        const row = state.variantSession.rows.find(item => item.id === context.rowId);
        if (row) {
          row.images = row.images || [];
          row.images.push(...urls);
          renderVariants();
        }
      }
      toast(`${urls.length} 张图片已发布，可用于亚马逊正式上传`, "success");
    } catch (error) {
      toast(`图片上传失败：${error.message}`, "error");
    }
  }

  function openCollectionDetails(item) {
    const rows = collectedVariantRows(item);
    if (!state.detailSelection || state.detailSelection.itemId !== item.id) {
      const initial = rows.find(row => row.asin === item.asin) || rows[0] || { color: "", size: "", images: item.images || [] };
      state.detailSelection = { itemId: item.id, color: initial.color || "", size: initial.size || "" };
    }
    let selectedRow = rows.find(row =>
      row.color === state.detailSelection.color && row.size === state.detailSelection.size
    ) || rows.find(row => row.color === state.detailSelection.color)
      || rows.find(row => row.size === state.detailSelection.size)
      || rows[0];
    if (selectedRow) {
      state.detailSelection.color = selectedRow.color || "";
      state.detailSelection.size = selectedRow.size || "";
    }
    const images = unique(((selectedRow?.images?.length ? selectedRow.images : item.images) || []).map(safeImage)).slice(0, 20);
    const hero = images[0] || "";
    const detailPrice = selectedRow?.originalPrice ?? selectedRow?.price ?? item.price;
    const detailCurrency = selectedRow?.priceCurrency || selectedRow?.currency || item.currency || "GBP";
    const points = (item.bulletPoints || []).filter(Boolean);
    const colors = unique(rows.map(row => row.color));
    const sizes = unique(rows.map(row => row.size));
    const optionButton = (dimension, value, selected) => `<button type="button" class="detail-option ${selected ? "selected" : ""}" data-action="select-detail-variant" data-dimension="${dimension}" data-value="${attr(value)}" data-id="${attr(item.id)}"><span></span>${esc(value)}</button>`;
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal detail-modal">
      <div class="modal-head"><div><h2>产品详情</h2><p>${esc(item.sku)} · ${esc(item.asin || "无 ASIN")}</p></div><button class="modal-close" data-action="close-modal">×</button></div>
      <div class="modal-body detail-body">
        <div class="detail-gallery">
          <div class="detail-hero">${hero ? `<img src="${attr(hero)}" alt="">` : '<div class="detail-no-image">暂无产品图</div>'}</div>
          <div class="detail-thumbs">${images.map(url => `<button data-action="preview-detail-image" data-url="${attr(url)}"><img src="${attr(url)}" alt=""></button>`).join("")}</div>
        </div>
        <div class="detail-copy">
          <h3>${esc(item.title)}</h3>
          <dl><dt>ASIN</dt><dd>${esc(selectedRow?.asin || item.asin || "—")}</dd><dt>Parent ASIN</dt><dd>${esc(item.parentAsin || "—")}</dd><dt>所属分类</dt><dd>${esc(item.categoryPath || item.category || "—")}</dd><dt>分类节点</dt><dd>${esc(item.categoryId || "—")}</dd><dt>采集价格</dt><dd>${esc(detailCurrency)} ${detailPrice === "" || detailPrice == null ? "—" : num(detailPrice).toFixed(2)}</dd></dl>
          <div class="detail-variants"><strong>颜色</strong><div>${colors.length ? colors.map(value => optionButton("color", value, value === state.detailSelection.color)).join("") : '<span class="meta">无颜色变体</span>'}</div></div>
          <div class="detail-variants"><strong>尺寸</strong><div>${sizes.length ? sizes.map(value => optionButton("size", value, value === state.detailSelection.size)).join("") : '<span class="meta">无尺寸变体</span>'}</div></div>
          <div class="detail-points"><strong>产品五点</strong>${points.length ? `<ul>${points.map(point => `<li>${esc(point)}</li>`).join("")}</ul>` : '<p class="meta">暂无五点描述</p>'}</div>
        </div>
        <div class="detail-description"><strong>产品描述</strong><p>${escLines(item.description || "暂无产品描述")}</p></div>
      </div>
      <div class="modal-foot"><button class="btn btn-primary" data-action="close-modal">关闭</button></div>
    </div></div>`;
  }

  function openProductDetails(product) {
    const primary = COUNTRIES.find(item => item.code === product.countries?.[0]) || COUNTRIES[0];
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal wide"><div class="modal-head"><div><h2>${esc(product.title)}</h2><p>${esc(product.sku)} · ${esc(product.asin)}</p></div><button class="modal-close" data-action="close-modal">×</button></div>
      <div class="modal-body"><div class="form-grid">
        <div class="field"><label>上架国家</label><div>${product.countries.map(countryName).join("、")}</div></div><div class="field"><label>状态</label><div>${esc(product.listingStatus)}</div></div><div class="field"><label>人民币售价</label><div>¥${num(product.rmbPrice).toFixed(2)}</div></div><div class="field"><label>外币售价</label><div>${primary.symbol}${num(product.foreignPrice).toFixed(2)}</div></div>
        <div class="field span-4"><label>产品描述</label><div style="white-space:pre-wrap">${escLines(product.description)}</div></div>
      </div><div class="image-grid">${(product.images || []).map((url, index) => `<div class="image-card"><div class="image-stage"><img src="${attr(safeImage(url))}" alt=""></div><div class="image-meta"><span>图片 ${index + 1}</span></div></div>`).join("")}</div></div><div class="modal-foot"><button class="btn" data-action="close-modal">关闭</button></div></div></div>`;
  }

  function applyFilters(scope, { renderNow = true } = {}) {
    if (scope === "collection") state.filters.collection = { query: $("collectionQuery").value, country: $("collectionCountry").value, collectionStatus: $("collectionStatus").value, start: $("collectionStart").value, end: $("collectionEnd").value };
    if (scope === "drafts") state.filters.drafts = { query: $("draftQuery").value, country: $("draftCountry").value, creationStatus: $("draftCreation").value };
    if (scope === "products") state.filters.products = { query: $("productQuery").value, country: $("productCountry").value, listingStatus: $("productListing").value };
    if (renderNow) render();
  }

  async function deleteItems(scope, ids) {
    if (!ids.length || !confirm(`确定删除 ${ids.length} 条记录吗？`)) return;
    const key = scope === "collection" ? "collections" : scope;
    const removed = state.db[key].filter(item => ids.includes(item.id));
    if (scope !== "collection") {
      state.db[key] = state.db[key].filter(item => !ids.includes(item.id));
      state.selected[scope].clear();
      saveDb();
      render();
      toast("记录已删除", "success");
      return;
    }
    // 使用后端专用窄接口，只 DELETE workflow_collection 指定 ID 及其
    // capture_task；不会全量重写、按 ASIN 扫描或删除 drafts/products。
    let result;
    try {
      result = await apiJson("/workflow/collections/delete", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ids })
      });
    } catch (error) {
      toast(`采集库删除失败：${error.message}`, "error");
      return;
    }
    if (Number.isFinite(Number(result?.data?.revision))) serverRevision = Number(result.data.revision);
    state.db.collections = state.db.collections.filter(item => !ids.includes(item.id));
    snapshotDelete("collections", ids);
    state.selected.collection.clear();
    saveDb({ sync: false });
    render();
    const targets = removed.flatMap(item => {
      const variants = Array.isArray(item.variantRows) ? item.variantRows : [];
      const asins = [...new Set([item.asin, ...variants.map(row => row.asin)].filter(Boolean))];
      // `site` is a display value such as "UK", not a hostname.  Passing it as
      // domain prevents the extension from matching and removing its durable
      // task/cache entry, so a later recollection can reuse stale progress.
      return asins.map(asin => ({ asin, domain: item.domain || item.url, url: item.url, storeId: item.storeId || authState.activeStoreId }));
    });
    if (targets.length) await sendExtensionMessage({ type: "RELEASE_CAPTURE_PRODUCTS", targets }).catch(() => {});
    toast("采集库记录已彻底删除", "success");
  }

  function captureSettingsInputs() {
    if (!$("settingBrand")) return;
    if (!authState.activeStoreId) { toast("请先添加并启用店铺", "error"); return; }
    const root = normalizeSettings(state.db.settings);
    const current = activeSettings();
    const translationTools = normalizeTranslationTools(current.translationTools);
    const officialSchemaDefaults = clone(current.officialSchemaDefaults || {});
    const schemaCountry = amazonRequiredSchema.country || $("schemaCountry")?.value;
    const schemaProductType = clean(amazonRequiredSchema.productType || $("schemaProductType")?.value).toUpperCase();
    if (schemaCountry && schemaProductType) {
      const schemaKey = `${schemaCountry}:${schemaProductType}`;
      const schemaProfile = officialSchemaDefaults[schemaKey] && typeof officialSchemaDefaults[schemaKey] === "object"
        ? clone(officialSchemaDefaults[schemaKey]) : {};
      document.querySelectorAll("[data-schema-default]").forEach(input => {
        const attribute = input.dataset.schemaAttribute;
        const path = input.dataset.schemaPath || "value";
        if (!attribute) return;
        const definition = amazonRequiredSchema.fields.find(item => item.attribute === attribute) || {};
        schemaProfile[attribute] = schemaProfile[attribute] && typeof schemaProfile[attribute] === "object" ? schemaProfile[attribute] : {};
        schemaProfile[attribute].supportsMarketplaceId = Boolean(definition.supportsMarketplaceId);
        schemaProfile[attribute].supportsLanguageTag = Boolean(definition.supportsLanguageTag);
        schemaProfile[attribute].fields = schemaProfile[attribute].fields && typeof schemaProfile[attribute].fields === "object" ? schemaProfile[attribute].fields : {};
        schemaProfile[attribute].fields[path] = { value: input.value, type: input.dataset.schemaType || "string" };
      });
      officialSchemaDefaults[schemaKey] = schemaProfile;
    }
    const profile = {
      ...current,
      brand: clean($("settingBrand").value) || "Generic",
      manufacturer: clean($("settingManufacturer").value) || "Generic",
      defaultStoreName: activeStore()?.name || "",
      translationTools,
      defaultTranslationToolId: document.querySelector("[name=defaultTranslationTool]:checked")?.value || current.defaultTranslationToolId || translationTools[0].id,
      inventory: num($("settingInventory").value, 30),
      handlingTime: num($("settingHandling").value, 3),
      condition: "new",
      upcMode: ["same", "replace", "exempt"].includes($("settingUpc").value) ? $("settingUpc").value : "exempt",
      usdRate: num(current.usdRate, 7.2),
      cadRate: num(current.cadRate, 5.25),
      mxnRate: num(current.mxnRate, 0.42),
      gbpRate: num(current.gbpRate, 9.2),
      eurRate: num(current.eurRate, 7.85),
      defaultCost: 1,
      defaultShipping: 1,
      defaultProfitRate: 1,
      aiTool: authState.aiServices?.activeAiProvider || current.aiTool || "deepseek",
      officialProductIdType: $("settingOfficialProductIdType").value,
      officialCountryOfOrigin: clean($("settingOfficialCountry").value) || "CN",
      officialDangerousGoods: $("settingOfficialDangerousGoods").value,
      officialAssemblyRequired: $("settingOfficialAssemblyRequired").value,
      officialBatteriesRequired: $("settingOfficialBatteriesRequired").value,
      officialBaseMaterial: clean($("settingOfficialBaseMaterial").value),
      officialNumberOfItems: num($("settingOfficialNumberOfItems").value, 1),
      officialUnitCountValue: num($("settingOfficialUnitCountValue").value, 1),
      officialUnitCountUnit: $("settingOfficialUnitCountUnit").value || "count",
      officialIncludedComponents: clean($("settingOfficialIncludedComponents").value),
      officialItemWeightValue: num($("settingOfficialItemWeightValue").value, 0),
      officialItemWeightUnit: $("settingOfficialItemWeightUnit").value,
      officialFragile: $("settingOfficialFragile").value,
      officialItemDepthValue: num($("settingOfficialItemDepthValue").value, 0),
      officialItemDepthUnit: $("settingOfficialDimensionUnit").value,
      officialItemWidthValue: num($("settingOfficialItemWidthValue").value, 0),
      officialItemHeightValue: num($("settingOfficialItemHeightValue").value, 0),
      officialDimensionUnit: $("settingOfficialDimensionUnit").value,
      officialModelName: clean($("settingOfficialModelName").value),
      officialNetContentValue: num($("settingOfficialNetContentValue").value, 1),
      officialNetContentUnit: clean($("settingOfficialNetContentUnit").value) || "count",
      officialDefaultSize: clean($("settingOfficialDefaultSize").value),
      officialSchemaDefaults
    };
    delete profile.storeProfiles;
    root.storeProfiles = { ...root.storeProfiles, [authState.activeStoreId]: profile };
    state.db.settings = root;
  }

  function saveSettings() {
    const origin = clean($("settingOfficialCountry")?.value);
    const normalizedOrigin = origin.toUpperCase().replace(/\s+/g, "");
    const supportedOrigins = new Set(["CN", "CHINA", "中国", "US", "UNITEDSTATES", "美国", "GB", "UK", "UNITEDKINGDOM", "英国", "DE", "GERMANY", "德国", "ES", "SPAIN", "西班牙", "FR", "FRANCE", "法国", "IT", "ITALY", "意大利", "CA", "CANADA", "加拿大", "MX", "MEXICO", "墨西哥"]);
    if (!supportedOrigins.has(normalizedOrigin)) {
      toast("原产国格式无效，请填写两位国家代码、中文名称或英文名称，例如 CN、中国或 China", "error");
      $("settingOfficialCountry")?.focus();
      return;
    }
    captureSettingsInputs();
    saveDb();
    toast("默认设置已保存", "success");
  }

  async function submitProduct() {
    const session = state.variantSession;
    const editing = state.editing;
    if (!session || !editing || !session.draftId) {
      toast("当前编辑会话已失效，请返回列表后重新打开", "error");
      return;
    }
    const productMode = session.sourceMode === "product" || state.editingMode === "product";
    const current = (productMode ? state.db.products : state.db.drafts).find(item => item.id === session.draftId) || {};
    const submitted = {
      ...current, ...clone(editing), id: productMode ? session.draftId : uid("product"),
      variantRows: clone(session.rows), variants: clone(session.rows), colors: clone(session.colors), sizes: clone(session.sizes),
      ...(shouldWriteVariantPricingV2(editing, session.rows) ? { variantPricingVersion: 2, variantBaseRmb: num(editing.variantBaseRmb) } : {}),
      countries: clone(editing.countries || current.countries || []), localizedContent: clone(editing.localizedContent || current.localizedContent || {}),
      countryPrices: clone(editing.countryPrices || current.countryPrices || {}), creationStatus: "已创建",
      listingStatus: productMode && current.listingStatus !== "未上架" ? (current.listingStatus || "未上传") : "未上传", updatedAt: nowIso()
    };
    document.querySelectorAll('[data-action="submit-product"]').forEach(button => { button.disabled = true; });
    try {
      const response = await apiJson("/workflow/editor/commit", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ sourceId: session.draftId, sourceMode: productMode ? "product" : "draft", product: submitted })
      });
      const saved = normalizeDraft(response?.data?.product || submitted);
      saved.__rowVersion = clean(saved.updatedAt);
      if (Number.isFinite(Number(response?.data?.revision))) serverRevision = Number(response.data.revision);
      state.db.products = state.db.products.filter(item => item.id !== saved.id);
      state.db.products.unshift(saved);
      snapshotUpsert("products", saved);
      if (!productMode) {
        state.db.drafts = state.db.drafts.filter(item => item.id !== session.draftId);
        snapshotDelete("drafts", [session.draftId]);
        const source = state.db.collections.find(item => item.id === saved.sourceId);
        if (source) {
          source.collectionStatus = "已入产品库";
          source.updatedAt = saved.updatedAt || nowIso();
          source.__rowVersion = source.updatedAt;
          snapshotUpsert("collections", source);
        }
      }
      saveDb({ sync: false });
      finishActiveEditSession("products");
      toast(productMode ? "产品修改已保存" : "产品已提交到产品库，草稿库记录已移除", "success");
    } catch (error) {
      toast(`提交保存失败：${error.message}`, "error");
      renderVariants();
    }
  }

  async function importCapturedQueue({ silent = false } = {}) {
    if (!HAS_EXTENSION_API) return false;
    if (isImportingCapturedQueue) return false;
    isImportingCapturedQueue = true;
    try {
      const result = await chrome.storage.local.get("capturedQueue");
      const queue = Array.isArray(result.capturedQueue) ? result.capturedQueue : [];
      const accountQueue = queue.filter(product => !product?.dataAccount || product.dataAccount === authState.dataAccount);
      if (!accountQueue.length) return false;
      const remaining = queue.filter(product => product?.dataAccount && product.dataAccount !== authState.dataAccount);
      let added = 0, updated = 0;
      [...accountQueue].reverse().forEach(product => {
        const resultItem = upsertCollection(product);
        resultItem.added ? added++ : updated++;
      });
      if (remaining.length) await chrome.storage.local.set({ capturedQueue: remaining });
      else await chrome.storage.local.remove("capturedQueue");
      render();
      if (!silent) toast(`插件采集已进入采集库：新增 ${added}，更新 ${updated}`, "success");
      return true;
    } catch (error) {
      toast(`读取插件采集结果失败：${error.message}`, "error");
      return false;
    } finally {
      isImportingCapturedQueue = false;
    }
  }

  function exportBackup() {
    const blob = new Blob([JSON.stringify(state.db, null, 2)], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = `易联跨境-完整备份-${new Date().toISOString().slice(0,10)}.json`;
    link.click();
    setTimeout(() => URL.revokeObjectURL(url), 500);
  }

  function csvCell(value) {
    const text = String(value ?? "").replace(/\r\n?/g, "\n");
    return `"${text.replace(/"/g, '""')}"`;
  }

  async function exportSelectedProducts() {
    const products = state.db.products.filter(item => state.selected.products.has(item.id));
    if (!products.length) {
      toast("请先勾选需要导出的产品", "info");
      return;
    }
    toast("正在生成产品数据 XLSX…", "info");
    try {
      const response = await authFetch(`${API_BASE}/export/products`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ products, settings: activeSettings() })
      });
      if (!response.ok) {
        const error = await response.json().catch(() => ({}));
        throw new Error(typeof error.detail === "string" ? error.detail : "产品数据生成失败");
      }
      const blob = await response.blob();
      const url = URL.createObjectURL(blob);
      const link = document.createElement("a");
      link.href = url;
      link.download = `易联跨境-产品数据-${new Date().toISOString().slice(0, 10)}.xlsx`;
      link.click();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
      const rowCount = response.headers.get("X-Export-Row-Count");
      toast(`已导出 ${products.length} 个产品${rowCount ? `，共 ${rowCount} 行` : ""}；图片已保存为链接`, "success");
    } catch (error) {
      toast(`导出失败：${error.message}`, "error");
    }
  }

  async function refreshAccounts() {
    const result = await apiJson("/auth/me");
    const previousAccount = authState.dataAccount;
    authState.user = result.data.user;
    authState.accounts = result.data.accounts || [result.data.user];
    authState.dataAccount = authState.accounts.some(account => account.username === previousAccount && account.active !== false)
      ? previousAccount
      : (result.data.dataAccount || (authState.user.accountLevel === 3 ? authState.user.parentUsername : authState.user.username));
    const activeAccount = authState.accounts.find(account => account.username === authState.dataAccount);
    authState.dataAccountType = activeAccount?.accountType || "internal";
    authState.dataAccessMode = activeAccount?.effectiveOwnerAccessMode || activeAccount?.ownerAccessMode || "managed";
    authState.dataAccessExpiresAt = activeAccount?.ownerAccessExpiresAt || "";
    persistSession();
    refreshAccountChrome();
  }

  async function createChildAccount() {
    const username = clean($("newChildUsername")?.value);
    const password = $("newChildPassword")?.value || "";
    const permissions = [...document.querySelectorAll("[data-child-permission]:checked")].map(input => input.dataset.childPermission);
    try {
      await apiJson("/auth/accounts", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          username, password, permissions,
          displayName: clean($("newChildDisplayName")?.value),
          accountType: $("newAccountType")?.value || "internal",
          ownerAccessMode: $("newOwnerAccessMode")?.value || "managed",
          ownerAccessExpiresAt: clean($("newOwnerAccessExpiresAt")?.value),
          maxChildAccounts: Number($("newChildQuota")?.value || 0),
          maxSellerAccounts: Number($("newShopQuota")?.value || 0),
          orderDeductionRate: clean($("newDeductionRate")?.value || "0"),
          aiCallMode: $("newAiCallMode")?.value || "internal"
        })
      });
      await refreshAccounts();
      renderAccounts();
      closeModal();
      toast(`子账号 ${username} 已创建`, "success");
    } catch (error) {
      toast(error.message, "error");
    }
  }

  async function saveChildPermissions(account) {
    const permissions = [...document.querySelectorAll(`[data-account-permission="${CSS.escape(account)}"]:checked`)].map(input => input.dataset.permission);
    try {
      await apiJson(`/auth/accounts/${encodeURIComponent(account)}/permissions`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ permissions, aiCallMode: $("accountAiCallMode")?.value || "internal" })
      });
      await refreshAccounts();
      renderAccounts();
      toast(`${account} 的菜单权限已保存`, "success");
    } catch (error) {
      toast(error.message, "error");
    }
  }

  async function saveChildStores(account) {
    const storeIds = [...document.querySelectorAll(`[data-account-store="${CSS.escape(account)}"]:checked`)].map(input => input.value);
    try {
      await apiJson(`/auth/accounts/${encodeURIComponent(account)}/stores`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ storeIds, marketplaceScopes: [] })
      });
      await refreshStoreContext();
      renderAccounts();
      toast(`${account} 的店铺权限已保存`, "success");
    } catch (error) {
      toast(error.message, "error");
    }
  }

  async function saveAccountAccess(account) {
    const permissions = [...document.querySelectorAll(`[data-account-permission="${CSS.escape(account)}"]:checked`)].map(input => input.dataset.permission);
    const storeIds = [...document.querySelectorAll(`[data-account-store="${CSS.escape(account)}"]:checked`)].map(input => input.value);
    try {
      await apiJson(`/auth/accounts/${encodeURIComponent(account)}/permissions`, {
        method: "PUT", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ permissions, aiCallMode: $("accountAiCallMode")?.value || "internal" })
      });
      await apiJson(`/auth/accounts/${encodeURIComponent(account)}/stores`, {
        method: "PUT", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ storeIds, marketplaceScopes: [] })
      });
      await Promise.all([refreshAccounts(), refreshStoreContext()]);
      closeModal();
      renderAccounts();
      toast(`${account} 的菜单和店铺权限已保存`, "success");
    } catch (error) {
      toast(error.message, "error");
    }
  }

  async function resetChildPassword(account) {
    const input = document.querySelector(`[data-child-password="${CSS.escape(account)}"]`);
    const password = input?.value || "";
    try {
      await apiJson(`/auth/accounts/${encodeURIComponent(account)}/password`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ password })
      });
      if (input) input.value = "";
      closeModal();
      toast(`${account} 的密码已修改，原登录会话已退出`, "success");
    } catch (error) {
      toast(error.message, "error");
    }
  }

  async function toggleChildActive(account, active) {
    try {
      await apiJson(`/auth/accounts/${encodeURIComponent(account)}/active`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ active })
      });
      await refreshAccounts();
      renderAccounts();
      toast(`${account} 已${active ? "启用" : "停用"}`, "success");
    } catch (error) {
      toast(error.message, "error");
    }
  }

  async function saveAccountProfile(account) {
    try {
      await apiJson(`/auth/accounts/${encodeURIComponent(account)}/profile`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          displayName: clean($("editChildDisplayName")?.value),
          accountType: $("editAccountType")?.value || "internal",
          ownerAccessMode: $("editOwnerAccessMode")?.value || "managed",
          ownerAccessExpiresAt: clean($("editOwnerAccessExpiresAt")?.value),
          maxChildAccounts: Number($("editChildQuota")?.value || 0),
          maxSellerAccounts: Number($("editShopQuota")?.value || 0),
          orderDeductionRate: clean($("editDeductionRate")?.value || "0")
        })
      });
      await apiJson(`/auth/accounts/${encodeURIComponent(account)}/active`, {
        method: "PUT", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ active: $("editChildActive")?.value === "1" })
      });
      await refreshAccounts();
      closeModal();
      renderAccounts();
      toast(`${account} 的层级配置已保存`, "success");
    } catch (error) { toast(error.message, "error"); }
  }

  async function deleteChildAccount(account) {
    if (!confirm(`确认删除子账号“${account}”吗？账号记录会删除，独立产品数据库会保留。`)) return;
    try {
      await apiJson(`/auth/accounts/${encodeURIComponent(account)}`, { method: "DELETE" });
      await refreshAccounts();
      renderAccounts();
      toast(`${account} 已删除，产品数据库已保留`, "success");
    } catch (error) {
      toast(error.message, "error");
    }
  }

  function openAddStoreModal() {
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal"><div class="modal-head"><div><h2>添加亚马逊店铺资料</h2><p>保存后还需到 API 设置完成真实授权验证</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body">
      <div class="field"><label>所属站点</label><select class="control" id="storeRegion"><option value="">请选择所属站点</option><option value="americas">美洲站（美国、加拿大、墨西哥）</option><option value="europe">欧洲站（英国、德国、西班牙、法国、意大利）</option></select></div>
      <div class="field"><label>所属国家</label><input class="control" id="storeCountriesPreview" value="选择站点后自动生成" readonly></div>
      <div class="field"><label>店铺名称</label><input class="control" id="storeName" maxlength="80" placeholder="请输入店铺名称"></div>
      <div class="field"><label>亚马逊账号</label><input class="control" id="storeAmazonAccount" maxlength="120" placeholder="请输入亚马逊账号"></div>
      <div class="field"><label>卖家编号</label><input class="control" id="storeSellerId" maxlength="40" placeholder="请输入 Seller ID"></div>
    </div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-primary" data-action="submit-store">保存店铺资料</button></div></div></div>`;
  }

  async function submitStore() {
    try {
      await apiJson("/stores", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          region: $("storeRegion")?.value,
          name: clean($("storeName")?.value),
          amazonAccount: clean($("storeAmazonAccount")?.value),
          sellerId: clean($("storeSellerId")?.value)
        })
      });
      closeModal();
      await refreshStoreContext();
      renderSettings();
      toast("店铺资料已保存；请到 API 设置完成生产授权验证", "success");
    } catch (error) {
      toast(error.message, "error");
    }
  }

  function submitSelectedDraftsToProducts(ids) {
    const selectedIds = new Set(ids);
    const drafts = state.db.drafts.filter(item => selectedIds.has(item.id));
    if (!drafts.length) { toast("请先勾选需要提交的草稿", "info"); return; }
    const createdAt = nowIso();
    drafts.forEach(draft => {
      const normalized = normalizeDraft(clone(draft));
      const rows = assignVariantSkus(normalized.sku, normalized.variantRows || []);
      state.db.products.unshift({ ...normalized, id: uid("product"), variantRows: clone(rows), variants: clone(rows), creationStatus: "已创建", listingStatus: "未上传", updatedAt: createdAt });
      const source = state.db.collections.find(item => item.id === draft.sourceId);
      if (source) { source.collectionStatus = "已入产品库"; source.updatedAt = createdAt; }
      const key = editTabKey("draft", draft.id);
      cancelAiPendingForTab(key); delete state.editSessions[key]; clearEditorScroll(key);
      state.openTabs = state.openTabs.filter(item => item !== key);
    });
    state.db.drafts = state.db.drafts.filter(item => !selectedIds.has(item.id));
    state.selected.drafts.clear();
    saveDb(); render();
    toast(`已将 ${drafts.length} 个草稿提交到产品库`, "success");
  }

  function openDeleteStoreConfirm(storeId) {
    const store = (authState.stores || []).find(item => String(item.id) === String(storeId));
    const storeName = store?.name || "当前店铺";
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal"><div class="modal-head"><div><h2>确认删除店铺</h2><p>此操作不会自动撤销亚马逊侧授权</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body"><div class="settings-note danger-note">确定删除“${esc(storeName)}”吗？删除后该店铺将从易联跨境的店铺列表中移除，已绑定子账号也将无法继续使用它。</div></div><div class="modal-foot"><button class="btn" data-action="close-modal">取消</button><button class="btn btn-danger" data-action="confirm-delete-store" data-store="${attr(storeId)}">确认删除</button></div></div></div>`;
  }

  async function deleteStore(storeId) {
    try {
      await apiJson(`/stores/${encodeURIComponent(storeId)}`, { method: "DELETE" });
      closeModal();
      await refreshStoreContext();
      renderSettings();
      toast("店铺已删除", "success");
    } catch (error) {
      toast(error.message, "error");
    }
  }

  function openHelp() {
    $("modalRoot").innerHTML = `<div class="modal-backdrop"><div class="modal"><div class="modal-head"><div><h2>易联跨境工作流</h2><p>采集库 → 草稿库 → 产品库</p></div><button class="modal-close" data-action="close-modal">×</button></div><div class="modal-body">
      <ol style="font-size:12px;line-height:1.9;color:#5f6877"><li>插件采集和链接采集的结果先进入采集库。</li><li>在采集库勾选产品，上传到草稿库；采集记录不会删除，只改变状态。</li><li>在草稿库点击“编辑产品”，完成基本信息、售价、文案和图片。</li><li>下一步编辑颜色与尺寸变体。点击上一步会放弃本次变体修改。</li><li>提交后产品进入产品库，并从草稿库移除。</li></ol>
      <div class="settings-note">“翻译为中文”会等待Google完成全部字段后回填右侧；“翻译”和“一键优化”使用所选AI，并优先复用置顶的“优化亚马逊产品信息”对话。图片区的复制按钮会直接复制图片；受图片来源限制时会复制图片链接。</div>
    </div><div class="modal-foot"><button class="btn btn-primary" data-action="close-modal">知道了</button></div></div></div>`;
  }

  document.addEventListener("pointerover", event => {
    const button = event.target.closest?.(".product-thumb-button");
    if (button) positionProductThumbPopover(button);
  });

  document.addEventListener("dragstart", event => {
    const variantCard = event.target.closest?.(".variant-image-card[data-variant-image-index]");
    if (variantCard && state.variantSession && !event.target.closest("button")) {
      draggedVariantImage = { rowId: variantCard.dataset.row, index: num(variantCard.dataset.variantImageIndex, -1) };
      variantCard.classList.add("dragging");
      if (event.dataTransfer) {
        event.dataTransfer.effectAllowed = "move";
        event.dataTransfer.setData("text/plain", `${draggedVariantImage.rowId}:${draggedVariantImage.index}`);
      }
      return;
    }
    const card = event.target.closest?.("#mainImageGrid .image-card[data-image-index]");
    if (!card || !state.editing || event.target.closest("button")) return;
    captureDraftForm();
    draggedMainImageIndex = num(card.dataset.imageIndex, -1);
    card.classList.add("dragging");
    if (event.dataTransfer) {
      event.dataTransfer.effectAllowed = "move";
      event.dataTransfer.setData("text/plain", String(draggedMainImageIndex));
    }
  });

  document.addEventListener("dragover", event => {
    const variantCard = event.target.closest?.(".variant-image-card[data-variant-image-index]");
    if (variantCard && draggedVariantImage && String(variantCard.dataset.row) === String(draggedVariantImage.rowId)) {
      event.preventDefault();
      if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
      document.querySelectorAll(".variant-image-card.drag-over").forEach(item => item.classList.remove("drag-over"));
      variantCard.classList.add("drag-over");
      return;
    }
    const card = event.target.closest?.("#mainImageGrid .image-card[data-image-index]");
    if (!card || draggedMainImageIndex < 0) return;
    event.preventDefault();
    if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
    document.querySelectorAll("#mainImageGrid .image-card.drag-over").forEach(item => item.classList.remove("drag-over"));
    card.classList.add("drag-over");
  });

  document.addEventListener("drop", event => {
    const variantCard = event.target.closest?.(".variant-image-card[data-variant-image-index]");
    if (variantCard && draggedVariantImage && String(variantCard.dataset.row) === String(draggedVariantImage.rowId)) {
      event.preventDefault();
      const changed = reorderVariantImages(draggedVariantImage.rowId, draggedVariantImage.index, num(variantCard.dataset.variantImageIndex, -1));
      draggedVariantImage = null;
      if (changed) {
        renderVariants();
        toast("变体图片顺序已调整，提交保存后生效", "success");
      }
      return;
    }
    const card = event.target.closest?.("#mainImageGrid .image-card[data-image-index]");
    if (!card || draggedMainImageIndex < 0) return;
    event.preventDefault();
    const targetIndex = num(card.dataset.imageIndex, -1);
    const changed = reorderMainImages(draggedMainImageIndex, targetIndex);
    draggedMainImageIndex = -1;
    if (changed) {
      renderEdit();
      toast("图片顺序已调整，提交保存后生效", "success");
    }
  });

  document.addEventListener("dragend", () => {
    draggedMainImageIndex = -1;
    draggedVariantImage = null;
    document.querySelectorAll("#mainImageGrid .image-card.dragging, #mainImageGrid .image-card.drag-over, .variant-image-card.dragging, .variant-image-card.drag-over").forEach(item => item.classList.remove("dragging", "drag-over"));
  });

  document.addEventListener("click", async event => {
    const customOption = event.target.closest("[data-select-value]");
    if (customOption) {
      event.preventDefault();
      event.stopPropagation();
      const wrapper = customOption.closest(".sdm-select");
      const select = wrapper?.querySelector("select");
      if (select && !customOption.disabled) {
        select.value = customOption.dataset.selectValue;
        select.dispatchEvent(new Event("change", { bubbles: true }));
        syncCustomSelect(select);
      }
      wrapper?.classList.remove("open");
      return;
    }
    const customTrigger = event.target.closest("[data-select-trigger]");
    if (customTrigger) {
      event.preventDefault();
      event.stopPropagation();
      const wrapper = customTrigger.closest(".sdm-select");
      const select = wrapper?.querySelector("select");
      const willOpen = !wrapper.classList.contains("open");
      closeCustomSelects(wrapper);
      if (willOpen && select && !select.disabled) {
        rebuildCustomSelectMenu(select);
        wrapper.classList.add("open");
      } else wrapper.classList.remove("open");
      return;
    }
    if (!event.target.closest(".sdm-select")) closeCustomSelects();
    const closeTab = event.target.closest("[data-close-workspace-tab]");
    if (closeTab) {
      event.stopPropagation();
      const key = closeTab.dataset.closeWorkspaceTab;
      state.openTabs = state.openTabs.filter(item => item !== key);
      if (isEditTabKey(key)) {
        cancelAiPendingForTab(key);
        delete state.editSessions[key];
        clearEditorScroll(key);
        if (workspaceTabKey() === key) {
          state.activeEditTab = "";
          state.editing = null;
          state.variantSession = null;
          state.editingMode = "draft";
          state.editLocale = "";
          const fallback = state.openTabs.at(-1) || firstAllowedView();
          if (isEditTabKey(fallback) && activateEditTab(fallback)) render();
          else setView(fallback);
        } else renderWorkspaceTabs();
      } else if (workspaceTabKey() === key) {
        const fallback = state.openTabs.at(-1) || firstAllowedView();
        if (isEditTabKey(fallback) && activateEditTab(fallback)) render();
        else setView(fallback);
      } else renderWorkspaceTabs();
      return;
    }
    const workspaceTab = event.target.closest("[data-workspace-tab]");
    if (workspaceTab) {
      const key = workspaceTab.dataset.workspaceTab;
      const navigationRequestId = ++workspaceNavigationRequestId;
      if (isEditTabKey(key)) {
        if (activateEditTab(key)) render();
      } else {
        saveActiveEditSession();
        setView(key);
        refreshWorkflowStateForInteraction().then(() => {
          if (navigationRequestId === workspaceNavigationRequestId && state.view === key) render();
        }).catch(() => false);
      }
      return;
    }
    const nav = event.target.closest("[data-nav]");
    if (nav) {
      const requestedView = nav.dataset.nav;
      const navigationRequestId = ++workspaceNavigationRequestId;
      saveActiveEditSession();
      setView(requestedView);
      refreshWorkflowStateForInteraction().then(() => {
        if (navigationRequestId === workspaceNavigationRequestId && state.view === requestedView) render();
      }).catch(() => false);
      return;
    }
    const target = event.target.closest("[data-action]");
    if (!target) return;
    const action = target.dataset.action;
    const id = target.dataset.id;
    if (action === "scroll-tabs") {
      const tabs = $("workspaceTabs");
      tabs?.scrollBy({ left: Number(target.dataset.direction || 1) * Math.max(240, tabs.clientWidth * 0.7), behavior: "smooth" });
    }
    if (action === "sync-required-schema") {
      captureSettingsInputs();
      amazonRequiredSchema.country = $("schemaCountry")?.value || amazonRequiredSchema.country;
      amazonRequiredSchema.productType = clean($("schemaProductType")?.value || amazonRequiredSchema.productType).toUpperCase();
      await loadAmazonRequiredSchema();
    }
    if (action === "set-page") {
      const scope = target.dataset.scope;
      const pager = state.pagination[scope];
      if (pager) {
        pager.page = Math.max(1, Number(target.dataset.page) || 1);
        render();
      }
    }
    if (action === "set-order-page") {
      state.pagination.orders.page = Math.max(1, Number(target.dataset.page) || 1);
      state.orders.items = [];
      await loadOrders(true);
    }
    if (action === "apply-order-filters") {
      state.filters.orders = {
        query: clean($("orderQuery")?.value), status: clean($("orderStatus")?.value),
        country: clean($("orderCountry")?.value), startDate: clean($("orderStartDate")?.value),
        endDate: clean($("orderEndDate")?.value), storeId: clean($("orderStore")?.value || authState.activeStoreId)
      };
      state.pagination.orders.page = 1;
      state.orders.items = [];
      await loadOrders(true);
    }
    if (action === "reset-order-filters") {
      state.filters.orders = {};
      state.pagination.orders.page = 1;
      state.orders.items = [];
      await loadOrders(true);
    }
    if (action === "sync-amazon-orders") await syncAmazonOrders();
    if (action === "export-amazon-orders") await exportAmazonOrders();
    if (action === "open-order-performance") openOrderPerformanceModal();
    if (action === "set-order-performance-period") {
      state.orders.period = ["week", "month", "quarter", "year"].includes(target.dataset.period) ? target.dataset.period : "month";
      try {
        const result = await apiJson(`/orders?${orderQueryString(false)}`);
        state.orders.performance = result.data?.performance || [];
        state.orders.period = result.data?.period || state.orders.period;
        openOrderPerformanceModal();
      } catch (error) {
        toast(`业绩数据计算失败：${error.message}`, "error");
      }
    }
    if (action === "edit-order-costs") openOrderCostsModal(id);
    if (action === "save-order-costs") await saveOrderCosts(id);
    if (action === "open-collect" || action === "open-link-collect") openCollectModal();
    if (action === "close-modal") {
      if (target.classList.contains("image-lightbox") && event.target !== target) return;
      closeModal();
    }
    if (action === "confirm-draft-upload") {
      const scopes = [
        $("draftOptimizeTitle")?.checked ? "title" : "",
        $("draftOptimizeBullets")?.checked ? "bulletPoints" : "",
        $("draftOptimizeDescription")?.checked ? "description" : ""
      ].filter(Boolean);
      const ids = [...state.pendingDraftUploadIds];
      const multiplierText = clean($("draftPriceMultiplier")?.value);
      const priceMultiplier = multiplierText ? Number(multiplierText) : 1;
      if (!Number.isFinite(priceMultiplier) || priceMultiplier <= 0) {
        toast("请输入大于 0 的价格倍数，例如 1.1 或 0.9", "error");
        return;
      }
      state.pendingDraftUploadIds = [];
      closeModal();
      await moveCollectionsToDraft(ids, { optimizationScopes: scopes, priceMultiplier });
    }
    if (action === "run-collection") runCollection();
    if (action === "apply-filters") {
      await importCapturedQueue({ silent: true });
      applyFilters(target.dataset.scope, { renderNow: false });
      await refreshWorkflowStateForInteraction();
      render();
    }
    if (action === "reset-filters") {
      await importCapturedQueue({ silent: true });
      state.filters[target.dataset.scope] = {};
      if (state.pagination[target.dataset.scope]) state.pagination[target.dataset.scope].page = 1;
      await refreshWorkflowStateForInteraction();
      render();
    }
    if (action === "upload-selected-drafts") openDraftUploadModal([...state.selected.collection]);
    if (action === "upload-one-draft") await moveCollectionsToDraft([id]);
    if (action === "submit-selected-products") submitSelectedDraftsToProducts([...state.selected.drafts]);
    if (action === "delete-selected") await deleteItems(target.dataset.scope, [...state.selected[target.dataset.scope]]);
    if (action === "delete-one") await deleteItems(target.dataset.scope, [id]);
    if (action === "recollect-one") {
      const item = state.db.collections.find(product => product.id === id);
      if (item) openCollectModal(item.url, true);
    }
    if (action === "recollect-selected") {
      const item = state.db.collections.find(product => state.selected.collection.has(product.id));
      if (item) openCollectModal(item.url, true);
    }
    if (action === "open-source") {
      const item = state.db.collections.find(product => product.id === id);
      if (item?.url) window.open(item.url, "_blank", "noopener,noreferrer");
    }
    if (action === "edit-draft") {
      const draft = state.db.drafts.find(item => item.id === id);
      if (draft) openEditor(draft, "draft");
    }
    if (action === "edit-product") {
      const product = state.db.products.find(item => item.id === id);
      if (product) openEditor(product, "product");
    }
    if (action === "view-collection-detail") {
      const item = state.db.collections.find(product => product.id === id);
      if (item) {
        state.detailSelection = null;
        openCollectionDetails(item);
      }
    }
    if (action === "select-detail-variant") {
      const item = state.db.collections.find(product => product.id === id);
      if (item) {
        const rows = collectedVariantRows(item);
        const dimension = target.dataset.dimension;
        const value = target.dataset.value || "";
        const preferred = rows.find(row =>
          row[dimension] === value
          && row[dimension === "color" ? "size" : "color"] === state.detailSelection?.[dimension === "color" ? "size" : "color"]
        ) || rows.find(row => row[dimension] === value);
        if (preferred) state.detailSelection = { itemId: item.id, color: preferred.color || "", size: preferred.size || "" };
        openCollectionDetails(item);
      }
    }
    if (action === "edit-draft-from-modal") {
      const draft = state.db.drafts.find(item => item.id === id);
      closeModal();
      if (draft) openEditor(draft, "draft");
    }
    if (action === "preview-detail-image" && target.dataset.url) previewImage(target.dataset.url);
    if (action === "view-product") {
      const product = state.db.products.find(item => item.id === id);
      if (product) openProductDetails(product);
    }
    if (action === "sandbox-upload-product" || action === "upload-product") {
      const product = state.db.products.find(item => item.id === id);
      closeModal();
      await uploadProduct(product);
    }
    if (action === "save-missing-upload-fields") {
      const product = state.db.products.find(item => item.id === id);
      await saveMissingUploadFields(product);
    }
    if (action === "batch-sandbox-upload" || action === "batch-upload") await batchUpload([...state.selected.products]);
    if (action === "view-upload-failure") {
      const product = state.db.products.find(item => item.id === id);
      if (product) openUploadFailure(product);
    }
    if (action === "create-child-account") await createChildAccount();
    if (action === "open-create-child") openCreateChildModal();
    if (action === "open-account-permissions") openAccountPermissionsModal(target.dataset.account);
    if (action === "open-account-password") openAccountPasswordModal(target.dataset.account);
    if (action === "open-account-edit") openAccountEditModal(target.dataset.account);
    if (action === "open-account-settlement") await openAccountSettlement(target.dataset.account);
    if (action === "settle-account-month") await settleAccountMonth(target.dataset.account, target.dataset.month);
    if (action === "delete-child-account") await deleteChildAccount(target.dataset.account);
    if (action === "save-account-access") await saveAccountAccess(target.dataset.account);
    if (action === "save-account-active") {
      await toggleChildActive(target.dataset.account, $("editChildActive")?.value === "1");
      closeModal();
    }
    if (action === "save-account-profile") await saveAccountProfile(target.dataset.account);
    if (action === "save-child-permissions") await saveChildPermissions(target.dataset.account);
    if (action === "save-child-stores") await saveChildStores(target.dataset.account);
    if (action === "reset-child-password") await resetChildPassword(target.dataset.account);
    if (action === "toggle-child-active") await toggleChildActive(target.dataset.account, target.dataset.active === "1");
    if (action === "open-add-store") openAddStoreModal();
    if (action === "submit-store") await submitStore();
    if (action === "activate-store") await switchActiveStore(target.dataset.store);
    if (action === "delete-store") openDeleteStoreConfirm(target.dataset.store);
    if (action === "confirm-delete-store") await deleteStore(target.dataset.store);
    if (action === "start-store-oauth") await startStoreOauth(target.dataset.store);
    if (action === "refresh-center") await loadAccountCenter(target.dataset.center);
    if (action === "apply-billing-filters") {
      state.filters.billing = { account: $("billingAccountFilter")?.value || "", storeId: $("billingStoreFilter")?.value || "" };
      state.pagination.billing.page = 1; authState.billing.items = []; await loadBilling(true);
    }
    if (action === "reset-billing-filters") {
      state.filters.billing = {}; state.pagination.billing.page = 1; authState.billing.items = []; await loadBilling(true);
    }
    if (action === "refresh-billing") { authState.billing.items = []; await loadBilling(true); }
    if (action === "billing-page") {
      state.pagination.billing.page = Math.max(1, num(target.dataset.page, 1)); authState.billing.items = []; await loadBilling(true);
    }
    if (action === "open-logistics") openLogisticsModal();
    if (action === "save-logistics") await saveLogisticsBinding();
    if (action === "delete-logistics") {
      try {
        await apiJson(`/account-center/logistics/${encodeURIComponent(id)}`, { method: "DELETE" });
        await loadAccountCenter("logistics"); toast("物流绑定已删除", "success");
      } catch (error) { toast(error.message, "error"); }
    }
    if (action === "open-transparency") openTransparencyModal();
    if (action === "save-transparency") await saveTransparencyAuthorization();
    if (action === "open-list-settings") openListSettings(target.dataset.scope);
    if (action === "save-list-settings") saveListSettings(target.dataset.scope);
    if (action === "save-settings") saveSettings();
    if (action === "save-compliance-label") await saveComplianceLabel();
    if (action === "download-compliance-label") await downloadComplianceLabel();
    if (action === "toggle-api-secret") {
      const input = $(target.dataset.target);
      if (input) { input.type = input.type === "password" ? "text" : "password"; target.classList.toggle("visible", input.type === "text"); }
    }
    if (action === "save-api-settings") {
      try { await saveApiSettings(true); } catch (error) { toast(error.message, "error"); }
    }
    if (action === "save-ai-services") {
      try { await saveAiServices(); } catch (error) { toast(`配置保存失败：${error.message}`, "error"); }
    }
    if (action === "retry-ai-services") await loadAiServices(true);
    if (action === "refresh-ai-usage") await refreshAiUsage();
    if (action === "ai-usage-page") {
      try { await loadAiUsage(num(target.dataset.page, 1), $("serviceUsageDate")?.value || ""); }
      catch (error) { toast(`用量分页读取失败：${error.message}`, "error"); }
    }
    if (action === "renew-provider") {
      const url = target.dataset.url;
      if (url) window.open(url, "_blank", "noopener,noreferrer");
    }
    if (action === "switch-api-environment") {
      await setApiEnvironment(target.dataset.environment);
    }
    if (action === "test-sandbox-connection") await testSandboxConnection();
    if (action === "test-production-connection") await testProductionConnection();
    if (action === "export-selected-products") exportSelectedProducts();
    if (action === "add-store") {
      const input = $("settingStoreInput");
      const store = clean(input?.value);
      if (store) {
        captureSettingsInputs();
        state.db.settings.stores = unique([...(state.db.settings.stores || []), store]);
        if (!state.db.settings.defaultStoreName) state.db.settings.defaultStoreName = store;
        renderSettings();
      }
    }
    if (action === "remove-store") {
      captureSettingsInputs();
      state.db.settings.stores = (state.db.settings.stores || []).filter((_, index) => index !== num(target.dataset.index));
      if (!state.db.settings.stores.includes(state.db.settings.defaultStoreName)) {
        state.db.settings.defaultStoreName = state.db.settings.stores[0] || "";
      }
      renderSettings();
    }
    if (action === "add-translation-tool") {
      captureSettingsInputs();
      const name = clean($("settingTranslatorName")?.value);
      const url = clean($("settingTranslatorUrl")?.value);
      if (!name || !/^https?:\/\//i.test(url)) {
        toast("请填写翻译软件名称和完整的 http/https 网址", "error");
      } else {
        const tool = { id: uid("translator"), name, url };
        state.db.settings.translationTools = [...state.db.settings.translationTools, tool];
        if (!state.db.settings.defaultTranslationToolId) state.db.settings.defaultTranslationToolId = tool.id;
        renderSettings();
      }
    }
    if (action === "remove-translation-tool") {
      captureSettingsInputs();
      if ((state.db.settings.translationTools || []).length <= 1) {
        toast("至少保留一个翻译软件", "info");
      } else {
        state.db.settings.translationTools = state.db.settings.translationTools.filter((_, index) => index !== num(target.dataset.index));
        if (!state.db.settings.translationTools.some(tool => tool.id === state.db.settings.defaultTranslationToolId)) {
          state.db.settings.defaultTranslationToolId = state.db.settings.translationTools[0].id;
        }
        renderSettings();
      }
    }
    if (action === "import-backup") $("importFileInput").click();
    if (action === "open-purchase" && state.editing?.purchaseUrl) window.open(state.editing.purchaseUrl, "_blank", "noopener,noreferrer");
    if (action === "search-product-type") {
      await matchAmazonProductType($("editAmazonProductType")?.value, "keyword");
    }
    if (action === "select-category-path") {
      if (state.editing) {
        state.editing.categoryPath = target.dataset.path || state.editing.categoryPath;
        state.editing.categoryId = target.dataset.nodeId || "";
        const categoryType = clean(target.dataset.productType).toUpperCase();
        if (categoryType) setManualProductType(state.editing, state.editing.countries?.[0] || state.editLocale, categoryType, categoryType);
        if ($("editCategory")) $("editCategory").value = state.editing.categoryPath;
        if ($("editAmazonProductType")) $("editAmazonProductType").value = state.editing.amazonProductType;
        amazonCategorySearch.open = false;
        renderCategorySearchResults();
      }
    }
    if (action === "select-product-type" && state.editing) {
      const country = state.editLocale || state.editing.countries?.[0] || "";
      const selectedType = clean(target.dataset.productType).toUpperCase();
      const selectedLabel = clean(target.dataset.productTypeLabel || selectedType);
      setManualProductType(state.editing, country, selectedType, selectedLabel);
      if ($("editAmazonProductType")) {
        $("editAmazonProductType").value = selectedLabel || selectedType;
        $("editAmazonProductType").dataset.selectedProductType = selectedType;
        $("editAmazonProductType").dataset.selectedProductTypeLabel = selectedLabel;
      }
      amazonProductTypeMatch.open = false;
      renderProductTypeMatch();
    }
    if (action === "calculate-price") { captureDraftForm(); calculatePrices(state.editing, true); renderEdit(); }
    if (action === "ai-optimize") runAiTask("optimize");
    if (action === "ai-optimize-field") runAiTask("optimize", target.dataset.aiField);
    if (action === "translate") openTranslationConfirm();
    if (action === "confirm-locale-translation") await runLocaleTranslation($("translationSource")?.value);
    if (action === "switch-edit-locale") { captureDraftForm(); state.editLocale = target.dataset.countryCode; renderEdit(); }
    if (action === "translate-cn") runGoogleChineseTranslation();
    if (action === "dedupe-title") dedupeCurrentTitle();
    if (action === "restore-text-field") restoreTextField(target.dataset.target);
    if (action === "copy-text-field") {
      const field = $(target.dataset.target);
      if (field) {
        try {
          await navigator.clipboard.writeText(field.value || "");
          toast("已复制当前文本框内容", "success");
        } catch {
          field.select();
          document.execCommand("copy");
          toast("已复制当前文本框内容", "success");
        }
      }
    }
    if (action === "clear-text-field") {
      const field = $(target.dataset.target);
      if (field) {
        field.value = "";
        field.dispatchEvent(new Event("input", { bubbles: true }));
        toast("当前文本框已清空", "info");
      }
    }
    if (action === "clear-bullet") {
      captureDraftForm(); state.editing.bulletPoints[num(target.dataset.index)] = ""; renderEdit();
    }
    if (action === "preview-list-image") previewImage(target.dataset.imageUrl || "");
    if (action === "preview-image") previewImage(state.editing.images[num(target.dataset.index)]);
    if (action === "copy-image") await copyImageToClipboard(state.editing.images[num(target.dataset.index)]);
    if (action === "delete-image") { captureDraftForm(); state.editing.images.splice(num(target.dataset.index), 1); renderEdit(); }
    if (action === "upload-main-images") { state.uploadContext = { type: "main" }; $("imageFileInput").click(); }
    if (action === "cancel-edit") {
      const returnView = state.editingMode === "product" ? "products" : "drafts";
      finishActiveEditSession(returnView);
    }
    if (action === "next-variants") {
      captureDraftForm();
      if (state.editing.title.length > 75 || state.editing.bulletPoints.some(point => point.length > 500) || state.editing.description.length > 2000) {
        toast("存在超过字符限制的内容，请先修改", "error");
      } else beginVariants();
    }
    if (action === "add-token") {
      const type = target.dataset.type;
      const input = type === "color" ? $("newColor") : $("newSize");
      const value = clean(input.value);
      if (value) {
        const list = type === "color" ? state.variantSession.colors : state.variantSession.sizes;
        if (!list.includes(value)) list.push(value);
        rebuildVariantRows(state.variantSession); renderVariants();
      }
    }
    if (action === "remove-token") {
      const list = target.dataset.type === "color" ? state.variantSession.colors : state.variantSession.sizes;
      const index = list.indexOf(target.dataset.value);
      if (index >= 0) list.splice(index, 1);
      rebuildVariantRows(state.variantSession); renderVariants();
    }
    if (action === "batch-price") {
      const type = $("batchType").value, option = $("batchOption").value;
      const increaseValue = $("batchIncrease").value, decreaseValue = $("batchDecrease").value;
      const increase = num(increaseValue), decrease = num(decreaseValue);
      if (increase > 0 && decrease > 0) { toast("加价和降价只能填写一个", "error"); return; }
      state.variantSession.batch = { type, option, increase: increaseValue, decrease: decreaseValue };
      const delta = increase > 0 ? increase : decrease > 0 ? -decrease : 0;
      state.variantSession.rows.forEach(row => {
        if (!type || !option || row[type] === option) row.priceDelta = delta;
      });
      renderVariants(); toast("批量价格已设置", "success");
    }
    if (action === "delete-variant") { state.variantSession.rows = state.variantSession.rows.filter(row => row.id !== target.dataset.row); renderVariants(); }
    if (action === "copy-main-image") {
      const row = state.variantSession.rows.find(item => item.id === target.dataset.row);
      if (row) row.images = clone(state.editing.images || []);
      renderVariants();
    }
    if (action === "copy-same-color") {
      const source = state.variantSession.rows.find(item => item.id === target.dataset.row);
      if (source) state.variantSession.rows.forEach(row => { if (row.color === source.color) row.images = clone(source.images || []); });
      renderVariants();
    }
    if (action === "preview-variant-image" || action === "copy-variant-image" || action === "delete-variant-image") {
      const row = state.variantSession.rows.find(item => item.id === target.dataset.row);
      const imageIndex = num(target.dataset.index);
      const url = row?.images?.[imageIndex];
      if (action === "preview-variant-image" && url) previewImage(url);
      if (action === "copy-variant-image" && url) await copyImageToClipboard(url);
      if (action === "delete-variant-image" && row) {
        row.images.splice(imageIndex, 1);
        renderVariants();
      }
    }
    if (action === "upload-variant-image") { state.uploadContext = { type: "variant", rowId: target.dataset.row }; $("imageFileInput").click(); }
    if (action === "back-from-variants") {
      if (state.variantSession && state.editing) {
        state.editing.variantRows = clone(state.variantSession.rows);
        state.editing.variants = clone(state.variantSession.rows);
        state.editing.colors = clone(state.variantSession.colors);
        state.editing.sizes = clone(state.variantSession.sizes);
      }
      state.variantSession = null;
      state.view = "edit";
      renderEdit();
      toast("变体修改仍保留在本次编辑会话中，提交保存前不会写入正式草稿", "info");
    }
    if (action === "submit-product") await submitProduct();
  });

  document.addEventListener("change", event => {
    const input = event.target;
    if (input.matches("[data-compliance-field]")) {
      scheduleCompliancePreview();
      return;
    }
    if (input.matches("[data-service-provider]")) {
      authState.aiServices = authState.aiServices || {};
      if (input.dataset.serviceProvider === "ai") authState.aiServices.activeAiProvider = input.value;
      else authState.aiServices.activeTranslationProvider = input.value;
      renderAiServices();
      return;
    }
    if (input.id === "aiApiEnabled" || input.id === "translationApiEnabled") {
      saveServiceSwitches(input);
      return;
    }
    if (input.matches("[data-page-size]")) {
      const pager = state.pagination[input.dataset.scope];
      if (pager) {
        pager.pageSize = Number(input.value) || 10;
        pager.page = 1;
        if (input.dataset.scope === "orders") {
          state.orders.items = [];
          loadOrders(true);
        } else render();
      }
      return;
    }
    if (input.matches("[data-page-jump]")) {
      const pager = state.pagination[input.dataset.scope];
      if (pager) {
        pager.page = Math.max(1, Number(input.value) || 1);
        render();
      }
      return;
    }
    if (input.id === "storeRegion") {
      const countries = REGION_COUNTRIES[input.value] || [];
      if ($("storeCountriesPreview")) $("storeCountriesPreview").value = countries.map(countryName).join("、") || "选择站点后自动生成";
      return;
    }
    if (input.matches("[name=upcMode]")) {
      captureDraftForm();
      state.editing.upcMode = input.value;
      if (input.value === "exempt") state.editing.upc = "";
      renderEdit();
      return;
    }
    if (input.matches("[data-country]")) {
      captureDraftForm();
      const allowed = activeStoreCountries();
      state.editing.countries = [...document.querySelectorAll("[data-country]:checked")].map(node => node.value).filter(code => allowed.includes(code));
      if (!state.editing.countries.length && allowed.length) state.editing.countries = [allowed[0]];
      state.editing.localizedContent = state.editing.localizedContent || {};
      state.editing.amazonProductTypes = state.editing.amazonProductTypes || {};
      state.editing.amazonProductTypeLabels = state.editing.amazonProductTypeLabels || {};
      state.editing.productTypeResolutions = state.editing.productTypeResolutions || {};
      state.editing.countries.forEach(code => { if (!state.editing.localizedContent[code]) state.editing.localizedContent[code] = emptyListing(); });
      Object.keys(state.editing.localizedContent).forEach(code => { if (!state.editing.countries.includes(code)) delete state.editing.localizedContent[code]; });
      Object.keys(state.editing.amazonProductTypes).forEach(code => { if (!state.editing.countries.includes(code)) delete state.editing.amazonProductTypes[code]; });
      Object.keys(state.editing.amazonProductTypeLabels).forEach(code => { if (!state.editing.countries.includes(code)) delete state.editing.amazonProductTypeLabels[code]; });
      Object.keys(state.editing.productTypeResolutions).forEach(code => { if (!state.editing.countries.includes(code)) delete state.editing.productTypeResolutions[code]; });
      if (!state.editing.countries.includes(state.editLocale)) state.editLocale = state.editing.countries[0];
      calculatePrices(state.editing);
      renderEdit();
      return;
    }
    if (input.matches("[data-select]")) {
      const scope = input.dataset.select;
      input.checked ? state.selected[scope].add(input.value) : state.selected[scope].delete(input.value);
      render();
    }
    if (input.matches("[data-select-all]")) {
      const scope = input.dataset.selectAll;
      const pageIds = [...document.querySelectorAll(`[data-select="${scope}"]`)].map(node => node.value).filter(Boolean);
      state.selected[scope].clear();
      if (input.checked) pageIds.forEach(id => state.selected[scope].add(id));
      render();
    }
    if (input.id === "batchType") {
      state.variantSession.batch.type = input.value;
      state.variantSession.batch.option = "";
      const values = input.value === "color" ? state.variantSession.colors : input.value === "size" ? state.variantSession.sizes : [];
      $("batchOption").innerHTML = `<option value="">请选择</option>${values.map(value => `<option value="${attr(value)}">${esc(value)}</option>`).join("")}`;
    }
    if (input.id === "batchType") queueMicrotask(() => enhanceSelects(document));
    if (input.id === "batchOption") state.variantSession.batch.option = input.value;
  });

  document.addEventListener("input", event => {
    const input = event.target;
    if (input.matches("[data-compliance-field]")) {
      scheduleCompliancePreview();
      return;
    }
    if (input.id === "editCategory") {
      if (state.editing) state.editing.categoryPath = input.value;
      clearTimeout(categorySearchTimer);
      const leaf = clean(input.value).split(/[\/>＞]/).pop().trim();
      if (leaf.length >= 2) categorySearchTimer = setTimeout(() => loadAmazonCategoryPaths(leaf), 350);
      else {
        amazonCategorySearch = { draftId: state.editing?.id || "", keyword: leaf, items: [], loading: false, error: "", open: false };
        renderCategorySearchResults();
      }
    }
    if (input.matches("#editTitle,#editHighlight,#editKeywords,#editDescription,[data-bullet]")) {
      const limit = Number(input.getAttribute("maxlength"));
      const counterNode = input.parentElement.querySelector("[data-counter]");
      if (counterNode) {
        counterNode.textContent = `${input.value.length}/${limit}`;
        counterNode.classList.toggle("over", input.value.length > limit);
      }
    }
    if (input.id === "batchIncrease" && state.variantSession) state.variantSession.batch.increase = input.value;
    if (input.id === "batchDecrease" && state.variantSession) state.variantSession.batch.decrease = input.value;
    if (input.matches("[data-variant-price-delta]") && state.variantSession && state.editing) {
      const row = state.variantSession.rows.find(item => item.id === input.dataset.row);
      if (!row) return;
      row.priceDelta = num(input.value);
      input.classList.toggle("positive", row.priceDelta > 0);
      input.classList.toggle("negative", row.priceDelta < 0);
      state.editing.variantPricingReviewed = true;
      const latest = variantPriceFromRmb(state.editing, variantLatestRmb(state.editing, row));
      const latestNode = document.querySelector(`[data-variant-latest="${CSS.escape(row.id)}"]`);
      if (latestNode) latestNode.textContent = latest == null ? "待确认" : `${primaryVariantCountry(state.editing).symbol}${latest.toFixed(2)}`;
      return;
    }
    if (input.matches("[data-country-price]") && state.editing) {
      const code = input.dataset.countryPrice;
      const country = countryMeta(code);
      const foreign = Math.max(0, num(input.value));
      const rate = num(activeSettings()[country.rateKey], 1);
      const rmb = foreign * rate;
      state.editing.countryPrices = state.editing.countryPrices || {};
      state.editing.countryPrices[code] = { ...(state.editing.countryPrices[code] || {}), foreign, rmb, currency: country.currency, manual: true };
      const profit = rmb - num(state.editing.cost) - num(state.editing.shipping);
      const rmbCell = document.querySelector(`[data-country-rmb="${CSS.escape(code)}"]`);
      const profitCell = document.querySelector(`[data-country-profit="${CSS.escape(code)}"]`);
      if (rmbCell) rmbCell.textContent = `¥${rmb.toFixed(2)}`;
      if (profitCell) profitCell.textContent = `¥${profit.toFixed(2)}`;
      if (code === state.editing.countries?.[0]) {
        state.editing.rmbPrice = rmb;
        state.editing.foreignPrice = foreign;
      }
      applyVariantBaseShift(state.editing);
    }
  });

  document.addEventListener("focusin", event => {
    if (event.target?.id === "editAmazonProductType" && amazonProductTypeMatch.items.length) {
      const currentCountry = state.editLocale || state.editing?.countries?.[0] || "";
      if (amazonProductTypeMatch.country === currentCountry && amazonProductTypeMatch.mode === "keyword") {
        amazonProductTypeMatch.open = true;
        renderProductTypeMatch();
      }
    }
    if (event.target?.id !== "editCategory") return;
    const leaf = clean(event.target.value).split(/[\/>＞]/).pop().trim();
    if (amazonCategorySearch.draftId === state.editing?.id && amazonCategorySearch.keyword === leaf && (amazonCategorySearch.items.length || amazonCategorySearch.error)) {
      amazonCategorySearch.open = true;
      renderCategorySearchResults();
    }
  });

  document.addEventListener("focusout", event => {
    if (event.target?.id === "editTitle") {
      const title = event.target.value;
      setTimeout(() => matchAmazonProductType(title, "title"), 0);
    }
    if (event.target?.id === "editAmazonProductType") {
      setTimeout(() => {
        if (document.activeElement?.closest?.(".product-type-combobox")) return;
        amazonProductTypeMatch.open = false;
        renderProductTypeMatch();
      }, 0);
    }
    if (!event.target?.closest?.(".category-select-wrap")) return;
    setTimeout(() => {
      const active = document.activeElement;
      if (active?.id === "editCategory" || active?.closest?.("#categorySearchResults")) return;
      amazonCategorySearch.open = false;
      renderCategorySearchResults();
    }, 0);
  });

  document.addEventListener("keydown", event => {
    if (event.key === "Enter" && event.target?.id === "editAmazonProductType") {
      event.preventDefault();
      matchAmazonProductType(event.target.value, "keyword");
      return;
    }
    if (event.key === "Escape") closeCustomSelects();
  });

  document.addEventListener("load", event => {
    const image = event.target;
    if (!(image instanceof HTMLImageElement) || !image.matches("[data-measure-image]")) return;
    const size = image.closest(".image-card")?.querySelector("[data-image-size]");
    if (size) size.textContent = `${image.naturalWidth}×${image.naturalHeight} 像素`;
  }, true);

  document.addEventListener("error", event => {
    const image = event.target;
    if (!(image instanceof HTMLImageElement) || !image.matches("[data-image-fallback]")) return;
    const fallback = document.createElement("div");
    fallback.className = "thumb-fallback";
    fallback.textContent = "A";
    image.replaceWith(fallback);
  }, true);

  $("collectProductBtn").addEventListener("click", () => openCollectModal());
  $("exportDataBtn").addEventListener("click", exportBackup);
  $("downloadExtensionBtn")?.addEventListener("click", () => {
    const link = document.createElement("a");
    link.href = extensionDownloadUrl();
    link.download = "yilian-chrome-extension-v3.0.0.zip";
    document.body.appendChild(link);
    link.click();
    link.remove();
    toast("正在下载最新版易联跨境扩展", "success");
  });
  $("dataAccountSelect").addEventListener("change", event => switchDataAccount(event.target.value));
  $("activeStoreSelect")?.addEventListener("change", event => switchActiveStore(event.target.value));
  $("logoutBtn").addEventListener("click", async () => {
    await clearSession(true);
    const status = await apiJson("/auth/status", {}, false).catch(() => ({ data: { registrationOpen: false } }));
    authState.registrationOpen = Boolean(status.data?.registrationOpen);
    authState.passwordResetEnabled = Boolean(status.data?.passwordResetEnabled);
    authState.deploymentMode = status.data?.deploymentMode === "cloud" ? "cloud" : "local";
    renderAuth(authState.registrationOpen ? "register" : "login", "已安全退出", "success");
  });
  $("authRoot").addEventListener("click", async event => {
    const target = event.target.closest("[data-auth-action]");
    if (!target) return;
    if (target.dataset.authAction === "toggle-password") {
      const input = $("authPassword");
      if (!input) return;
      const visible = input.type === "text";
      input.type = visible ? "password" : "text";
      target.classList.toggle("visible", !visible);
      target.setAttribute("aria-label", visible ? "显示密码" : "隐藏密码");
      target.title = visible ? "显示密码" : "隐藏密码";
      return;
    }
    if (target.dataset.authAction === "mode") {
      renderAuth(target.dataset.mode || "login");
      return;
    }
    if (target.dataset.authAction === "send-code") {
      const phone = clean($("authPhone")?.value);
      try {
        target.disabled = true;
        const result = await apiJson("/auth/send-code", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ phone, purpose: authState.mode === "forgot" ? "reset" : "register" })
        }, false);
        const localCode = result.data?.localCode;
        setAuthMessage(localCode ? `本机验证模式，验证码：${localCode}（5 分钟有效）` : "验证码已发送到手机", "success");
      } catch (error) {
        setAuthMessage(error.message, "error");
      } finally {
        target.disabled = false;
      }
    }
  });
  $("authRoot").addEventListener("submit", async event => {
    if (event.target.id !== "authForm") return;
    event.preventDefault();
    const password = $("authPassword")?.value || "";
    try {
      if (authState.mode === "login") {
        const username = clean($("authUsername")?.value);
        const result = await apiJson("/auth/login", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ username, password })
        }, false);
        saveRememberedLogin(username, password, Boolean($("rememberPassword")?.checked));
        applySession(result.data);
        await startAuthenticatedApp({ serverWins: true });
        toast("登录成功", "success");
      } else if (authState.mode === "register") {
        await apiJson("/auth/register", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ phone: clean($("authPhone")?.value), code: clean($("authCode")?.value), password })
        }, false);
        authState.registrationOpen = false;
        renderAuth("login", "注册成功，请使用手机号和密码登录", "success");
      } else {
        await apiJson("/auth/reset-password", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ phone: clean($("authPhone")?.value), code: clean($("authCode")?.value), password })
        }, false);
        renderAuth("login", "密码修改成功，请重新登录", "success");
      }
    } catch (error) {
      setAuthMessage(error.message, "error");
    }
  });
  $("imageFileInput").addEventListener("change", event => {
    if (state.uploadContext) readImages(event.target.files, state.uploadContext);
    event.target.value = "";
  });
  $("importFileInput").addEventListener("change", async event => {
    const file = event.target.files[0];
    if (!file) return;
    try {
      const imported = JSON.parse(await file.text());
      if (!imported.collections || !imported.drafts || !imported.products) throw new Error("备份格式不正确");
      state.db = { collections: imported.collections, drafts: imported.drafts, products: imported.products, settings: normalizeSettings(imported.settings) };
      state.db.settings.stores = unique(Array.isArray(state.db.settings.stores) ? state.db.settings.stores : []);
      saveDb(); render(); toast("备份已导入", "success");
    } catch (error) { toast(`导入失败：${error.message}`, "error"); }
    event.target.value = "";
  });

  if (HAS_EXTENSION_API) {
    chrome.runtime.onMessage.addListener(message => {
      if (message?.type === "AI_TASK_RESULT") receiveAiResult(message);
      if (message?.type === "GOOGLE_TRANSLATE_RESULT") receiveGoogleTranslation(message);
    });

    chrome.storage.onChanged.addListener((changes, areaName) => {
      if (areaName !== "local") return;
      if (changes.capturedQueue?.newValue?.length) importCapturedQueue();
      if (changes.lastGoogleTranslateResult?.newValue) receiveGoogleTranslation(changes.lastGoogleTranslateResult.newValue);
      if (changes.lastAiTaskResult?.newValue) receiveAiResult(changes.lastAiTaskResult.newValue);
    });
  }
  window.addEventListener("message", event => {
    if (event.source !== window || event.data?.type !== "SDM_EXTENSION_EVENT") return;
    const message = event.data.message;
    if (message?.type === "AI_TASK_RESULT") receiveAiResult(message);
    if (message?.type === "GOOGLE_TRANSLATE_RESULT") receiveGoogleTranslation(message);
  });
  window.addEventListener("beforeunload", () => {
    persistUiState();
    flushLocalCache();
  });

  async function initialize() {
    prepareChromeLayout();
    enhanceSelects(document);
    document.body.classList.add("auth-active");
    let stored = null;
    try { stored = JSON.parse(localStorage.getItem(AUTH_KEY) || "null"); } catch { localStorage.removeItem(AUTH_KEY); }
    authState.token = stored?.token || "";
    const status = await apiJson("/auth/status", {}, false).catch(() => null);
    authState.registrationOpen = Boolean(status?.data?.registrationOpen);
    authState.passwordResetEnabled = Boolean(status?.data?.passwordResetEnabled);
    authState.deploymentMode = status?.data?.deploymentMode === "cloud" ? "cloud" : "local";
    if (authState.token) {
      try {
        const result = await apiJson("/auth/me");
        applySession({ ...result.data, token: authState.token });
        await startAuthenticatedApp({ serverWins: true });
        return;
      } catch {
        await clearSession(false);
      }
    }
    renderAuth(authState.registrationOpen ? "register" : "login");
  }

  initialize();
})();
