// 小青蛙电商助手 - 后台页面脚本 v3.1 (大白鹅风格)

function normalizeMallIdValue(value) {
  const text = String(value || '').trim();
  if (!text) return '';
  if (text.toLowerCase().startsWith('ozon:')) return text.split(',')[0].trim();
  const match = text.match(/\d{6,}/);
  return match ? match[0] : text.split(',')[0].trim();
}

function getMallIdPlatform(value) {
  return String(value || '').trim().toLowerCase().startsWith('ozon:') ? 'ozon' : 'temu';
}

function normalizeTemuMallId(value) {
  const mallId = normalizeMallIdValue(value);
  if (!mallId || getMallIdPlatform(mallId) !== 'temu') return '';
  return mallId;
}

function getActiveWorkspacePlatform() {
  try {
    if (typeof getSelectedAuthPlatform === 'function') return getSelectedAuthPlatform();
  } catch (error) {
    // The platform helpers are declared later in the bundled script.
  }
  return 'temu';
}

function isMallIdAllowedForActivePlatform(value) {
  const platform = getMallIdPlatform(value);
  const mallId = normalizeMallIdValue(value);
  if (!mallId) return false;
  return platform === getActiveWorkspacePlatform();
}

function clearMismatchedShopState(mallId, context = '平台校验') {
  if (!mallId || isMallIdAllowedForActivePlatform(mallId)) return false;
  state.mallId = null;
  state.antiContent = null;
  state.mallName = null;
  try {
    if (typeof chrome !== 'undefined') chrome.storage?.local?.remove?.(['temuAuth', 'temuLocalMallId'])?.catch?.(() => {});
  } catch (error) {
    // Ignore storage cleanup failures; keeping state clean is enough for this render.
  }
  console.warn(`[小青蛙电商助手] ${context}: 已忽略跨平台店铺 ${mallId}`);
  return true;
}

function normalizeAntiContentValue(value) {
  const text = String(value || '').trim();
  if (!text) return '';
  return text.split(',').map(part => part.trim()).filter(Boolean)[0] || '';
}

function normalizeTemuAuth(auth = {}) {
  return {
    ...auth,
    mallId: normalizeMallIdValue(auth.mallId),
    antiContent: normalizeAntiContentValue(auth.antiContent)
  };
}

function applyTemuAuthToState(auth = {}) {
  const normalized = normalizeTemuAuth(auth);
  if (normalized.mallId && !isMallIdAllowedForActivePlatform(normalized.mallId)) {
    clearMismatchedShopState(normalized.mallId, '恢复当前店铺');
    return { ...normalized, mallId: '' };
  }
  if (normalized.mallId) state.mallId = normalized.mallId;
  if (normalized.antiContent) state.antiContent = normalized.antiContent;
  if (auth.mallName !== undefined) state.mallName = auth.mallName || null;
  return normalized;
}

// ===== 全局图片加载失败处理（替代 onerror 内联事件，兼容 MV3 CSP）=====
document.addEventListener('error', function(e) {
  if (e.target && e.target.tagName === 'IMG') {
    const size = parseInt(e.target.dataset.fallbackSize) || 44;
    const fontSize = size >= 48 ? 18 : 16;
    const placeholder = document.createElement('div');
    placeholder.style.cssText = `width:${size}px;height:${size}px;background:#f5f5f5;border-radius:4px;display:flex;align-items:center;justify-content:center;color:#ccc;font-size:${fontSize}px;flex-shrink:0;`;
    placeholder.textContent = '📷';
    e.target.replaceWith(placeholder);
  }
}, true); // 使用捕获阶段，确保先于其他处理器

window.addEventListener('unhandledrejection', (event) => {
  const message = event.reason?.message || String(event.reason || '未知异步错误');
  console.error('[小青蛙电商助手] 未捕获异步错误:', event.reason);
  if (typeof showToast === 'function') {
    showToast(`操作失败: ${message}`, 'error');
  }
  event.preventDefault();
});

window.addEventListener('error', (event) => {
  if (event.target && event.target.tagName === 'IMG') return;
  const message = event.message || event.error?.message || '未知脚本错误';
  console.error('[小青蛙电商助手] 未捕获脚本错误:', event.error || message);
  if (typeof showToast === 'function') {
    showToast(`页面脚本错误: ${message}`, 'error');
  }
  event.preventDefault();
});

const LATEST_EXTENSION_VERSION = '2.7.90';
const EXTENSION_DOWNLOAD_FILENAME = `小青蛙助手${LATEST_EXTENSION_VERSION}.zip`;
const EXTENSION_DOWNLOAD_URL = `https://xiaoqingwa.vip/downloads/${encodeURIComponent(EXTENSION_DOWNLOAD_FILENAME)}`;

async function safeRuntimeSendMessage(message, fallback = null, timeoutMs = 30000) {
  try {
    const messageWithTimeout = message && typeof message === 'object'
      ? { timeoutMs: Math.max(Number(timeoutMs || 30000), Number(message.timeoutMs || 0)), ...message }
      : message;
    const enrichedMessage = await enrichRuntimeMessageWithServerAuth(messageWithTimeout);
    return await Promise.race([
      chrome.runtime.sendMessage(enrichedMessage),
      new Promise((_, reject) => setTimeout(() => reject(new Error('background 响应超时')), timeoutMs))
    ]);
  } catch (error) {
    const text = error?.message || String(error) || '消息发送失败';
    console.warn('[小青蛙电商助手] 后台消息失败:', message?.action || message?.type || message, text);
    return fallback ?? { success: false, error: text };
  }
}

function isExtensionContextInvalidatedError(error) {
  const text = typeof error === 'string' ? error : (error?.message || error?.error || String(error || ''));
  return /Extension context invalidated|Execution plugin is not connected|Receiving end does not exist|Could not establish connection|消息发送失败|background 响应超时/i.test(text);
}

function getRuntimeReconnectMessage(featureName = '当前功能') {
  return `${featureName}连接已失效，请刷新小青蛙网页，并在首页点击“重新连接”后再试。`;
}

async function enrichRuntimeMessageWithServerAuth(message) {
  if (!message || typeof message !== 'object' || message.serverAuthToken) return message;
  try {
    const auth = await getServerAuthState();
    if (!auth?.temuServerToken) return message;
    return {
      ...message,
      serverAuthToken: auth.temuServerToken,
      serverAuthUser: auth.temuServerUser || null,
      serverAuthAt: auth.temuServerAuthAt || Date.now()
    };
  } catch {
    return message;
  }
}

async function sendRuntimeMessageWithRetry(message, fallback = null, timeoutMs = 30000, retries = 1) {
  let lastResult = fallback;
  for (let attempt = 0; attempt <= retries; attempt += 1) {
    const result = await safeRuntimeSendMessage(message, fallback, timeoutMs);
    lastResult = result;
    if (result?.success !== false || attempt >= retries) return result;
    const errorText = String(result.error || '');
    if (!/未完成连接|未连接|Extension context invalidated|background 响应超时|消息发送失败|Receiving end does not exist/i.test(errorText)) {
      return result;
    }
    await new Promise(resolve => setTimeout(resolve, 800));
  }
  return lastResult;
}

const rawRuntimeSendMessage = chrome.runtime.sendMessage.bind(chrome.runtime);
chrome.runtime.sendMessage = function safeGlobalRuntimeSendMessage(...args) {
  try {
    if (args[0] && typeof args[0] === 'object' && !args[0].serverAuthToken) {
      const enrichedPromise = enrichRuntimeMessageWithServerAuth(args[0])
        .then(enrichedMessage => rawRuntimeSendMessage(enrichedMessage, ...args.slice(1)))
        .catch(error => {
          const text = error?.message || String(error) || '消息发送失败';
          console.warn('[小青蛙电商助手] 全局后台消息失败:', args[0]?.action || args[0]?.type || args[0], text);
          return { success: false, error: text };
        });
      return enrichedPromise;
    }
    const maybePromise = rawRuntimeSendMessage(...args);
    if (maybePromise && typeof maybePromise.then === 'function') {
      return maybePromise.catch(error => {
        const text = error?.message || String(error) || '消息发送失败';
        console.warn('[小青蛙电商助手] 全局后台消息失败:', args[0]?.action || args[0]?.type || args[0], text);
        return { success: false, error: text };
      });
    }
    return maybePromise;
  } catch (error) {
    const text = error?.message || String(error) || '消息发送失败';
    console.warn('[小青蛙电商助手] 全局后台消息异常:', args[0]?.action || args[0]?.type || args[0], text);
    return Promise.resolve({ success: false, error: text });
  }
};

const PRICING_HALF_MANAGED_PENDING_STATUS = 0;
const PRICING_PENDING_STATUS_LIST = [PRICING_HALF_MANAGED_PENDING_STATUS, 1];

function isPricingPendingReviewStatus(value) {
  const status = Number(value);
  return PRICING_PENDING_STATUS_LIST.includes(status);
}

async function safeStorageSet(values = {}, context = 'storage') {
  try {
    await chrome.storage.local.set(values);
    return true;
  } catch (error) {
    console.warn(`[小青蛙电商助手] ${context} 写入失败:`, error);
    return false;
  }
}

function isWebsiteBridgeMode() {
  return window.location.protocol.startsWith('http');
}

function getBridgeRuntimeState() {
  const active = Boolean(window.__TEMU_WEB_BRIDGE_ACTIVE__) || document.documentElement?.getAttribute?.('data-temu-extension-installed') === '1';
  const ready = Boolean(window.__TEMU_WEB_BRIDGE_READY__);
  const attrReady = document.documentElement?.getAttribute?.('data-temu-bridge-ready') === '1';
  return {
    active,
    ready: ready || attrReady
  };
}

function updateBridgeBanner(options = {}) {
  const banner = document.getElementById('bridgeBanner');
  const titleEl = document.getElementById('bridgeBannerTitle');
  const descEl = document.getElementById('bridgeBannerDesc');
  if (!banner || !titleEl || !descEl) return;

  const visible = Boolean(options.visible);
  banner.classList.toggle('show', visible);
  banner.classList.toggle('is-warning', Boolean(options.warning));
  banner.style.display = 'none';
  if (!visible) return;

  titleEl.textContent = options.title || '网站工作台已启动';
  descEl.textContent = options.description || '请保持“小青蛙电商助手”已安装，并在另一个标签页登录 TEMU 卖家后台，这样网站上的操作才能直接调用本地浏览器执行。';
}

async function reconnectCurrentShop() {
  const btn = document.getElementById('reconnectBtn');
  const dhBtn = document.getElementById('dhTemuReconnectBtn');
  const dhQuickBtn = document.getElementById('dhTemuReconnectQuickBtn');
  const topbarBtn = document.getElementById('dhTemuReconnectTopBtn');
  let detectedDifferentMall = false;
  const stored = await chrome.storage.local.get(['temuAuth', 'temuLocalMallId']).catch(() => ({}));
  const storedAuth = stored?.temuAuth || {};
  const previousAuth = {
    mallId: normalizeMallIdValue(state.mallId || storedAuth.mallId || stored?.temuLocalMallId || ''),
    antiContent: normalizeAntiContentValue(state.antiContent || storedAuth.antiContent || ''),
    mallName: state.mallName || storedAuth.mallName || null
  };
  const hasPreviousAuth = Boolean(previousAuth.mallId && previousAuth.antiContent);
  if (btn) {
    btn.disabled = true;
    btn.textContent = '⏳ 检测中...';
  }
  if (dhBtn) {
    dhBtn.disabled = true;
    dhBtn.textContent = '正在检测店铺...';
  }
  if (dhQuickBtn) {
    dhQuickBtn.disabled = true;
    dhQuickBtn.textContent = '正在检测店铺...';
  }
  if (topbarBtn) {
    topbarBtn.disabled = true;
    topbarBtn.textContent = '正在检测店铺...';
  }

  try {
    const currentPageAuth = await safeRuntimeSendMessage({ action: 'getCurrentMallId' }, null, 4000);
    const currentPageMallId = normalizeMallIdValue(currentPageAuth?.mallId || '');
    if (currentPageMallId && currentPageMallId !== previousAuth.mallId) {
      detectedDifferentMall = true;
      rememberShopName(previousAuth.mallId, previousAuth.mallName);
      await chrome.storage.local.remove(['temuAuth']);
      await safeRuntimeSendMessage({ action: 'clearAuth' }, { success: false }, 2500);
      state.mallId = null;
      state.antiContent = null;
      state.mallName = currentPageAuth?.mallName || null;
      resetSalesDataState();
      updateSalesSyncDisplay();
      renderSalesDataTable();
      updateConnectionStatus(false);
    } else {
      await safeRuntimeSendMessage({ action: 'clearAuth' }, { success: false }, 2500);
    }

    const detectPromise = autoDetectAuth({ forceRefresh: true, rejectCache: true });
    const timeoutPromise = new Promise((_, reject) =>
      setTimeout(() => reject(new Error('检测超时（15秒），请刷新页面重试')), 15000)
    );
    await Promise.race([detectPromise, timeoutPromise]);
    if (!state.mallId || !state.antiContent) {
      throw new Error('未获取到完整店铺认证信息');
    }
    rememberShopName(state.mallId, state.mallName);
    await loadDashboardMembership();
    await hydrateTemuDashboardFromServerSnapshots();
  } catch (err) {
    console.error('[小青蛙电商助手] 重新连接失败:', err);
    if (hasPreviousAuth && !detectedDifferentMall) {
      const restoredAuth = applyTemuAuthToState(previousAuth);
      await safeStorageSet({
        temuAuth: { ...storedAuth, ...restoredAuth, updatedAt: Date.now() },
        temuLocalMallId: restoredAuth.mallId
      }, '恢复店铺认证信息');
      await safeRuntimeSendMessage({
        action: 'setAuth',
        mallId: restoredAuth.mallId,
        antiContent: restoredAuth.antiContent,
        mallName: restoredAuth.mallName || null
      });
      updateConnectionStatus(true, restoredAuth.mallName);
      showToast('重新连接暂未获取到新认证，已保持当前店铺连接', 'warning');
    } else {
      showToast('连接失败，请刷新 TEMU 后台后再试', 'error');
      updateConnectionStatus(false);
    }
  } finally {
    const latestBtn = document.getElementById('reconnectBtn');
    const latestDhBtn = document.getElementById('dhTemuReconnectBtn');
    const latestDhQuickBtn = document.getElementById('dhTemuReconnectQuickBtn');
    const latestTopbarBtn = document.getElementById('dhTemuReconnectTopBtn');
    if (latestBtn) {
      latestBtn.disabled = false;
      latestBtn.textContent = '🔄 重新连接';
    }
    if (latestDhBtn) {
      latestDhBtn.disabled = false;
      latestDhBtn.textContent = '重新连接店铺';
    }
    if (latestDhQuickBtn) {
      latestDhQuickBtn.disabled = false;
      latestDhQuickBtn.textContent = '重新连接店铺';
    }
    if (latestTopbarBtn) {
      latestTopbarBtn.disabled = false;
      latestTopbarBtn.textContent = '重新连接店铺';
    }
  }
}

function visitCurrentStore() {
  const mallId = state.mallId;
  if (mallId) {
    window.open(`https://www.temu.com/mall.html?mall_id=${mallId}`, '_blank');
  } else {
    showToast('未检测到店铺ID，请先连接店铺', 'warning');
  }
}

function syncWebsiteBridgeUi() {
  if (!isWebsiteBridgeMode()) return;
  const statusEl = document.getElementById('connectionStatus');
  const mallInfoEl = document.getElementById('mallInfo');
  const bridge = getBridgeRuntimeState();

  if (state.mallId && state.antiContent) {
    updateConnectionStatus(true, state.mallName);
    return;
  }

  if (!bridge.active) {
    if (statusEl) {
      statusEl.textContent = '未装助手';
      statusEl.className = 'status-badge status-bridge-missing';
    }
    if (mallInfoEl) mallInfoEl.textContent = '请先安装并启用小青蛙电商助手';
    updateBridgeBanner({
      visible: true,
      warning: true,
      title: '还没有连接到小青蛙电商助手',
      description: '当前网页已经切到网站版工作台，但真正执行 TEMU 操作仍需要浏览器里的“小青蛙电商助手”。请确认插件已加载，并允许它在 xiaoqingwa.vip 运行。'
    });
    syncDingHuiTemuHome();
    return;
  }

  if (!bridge.ready) {
    if (statusEl) {
      statusEl.textContent = '等待助手';
      statusEl.className = 'status-badge status-disconnected';
    }
    if (mallInfoEl) mallInfoEl.textContent = '小青蛙电商助手已安装，正在等待连接';
    updateBridgeBanner({
      visible: true,
      title: '正在连接小青蛙电商助手',
      description: '助手已注入网站页面。如果长时间停留在这里，请刷新当前页面，或确认扩展已允许在本站运行。'
    });
    syncDingHuiTemuHome();
    return;
  }

  initDeferredBridgeModulesIfReady();
  restoreConnectedShopFromStorage({ updateUi: true, detectIfMissing: true }).then((connected) => {
    if (!connected && !(state.mallId && state.antiContent)) {
      scheduleBridgeShopDetection();
    }
  }).catch(err => {
    console.warn('[小青蛙电商助手] 桥接就绪后主动读取店铺失败:', err);
  });
  if (statusEl) {
    statusEl.textContent = '助手已连接';
    statusEl.className = 'status-badge status-bridge-ready';
  }
  if (mallInfoEl) mallInfoEl.textContent = getCurrentShopDisplayName();
  updateBridgeBanner({
    visible: true,
    title: '网站工作台已连接小青蛙电商助手',
    description: '账号和店铺授权以服务器为准；需要执行 TEMU 操作时，助手会再读取当前浏览器里的 TEMU 店铺。'
  });
  syncDingHuiTemuHome();
}

window.addEventListener('temu-web-bridge-state-change', () => {
  syncWebsiteBridgeUi();
  restoreConnectedShopFromStorage({ updateUi: true, detectIfMissing: true }).catch(err => {
    console.warn('[小青蛙电商助手] 桥接就绪后恢复店铺状态失败:', err);
  });
});

function shouldDeferBridgeDependentInit() {
  if (!isWebsiteBridgeMode()) return false;
  const bridge = getBridgeRuntimeState();
  return !(bridge.active && bridge.ready);
}

const deferredBridgeModules = {
  customized: false,
  activity: false,
  qualification: false
};
const temuModuleInitState = {
  customized: false,
  activity: false,
  qualification: false
};
let deferredStartupTasksScheduled = false;
let restoreShopPromise = null;
let bridgeShopDetectPromise = null;
let bridgeShopDetectStartedAt = 0;

function scheduleDeferredStartupTasks() {
  if (deferredStartupTasksScheduled) return;
  deferredStartupTasksScheduled = true;
  window.setTimeout(() => {
    const tasks = [
      ['检测认证信息', autoDetectAuth],
      ['加载设置', loadSettings],
      ['加载合规模板', loadTemplates],
      ['加载销量缓存', loadSalesDataCache]
    ];
    tasks.reduce((chain, [label, task]) => chain.then(() => task().catch((error) => {
      console.warn(`[小青蛙电商助手] 延迟${label}失败:`, error);
    })), Promise.resolve());
  }, 1200);
}

async function restoreConnectedShopFromStorage(options = {}) {
  const { updateUi = false, detectIfMissing = false } = options;
  if (restoreShopPromise) return restoreShopPromise;
  restoreShopPromise = (async () => {
  try {
    const stored = await Promise.race([
      chrome.storage.local.get(['temuAuth', 'temuLocalMallId']),
      new Promise((resolve) => window.setTimeout(() => resolve({}), 700))
    ]);
    let auth = normalizeTemuAuth(stored?.temuAuth || {});
    if ((!auth.mallId || !auth.antiContent) && getBridgeRuntimeState().ready) {
      const authResult = await safeRuntimeSendMessage({ action: 'getAuth' }, null, 1200);
      if (authResult?.mallId) {
        auth = normalizeTemuAuth({
          mallId: authResult.mallId,
          antiContent: authResult.antiContent || auth.antiContent || '',
          mallName: authResult.mallName || auth.mallName || null,
          updatedAt: Date.now()
        });
        await safeStorageSet({ temuAuth: auth, temuLocalMallId: auth.mallId }, '店铺认证信息');
      }
    }
    const mallId = normalizeMallIdValue(auth.mallId || stored?.temuLocalMallId || '');
    if (!mallId) return false;

    state.mallId = mallId;
    state.mallName = auth.mallName || state.mallName || null;
    if (auth.antiContent) state.antiContent = normalizeAntiContentValue(auth.antiContent);
    if (updateUi) updateConnectionStatus(true, state.mallName);
    hydrateTemuSnapshotSyncTimes();
    return true;
  } catch (error) {
    console.warn('[小青蛙电商助手] 恢复店铺连接状态失败:', error?.message || error);
    return false;
  }
  })();
  try {
    const connected = await restoreShopPromise;
    if (!connected && detectIfMissing) {
      scheduleBridgeShopDetection();
    }
    return connected;
  } finally {
    restoreShopPromise = null;
  }
}

function scheduleBridgeShopDetection(force = false) {
  if (!isWebsiteBridgeMode()) return;
  const bridge = getBridgeRuntimeState();
  if (!bridge.active || !bridge.ready) return;
  if (state.mallId && state.antiContent) return;
  const now = Date.now();
  if (!force && bridgeShopDetectPromise && now - bridgeShopDetectStartedAt < 15000) return;
  bridgeShopDetectStartedAt = now;
  bridgeShopDetectPromise = (async () => {
    try {
      const result = await safeRuntimeSendMessage({ action: 'autoDetectAuth' }, null, 6500);
      if (result?.success && result.mallId) {
        const normalized = normalizeTemuAuth(result);
        state.mallId = normalized.mallId;
        state.mallName = result.mallName || state.mallName || null;
        if (normalized.antiContent) state.antiContent = normalized.antiContent;
        await safeStorageSet({
          temuAuth: {
            mallId: state.mallId,
            antiContent: state.antiContent || '',
            mallName: state.mallName || null,
            updatedAt: Date.now()
          },
          temuLocalMallId: state.mallId
        }, '店铺认证信息');
        updateConnectionStatus(Boolean(state.mallId && state.antiContent), state.mallName);
        loadDashboardMembership().catch(err => console.warn('[小青蛙电商助手] 店铺检测后刷新会员状态失败:', err));
        return true;
      }
      console.warn('[小青蛙电商助手] 桥接店铺检测未成功:', result?.error || result);
      return false;
    } catch (error) {
      console.warn('[小青蛙电商助手] 桥接店铺检测失败:', error?.message || error);
      return false;
    } finally {
      window.setTimeout(() => {
        bridgeShopDetectPromise = null;
      }, 1000);
    }
  })();
}

function scheduleDashboardDataRefresh() {
  if (!state.mallId || !state.antiContent) return;
  window.setTimeout(() => {
    Promise.allSettled([
      hydrateTemuDashboardFromServerSnapshots()
    ]).catch(err => console.warn('[小青蛙电商助手] 后台刷新首页数据失败:', err));
  }, 700);
}

function getShopDisplayName(shop = null) {
  if (!shop || typeof shop !== 'object') return '';
  const mallId = normalizeMallIdValue(shop.mallId);
  const name = pickRealShopName([shop.shopName, shop.mallName, shop.storeName, shop.name], mallId);
  if (name) {
    rememberShopName(mallId, name);
    return name;
  }
  return getCachedShopName(mallId);
}

function getCurrentShopDisplayName() {
  if (state.mallId) {
    const matchedShop = findBoundShopForMall(state.boundShops || [], state.mallId);
    const name = pickRealShopName([state.mallName, getShopDisplayName(matchedShop), getCachedShopName(state.mallId)], state.mallId)
      || `店铺 ${state.mallId}`;
    return `${name} (${state.mallId})`;
  }
  const shopList = Array.isArray(state.boundShops) ? state.boundShops : [];
  if (shopList.length === 1) {
    const shop = shopList[0];
    const name = getShopDisplayName(shop) || (shop.mallId ? `店铺 ${shop.mallId}` : '已绑定店铺');
    return `${name}${shop.mallId ? ` (${shop.mallId})` : ''}`;
  }
  if (shopList.length > 1) return `已加载 ${shopList.length} 个绑定店铺`;
  return '账号已登录，暂无绑定店铺';
}

function enterDashboardOptimistically(token, user = null, message = '登录成功，正在同步会员和店铺信息...') {
  applyRoleFeatureVisibility(user);
  renderDashAuthStatus(token, user);
  setDashboardAuthPanelVisible(false);
  setDashboardLockedState(false);
  setDashAuthResult(message, 'success');
  if (isOzonWorkspace()) {
    setFeatureAccessState(false, '');
    switchWorkspaceForPlatform('ozon');
    return;
  }
  switchWorkspaceForPlatform('temu');
  renderDashboardAccessPanels(user, null, state.boundShops || [], null);
  window.setTimeout(() => {
    restoreConnectedShopFromStorage({ updateUi: true, detectIfMissing: true }).catch(err => {
      console.warn('[小青蛙电商助手] 登录后恢复店铺状态失败:', err);
    });
  }, 350);
  window.setTimeout(() => {
    loadDashboardMembership().catch(err => console.warn('[xiaoqingwa] refresh membership after login failed:', err));
  }, 2200);
}


function waitForDashboardPaint() {
  return new Promise((resolve) => {
    if (typeof requestAnimationFrame !== 'function') {
      window.setTimeout(resolve, 0);
      return;
    }
    requestAnimationFrame(() => window.setTimeout(resolve, 0));
  });
}

function renderPendingDashboardLogin(user = {}) {
  const phoneEl = document.getElementById('dashLoginPhone');
  const nameEl = document.getElementById('dashLoginNickname');
  const roleEl = document.getElementById('dashLoginRole');
  const memberEl = document.getElementById('dashMemberStatus');
  if (phoneEl) phoneEl.textContent = user.phone || '同步中...';
  if (nameEl) nameEl.textContent = user.nickname || '同步中...';
  if (roleEl) roleEl.textContent = '同步中...';
  if (memberEl) memberEl.textContent = '同步中...';
}

function initDeferredBridgeModulesIfReady() {
  if (shouldDeferBridgeDependentInit()) return false;

  initTemuUiModules();
  deferredBridgeModules.customized = true;
  deferredBridgeModules.activity = true;
  deferredBridgeModules.qualification = true;
  scheduleDeferredStartupTasks();
  return true;
}

function initTemuUiModules() {
  if (!temuModuleInitState.customized) {
    initCustomizedTab();
    temuModuleInitState.customized = true;
  }
  if (!temuModuleInitState.activity) {
    initActivityTab();
    temuModuleInitState.activity = true;
  }
  if (!temuModuleInitState.qualification) {
    initQualificationTab();
    temuModuleInitState.qualification = true;
  }
}

const SERVER_AUTH_KEYS = ['temuServerToken', 'temuServerUser', 'temuServerAuthAt'];
let xlsxLoadPromise = null;
const SHOP_NAME_CACHE_KEY = 'temuShopNameCache';

function getWebsiteAssetUrl(assetName) {
  const cleanName = String(assetName || '').replace(/^\/+/, '');
  const platform = /^\/ozon(?:\/|$)/i.test(window.location.pathname || '') ? 'ozon' : 'temu';
  return `/${platform}/${cleanName}`;
}

async function ensureXlsxLoaded() {
  if (window.XLSX) return window.XLSX;
  if (!xlsxLoadPromise) {
    xlsxLoadPromise = new Promise((resolve, reject) => {
      const existing = document.querySelector('script[data-temu-xlsx-loader="1"]');
      if (existing) {
        existing.addEventListener('load', () => resolve(window.XLSX), { once: true });
        existing.addEventListener('error', () => reject(new Error('Excel 组件加载失败')), { once: true });
        return;
      }
      const script = document.createElement('script');
      script.src = getWebsiteAssetUrl('xlsx.full.min.js');
      script.async = true;
      script.dataset.temuXlsxLoader = '1';
      script.onload = () => window.XLSX ? resolve(window.XLSX) : reject(new Error('Excel 组件未正确初始化'));
      script.onerror = () => reject(new Error(`Excel 组件加载失败：${script.src}`));
      document.head.appendChild(script);
    }).catch((error) => {
      xlsxLoadPromise = null;
      throw error;
    });
  }
  return xlsxLoadPromise;
}

function readLocalJson(key, fallback = null) {
  try {
    const value = localStorage.getItem(key);
    return value ? JSON.parse(value) : fallback;
  } catch {
    return fallback;
  }
}

function isPlaceholderShopName(value, mallId = '') {
  const text = String(value ?? '').trim();
  if (!text) return true;
  const id = String(mallId || '').trim();
  return text === '当前店铺'
    || text === '当前店铺 '
    || text === '当前连接店铺'
    || text === '未命名店铺'
    || (id && text === `店铺 ${id}`);
}

function readShopNameCache() {
  const cache = readLocalJson(SHOP_NAME_CACHE_KEY, {});
  return cache && typeof cache === 'object' ? cache : {};
}

function writeShopNameCache(cache = {}) {
  const safeCache = cache && typeof cache === 'object' ? cache : {};
  try {
    localStorage.setItem(SHOP_NAME_CACHE_KEY, JSON.stringify(safeCache));
  } catch (error) {
    console.warn('[小青蛙电商助手] 店铺名称本地缓存失败:', error?.message || error);
  }
  safeStorageSet({ [SHOP_NAME_CACHE_KEY]: safeCache }, '店铺名称缓存').catch(() => {});
}

function getCachedShopName(mallId = '') {
  const id = normalizeMallIdValue(mallId);
  if (!id) return '';
  const name = String(readShopNameCache()[id] || '').trim();
  return isPlaceholderShopName(name, id) ? '' : name;
}

function pickRealShopName(values = [], mallId = '') {
  const id = normalizeMallIdValue(mallId);
  return (Array.isArray(values) ? values : [])
    .map(value => String(value ?? '').trim())
    .find(value => !isPlaceholderShopName(value, id)) || '';
}

function pickShopName(...values) {
  const flatValues = values.flatMap(value => Array.isArray(value) ? value : [value]);
  const fallback = flatValues.length ? String(flatValues[flatValues.length - 1] ?? '').trim() : '';
  return pickRealShopName(flatValues, fallback) || fallback;
}

function rememberShopName(mallId = '', name = '') {
  const id = normalizeMallIdValue(mallId);
  const realName = pickRealShopName([name], id);
  if (!id || !realName) return;
  const cache = readShopNameCache();
  if (cache[id] === realName) return;
  cache[id] = realName;
  writeShopNameCache(cache);
}

function rememberShopNames(shops = [], current = {}) {
  (Array.isArray(shops) ? shops : []).forEach(shop => {
    const mallId = normalizeMallIdValue(shop?.mallId);
    const name = pickRealShopName([shop?.shopName, shop?.mallName, shop?.storeName, shop?.name], mallId);
    rememberShopName(mallId, name);
  });
  rememberShopName(current.mallId, current.mallName || current.shopName);
}

function hydrateShopNameFromCache(shop = {}) {
  if (!shop || typeof shop !== 'object') return shop;
  const mallId = normalizeMallIdValue(shop.mallId);
  const realName = pickRealShopName([shop.shopName, shop.mallName, shop.storeName, shop.name], mallId);
  if (realName) {
    rememberShopName(mallId, realName);
    return shop;
  }
  const cachedName = getCachedShopName(mallId);
  return cachedName ? { ...shop, shopName: cachedName } : shop;
}

function normalizeServerAuthToken(value) {
  if (value == null) return '';
  const token = String(value).trim().replace(/^"|"$/g, '');
  return token === 'undefined' || token === 'null' ? '' : token;
}

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function normalizeServerAuthAt(value) {
  const timestamp = Number(value || 0);
  return Number.isFinite(timestamp) ? timestamp : 0;
}

function normalizeServerAuthUser(value) {
  if (!value) return null;
  if (typeof value === 'string') {
    try {
      const parsed = JSON.parse(value);
      return parsed && typeof parsed === 'object' ? parsed : null;
    } catch {
      return null;
    }
  }
  return typeof value === 'object' ? value : null;
}

function normalizeServerAuthState(data = {}) {
  return {
    temuServerToken: normalizeServerAuthToken(data.temuServerToken),
    temuServerUser: normalizeServerAuthUser(data.temuServerUser),
    temuServerAuthAt: normalizeServerAuthAt(data.temuServerAuthAt)
  };
}

function pickNewestServerAuthState(localState, pluginState) {
  const localAuth = normalizeServerAuthState(localState);
  const pluginAuth = normalizeServerAuthState(pluginState);
  if (!pluginAuth.temuServerToken) return localAuth;
  if (!localAuth.temuServerToken) return pluginAuth;
  return pluginAuth.temuServerAuthAt > localAuth.temuServerAuthAt ? pluginAuth : localAuth;
}

function getLocalServerAuthState() {
  return normalizeServerAuthState({
    temuServerToken: localStorage.getItem('temuServerToken') || '',
    temuServerUser: readLocalJson('temuServerUser', null),
    temuServerAuthAt: Number(localStorage.getItem('temuServerAuthAt') || 0)
  });
}

async function getServerAuthState() {
  const localState = getLocalServerAuthState();
  if (isWebsiteBridgeMode()) {
    return localState;
  }

  if (!shouldDeferBridgeDependentInit()) {
    try {
      const pluginState = await chrome.storage.local.get(SERVER_AUTH_KEYS);
      return pickNewestServerAuthState(localState, pluginState);
    } catch (error) {
      console.warn('[小青蛙电商助手] 读取插件登录态失败，降级读取网站本地登录态:', error?.message || error);
    }
  }

  return localState;
}

async function setServerAuthState({ token, user, authAt = Date.now() }) {
  state.serverUser = user || null;
  const values = normalizeServerAuthState({
    temuServerToken: token || '',
    temuServerUser: user || null,
    temuServerAuthAt: authAt
  });

  localStorage.setItem('temuServerToken', values.temuServerToken);
  localStorage.setItem('temuServerUser', JSON.stringify(values.temuServerUser || {}));
  localStorage.setItem('temuServerAuthAt', String(values.temuServerAuthAt || Date.now()));

  if (!shouldDeferBridgeDependentInit()) {
    const writePluginAuth = () => chrome.storage.local.set(values);
    if (isWebsiteBridgeMode()) {
      Promise.resolve().then(writePluginAuth).catch((error) => {
        console.warn('[小青蛙电商助手] 写入插件登录态失败，已保存在网站本地:', error?.message || error);
      });
    } else {
      try {
        await writePluginAuth();
      } catch (error) {
        console.warn('[小青蛙电商助手] 写入插件登录态失败，已保存在网站本地:', error?.message || error);
      }
    }
  }
}

async function clearServerAuthState(options = {}) {
  const { localOnly = false } = options;
  SERVER_AUTH_KEYS.forEach(key => localStorage.removeItem(key));
  state.serverUser = null;
  applyRoleFeatureVisibility(null);
  if (!localOnly && !shouldDeferBridgeDependentInit()) {
    try {
      await chrome.storage.local.remove([...SERVER_AUTH_KEYS]);
    } catch (error) {
      console.warn('[小青蛙电商助手] 清理插件登录态失败，已清理网站本地登录态:', error?.message || error);
    }
  }
}

// 模板 storage key（按店铺隔离）
function getTemplateKey(mallId) {
  return mallId ? `temuTemplates_${mallId}` : 'temuTemplates';
}

async function resolveCurrentMallIdForStorage() {
  if (state.mallId) return String(state.mallId);
  const stored = await chrome.storage.local.get(['temuAuth', 'temuLocalMallId']);
  const auth = stored.temuAuth || {};
  const mallId = String(auth.mallId || stored.temuLocalMallId || '').trim();
  if (mallId) {
    state.mallId = mallId;
    state.mallName = auth.mallName || state.mallName || null;
    if (auth.antiContent && !state.antiContent) state.antiContent = auth.antiContent;
  }
  return mallId;
}

function mergeComplianceTemplates(...lists) {
  const merged = new Map();
  lists.flat().forEach(template => {
    if (!template || typeof template !== 'object') return;
    const key = String(
      template.serverId
        ? `server:${template.serverId}`
        : (template.id || `${template.name || 'template'}__${template.createdAt || ''}__${template.spuId || ''}`)
    );
    merged.set(key, template);
  });
  return Array.from(merged.values());
}

async function getComplianceTemplatesForCurrentMall() {
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) return [];
  const key = getTemplateKey(mallId);
  const result = await chrome.storage.local.get([key]);
  const current = Array.isArray(result[key]) ? result[key] : [];
  return current;
}

async function getServerAuthToken() {
  const data = await getServerAuthState();
  return data.temuServerToken || '';
}

async function getServerAuthTokenForWrite() {
  let token = await getServerAuthToken();
  if (token) return token;

  await wait(250);
  token = await getServerAuthToken();
  if (token) return token;

  try {
    const stored = await chrome.storage.local.get(SERVER_AUTH_KEYS);
    token = normalizeServerAuthToken(stored?.temuServerToken);
    if (token) return token;
  } catch (error) {
    console.warn('[小青蛙电商助手] 保存前读取插件登录态失败:', error?.message || error);
  }

  return '';
}

function getCurrentTemuMallId() {
  return normalizeTemuMallId(state.mallId || '');
}

function normalizeTemuSnapshotPayload(snapshot = null) {
  if (!snapshot || typeof snapshot !== 'object') return null;
  const payload = snapshot.payload && typeof snapshot.payload === 'object' ? snapshot.payload : {};
  return {
    ...payload,
    mallId: payload.mallId || snapshot.mallId || '',
    mallName: payload.mallName || snapshot.mallName || '',
    type: payload.type || snapshot.type || snapshot.dataType || '',
    itemCount: Number(payload.itemCount ?? snapshot.itemCount ?? 0) || 0,
    sourceStatus: payload.sourceStatus || snapshot.sourceStatus || '',
    syncedAt: snapshot.syncedAt || payload.syncedAt || payload.lastSyncAt || null,
    updatedAt: snapshot.updatedAt || payload.updatedAt || null
  };
}

function normalizeTemuSnapshotType(value) {
  const type = String(value || '').trim().toLowerCase();
  return [
    'sales',
    'compliance',
    'livephotos',
    'customized',
    'qualification',
    'collection',
    'finance',
    'stock_reminders'
  ].includes(type) ? type : '';
}

async function fetchTemuSnapshot(type, mallIdOverride = null) {
  const token = await getServerAuthToken();
  if (!token) return null;
  const mallId = normalizeTemuMallId(mallIdOverride || state.mallId || '');
  if (!type || !mallId || getMallIdPlatform(mallId) !== 'temu') return null;
  const params = new URLSearchParams({ mallId, type });
  try {
    const resp = await fetch(`${SERVER_URL}/data/temu-snapshots?${params.toString()}`, {
      headers: { Authorization: `Bearer ${token}` }
    });
    const result = await resp.json().catch(() => ({}));
    if (!resp.ok || !result.ok) {
      console.warn('[小青蛙电商助手] 读取 TEMU 服务器快照失败:', type, result.error || resp.status);
      return null;
    }
    return normalizeTemuSnapshotPayload(result.snapshot || result.snapshots?.[0] || null);
  } catch (error) {
    console.warn('[小青蛙电商助手] 读取 TEMU 服务器快照异常:', type, error?.message || error);
    return null;
  }
}

async function fetchAllTemuSnapshots(type) {
  const token = await getServerAuthToken();
  if (!token || !type) return [];
  try {
    const resp = await fetch(`${SERVER_URL}/data/temu-snapshots/all?type=${encodeURIComponent(type)}`, {
      headers: { Authorization: `Bearer ${token}` }
    });
    const result = await resp.json().catch(() => ({}));
    if (!resp.ok || !result.ok) return [];
    return (Array.isArray(result.snapshots) ? result.snapshots : [])
      .map(normalizeTemuSnapshotPayload)
      .filter(Boolean)
      .filter(item => getMallIdPlatform(item.mallId) === 'temu');
  } catch (error) {
    console.warn('[小青蛙电商助手] 读取 TEMU 全量快照异常:', type, error?.message || error);
    return [];
  }
}

async function fetchTemuSnapshotMeta(type = '', mallIdOverride = null) {
  const token = await getServerAuthToken();
  if (!token) return null;
  const mallId = normalizeTemuMallId(mallIdOverride || state.mallId || '');
  if (!mallId || getMallIdPlatform(mallId) !== 'temu') return null;
  const params = new URLSearchParams();
  if (mallId) params.set('mallId', mallId);
  if (type) params.set('type', type);
  try {
    const resp = await fetch(`${SERVER_URL}/data/temu-snapshots/meta?${params.toString()}`, {
      headers: { Authorization: `Bearer ${token}` }
    });
    const result = await resp.json().catch(() => ({}));
    if (!resp.ok || !result.ok) return null;
    return normalizeTemuSnapshotPayload(result.snapshot || result.snapshots?.[0] || null);
  } catch (error) {
    console.warn('[小青蛙电商助手] 读取 TEMU 服务器快照摘要异常:', type, error?.message || error);
    return null;
  }
}

async function fetchTemuSnapshotMetas(types = [], mallIdOverride = null) {
  const token = await getServerAuthToken();
  if (!token) return [];
  const mallId = normalizeTemuMallId(mallIdOverride || state.mallId || '');
  if (!mallId || getMallIdPlatform(mallId) !== 'temu') return [];
  const cleanTypes = [...new Set((Array.isArray(types) ? types : [types]).map(normalizeTemuSnapshotType).filter(Boolean))];
  const params = new URLSearchParams();
  if (mallId) params.set('mallId', mallId);
  if (cleanTypes.length) params.set('types', cleanTypes.join(','));
  try {
    const resp = await fetch(`${SERVER_URL}/data/temu-snapshots/meta?${params.toString()}`, {
      headers: { Authorization: `Bearer ${token}` }
    });
    const result = await resp.json().catch(() => ({}));
    if (!resp.ok || !result.ok) return [];
    return (Array.isArray(result.snapshots) ? result.snapshots : [])
      .map(normalizeTemuSnapshotPayload)
      .filter(Boolean)
      .filter(item => !item.mallId || getMallIdPlatform(item.mallId) === 'temu');
  } catch (error) {
    console.warn('[小青蛙电商助手] 读取 TEMU 服务器快照摘要列表异常:', error?.message || error);
    return [];
  }
}

async function saveTemuSnapshot(type, payload = {}, options = {}) {
  const token = await getServerAuthTokenForWrite();
  const mallId = normalizeTemuMallId(options.mallId || payload.mallId || state.mallId || '');
  if (!token || !type || !mallId || getMallIdPlatform(mallId) !== 'temu') return null;
  try {
    const resp = await fetch(`${SERVER_URL}/data/temu-snapshots`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        type,
        mallId,
        payload: {
          ...payload,
          mallId,
          mallName: payload.mallName || state.mallName || getCachedShopName(mallId) || ''
        },
        itemCount: options.itemCount,
        sourceStatus: options.sourceStatus || 'success'
      })
    });
    const result = await resp.json().catch(() => ({}));
    if (!resp.ok || !result.ok) {
      console.warn('[小青蛙电商助手] 保存 TEMU 服务器快照失败:', type, result.error || resp.status);
      return null;
    }
    const snapshot = normalizeTemuSnapshotPayload(result.snapshot);
    updateTemuSnapshotSyncMeta(type, snapshot?.syncedAt);
    return snapshot;
  } catch (error) {
    console.warn('[小青蛙电商助手] 保存 TEMU 服务器快照异常:', type, error?.message || error);
    return null;
  }
}

function formatTemuSnapshotTime(value) {
  if (!value) return '—';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    const numeric = Number(value);
    if (!Number.isFinite(numeric)) return String(value);
    const numericDate = new Date(numeric > 1e12 ? numeric : numeric * 1000);
    return Number.isNaN(numericDate.getTime()) ? '—' : numericDate.toLocaleString('zh-CN', { hour12: false });
  }
  return date.toLocaleString('zh-CN', { hour12: false });
}

const TEMU_TAB_SYNC_META = {
  compliance: { sectionId: 'compliance', label: '合规信息' },
  livephotos: { sectionId: 'livephotos', label: '实拍图' },
  customized: { sectionId: 'customized', label: '商品管理' },
  qualification: { sectionId: 'qualification', label: '资质上传' },
  collection: { sectionId: 'collection', label: '店铺搬家' },
  finance: { sectionId: 'finance', label: '财务数据' }
};

function updateTemuSnapshotSyncMeta(type, syncedAt) {
  const meta = TEMU_TAB_SYNC_META[type];
  if (!meta) return;
  const el = document.getElementById(`temuSyncMeta_${type}`);
  if (!el) return;
  el.textContent = `${meta.label}最后同步：${formatTemuSnapshotTime(syncedAt)}`;
}

function initTemuSnapshotSyncMetaBars() {
  Object.entries(TEMU_TAB_SYNC_META).forEach(([type, meta]) => {
    const section = document.getElementById(meta.sectionId);
    if (!section || document.getElementById(`temuSyncMeta_${type}`)) return;
    const bar = document.createElement('div');
    bar.className = 'temu-sync-meta';
    bar.id = `temuSyncMeta_${type}`;
    bar.textContent = `${meta.label}最后同步：—`;
    section.insertBefore(bar, section.firstElementChild || null);
  });
}

async function hydrateTemuSnapshotSyncTimes() {
  try {
    const snapshots = await fetchTemuSnapshotMetas(Object.keys(TEMU_TAB_SYNC_META));
    const latestByType = new Map();
    snapshots.forEach((snapshot) => {
      const type = normalizeTemuSnapshotType(snapshot.type);
      if (!type) return;
      if (!latestByType.has(type)) latestByType.set(type, snapshot);
    });
    Object.keys(TEMU_TAB_SYNC_META).forEach((type) => {
      updateTemuSnapshotSyncMeta(type, latestByType.get(type)?.syncedAt || null);
    });
  } catch {
    Object.keys(TEMU_TAB_SYNC_META).forEach((type) => updateTemuSnapshotSyncMeta(type, null));
  }
}

function applyComplianceSnapshot(snapshot = null) {
  if (!snapshot || !Array.isArray(snapshot.products)) return false;
  state.products = snapshot.products;
  state.pageNum = snapshot.pageNum || state.pageNum || 1;
  state.pageSize = snapshot.pageSize || state.pageSize || 10;
  state.total = snapshot.total || snapshot.products.length;
  state.totalPages = snapshot.totalPages || Math.ceil(state.total / Math.max(1, state.pageSize)) || 1;
  renderProductList();
  renderPagination();
  updateTemuSnapshotSyncMeta('compliance', snapshot.syncedAt);
  return true;
}

async function loadComplianceProductSnapshot({ showToastOnHit = false } = {}) {
  const snapshot = await fetchTemuSnapshot('compliance');
  const loaded = applyComplianceSnapshot(snapshot);
  if (loaded && showToastOnHit) showToast('已显示服务器上次成功同步的合规商品数据', 'info');
  return loaded;
}

function applyLivePhotoSnapshot(snapshot = null) {
  if (!snapshot || !Array.isArray(snapshot.products)) return false;
  state.lpProducts = snapshot.products;
  state.livePhotoPageNum = snapshot.pageNum || state.livePhotoPageNum || 1;
  state.livePhotoPageSize = snapshot.pageSize || state.livePhotoPageSize || 10;
  state.livePhotoTotal = snapshot.total || snapshot.products.length;
  state.livePhotoTotalPages = snapshot.totalPages || Math.ceil(state.livePhotoTotal / Math.max(1, state.livePhotoPageSize)) || 1;
  renderLivePhotoProductList();
  renderLivePhotoPagination();
  updateTemuSnapshotSyncMeta('livephotos', snapshot.syncedAt);
  return true;
}

async function loadLivePhotoProductSnapshot({ showToastOnHit = false } = {}) {
  const snapshot = await fetchTemuSnapshot('livephotos');
  const loaded = applyLivePhotoSnapshot(snapshot);
  if (loaded && showToastOnHit) showToast('已显示服务器上次成功同步的实拍图商品数据', 'info');
  return loaded;
}

const PRICING_LOCAL_CACHE_TTL_MS = 2 * 60 * 60 * 1000;
const PRICING_LOCAL_CACHE_PREFIX = 'xqw:temu:pricing:';

function getPricingLocalCacheKey(mallIdOverride = '') {
  const mallId = normalizeTemuMallId(mallIdOverride || state.mallId || '');
  return mallId ? `${PRICING_LOCAL_CACHE_PREFIX}${mallId}` : '';
}

function normalizePricingLocalCachePayload(payload = null) {
  if (!payload || typeof payload !== 'object' || !Array.isArray(payload.items)) return null;
  const cachedAt = Number(payload.cachedAt || payload.loadedAt || 0) || 0;
  if (cachedAt && Date.now() - cachedAt > PRICING_LOCAL_CACHE_TTL_MS) return null;
  const mallId = normalizeTemuMallId(payload.mallId || '');
  const currentMallId = normalizeTemuMallId(state.mallId || '');
  if (mallId && currentMallId && mallId !== currentMallId) return null;
  return {
    ...payload,
    mallId: mallId || currentMallId,
    items: payload.items,
    allItems: Array.isArray(payload.allItems) ? payload.allItems : payload.items,
    total: Number(payload.total || payload.items.length || 0) || 0,
    pageNum: Number(payload.pageNum || 1) || 1,
    pageSize: Number(payload.pageSize || payload.items.length || 100) || 100,
    minPriceMap: payload.minPriceMap && typeof payload.minPriceMap === 'object' ? payload.minPriceMap : {},
    cachedAt
  };
}

function savePricingLocalCache(extraPayload = {}) {
  const key = getPricingLocalCacheKey(extraPayload.mallId);
  if (!key) return false;
  try {
    const payload = {
      mallId: normalizeTemuMallId(extraPayload.mallId || state.mallId || ''),
      mallName: extraPayload.mallName || state.mallName || getCachedShopName(state.mallId) || '',
      items: Array.isArray(extraPayload.items) ? extraPayload.items : pricingState.list,
      allItems: Array.isArray(extraPayload.allItems) ? extraPayload.allItems : pricingState.allList,
      total: Number(extraPayload.total ?? pricingState.total ?? 0) || 0,
      pageNum: Number(extraPayload.pageNum ?? pricingState.pageNum ?? 1) || 1,
      pageSize: Number(extraPayload.pageSize ?? pricingState.pageSize ?? 100) || 100,
      minPriceMap: extraPayload.minPriceMap && typeof extraPayload.minPriceMap === 'object' ? extraPayload.minPriceMap : pricingState.minPriceMap,
      cachedAt: Date.now()
    };
    sessionStorage.setItem(key, JSON.stringify(payload));
    return true;
  } catch (error) {
    console.warn('[小青蛙电商助手] 保存核价本地缓存失败:', error?.message || error);
    return false;
  }
}

function applyPricingLocalCache(payload = null) {
  const snapshot = normalizePricingLocalCachePayload(payload);
  if (!snapshot) return false;
  pricingState.list = snapshot.items;
  pricingState.allList = snapshot.allItems.slice();
  pricingState.total = Number(snapshot.total || snapshot.items.length || 0);
  pricingState.pageNum = snapshot.pageNum || 1;
  pricingState.pageSize = snapshot.pageSize || pricingState.list.length || 100;
  pricingState.selected.clear();
  pricingState.minPriceMap = snapshot.minPriceMap && typeof snapshot.minPriceMap === 'object' ? snapshot.minPriceMap : {};
  rebuildPricingRefPriceMap(pricingState.list);
  renderPricingTable();
  renderPricingPagination();
  return true;
}

function loadPricingLocalCache({ showToastOnHit = false } = {}) {
  const key = getPricingLocalCacheKey();
  if (!key) return false;
  let cached = null;
  try {
    cached = JSON.parse(sessionStorage.getItem(key) || 'null');
  } catch (error) {
    console.warn('[小青蛙电商助手] 读取核价本地缓存失败:', error?.message || error);
    sessionStorage.removeItem(key);
    return false;
  }
  const loaded = applyPricingLocalCache(cached);
  if (!loaded && cached) {
    try { sessionStorage.removeItem(key); } catch {}
  }
  if (loaded && showToastOnHit) showToast('已显示当前店铺本地临时缓存的核价数据', 'info');
  return loaded;
}

function applyStockReminderSnapshot(snapshot = null) {
  if (!snapshot) return false;
  const payload = {
    shipping: Array.isArray(snapshot.shipping) ? snapshot.shipping : [],
    arrival: Array.isArray(snapshot.arrival) ? snapshot.arrival : [],
    updatedAt: snapshot.updatedAt || snapshot.syncedAt || snapshot.lastSyncAt || '',
    message: snapshot.message || ''
  };
  dashboardStockReminderState.mallId = snapshot.mallId || state.mallId || '';
  dashboardStockReminderState.loadedAt = Date.now();
  dashboardStockReminderState.payload = payload;
  dashboardStockReminderState.error = '';
  renderDashboardStockReminder(payload);
  return true;
}

async function loadStockReminderSnapshot() {
  const snapshot = await fetchTemuSnapshot('stock_reminders');
  return applyStockReminderSnapshot(snapshot);
}

async function hydrateTemuDashboardFromServerSnapshots() {
  await Promise.allSettled([
    loadSalesDataCache(),
    loadStockReminderSnapshot(),
    hydrateTemuSnapshotSyncTimes()
  ]);
}

async function fetchUserTemplates(type = '', mallIdOverride = null) {
  const token = await getServerAuthToken();
  if (!token) return { success: true, templates: [], skipped: true };
  const mallId = String(mallIdOverride || state.mallId || '').trim();
  if (!mallId) return { success: true, templates: [], skipped: true };
  const params = new URLSearchParams();
  if (type) params.set('type', type);
  params.set('mallId', mallId);
  const query = params.toString() ? `?${params.toString()}` : '';
  const resp = await fetch(`${SERVER_URL}/data/templates${query}`, {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || !result.ok) {
    return { success: false, error: result.error || result.message || `HTTP ${resp.status}` };
  }
  return { success: true, templates: Array.isArray(result.templates) ? result.templates : [] };
}

async function saveUserTemplate(type, template) {
  const token = await getServerAuthTokenForWrite();
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) return { success: false, error: '未检测到当前店铺，无法按店铺保存模板' };
  if (!token) {
    return {
      success: false,
      authRequired: true,
      error: '请先登录会员账号，模板必须保存到云端'
    };
  }
  let resp;
  let result = {};
  try {
    resp = await fetch(`${SERVER_URL}/data/templates`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        name: template.name || '未命名模板',
        type,
        data: { ...template, mallId },
        mallId,
        isDefault: 0
      })
    });
    result = await resp.json().catch(() => ({}));
  } catch (error) {
    return {
      success: false,
      error: `模板云端保存失败：${error?.message || error}`
    };
  }
  if (!resp.ok || !result.ok) {
    const errorText = result.error || result.message || `HTTP ${resp.status}`;
    if (resp.status === 401 || resp.status === 403) {
      await clearServerAuthState({ localOnly: true }).catch(() => {});
      setDashboardAuthPanelVisible(true);
      setDashboardLockedState(true);
    }
    return {
      success: false,
      authRequired: resp.status === 401 || resp.status === 403,
      error: resp.status === 401 || resp.status === 403
        ? '会员登录已失效，请重新登录后再保存模板'
        : `模板云端保存失败：${errorText}`
    };
  }
  return { success: true, id: result.id, message: result.message || '保存成功', result };
}

async function deleteUserTemplate(serverId) {
  if (!serverId) return { success: true, skipped: true };
  const token = await getServerAuthTokenForWrite();
  if (!token) return { success: false, error: '请先登录会员账号后再删除模板' };
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) return { success: false, error: '未检测到当前店铺，无法删除模板' };
  const params = new URLSearchParams();
  params.set('mallId', mallId);
  const query = params.toString() ? `?${params.toString()}` : '';
  const resp = await fetch(`${SERVER_URL}/data/templates/${encodeURIComponent(serverId)}${query}`, {
    method: 'DELETE',
    headers: { 'Authorization': `Bearer ${token}` }
  });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || !result.ok) {
    return { success: false, error: result.error || result.message || `HTTP ${resp.status}` };
  }
  return { success: true };
}

async function fetchProductMoveTemplates(sourceMallId = '') {
  const token = await getServerAuthToken();
  if (!token) return { success: true, templates: [], skipped: true };
  const params = new URLSearchParams();
  if (sourceMallId) params.set('sourceMallId', sourceMallId);
  const query = params.toString() ? `?${params.toString()}` : '';
  const resp = await fetch(`${SERVER_URL}/data/product-templates${query}`, {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || !result.ok) {
    return { success: false, error: result.error || result.message || `HTTP ${resp.status}` };
  }
  return { success: true, templates: Array.isArray(result.templates) ? result.templates : [] };
}

async function saveProductMoveTemplate(product) {
  const token = await getServerAuthTokenForWrite();
  if (!token) return { success: false, error: '请先登录会员账号后再保存商品模板' };
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) return { success: false, error: '未检测到当前源店铺，无法保存商品模板' };
  const productName = product?.title || product?.productName || product?.spuName || `商品 ${product?.goodsId || product?.spuId || ''}`.trim();
  const payload = normalizeProductMoveTemplatePayload(product, mallId);
  const resp = await fetch(`${SERVER_URL}/data/product-templates`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: productName || '未命名商品模板',
      mallId,
      mallName: state.mallName || '',
      data: payload
    })
  });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || !result.ok) {
    return { success: false, error: result.error || result.message || `HTTP ${resp.status}` };
  }
  return { success: true, id: result.id, message: result.message || '商品模板已保存' };
}

async function deleteProductMoveTemplate(serverId) {
  const token = await getServerAuthTokenForWrite();
  if (!token) return { success: false, error: '请先登录会员账号后再删除商品模板' };
  const resp = await fetch(`${SERVER_URL}/data/product-templates/${encodeURIComponent(serverId)}`, {
    method: 'DELETE',
    headers: { 'Authorization': `Bearer ${token}` }
  });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || !result.ok) {
    return { success: false, error: result.error || result.message || `HTTP ${resp.status}` };
  }
  return { success: true };
}

async function fetchTemuProductUploadTemplates() {
  const token = await getServerAuthToken();
  if (!token) return { success: false, error: '请先登录会员账号后再加载 TEMU 商品模板' };
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) return { success: false, error: '未检测到当前 TEMU 店铺' };
  const params = new URLSearchParams({ mallId });
  const resp = await fetch(`${SERVER_URL}/data/temu-product-upload-templates?${params.toString()}`, {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || !result.ok) return { success: false, error: result.error || result.message || `HTTP ${resp.status}` };
  return { success: true, templates: Array.isArray(result.templates) ? result.templates : [] };
}

async function saveTemuProductUploadTemplate(template) {
  const token = await getServerAuthTokenForWrite();
  if (!token) return { success: false, error: '请先登录会员账号后再保存 TEMU 商品模板' };
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) return { success: false, error: '未检测到当前 TEMU 店铺' };
  const resp = await fetch(`${SERVER_URL}/data/temu-product-upload-templates`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ ...template, mallId })
  });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || !result.ok) return { success: false, error: result.error || result.message || `HTTP ${resp.status}` };
  return { success: true, id: result.id, message: result.message || 'TEMU 商品模板已保存' };
}

async function updateTemuProductUploadTemplate(serverId, template) {
  const token = await getServerAuthTokenForWrite();
  if (!token) return { success: false, error: '请先登录会员账号后再保存 TEMU 商品模板' };
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) return { success: false, error: '未检测到当前 TEMU 店铺' };
  const resp = await fetch(`${SERVER_URL}/data/temu-product-upload-templates/${encodeURIComponent(serverId)}`, {
    method: 'PUT',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ ...template, mallId })
  });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || !result.ok) return { success: false, error: result.error || result.message || `HTTP ${resp.status}` };
  return { success: true, id: result.id || serverId, message: result.message || 'TEMU 商品模板已保存' };
}

async function deleteTemuProductUploadTemplate(serverId) {
  const token = await getServerAuthTokenForWrite();
  if (!token) return { success: false, error: '请先登录会员账号后再删除 TEMU 商品模板' };
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) return { success: false, error: '未检测到当前 TEMU 店铺' };
  const params = new URLSearchParams({ mallId });
  const resp = await fetch(`${SERVER_URL}/data/temu-product-upload-templates/${encodeURIComponent(serverId)}?${params.toString()}`, {
    method: 'DELETE',
    headers: { 'Authorization': `Bearer ${token}` }
  });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || !result.ok) return { success: false, error: result.error || result.message || `HTTP ${resp.status}` };
  return { success: true };
}


// ===== 全局状态 =====
const state = {
  mallId: null,
  mallName: null,
  antiContent: null,
  boundShops: [],
  templates: [],
  products: [],
  selectedProducts: [],
  isUploading: false,
  uploadProgress: 0,
  pageNum: 1,
  pageSize: 10,
  totalPages: 0,
  total: 0,
  complianceSearchIds: [],
  complianceSearchText: '',
  // 新增：选中的模板 ID
  selectedTemplateId: null,
  // 新增：商品选择模式 'list' | 'manual'
  productSelectMode: 'list',
  // 新增：实拍图分页状态
  livePhotoPageNum: 1,
  livePhotoPageSize: 10,
  livePhotoTotalPages: 0,
  livePhotoTotal: 0,
  // 新增：实拍图商品列表
  lpProducts: [],
  lpSelectedProducts: [],
  lpSelectedProductMap: {},
  lpProductSelectMode: 'list',
  lpSearchQuery: '',
  lpSearchIds: [],
  lpSearchTimer: null,
  lpIsSearching: false,
  temuAiDiagnosisProducts: [],
  temuAiDiagnosisSelected: []
};

const featureAccessState = {
  locked: true,
  reason: '请先登录并完成店铺授权'
};
const ADMIN_ONLY_TABS = new Set(['collection', 'finance']);
let adminFeatureMounted = false;
let adminFeatureEventsBound = false;

function isAdminUser(user = null) {
  return String(user?.role || '').toLowerCase() === 'admin';
}

function getCurrentServerUser() {
  return state.serverUser || readLocalJson('temuServerUser', null) || null;
}

function persistCurrentServerUser(user = null) {
  state.serverUser = user || null;
  if (!user) {
    localStorage.removeItem('temuServerUser');
    return;
  }
  localStorage.setItem('temuServerUser', JSON.stringify(user));
}

function mountAdminFeatureDom() {
  if (adminFeatureMounted) return;
  const template = document.getElementById('adminFeatureTemplate');
  const navMount = document.getElementById('adminFeatureNavMount');
  const contentArea = document.querySelector('.content-area');
  if (!template?.content || !navMount || !contentArea) return;

  const fragment = template.content.cloneNode(true);
  const sections = Array.from(fragment.querySelectorAll('.section.feature-admin-only'));
  const navButtons = [
    { tab: 'collection', text: '🛒', label: '店铺搬家' },
    { tab: 'finance', text: '💳', label: '财务数据' }
  ].map(item => {
    const button = document.createElement('button');
    button.className = 'tab nav-item feature-admin-only';
    button.dataset.tab = item.tab;
    button.dataset.adminMountedFeature = '1';
    button.type = 'button';
    button.append(document.createTextNode(item.text));
    const span = document.createElement('span');
    span.textContent = item.label;
    button.appendChild(span);
    return button;
  });

  sections.forEach(section => {
    section.dataset.adminMountedFeature = '1';
  });
  navButtons.forEach(button => navMount.appendChild(button));
  sections.forEach(section => contentArea.appendChild(section));
  adminFeatureMounted = true;
  normalizeWorkspaceTabLabels();
  normalizeButtonTypes(document);
  initTabs();
  bindAdminFeatureEvents();
  initTemuSnapshotSyncMetaBars();
  hydrateTemuSnapshotSyncTimes();
}

function unmountAdminFeatureDom() {
  if (!adminFeatureMounted) return;
  document.querySelectorAll('[data-admin-mounted-feature="1"]').forEach(node => node.remove());
  adminFeatureMounted = false;
  adminFeatureEventsBound = false;
}

function applyRoleFeatureVisibility(user = getCurrentServerUser()) {
  const admin = isAdminUser(user);
  state.serverUser = user || null;
  if (admin) mountAdminFeatureDom();
  else unmountAdminFeatureDom();
  document.body.classList.toggle('admin-user', admin);
  document.querySelectorAll('.section.feature-admin-only').forEach((section) => {
    section.hidden = !admin;
  });
  document.querySelectorAll('.tab').forEach((tab) => {
    const tabId = tab.dataset.tab;
    if (!ADMIN_ONLY_TABS.has(tabId)) return;
    tab.hidden = !admin;
    tab.disabled = !admin;
    if (!admin && tab.classList.contains('active')) {
      document.querySelector('.tab[data-tab="dashboard"]')?.click();
    }
  });
}

// 任务类型名称映射
const TASK_TYPE_NAMES = {
  25: '欧盟负责人',
  60: '制造商信息',
  61: '加州65号提案',
  84: '土耳其负责人',
  166: '包装材料信息',
  167: '韩国公示信息'
};

// 商品状态映射（TEMU goods_status）


// ===== 初始化 =====
document.addEventListener('DOMContentLoaded', async () => {
  console.log('[小青蛙电商助手] 后台页面已加载');
  window.__XQW_FULL_APP_READY__ = true;
  const fastLoginBinding = window.__XQW_FAST_LOGIN_BINDING__;
  if (fastLoginBinding) {
    fastLoginBinding.btn?.removeEventListener?.('click', fastLoginBinding.onClick);
    fastLoginBinding.passwordEl?.removeEventListener?.('keydown', fastLoginBinding.onKeydown);
    window.__XQW_FAST_LOGIN_BINDING__ = null;
  }
  normalizeButtonTypes(document);
  bindTemuImportExportFallbacks();
  applyRoleFeatureVisibility();
  const deferBridgeInit = shouldDeferBridgeDependentInit();
  const bootSteps = [
    ['解析服务器地址', resolveServerUrl],
    ['初始化标签页', initTabs],
    ['初始化模式切换', initModeSwitch],
    ['初始化手动输入', initManualSpuInput],
    ['绑定按钮事件', bindEvents],
    ['初始化批量上架测试', initTemuUploadTestTab],
    ['初始化同步时间', initTemuSnapshotSyncMetaBars],
    ['初始化首页', initDashboard]
  ];
  for (const [label, step] of bootSteps) {
    try {
      await step();
    } catch (error) {
      console.error(`[小青蛙电商助手] ${label}失败:`, error);
      if (typeof showToast === 'function') {
        showToast(`${label}失败：${error?.message || String(error)}`, 'error');
      }
    }
  }
  warmServerApi();
  hydrateTemuSnapshotSyncTimes();
  window.setTimeout(syncWebsiteBridgeUi, 0);
  getServerAuthState().then((auth) => {
    if (auth?.temuServerToken) {
      enterDashboardOptimistically(auth.temuServerToken, auth.temuServerUser || null, '已登录，正在同步店铺授权信息...');
    }
  }).catch(err => console.warn('[小青蛙电商助手] 快速恢复登录态失败:', err));
  window.setTimeout(() => {
    restoreConnectedShopFromStorage({ updateUi: true, detectIfMissing: true }).catch(err => {
      console.warn('[小青蛙电商助手] 延迟恢复店铺状态失败:', err);
    });
  }, 650);
  window.setTimeout(() => {
    loadDashboardMembership().catch(err => console.warn('[小青蛙电商助手] 延迟刷新会员状态失败:', err));
  }, 1400);
  window.setTimeout(syncWebsiteBridgeUi, 1200);
  if (!deferBridgeInit) {
    scheduleDeferredStartupTasks();
  }
  if (deferBridgeInit) {
    console.log('[小青蛙电商助手] 当前为网站模式，桥接未就绪，已延后依赖插件的初始化步骤');
  }
});

window.addEventListener('temu-fast-login-success', () => {
  getServerAuthState().then((auth) => {
    if (auth?.temuServerToken) {
      enterDashboardOptimistically(auth.temuServerToken, auth.temuServerUser || null, '登录成功，正在同步店铺授权信息...');
    }
  }).catch(err => console.warn('[小青蛙电商助手] 快速登录后恢复状态失败:', err));
});

function normalizeButtonTypes(root = document) {
  root.querySelectorAll?.('button:not([type])').forEach(button => {
    button.setAttribute('type', 'button');
  });
}

const dynamicButtonTypeObserver = new MutationObserver((mutations) => {
  for (const mutation of mutations) {
    mutation.addedNodes.forEach(node => {
      if (node?.nodeType !== Node.ELEMENT_NODE) return;
      if (node.matches?.('button:not([type])')) {
        node.setAttribute('type', 'button');
      }
      normalizeButtonTypes(node);
    });
  }
});

dynamicButtonTypeObserver.observe(document.documentElement, {
  childList: true,
  subtree: true
});

document.addEventListener('click', (event) => {
  const button = event.target?.closest?.('button');
  if (!button) return;
  if (!button.getAttribute('type')) button.setAttribute('type', 'button');
  if (button.id === 'dhTemuBindCurrentShopBtn') {
    handleCurrentDashboardShopBindClick(event);
  }
}, true);

function bindExtensionDownloadButtons(root = document) {
  root.querySelectorAll?.('#dashKpiExtensionDownloadBtn,#dashExtensionDownloadBtn').forEach((button) => {
    if (button.dataset.xqwDownloadBound === '1') return;
    button.dataset.xqwDownloadBound = '1';
    button.setAttribute('href', EXTENSION_DOWNLOAD_URL);
    button.removeAttribute('target');
    button.removeAttribute('rel');
    button.setAttribute('download', EXTENSION_DOWNLOAD_FILENAME);
    button.addEventListener('click', (event) => {
      event.preventDefault();
      window.location.href = EXTENSION_DOWNLOAD_URL;
    });
  });
}

function markLivePhotoChooserClicked() {
  const status = document.getElementById('livePhotoUploadStatus');
  if (status) {
    status.textContent = '已点击选择文件，请在弹出的文件窗口中选择图片';
    status.style.color = 'var(--text-secondary)';
  }
}

function openLivePhotoFileChooser(inputId) {
  const input = document.getElementById(inputId);
  if (!input) return false;
  if (input.value) input.value = '';
  markLivePhotoChooserClicked();
  input.click();
  return true;
}

function bindTemuFilePickerEvents() {
  if (window.__TEMU_FILE_PICKER_EVENTS_BOUND__) return;
  window.__TEMU_FILE_PICKER_EVENTS_BOUND__ = true;

  document.addEventListener('click', (event) => {
    const livePhotoBtn = event.target?.closest?.('#livePhotoSelectFilesBtn,#livePhotoAddFilesBtn');
    if (livePhotoBtn) {
      if (livePhotoBtn.tagName === 'LABEL') {
        markLivePhotoChooserClicked();
        return;
      }
      if (livePhotoBtn.dataset.livePhotoFileBound === '1') return;
      event.preventDefault();
      event.stopPropagation();
      openLivePhotoFileChooser(livePhotoBtn.id === 'livePhotoAddFilesBtn' ? 'livePhotoFilesInputAdd' : 'livePhotoFilesInput');
      return;
    }

  }, true);

  document.addEventListener('change', (event) => {
    const target = event.target;
    if (!target?.id) return;
    if (target.id === 'livePhotoFilesInput' || target.id === 'livePhotoFilesInputAdd') {
      if (target.files && target.files.length > 0) addLivePhotoFiles(target.files);
      target.value = '';
      return;
    }
  }, true);
}

bindTemuFilePickerEvents();

function bindLivePhotoFileButtons() {
  [
    ['livePhotoSelectFilesBtn', 'livePhotoFilesInput'],
    ['livePhotoAddFilesBtn', 'livePhotoFilesInputAdd']
  ].forEach(([buttonId, inputId]) => {
    const button = document.getElementById(buttonId);
    if (!button || button.dataset.livePhotoFileBound === '1') return;
    button.dataset.livePhotoFileBound = '1';
    if (button.tagName === 'LABEL') {
      button.setAttribute('for', inputId);
      button.setAttribute('role', 'button');
      button.setAttribute('tabindex', '0');
      button.addEventListener('keydown', (event) => {
        if (event.key !== 'Enter' && event.key !== ' ') return;
        event.preventDefault();
        openLivePhotoFileChooser(inputId);
      });
      return;
    }
    button.setAttribute('type', 'button');
    button.addEventListener('click', (event) => {
      event.preventDefault();
      openLivePhotoFileChooser(inputId);
    });
  });
}

let temuActionModalState = null;

function ensureTemuActionModal() {
  let mask = document.getElementById('temuActionModal');
  if (mask) return mask;

  mask = document.createElement('div');
  mask.id = 'temuActionModal';
  mask.className = 'temu-action-modal-mask';
  mask.setAttribute('aria-hidden', 'true');
  mask.innerHTML = `
    <div class="temu-action-modal" role="dialog" aria-modal="true" aria-labelledby="temuActionModalTitle">
      <div class="temu-action-modal-head">
        <h3 class="temu-action-modal-title" id="temuActionModalTitle">功能设置</h3>
        <button class="temu-action-modal-close" type="button" data-temu-action-modal-close aria-label="关闭">×</button>
      </div>
      <div class="temu-action-modal-body" id="temuActionModalBody"></div>
    </div>
  `;
  mask.addEventListener('click', (event) => {
    if (event.target === mask || event.target?.closest?.('[data-temu-action-modal-close]')) {
      closeTemuActionModal();
    }
  });
  document.body.appendChild(mask);
  return mask;
}

function closeTemuActionModal() {
  const mask = document.getElementById('temuActionModal');
  if (temuActionModalState?.panel && temuActionModalState?.placeholder?.parentNode) {
    const { panel, placeholder } = temuActionModalState;
    panel.hidden = true;
    placeholder.parentNode.insertBefore(panel, placeholder);
    placeholder.remove();
  }
  temuActionModalState = null;
  if (mask) {
    mask.classList.remove('is-open');
    mask.setAttribute('aria-hidden', 'true');
    const body = mask.querySelector('#temuActionModalBody');
    if (body) body.innerHTML = '';
  }
}

function openTemuActionModal(panelId, title = '功能设置') {
  const panel = document.getElementById(panelId);
  if (!panel) {
    if (typeof showToast === 'function') {
      showToast('功能参数面板不存在，请刷新页面后重试', 'error');
    }
    return;
  }

  closeTemuActionModal();

  const mask = ensureTemuActionModal();
  const titleEl = mask.querySelector('#temuActionModalTitle');
  const body = mask.querySelector('#temuActionModalBody');
  if (!body) return;

  const placeholder = document.createComment(`temu-action-modal:${panelId}`);
  panel.parentNode.insertBefore(placeholder, panel.nextSibling);
  panel.hidden = false;
  body.appendChild(panel);
  titleEl.textContent = title || '功能设置';
  temuActionModalState = { panel, placeholder };
  mask.classList.add('is-open');
  mask.setAttribute('aria-hidden', 'false');

  const focusTarget = panel.querySelector('input:not([type="hidden"]):not(:disabled), select:not(:disabled), textarea:not(:disabled), button:not(:disabled)');
  if (focusTarget) {
    window.setTimeout(() => focusTarget.focus({ preventScroll: true }), 30);
  }
}

function initTemuActionModals() {
  if (window.__TEMU_ACTION_MODALS_BOUND__) return;
  window.__TEMU_ACTION_MODALS_BOUND__ = true;
  document.addEventListener('click', (event) => {
    const trigger = event.target?.closest?.('[data-temu-action-modal]');
    if (!trigger) return;
    event.preventDefault();
    openTemuActionModal(trigger.dataset.temuActionModal, trigger.dataset.temuActionTitle || trigger.textContent.trim());
  });
  document.addEventListener('keydown', (event) => {
    if (event.key === 'Escape' && temuActionModalState) closeTemuActionModal();
  });
}

const temuCheckboxPointerState = new WeakMap();

function isInteractiveCheckboxBypass(target) {
  if (!target?.closest) return false;
  const interactive = target.closest('button,a,textarea,select,input:not([type="checkbox"]),[contenteditable="true"]');
  return Boolean(interactive);
}

function isTemuManagedCheckbox(checkbox) {
  if (!checkbox || checkbox.type !== 'checkbox' || checkbox.disabled) return false;
  if (typeof isOzonWorkspace === 'function' && isOzonWorkspace()) return false;
  if (checkbox.matches('[data-ozon-product-check],[data-ozon-order-check],[data-ozon-link-check],.ozon-check')) return false;
  if (checkbox.closest('#ozonHome,#ozonProducts,#ozonOrders,#ozonCollection,#ozonFinance,#ozonMessages,#ozonTools,#ozonAccounts,[id^="ozon"],.ozon-modal')) return false;
  return Boolean(checkbox.closest('#dashboard,#salesdata,#temuAiDiagnosis,#compliance,#livephotos,#pricing,#customized,#temuUploadTest,#activity,#qualification,#collection,#finance,#customizedExportModal,.modal'));
}

function findTemuCheckboxIntent(event) {
  const target = event.target;
  if (!target?.closest) return null;
  const directCheckbox = target.closest('input[type="checkbox"]');
  if (directCheckbox && isTemuManagedCheckbox(directCheckbox)) return directCheckbox;
  if (isInteractiveCheckboxBypass(target)) return null;

  const intentHost = target.closest([
    '.customized-product-select',
    '.temu-ai-product-card',
    '.customized-export-option',
    '.product-table th:first-child',
    '.product-table td:first-child',
    'label'
  ].join(','));
  const checkbox = intentHost?.querySelector?.('input[type="checkbox"]');
  return isTemuManagedCheckbox(checkbox) ? checkbox : null;
}

function rememberTemuCheckboxIntent(event) {
  if (event.button !== undefined && event.button !== 0) return;
  const checkbox = findTemuCheckboxIntent(event);
  if (!checkbox) return;
  temuCheckboxPointerState.set(checkbox, {
    checked: checkbox.checked,
    at: Date.now()
  });
}

document.addEventListener('pointerdown', rememberTemuCheckboxIntent, true);
document.addEventListener('mousedown', rememberTemuCheckboxIntent, true);

document.addEventListener('click', (event) => {
  const checkbox = findTemuCheckboxIntent(event);
  if (!checkbox) return;
  const remembered = temuCheckboxPointerState.get(checkbox);
  if (!remembered) return;
  window.setTimeout(() => {
    if (!document.contains(checkbox) || checkbox.disabled) return;
    if (checkbox.checked === remembered.checked) {
      checkbox.indeterminate = false;
      checkbox.checked = !remembered.checked;
      checkbox.dispatchEvent(new Event('input', { bubbles: true }));
      checkbox.dispatchEvent(new Event('change', { bubbles: true }));
      console.warn('[小青蛙电商助手] 已修复一次被拦截的复选框点击:', checkbox.id || checkbox.className || checkbox.name || 'checkbox');
    }
    temuCheckboxPointerState.delete(checkbox);
  }, 0);
}, true);

// ===== 标签页 =====
function initTabs() {
  document.querySelectorAll('.tab').forEach(tab => {
    if (tab.dataset.tabBound === '1') return;
    tab.dataset.tabBound = '1';
    tab.addEventListener('click', (event) => {
      event.preventDefault();
      handleTabClick(tab);
    });
  });
}

function handleTabClick(tab) {
  const tabs = document.querySelectorAll('.tab');
  const sections = document.querySelectorAll('.section');
  const tabId = tab.dataset.tab;
  if (isOzonWorkspace() && !OZON_TAB_IDS.has(tabId)) {
    switchWorkspaceForPlatform('ozon');
    return;
  }
  if (ADMIN_ONLY_TABS.has(tabId) && !isAdminUser(getCurrentServerUser())) {
    showToast('该功能正在内部测试，暂未对普通账号开放', 'warning');
    return;
  }
  if (featureAccessState.locked && tabId !== 'dashboard') {
    showToast(featureAccessState.reason || '当前店铺未授权，请先绑定并完成卡密授权', 'warning');
    return;
  }
  tabs.forEach(t => t.classList.remove('active'));
  sections.forEach(s => s.classList.remove('active'));
  tab.classList.add('active');
  const targetSection = document.getElementById(tabId);
  if (!targetSection) {
    console.warn('[小青蛙电商助手] 标签页对应区域不存在:', tabId);
    showToast('页面区域未初始化完成，请刷新页面后重试', 'error');
    return;
  }
  targetSection.classList.add('active');
  if (OZON_TAB_IDS.has(tabId)) {
    loadOzonTab(tabId).catch(err => setOzonResult(err.message || '加载失败', 'error'));
    return;
  }
  if (tabId === 'salesdata' && salesDataState.allProducts.length === 0 && state.mallId && state.antiContent) {
    loadSalesDataCache().then((loaded) => {
      if (loaded) return;
      renderSalesDataTable();
    }).catch(err => console.warn('[小青蛙电商助手] 切换销量查询时读取服务器缓存失败:', err));
  }
  if (tabId === 'compliance' && state.products.length === 0 && state.mallId && state.antiContent) {
    loadComplianceProductSnapshot({ showToastOnHit: true }).then((loaded) => {
      if (loaded) return;
      const container = document.getElementById('productListContainer');
      if (container) {
        container.innerHTML = '<div class="empty"><div class="empty-icon">📦</div><p>暂无合规同步缓存，请点击“刷新列表”读取 TEMU 数据。</p></div>';
      }
    }).catch(err => console.warn('[小青蛙电商助手] 切换合规信息时读取服务器缓存失败:', err));
  }
  // 切换到实拍图 tab 时优先显示服务器缓存；只有用户点击刷新才读取 TEMU。
  if (tabId === 'livephotos' && state.lpProducts.length === 0 && state.mallId && state.antiContent) {
    loadLivePhotoProductSnapshot({ showToastOnHit: true }).then((loaded) => {
      if (loaded) return;
      const container = document.getElementById('lpProductListContainer');
      if (container) {
        container.innerHTML = '<div class="empty"><div class="empty-icon">📋</div><p>暂无实拍图同步缓存，请点击刷新后读取 TEMU 数据。</p></div>';
      }
    }).catch(err => console.warn('[小青蛙电商助手] 切换实拍图时读取服务器缓存失败:', err));
  }
  if (tabId === 'pricing' && pricingState.list.length === 0 && state.mallId && state.antiContent) {
    Promise.resolve(loadPricingLocalCache({ showToastOnHit: true })).then((loaded) => {
      if (loaded) return;
      const container = document.getElementById('pricingTableBody');
      if (container) {
        container.innerHTML = '<tr><td colspan="11" class="empty">暂无当前店铺本地临时核价缓存，请点击“加载全部待核价商品”读取 TEMU 数据。</td></tr>';
      }
    }).catch(err => console.warn('[小青蛙电商助手] 切换核价时读取本地缓存失败:', err));
  }
  // 切换到定制品 tab 时自动加载
  if (tabId === 'customized' && customizedProductState.products.length === 0) {
    if (state.mallId && state.antiContent) {
      loadCustomizedProducts();
    } else {
      showToast('请先打开 TEMU 卖家后台并点击重新连接，再加载定制品商品', 'warning');
    }
  }
  // 切换到活动报名 tab 时自动加载
  if (tabId === 'activity' && activityState.activities.length === 0) {
    if (state.mallId && state.antiContent) {
      loadActivityList();
    } else {
      showToast('请先打开 TEMU 卖家后台并点击重新连接，再加载活动数据', 'warning');
    }
  }
  // 切换到资质上传 tab 时自动加载
  if (tabId === 'qualification' && qualState.products.length === 0) {
    if (state.mallId && state.antiContent) {
      loadQualProductList();
    } else {
      showToast('请先打开 TEMU 卖家后台并点击重新连接，再加载资质商品', 'warning');
    }
  }
  if (tabId === 'finance') {
    renderFinanceData();
  }
  if (tabId === 'collection') {
    updateCollectionStats();
    renderCollectionResults();
  }
  if (tabId === 'temuUploadTest') {
    renderTemuUploadTestRows();
  }
  if (tabId === 'temuAiDiagnosis') {
    prepareTemuAiDiagnosisProducts({ silent: true });
  }
}

// ===== 商品选择模式切换 =====
function initModeSwitch() {
  const listBtn = document.getElementById('modeListBtn');
  const manualBtn = document.getElementById('modeManualBtn');
  const listSection = document.getElementById('productListSection') || document.getElementById('productListContainer')?.closest('.compliance-left');
  const manualSection = document.getElementById('manualSpuSection');
  const scopeWrap = document.getElementById('uploadScopeWrap');

  if (!listBtn || !manualBtn || !listSection || !manualSection) {
    console.warn('[小青蛙电商助手] 商品选择模式切换区域未初始化完成');
    return;
  }

  listBtn.addEventListener('click', () => {
    state.productSelectMode = 'list';
    listBtn.classList.add('active');
    manualBtn.classList.remove('active');
    listSection.classList.remove('hidden');
    manualSection.classList.remove('active');
    // 列表模式下显示上传范围
    if (scopeWrap) scopeWrap.style.display = 'flex';
  });

  manualBtn.addEventListener('click', () => {
    state.productSelectMode = 'manual';
    manualBtn.classList.add('active');
    listBtn.classList.remove('active');
    manualSection.classList.add('active');
    listSection.classList.add('hidden');
    // 手动输入模式下隐藏上传范围
    if (scopeWrap) scopeWrap.style.display = 'none';
    // 触发一次 SPU 计数
    updateManualSpuCount();
  });
}

// ===== 手动输入 SPU 的实时计数 =====
function initManualSpuInput() {
  const textarea = document.getElementById('manualSpuInput');
  if (!textarea) return;
  textarea.addEventListener('input', updateManualSpuCount);
}

function updateManualSpuCount() {
  const textarea = document.getElementById('manualSpuInput');
  const countEl = document.getElementById('manualSpuCount');
  if (!textarea || !countEl) return;

  const text = textarea.value.trim();
  if (!text) {
    countEl.style.display = 'none';
    return;
  }

  const spuList = text.split(/[\n,，\s]+/).map(s => s.trim()).filter(s => s && /^\d+$/.test(s));
  countEl.style.display = 'block';
  countEl.innerHTML = `共 <strong>${spuList.length}</strong> 个 SPU`;
}

// ===== 自动检测认证 =====
async function autoDetectAuth(options = {}) {
  showToast('正在检测认证信息...', 'info');

  try {
    // 先唤醒 Service Worker（发一个 ping）
    try {
      await chrome.runtime.sendMessage({ action: 'ping' });
    } catch (e) {
      console.log('[小青蛙电商助手] ping Service Worker 失败（可能正在唤醒）:', e.message);
    }

    // 带超时的 autoDetectAuth 消息
    const result = await Promise.race([
      chrome.runtime.sendMessage({
        action: 'autoDetectAuth',
        forceRefresh: options.forceRefresh === true,
        rejectCache: options.rejectCache === true
      }),
      new Promise((_, reject) => setTimeout(() => reject(new Error('background 响应超时')), 10000))
    ]);
    console.log('[小青蛙电商助手] autoDetectAuth 结果:', result);

    if (result.success) {
      if (options.rejectCache && result.fromCache) {
        throw new Error('重新连接需要读取当前 TEMU 页面，不能使用旧缓存认证');
      }
      const normalizedAuth = applyTemuAuthToState(result);
      state.mallName = result.mallName || null;
      rememberShopName(normalizedAuth.mallId, result.mallName);

      // 保存到 storage
      await safeStorageSet({
        temuAuth: {
          mallId: normalizedAuth.mallId,
          antiContent: normalizedAuth.antiContent,
          mallName: result.mallName || null,
          updatedAt: Date.now()
        },
        temuLocalMallId: normalizedAuth.mallId
      }, '认证信息');

      // 通知 background
      await safeRuntimeSendMessage({
        action: 'setAuth',
        mallId: normalizedAuth.mallId,
        antiContent: normalizedAuth.antiContent,
        mallName: result.mallName || null
      });

      updateConnectionStatus(true, result.mallName);
      hydrateTemuSnapshotSyncTimes();

      if (result.fromCache) {
        showToast('使用缓存的认证信息', 'info');
      } else {
        showToast('自动检测成功！' + (result.mallName ? ` 店铺: ${result.mallName}` : ''), 'success');
      }
      hydrateTemuDashboardFromServerSnapshots().catch(err => {
        console.warn('[小青蛙电商助手] 店铺连接后读取服务器缓存失败:', err);
      });
    } else {
      let errorMsg = result.error || '未获取到认证信息';
      const hasUsableExistingAuth = Boolean(state.mallId && state.antiContent);
      if (result.mallId) {
        errorMsg += ` (但获取到了 mallId: ${result.mallId})`;
        state.mallId = normalizeMallIdValue(result.mallId);
        state.mallName = result.mallName || null;
      }
      // 显示诊断信息
      if (result.diagnostics) {
        const d = result.diagnostics;
        console.log('[小青蛙电商助手] 诊断信息:', d);
        errorMsg += `\n诊断: ${JSON.stringify(d)}`;
      }
      if (!result.mallId) {
        errorMsg += '\n请先在 TEMU 卖家中心页面完成登录并刷新该页面，再点“重新连接”。';
      }
      if (hasUsableExistingAuth) {
        updateConnectionStatus(true, state.mallName);
        console.warn('[小青蛙电商助手] 自动检测未刷新认证，但已保留现有店铺连接:', errorMsg);
      } else {
        showToast('提示：' + errorMsg, 'info');
        updateConnectionStatus(false);
      }
    }
    await Promise.allSettled([
      loadTemplates(),
      loadCustomizedTemplates()
    ]);
  } catch (error) {
    console.error('[小青蛙电商助手] 自动检测失败:', error);
    if (state.mallId && state.antiContent) {
      updateConnectionStatus(true, state.mallName);
      console.warn('[小青蛙电商助手] 自动检测失败，已保留现有店铺连接');
    } else {
      showToast('提示：自动检测失败，请确保已登录 TEMU 店铺', 'info');
      updateConnectionStatus(false);
    }
  }
}

// ===== 加载设置 =====
async function loadSettings() {
  const result = await chrome.storage.local.get(['temuAuth']);

  // 尝试从当前页面获取最新 mallId 检测店铺切换
  let currentMallId = null;
  try {
    const authResult = await chrome.runtime.sendMessage({ action: 'getCurrentMallId' });
    if (authResult && authResult.mallId) {
      currentMallId = normalizeMallIdValue(authResult.mallId);
    }
  } catch (e) { /* ignore */ }

  if (result.temuAuth) {
    const normalizedStoredAuth = normalizeTemuAuth(result.temuAuth);
    const storedMallId = normalizedStoredAuth.mallId;

    if (currentMallId && storedMallId !== currentMallId) {
      console.log('[小青蛙电商助手] 检测到店铺切换！', storedMallId, '→', currentMallId);
      await chrome.storage.local.remove(['temuAuth']);
      try {
        await chrome.runtime.sendMessage({ action: 'clearAuth' });
      } catch (e) {
        console.warn('[小青蛙电商助手] 清理后台认证状态失败:', e);
      }
      state.mallId = null;
      state.antiContent = null;
      state.mallName = null;
      resetSalesDataState();
      updateSalesSyncDisplay();
      renderSalesDataTable();
      updateConnectionStatus(false);
      showToast('检测到店铺切换，请重新连接', 'warning');
      return;
    }

    state.mallId = normalizedStoredAuth.mallId;
    state.antiContent = normalizedStoredAuth.antiContent;
    state.mallName = result.temuAuth.mallName || state.mallName || null;
    if (state.mallId && state.antiContent && (result.temuAuth.mallId !== state.mallId || result.temuAuth.antiContent !== state.antiContent)) {
      await safeStorageSet({
        temuAuth: { ...result.temuAuth, mallId: state.mallId, antiContent: state.antiContent, updatedAt: Date.now() },
        temuLocalMallId: state.mallId
      }, '认证信息清理');
      await safeRuntimeSendMessage({ action: 'setAuth', mallId: state.mallId, antiContent: state.antiContent, mallName: state.mallName || null });
    }
    updateConnectionStatus(true);
  } else {
    // 尝试从 background 获取默认认证信息
    try {
      const authResult = await chrome.runtime.sendMessage({ action: 'getAuth' });
      if (authResult && authResult.mallId && authResult.antiContent) {
        const normalizedAuth = normalizeTemuAuth(authResult);
        state.mallId = normalizedAuth.mallId;
        state.antiContent = normalizedAuth.antiContent;
        state.mallName = authResult.mallName || state.mallName || null;

        await safeStorageSet({
          temuAuth: { mallId: state.mallId, antiContent: state.antiContent, mallName: authResult.mallName || null, updatedAt: Date.now() },
          temuLocalMallId: state.mallId
        }, '认证信息');
        await safeRuntimeSendMessage({ action: 'setAuth', mallId: state.mallId, antiContent: state.antiContent, mallName: authResult.mallName || null });

        updateConnectionStatus(true);
        showToast('已自动设置认证信息', 'success');
      } else {
        showToast('请先登录 TEMU 店铺', 'info');
      }
    } catch (error) {
      console.error('[小青蛙电商助手] 获取默认认证信息失败:', error);
      showToast('获取认证信息失败', 'error');
    }
  }

  if (state.mallId) {
    const tplKey = getTemplateKey(state.mallId);
    const tplResult = await chrome.storage.local.get([tplKey]);
    state.templates = Array.isArray(tplResult[tplKey]) ? tplResult[tplKey] : [];
  } else {
    state.templates = [];
  }
  renderTemplates();
}

// ===== 连接状态 =====
function updateConnectionStatus(connected, mallName = null) {
  const statusEl = document.getElementById('connectionStatus');
  const mallInfoEl = document.getElementById('mallInfo');
  const topAuthStatusEl = document.getElementById('topAuthStatus');

  if (connected === false) {
    state.mallId = null;
    state.antiContent = null;
    state.mallName = null;
  }

  if (connected && state.mallId) {
    if (statusEl) {
      statusEl.textContent = '已连接';
      statusEl.className = 'status-badge status-connected';
    }

    const name = [mallName, state.mallName].find(value => value && value !== '当前店铺' && value !== '当前店铺 ') || `店铺 ${state.mallId}`;
    if (mallInfoEl) mallInfoEl.textContent = `${name} (${state.mallId})`;
    updateBridgeBanner({
      visible: true,
      title: '网站工作台与 TEMU 店铺已连接',
      description: `当前店铺已连接${name ? `：${name}` : ''}，后续操作会通过本地浏览器里的“小青蛙电商助手”执行。`
    });
  } else {
    const bridge = getBridgeRuntimeState();
    if (statusEl) {
      statusEl.textContent = bridge.active && bridge.ready ? '助手已连接' : '未连接助手';
      statusEl.className = `status-badge ${bridge.active && bridge.ready ? 'status-bridge-ready' : 'status-disconnected'}`;
    }
    if (mallInfoEl) mallInfoEl.textContent = getCurrentShopDisplayName();
    if (topAuthStatusEl && !getCurrentServerUser()) topAuthStatusEl.style.display = 'none';
  }
  syncDingHuiTemuHome();
}

function requireAdminFeature(featureName = '该功能') {
  if (isAdminUser(getCurrentServerUser())) return true;
  showToast(`${featureName}正在内部测试，暂未对普通账号开放`, 'warning');
  return false;
}

function bindAdminFeatureEvents() {
  if (adminFeatureEventsBound || !adminFeatureMounted) return;
  adminFeatureEventsBound = true;

  document.getElementById('collectionLoadSourceBtn')?.addEventListener('click', loadCollectionSourceProducts);
  document.getElementById('collectionSaveTemplateBtn')?.addEventListener('click', () => saveSelectedSourceProducts());
  document.getElementById('collectionSourceSearchInput')?.addEventListener('input', renderCollectionSourceProducts);
  document.getElementById('collectionTemplateRefreshBtn')?.addEventListener('click', loadCollectionProductTemplates);
  document.getElementById('collectionTemplateSelectAllBtn')?.addEventListener('click', () => {
    if (collectionState.templateSelected.size === collectionState.templates.length) {
      collectionState.templateSelected.clear();
    } else {
      collectionState.templates.forEach(template => collectionState.templateSelected.add(getProductTemplateKey(template)));
    }
    renderCollectionTemplates();
  });
  document.getElementById('collectionAddTemplateToTaskBtn')?.addEventListener('click', () => {
    const templates = collectionState.templates.filter(template => collectionState.templateSelected.has(getProductTemplateKey(template)));
    if (!templates.length) return showToast('请先选择商品模板', 'error');
    addTemplatesToMoveTasks(templates);
  });
  document.getElementById('collectionCreateDraftBtn')?.addEventListener('click', createCollectionDrafts);
  document.getElementById('collectionClearBtn')?.addEventListener('click', clearCollection);
  document.getElementById('collectionSelectAllBtn')?.addEventListener('click', () => {
    if (collectionState.selected.size === collectionState.items.length) {
      collectionState.selected.clear();
    } else {
      collectionState.items.forEach(item => collectionState.selected.add(getCollectionItemKey(item)));
    }
    renderCollectionResults();
  });
  updateCollectionStats();
  renderCollectionSourceProducts();
  loadCollectionProductTemplates().catch(err => console.warn('[小青蛙电商助手] 初始化商品模板失败:', err));
  document.getElementById('collectionRepeatInput')?.addEventListener('input', updateCollectionStats);

  document.getElementById('financeRefreshBtn')?.addEventListener('click', refreshFinanceData);
  document.getElementById('financeQueryBtn')?.addEventListener('click', queryFinanceHistory);
  document.getElementById('financeExportBtn')?.addEventListener('click', () => {
    exportFinanceHistory();
  });
}

// ===== 模板管理 =====
async function loadTemplates() {
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) {
    state.templates = [];
    renderTemplates();
    return;
  }
  const result = await safeRuntimeSendMessage({ action: 'getTemplates', mallId }, { success: false, error: '后台模板加载失败' });
  state.templates = result?.success
    ? (Array.isArray(result.templates) ? result.templates : [])
    : [];
  console.log('[小青蛙电商助手] 加载模板:', state.templates.length, '个');
  renderTemplates();
}

async function refreshComplianceTemplatesFromButton(event) {
  const btn = event?.currentTarget;
  const oldText = btn?.textContent || '刷新';
  try {
    if (btn) {
      btn.disabled = true;
      btn.textContent = '刷新中...';
    }
    await loadTemplates();
    showToast(`合规模板已刷新，共 ${state.templates.length} 个`, 'success');
  } catch (error) {
    showToast(`刷新合规模板失败：${error?.message || error || '未知错误'}`, 'error');
  } finally {
    if (btn) {
      btn.disabled = false;
      btn.textContent = oldText;
    }
  }
}

function renderTemplates() {
  // 填充创建合规任务中的模板下拉选择
  const dropdown = document.getElementById('templateSelectDropdown');
  const inlineList = document.getElementById('templateInlineList');
  if (!dropdown) return;

  const currentVal = dropdown.value;
  dropdown.innerHTML = '<option value="">-- 请选择模板 --</option>';
  if (inlineList) inlineList.innerHTML = '';
  state.templates.forEach(tpl => {
    const opt = document.createElement('option');
    opt.value = tpl.id;
    opt.textContent = tpl.name || '未命名模板';
    if (tpl.id === currentVal || tpl.id === state.selectedTemplateId) {
      opt.selected = true;
    }
    dropdown.appendChild(opt);

    if (inlineList) {
      const item = document.createElement('div');
      item.tabIndex = 0;
      item.setAttribute('role', 'button');
      item.className = 'template-inline-item';
      item.dataset.templateId = String(tpl.id);
      item.title = tpl.name;
      if (tpl.id === currentVal || tpl.id === state.selectedTemplateId) {
        item.classList.add('active');
      }

      const name = document.createElement('span');
      name.className = 'template-inline-name';
      name.textContent = tpl.name || '未命名模板';

      const remove = document.createElement('button');
      remove.type = 'button';
      remove.className = 'template-inline-delete';
      remove.textContent = 'X';
      remove.title = `删除模板「${tpl.name}」`;
      remove.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        deleteComplianceTemplate(tpl.id);
      });

      item.append(name, remove);
      item.addEventListener('click', () => {
        dropdown.value = String(tpl.id);
        state.selectedTemplateId = String(tpl.id);
        renderTemplates();
      });
      item.addEventListener('keydown', (event) => {
        if (event.key !== 'Enter' && event.key !== ' ') return;
        event.preventDefault();
        dropdown.value = String(tpl.id);
        state.selectedTemplateId = String(tpl.id);
        renderTemplates();
      });
      inlineList.appendChild(item);
    }
  });
  // 同步下拉选中到 state
  state.selectedTemplateId = dropdown.value || null;
}

async function deleteSelectedComplianceTemplate() {
  const dropdown = document.getElementById('templateSelectDropdown');
  const templateId = dropdown?.value || state.selectedTemplateId;
  return await deleteComplianceTemplate(templateId);
}

async function deleteComplianceTemplate(templateId) {
  if (!templateId) {
    showToast('请先选择要删除的模板', 'error');
    return;
  }

  const template = state.templates.find(t => String(t.id) === String(templateId));
  if (!template) {
    showToast('模板不存在', 'error');
    return;
  }

  if (!confirm(`确定删除模板「${template.name}」吗？`)) return;

  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) {
    showToast('未检测到当前店铺，无法删除模板', 'error');
    return;
  }
  const deleteResult = await safeRuntimeSendMessage({ action: 'deleteTemplate', templateId: String(templateId), mallId }, { success: false, error: '后台删除失败' });
  if (!deleteResult?.success) {
    showToast(`删除失败：${deleteResult?.error || '服务器删除失败'}`, 'error');
    return;
  }
  state.templates = state.templates.filter(t => String(t.id) !== String(templateId));
  state.selectedTemplateId = null;
  renderTemplates();
  showToast(`模板「${template.name}」已删除`, 'success');
}

// ===== 商品列表 =====
async function fetchProductList(params = {}) {
  if (!state.mallId || !state.antiContent) {
    showToast('请先设置认证信息', 'error');
    return null;
  }
  const auth = normalizeTemuAuth({ mallId: state.mallId, antiContent: state.antiContent });
  state.mallId = auth.mallId;
  state.antiContent = auth.antiContent;
  return await safeRuntimeSendMessage({
    action: 'fetchProductList',
    params: { pageNum: params.pageNum, pageSize: params.pageSize, fetchAll: params.fetchAll, mallId: auth.mallId, antiContent: auth.antiContent }
  }, { success: false, error: '获取商品列表失败' }, 45000);
}

async function refreshProductList(params = {}) {
  const container = document.getElementById('productListContainer');
  container.innerHTML = '<div class="loading">加载中...</div>';

  const pageNum = params.pageNum || state.pageNum || 1;
  const pageSize = params.pageSize || state.pageSize || 50;
  const hasSearchIds = Array.isArray(state.complianceSearchIds) && state.complianceSearchIds.length > 0;
  const fetchAll = params.fetchAll || hasSearchIds || false;

  console.log('[小青蛙电商助手] refreshProductList:', { pageNum, pageSize, fetchAll });

  const result = await fetchProductList({ pageNum, pageSize, fetchAll });
  console.log('[小青蛙电商助手] fetchProductList 结果:', result);

  if (result && result.success) {
    const products = Array.isArray(result.products) ? result.products : [];
    state.products = hasSearchIds
      ? products.filter(product => matchesAnyTemuSearchTerm([product.spuId, product.productId, product.goodsId], state.complianceSearchIds))
      : products;
    state.pageNum = hasSearchIds ? 1 : (result.pageNum || pageNum);
    state.pageSize = result.pageSize || pageSize;
    state.total = hasSearchIds ? state.products.length : (result.total || 0);
    state.totalPages = hasSearchIds ? 1 : (result.totalPages || 0);
    renderProductList();
    renderPagination();
    await saveTemuSnapshot('compliance', {
      products: state.products,
      pageNum: state.pageNum,
      pageSize: state.pageSize,
      total: state.total,
      totalPages: state.totalPages
    }, { itemCount: state.products.length });
  } else {
    if (await loadComplianceProductSnapshot({ showToastOnHit: true })) return;
    const errorMsg = result?.error || '获取商品列表失败';
    const errorCode = result?.errorCode || '';
    container.innerHTML = `
      <div class="empty">
        <div class="empty-icon">❌</div>
        <p>${escapeHtml(errorMsg)}</p>
        ${errorCode ? `<p style="font-size: 11px; color: #bbb;">错误代码: ${escapeHtml(errorCode)}</p>` : ''}
      </div>
    `;
  }
}

// 同步商品状态（逐个查询 TEMU 后台）


function renderProductList() {
  const container = document.getElementById('productListContainer');
  if (state.products.length === 0) {
    const searchedIds = uniqueStringList(state.complianceSearchIds || []);
    const emptyText = searchedIds.length
      ? `未找到这些 SPU 的合规商品：${searchedIds.join('、')}`
      : '暂无商品数据';
    container.innerHTML = `<div class="empty"><div class="empty-icon">📦</div><p>${escapeHtml(emptyText)}</p></div>`;
    updateSelectedCountInfo();
    return;
  }

  const statusFilter = document.getElementById('statusFilter')?.value || '';
  const searchQuery = String(state.complianceSearchText || '').trim().toLowerCase();

  let filtered = state.products;
  if (statusFilter) filtered = filtered.filter(p => p.status === statusFilter);
  if (searchQuery) filtered = filtered.filter(p =>
    matchesAnyTemuSearchTerm([p.spuId, p.productId, p.goodsId, p.spuName], parseTemuSearchTerms(searchQuery))
  );

  const html = `
    <table class="product-table">
      <thead>
        <tr>
          <th style="width: 30px;"><input type="checkbox" class="checkbox" id="selectAll"></th>
          <th style="width: 60px;">主图</th>
          <th style="width: 110px;">SPU ID</th>
          <th style="min-width: 180px;">商品名称</th>
          <th style="width: 250px;">合规状态</th>
        </tr>
      </thead>
      <tbody>
        ${filtered.map(p => `
          <tr>
          <td><input type="checkbox" class="checkbox product-checkbox" data-spu="${p.spuId}" data-skc="${p.productId || ''}"></td>
            <td>
              ${p.mainImage
                ? `<img src="${escapeHtml(p.mainImage)}" class="product-thumb" style="width:48px;height:48px;object-fit:cover;border-radius:4px;border:1px solid #eee;" crossorigin="anonymous">`
                : '<div style="width:48px;height:48px;background:#f5f5f5;border-radius:4px;display:flex;align-items:center;justify-content:center;color:#ccc;font-size:18px;">📷</div>'}
            </td>
            <td style="font-size:12px;">${p.spuId}</td>
            <td style="max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${escapeHtml(p.spuName || '-')}">${escapeHtml(p.spuName || '-')}</td>
            <td style="text-align:left;padding:8px;">${renderTaskList(p)}</td>
          </tr>
        `).join('')}
      </tbody>
    </table>
  `;

  container.innerHTML = html;
  const complianceChecks = () => Array.from(container.querySelectorAll('.product-checkbox'));

  // 处理图片加载失败，显示占位符
  container.querySelectorAll('img.product-thumb').forEach(img => {
    img.addEventListener('error', function() {
      this.outerHTML = '<div style="width:48px;height:48px;background:#f5f5f5;border-radius:4px;display:flex;align-items:center;justify-content:center;color:#ccc;font-size:18px;">📷</div>';
    });
  });

  document.getElementById('selectAll')?.addEventListener('change', (e) => {
    complianceChecks().forEach(cb => cb.checked = e.target.checked);
    updateSelectedCountInfo();
  });

  // 绑定单个 checkbox 的 change 事件
  complianceChecks().forEach(cb => {
    cb.addEventListener('change', updateSelectedCountInfo);
  });

  updateSelectedCountInfo();
}

// ===== 更新已选择商品数 =====
function updateSelectedCountInfo() {
  const countEl = document.getElementById('selectedCountInfo');
  if (!countEl) return;

  const checkedCount = document.querySelectorAll('#productListContainer .product-checkbox:checked').length;
  countEl.style.display = checkedCount > 0 ? 'block' : 'none';
  countEl.innerHTML = `已选择 <strong>${checkedCount}</strong> 个商品`;
}


function renderTaskList(product) {
  if (!product.waitTaskList || product.waitTaskList.length === 0) {
    return '<span style="color: #52c41a; font-size: 12px;">✅ 合规信息已完整</span>';
  }

  return product.waitTaskList.map(task => {
    const isRequired = !task.is_not_required;
    const statusState = task.statusState || task.status_state || '';
    const statusText = task.statusText || task.status_name || task.statusName || task.status_desc || task.task_status_name
      || (statusState === 'completed' ? '上传成功' : statusState === 'reviewing' ? '审核中' : '待上传');
    const color = statusState === 'completed'
      ? '#52c41a'
      : statusState === 'reviewing'
        ? '#1677ff'
        : statusState === 'closed'
          ? '#94a3b8'
          : '#fa8c16';
    const taskName = task.task_name || `任务${task.task_type}`;
    const req = isRequired ? '<span style="color: #ff4d4f;">*</span>' : '';
    return `<div style="margin: 2px 0; font-size: 12px; line-height: 16px;">
      <span style="display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: ${color}; margin-right: 4px;"></span>
      ${req}${escapeHtml(taskName)} <span style="color: #999;">${statusText}</span>
    </div>`;
  }).join('');
}

// ===== 分页 =====
function renderPagination() {
  const info = document.getElementById('paginationInfo');
  const btns = document.getElementById('paginationButtons');
  if (!info || !btns) return;

  if (state.total > 0) {
    const start = (state.pageNum - 1) * state.pageSize + 1;
    const end = Math.min(state.pageNum * state.pageSize, state.total);
    info.innerHTML = `共 <strong>${state.total}</strong> 条，显示 <strong>${start}-${end}</strong>，第 <strong>${state.pageNum}</strong>/<strong>${state.totalPages}</strong> 页`;
  } else {
    info.innerHTML = '';
  }

  btns.innerHTML = '';
  if (state.totalPages <= 1) return;

  if (state.pageNum > 1) {
    const btn = document.createElement('button');
    btn.className = 'btn btn-default btn-sm'; btn.textContent = '上一页';
    btn.addEventListener('click', () => goToPage(state.pageNum - 1));
    btns.appendChild(btn);
  }

  const maxBtns = 5;
  let s = Math.max(1, state.pageNum - Math.floor(maxBtns / 2));
  let e = Math.min(state.totalPages, s + maxBtns - 1);
  if (e - s < maxBtns - 1) s = Math.max(1, e - maxBtns + 1);

  for (let i = s; i <= e; i++) {
    const btn = document.createElement('button');
    btn.className = i === state.pageNum ? 'btn btn-primary btn-sm' : 'btn btn-default btn-sm';
    btn.textContent = i;
    if (i !== state.pageNum) btn.addEventListener('click', () => goToPage(i));
    btns.appendChild(btn);
  }

  if (state.pageNum < state.totalPages) {
    const btn = document.createElement('button');
    btn.className = 'btn btn-default btn-sm'; btn.textContent = '下一页';
    btn.addEventListener('click', () => goToPage(state.pageNum + 1));
    btns.appendChild(btn);
  }
}

function goToPage(pageNum) {
  refreshProductList({ pageNum, pageSize: state.pageSize });
}

function searchComplianceProducts() {
  const raw = String(document.getElementById('searchInput')?.value || '').trim();
  state.pageNum = 1;
  state.complianceSearchIds = getTemuMultiIdSearchTerms(raw);
  state.complianceSearchText = state.complianceSearchIds.length ? '' : raw.toLowerCase();
  refreshProductList({ pageNum: 1, pageSize: state.pageSize, fetchAll: state.complianceSearchIds.length > 0 });
}

// ===== 批量上传 =====
async function startBatchUpload() {
  if (state.isUploading) {
    showToast('正在上传中，请稍候', 'info');
    return;
  }

  // 使用卡片选择的模板
  const templateId = state.selectedTemplateId;
  if (!templateId) { showToast('请先选择一个模板', 'error'); return; }

  const template = state.templates.find(t => t.id === templateId);
  if (!template) { showToast('模板不存在', 'error'); return; }
  if (template.compacted) {
    showToast('该模板是旧版本压缩缓存，格式不完整，请重新保存一次模板后再上传', 'error');
    return;
  }

  const checkCompliance = true;
  const checkPhoto = false;
  const checkQualification = false;

  let targetProducts = [];

  if (state.productSelectMode === 'manual') {
    // 手动输入模式：从 textarea 解析 SPU 列表
    const text = document.getElementById('manualSpuInput')?.value?.trim() || '';
    if (!text) { showToast('请输入 SPU ID', 'error'); return; }

    const spuList = text.split(/[\n,，\s]+/).map(s => s.trim()).filter(s => s && /^\d+$/.test(s));
    if (spuList.length === 0) { showToast('未找到有效的 SPU ID', 'error'); return; }

    // 将手动输入的 SPU 转换为商品对象格式
    targetProducts = spuList.map(spuId => ({
      spuId: parseInt(spuId),
      spuName: `SPU ${spuId}`,
      status: 'pending'
    }));
  } else {
    // 列表模式：使用原有逻辑
    const scope = document.getElementById('uploadScope')?.value || 'selected';

    switch (scope) {
      case 'selected': {
        const spuIds = Array.from(document.querySelectorAll('#productListContainer .product-checkbox:checked')).map(cb => cb.dataset.spu);
        targetProducts = state.products.filter(p => spuIds.includes(String(p.spuId)));
        break;
      }
      case 'pending':
        targetProducts = state.products.filter(p => p.status === 'pending');
        break;
      case 'all':
        targetProducts = state.products;
        break;
    }
  }

  if (targetProducts.length === 0) { showToast('没有可上传的商品', 'error'); return; }

  const confirmed = window.confirm(`即将向 TEMU 提交 ${targetProducts.length} 个商品的合规信息。\n\n只会提交合规信息，不会上传实拍图或资质文件。是否继续？`);
  if (!confirmed) return;

  state.isUploading = true;
  const uploadBtn = document.getElementById('startUploadBtn');
  if (uploadBtn) {
    uploadBtn.disabled = true;
    uploadBtn.textContent = '⏳ 上传中...';
  }

  // 显示进度区
  const progressSection = document.getElementById('uploadProgressSection');
  if (progressSection) progressSection.style.display = 'block';
  resetUploadProgress(targetProducts.length);

  const result = await safeRuntimeSendMessage({
    action: 'addTask',
    task: { type: 'batchUpload', params: { products: targetProducts, template, checkCompliance, checkPhoto, checkQualification } }
  }, { success: false, error: '添加上传任务失败' }, 30000);

  if (result.success) {
    showToast(`已添加上传任务，共 ${targetProducts.length} 个商品`, 'success');
    pollUploadProgress();
  } else {
    showToast('添加任务失败: ' + result.error, 'error');
    resetUploadState();
  }
}

function resetUploadProgress(total) {
  const fill = document.getElementById('progressFill');
  const text = document.getElementById('progressText');
  const list = document.getElementById('uploadResultList');
  fill.style.width = '0%';
  fill.className = 'progress-fill blue';
  text.textContent = `共 ${total} 个商品，排队中...`;
  list.innerHTML = '';
}

async function pollUploadProgress() {
  const poll = async () => {
    const result = await safeRuntimeSendMessage(
      { action: 'getTaskStatus' },
      { success: false, queue: [], error: '获取上传进度失败' },
      15000
    );
    if (result.success && result.queue.length > 0) {
      const latestTask = result.queue[result.queue.length - 1];
      updateUploadProgress(latestTask);

      if (latestTask.status === 'pending' || latestTask.status === 'processing') {
        setTimeout(poll, 2000);
      } else {
        resetUploadState();
        refreshProductList();
      }
    } else {
      // queue 为空说明 service worker 可能重启了，进度丢失
      const progressSection = document.getElementById('uploadProgressSection');
      if (progressSection && progressSection.style.display === 'block') {
        const text = document.getElementById('progressText');
        if (text && !text.textContent.includes('完成')) {
          text.textContent = '⚠️ 进度丢失（后台服务已重启），请刷新页面后重试';
        }
        resetUploadState();
      }
    }
  };
  poll();
}

function updateUploadProgress(task) {
  const fill = document.getElementById('progressFill');
  const text = document.getElementById('progressText');
  const list = document.getElementById('uploadResultList');

  const progressSection = document.getElementById('uploadProgressSection');
  if (progressSection) progressSection.style.display = 'block';

  // 从 task.progress 读取进度（processBatchUpload 写入的字段）
  const progress = task.progress || {};
  const details = progress.details || [];
  const total = progress.total || 0;
  const completed = progress.completed || 0;
  const failed = progress.failed || 0;
  const processed = completed + failed;

  // 进度百分比
  const pct = total > 0 ? Math.round((processed / total) * 100) : 0;
  fill.style.width = `${pct}%`;

  // 进度条颜色逻辑
  if (failed === 0 && completed > 0) {
    fill.className = 'progress-fill green';
  } else if (completed === 0 && failed > 0) {
    fill.className = 'progress-fill red';
  } else {
    fill.className = 'progress-fill blue';
  }

  // 文字
  if (task.status === 'completed') {
    fill.style.width = '100%';
    if (failed === 0 && completed > 0) {
      fill.className = 'progress-fill green';
      text.textContent = `✅ 上传完成！${completed}/${total} 成功`;
    } else {
      fill.className = failed > 0 && completed === 0 ? 'progress-fill red' : 'progress-fill blue';
      text.textContent = `上传结束：${completed} 成功, ${failed} 未上传/失败（共 ${total} 个）`;
    }
  } else if (task.status === 'failed') {
    fill.className = 'progress-fill red';
    text.textContent = `❌ 上传失败: ${task.error || '未知错误'}`;
  } else {
    const currentSpu = progress.currentSpuName || progress.currentSpuId || '';
    text.textContent = currentSpu
      ? `正在处理: ${currentSpu}（${processed}/${total}）— ${completed} 成功, ${failed} 未上传/失败`
      : `${processed}/${total} 已处理 — ${completed} 成功, ${failed} 未上传/失败`;
  }

  // 渲染每个商品的结果
  if (details.length > 0) {
    list.innerHTML = details.map(r => {
      const cls = r.status === 'success' ? 'success' : r.status === 'failed' ? 'fail' : '';
      const icon = r.status === 'success' ? '✅' : r.status === 'failed' ? '❌' : '⏳';
      const reason = r.message || '处理中...';
      const debug = r.debugSummary ? renderComplianceFailureDebug(r.debugSummary) : '';
      return `<div class="result-item ${cls}">
        <span class="icon">${icon}</span>
        <span class="reason"><strong>${escapeHtml(r.spuId || '')}</strong> — ${escapeHtml(reason)}</span>
        ${debug}
      </div>`;
    }).join('');
  }
}

function renderComplianceFailureDebug(debug = {}) {
  const tasks = Array.isArray(debug.tasks) ? debug.tasks : [];
  if (!tasks.length) return '';
  const response = debug.response || {};
  const taskText = tasks.map(item => {
    const flags = [];
    if (item.hasProperties) flags.push('属性');
    if (item.hasImages) flags.push('图片');
    if (item.hasInputText) flags.push('文本');
    if (item.repDetailCount !== undefined) flags.push(`REP:${item.repDetailCount}`);
    if (item.skuGroupCount !== undefined) flags.push(`包装:${item.skuGroupCount}`);
    return `type=${item.task_type}, id=${item.task_id || '-'}, status=${item.task_status ?? item.status ?? '-'}, template=${item.template_id || '-'}, ${flags.join('/') || '无内容标记'}`;
  }).join('；');
  const apiText = response.error_code || response.error_msg || response.message
    ? `官方返回: ${response.error_code || '-'} ${response.error_msg || response.message || ''}`
    : '';
  return `<div class="result-debug">诊断：${escapeHtml(apiText)}<br>${escapeHtml(taskText)}</div>`;
}

function resetUploadState() {
  state.isUploading = false;
  const uploadBtn = document.getElementById('startUploadBtn');
  if (uploadBtn) { uploadBtn.disabled = false; uploadBtn.textContent = '提交合规信息'; }
}

// ===== 事件绑定 =====
function bindEvents() {
  // 辅助函数：安全绑定事件（元素不存在不报错）
  const $on = (id, event, handler) => {
    const el = document.getElementById(id);
    if (el) {
      if (el.tagName === 'BUTTON' && !el.getAttribute('type')) {
        el.setAttribute('type', 'button');
      }
      el.addEventListener(event, handler);
    }
    else console.warn('[小青蛙电商助手] 元素不存在，跳过绑定:', id);
  };

  // 搜索/过滤
  $on('complianceSearchBtn', 'click', searchComplianceProducts);
  $on('searchInput', 'keydown', (event) => {
    if (event.key !== 'Enter') return;
    event.preventDefault();
    searchComplianceProducts();
  });
  $on('statusFilter', 'change', renderProductList);

  // 每页条数
  $on('pageSizeSelect', 'change', (e) => {
    state.pageSize = parseInt(e.target.value);
    state.pageNum = 1;
    refreshProductList({ pageNum: 1, pageSize: state.pageSize });
  });

  // 刷新
  $on('refreshBtn', 'click', () => {
    state.complianceSearchIds = [];
    state.complianceSearchText = '';
    const searchInput = document.getElementById('searchInput');
    if (searchInput) searchInput.value = '';
    refreshProductList();
  });

  // 开始上传
  $on('startUploadBtn', 'click', startBatchUpload);

  // 模板下拉选择
  $on('templateSelectDropdown', 'change', (e) => {
    state.selectedTemplateId = e.target.value || null;
    renderTemplates();
  });
  $on('refreshComplianceTemplatesBtn', 'click', refreshComplianceTemplatesFromButton);

  // 实拍图文件管理
  bindLivePhotoFileButtons();
  $on('startLivePhotoUploadBtn', 'click', startLivePhotoUpload);

  // 实拍图 Tab 初始化
  initLivePhotoTab();

  // 核价 Tab 初始化
  initPricingTab();

  // TEMU 功能按钮弹窗
  initTemuActionModals();

  // 销售数据 Tab 初始化
  initSalesDataTab();

  // AI 商品诊断 Tab 初始化
  initTemuAiDiagnosisTab();

  initTemuUiModules();
}

// ===== 实拍图文件管理 =====
const LIVE_PHOTO_MAX_FILES = 5;
let selectedLivePhotoFiles = []; // { file: File, dataUrl: string }

function renderLivePhotoFileList() {
  const listEl = document.getElementById('livePhotoFileList');
  const countEl = document.getElementById('livePhotoFileCount');
  const emptyHint = document.getElementById('livePhotoFileEmptyHint');
  const addBtn = document.getElementById('livePhotoFilesInputAdd');
  const addFilesBtn = document.getElementById('livePhotoAddFilesBtn');

  if (!listEl || !countEl || !emptyHint || !addBtn) return;

  listEl.innerHTML = '';

  if (selectedLivePhotoFiles.length === 0) {
    emptyHint.style.display = 'block';
    addBtn.hidden = true;
    if (addFilesBtn) addFilesBtn.hidden = true;
    countEl.textContent = '';
    return;
  }

  emptyHint.style.display = 'none';
  addBtn.hidden = false;
  if (addFilesBtn) addFilesBtn.hidden = false;
  countEl.textContent = `${selectedLivePhotoFiles.length}/${LIVE_PHOTO_MAX_FILES}`;

  selectedLivePhotoFiles.forEach((item, index) => {
    const li = document.createElement('li');
    li.className = 'file-list-item';
    const sizeKB = (item.file.size / 1024).toFixed(1);
    li.innerHTML = `
      <img class="file-thumb" src="${item.dataUrl}">
      <span class="file-name" title="${escapeHtml(item.file.name)}">${escapeHtml(item.file.name)}</span>
      <span class="file-size">${sizeKB} KB</span>
      <button class="file-remove" data-index="${index}" title="移除">×</button>
    `;
    listEl.appendChild(li);
  });

  // 绑定删除按钮
  listEl.querySelectorAll('.file-remove').forEach(btn => {
    btn.addEventListener('click', (e) => {
      const idx = parseInt(e.currentTarget.dataset.index);
      selectedLivePhotoFiles.splice(idx, 1);
      renderLivePhotoFileList();
    });
  });
}

function addLivePhotoFiles(fileList) {
  const remaining = LIVE_PHOTO_MAX_FILES - selectedLivePhotoFiles.length;
  if (remaining <= 0) {
    showToast(`最多 ${LIVE_PHOTO_MAX_FILES} 张图片`, 'warning');
    return;
  }

  const files = Array.from(fileList).filter(f => f.type.startsWith('image/'));
  const toAdd = files.slice(0, remaining);

  if (files.length > remaining) {
    showToast(`最多还能添加 ${remaining} 张，已截取`, 'warning');
  }
  if (toAdd.length === 0) return;

  let loaded = 0;
  toAdd.forEach((file) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      selectedLivePhotoFiles.push({ file, dataUrl: e.target.result });
      loaded++;
      if (loaded === toAdd.length) {
        renderLivePhotoFileList();
        showToast(`已选择 ${selectedLivePhotoFiles.length}/${LIVE_PHOTO_MAX_FILES} 张图片`, 'success');
      }
    };
    reader.readAsDataURL(file);
  });
}
// ===== 实拍图上传 =====

function initLivePhotoTab() {
  bindLivePhotoFileButtons();
  // 模式切换
  const listBtn = document.getElementById('lpModeListBtn');
  const manualBtn = document.getElementById('lpModeManualBtn');
  const manualSection = document.getElementById('lpManualSpuSection');

  listBtn.addEventListener('click', () => {
    state.lpProductSelectMode = 'list';
    listBtn.classList.add('active');
    manualBtn.classList.remove('active');
    manualSection.classList.remove('active');
  });
  manualBtn.addEventListener('click', () => {
    state.lpProductSelectMode = 'manual';
    manualBtn.classList.add('active');
    listBtn.classList.remove('active');
    manualSection.classList.add('active');
    updateLpManualSpuCount();
  });

  // 手动 SPU 输入实时计数
  const spuTextarea = document.getElementById('livePhotoSpuList');
  if (spuTextarea) {
    spuTextarea.addEventListener('input', updateLpManualSpuCount);
  }

  // 搜索
  document.getElementById('lpSearchBtn')?.addEventListener('click', () => searchLivePhotoProducts());
  document.getElementById('lpSearchInput')?.addEventListener('keydown', (event) => {
    if (event.key !== 'Enter') return;
    event.preventDefault();
    searchLivePhotoProducts();
  });

  // 状态筛选
  document.getElementById('lpStatusFilter')?.addEventListener('change', () => {
    renderLivePhotoProductList();
  });

  // 每页条数
  document.getElementById('lpPageSizeSelect')?.addEventListener('change', (e) => {
    state.livePhotoPageSize = parseInt(e.target.value);
    state.livePhotoPageNum = 1;
    refreshLivePhotoProductList();
  });

  // 刷新按钮
  document.getElementById('lpRefreshBtn')?.addEventListener('click', () => {
    state.livePhotoPageNum = 1;
    state.lpSearchQuery = '';
    state.lpSearchIds = [];
    state.lpIsSearching = false;
    const searchInput = document.getElementById('lpSearchInput');
    if (searchInput) searchInput.value = '';
    refreshLivePhotoProductList();
  });

  loadLivePhotoProductSnapshot().then((loaded) => {
    if (loaded) return;
    const container = document.getElementById('lpProductListContainer');
    if (container && !state.lpProducts.length) {
      container.innerHTML = '<div class="empty"><div class="empty-icon">📋</div><p>暂无实拍图同步缓存，请点击刷新后读取 TEMU 数据。</p></div>';
    }
  }).catch(err => console.warn('[小青蛙电商助手] 读取实拍图服务器缓存失败:', err));
}

function updateLpManualSpuCount() {
  const textarea = document.getElementById('livePhotoSpuList');
  const countEl = document.getElementById('lpManualSpuCount');
  if (!textarea || !countEl) return;
  const text = textarea.value.trim();
  if (!text) { countEl.style.display = 'none'; return; }
  const spuList = text.split(/[\n,，\s]+/).map(s => s.trim()).filter(s => s && /^\d+$/.test(s));
  countEl.style.display = 'block';
  countEl.innerHTML = `共 <strong>${spuList.length}</strong> 个 SPU`;
}

async function refreshLivePhotoProductList(params = {}) {
  const container = document.getElementById('lpProductListContainer');
  if (!container) return;
  const keyword = params.keyword !== undefined
    ? String(params.keyword || '').trim()
    : String(document.getElementById('lpSearchInput')?.value || state.lpSearchQuery || '').trim();
  const searchIds = Array.isArray(params.spuIdList)
    ? uniqueStringList(params.spuIdList)
    : uniqueStringList(state.lpSearchIds || []);
  state.lpSearchQuery = keyword;
  state.lpSearchIds = searchIds;
  state.lpIsSearching = Boolean(keyword || searchIds.length);
  container.innerHTML = `<div class="loading">${state.lpIsSearching ? `正在跨页筛选 SPU/商品：${escapeHtml(searchIds.length ? searchIds.join('、') : keyword)}...` : '加载中...'}</div>`;

  const pageNum = params.pageNum || state.livePhotoPageNum || 1;
  const pageSize = params.pageSize || state.livePhotoPageSize || 10;

  const result = await safeRuntimeSendMessage({
    action: 'getLivePhotoList',
    params: { pageNum, pageSize, keyword, spuIdList: searchIds.length ? searchIds : undefined }
  }, { success: false, error: '获取实拍图商品列表失败' }, 30000);
  console.log('[小青蛙电商助手] refreshLivePhotoProductList:', result);

  if (result && result.success) {
    state.lpProducts = result.list || [];
    state.livePhotoPageNum = pageNum;
    state.livePhotoPageSize = pageSize;
    state.livePhotoTotal = result.total || 0;
    state.livePhotoTotalPages = Math.ceil(state.livePhotoTotal / pageSize);
    renderLivePhotoProductList();
    renderLivePhotoPagination();
    if (!keyword) {
      await saveTemuSnapshot('livephotos', {
        products: state.lpProducts,
        pageNum: state.livePhotoPageNum,
        pageSize: state.livePhotoPageSize,
        total: state.livePhotoTotal,
        totalPages: state.livePhotoTotalPages
      }, { itemCount: state.lpProducts.length });
    }
  } else {
    if (!params.skipServerSnapshotFallback && !keyword && await loadLivePhotoProductSnapshot({ showToastOnHit: true })) return;
    const error = result?.error || '';
    if (error === 'NO_CAPTURED_DATA') {
      const hint = result?.hint || '请先在 TEMU 页面点击"实拍图"Tab加载一次数据';
      container.innerHTML = `<div class="empty"><div class="empty-icon">📋</div><p>${escapeHtml(hint)}</p><p style="font-size:11px;color:#999;margin-top:8px;">数据会在 TEMU 页面加载实拍图时自动缓存</p></div>`;
    } else {
      const errorMsg = error || '获取实拍图商品列表失败';
      container.innerHTML = `<div class="empty"><div class="empty-icon">❌</div><p>${escapeHtml(errorMsg)}</p></div>`;
    }
  }
}

function searchLivePhotoProducts() {
  const raw = String(document.getElementById('lpSearchInput')?.value || '').trim();
  state.livePhotoPageNum = 1;
  state.lpSearchIds = getTemuMultiIdSearchTerms(raw);
  state.lpSearchQuery = state.lpSearchIds.length ? '' : raw;
  refreshLivePhotoProductList({
    pageNum: 1,
    keyword: state.lpSearchQuery,
    spuIdList: state.lpSearchIds,
    skipServerSnapshotFallback: Boolean(raw)
  });
}

// 渲染实拍图识别状态（使用 real_picture/list API 的 rule_check_result）
function renderLivePhotoCheckStatus(product) {
  if (!product.rule_check_result_list || product.rule_check_result_list.length === 0) {
    return '<span style="color: #52c41a; font-size: 12px;">✅ 实拍图已通过</span>';
  }

  return product.rule_check_result_list.map(rule => {
    // 用 toast 文字作为状态显示（如"待传图"、"识别成功"、"系统识别能力待建设"）
    const toast = rule.rule_result?.[0]?.toast || '';
    let color, statusText;

    if (toast.includes('成功') || toast.includes('通过')) {
      color = '#52c41a';  // 绿色
      statusText = '识别成功';
    } else if (toast.includes('待建设') || toast.includes('不支持')) {
      color = '#1890ff';  // 蓝色
      statusText = '系统识别能力待建设';
    } else if (toast.includes('失败') || toast.includes('不通过') || toast.includes('驳回')) {
      color = '#ff4d4f';  // 红色
      statusText = toast || '不通过';
    } else {
      color = '#fa8c16';  // 橙色（待传图等）
      statusText = toast || '待传图';
    }

    const ruleName = rule.rule_name || `检查项${rule.check_type}`;
    // 参照 TEMU 截图样式：左规则名，右状态（彩色圆点+文字）
    return `<div style="display: flex; justify-content: space-between; align-items: center; padding: 6px 0; border-bottom: 1px solid #f0f0f0; font-size: 12px; line-height: 18px;">
      <span style="flex: 1; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${escapeHtml(ruleName)}</span>
      <span style="flex-shrink: 0; margin-left: 12px; color: ${color};">
        <span style="display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: ${color}; margin-right: 4px; vertical-align: middle;"></span>${escapeHtml(statusText)}
      </span>
    </div>`;
  }).join('');
}

function getLivePhotoProductSpuId(product = {}) {
  return String(product?.spu_id || product?.spuId || product?.product_id || product?.productId || '').trim();
}

function rememberLivePhotoProductSelection(product = {}) {
  const spuId = getLivePhotoProductSpuId(product);
  if (!spuId) return;
  state.lpSelectedProductMap = state.lpSelectedProductMap || {};
  state.lpSelectedProductMap[spuId] = product;
}

function forgetLivePhotoProductSelection(spuId) {
  const key = String(spuId || '').trim();
  if (!key || !state.lpSelectedProductMap) return;
  delete state.lpSelectedProductMap[key];
}

function getSelectedLivePhotoUploadTargets() {
  const selectedIds = [...new Set((state.lpSelectedProducts || []).map(id => String(id || '').trim()).filter(Boolean))];
  const currentRows = new Map((state.lpProducts || []).map(product => [getLivePhotoProductSpuId(product), product]).filter(([spuId]) => spuId));
  return selectedIds.map(spuId => ({
    spuId,
    productInfo: state.lpSelectedProductMap?.[spuId] || currentRows.get(spuId) || null
  }));
}

function renderLivePhotoProductList() {
  const container = document.getElementById('lpProductListContainer');
  if (!container) return;

  if (state.lpProducts.length === 0) {
    const searchedIds = uniqueStringList(state.lpSearchIds || []);
    const emptyText = searchedIds.length
      ? `未找到这些 SPU 的实拍图商品：${searchedIds.join('、')}`
      : '暂无商品数据';
    container.innerHTML = `<div class="empty"><div class="empty-icon">📦</div><p>${escapeHtml(emptyText)}</p></div>`;
    updateLpSelectedCountInfo();
    return;
  }

  const statusFilter = document.getElementById('lpStatusFilter')?.value || '';
  const searchQuery = String(state.lpSearchQuery || '').trim().toLowerCase();

  let filtered = state.lpProducts;
  if (statusFilter) filtered = filtered.filter(p => {
    const toast = p.rule_check_result_list?.[0]?.rule_result?.[0]?.toast || '';
    if (statusFilter === 'passed') return toast.includes('成功') || toast.includes('通过');
    if (statusFilter === 'pending') return !toast.includes('成功') && !toast.includes('通过');
    return true;
  });
  if (searchQuery && !state.lpIsSearching) filtered = filtered.filter(p =>
    matchesAnyTemuSearchTerm([
      getLivePhotoProductSpuId(p),
      p.spu_name,
      p.spuName,
      p.goods_name,
      p.goodsName,
      p.product_name,
      p.productName
    ], parseTemuSearchTerms(searchQuery))
  );

  const html = `
    <table class="product-table">
      <thead>
        <tr>
          <th style="width:30px;"><input type="checkbox" class="checkbox" id="lpSelectAll"></th>
          <th style="width:50px;">主图</th>
          <th style="width:100px;">SPU ID</th>
          <th style="min-width:140px;">商品名称</th>
          <th style="width:200px;">实拍图状态</th>
        </tr>
      </thead>
      <tbody>
        ${filtered.map(p => {
          const spuId = getLivePhotoProductSpuId(p);
          return `
          <tr>
            <td><input type="checkbox" class="checkbox lp-product-checkbox" data-spu="${escapeHtml(spuId)}"></td>
            <td>
              ${p.material_img_url
                ? `<img src="${escapeHtml(p.material_img_url)}" class="img-fallback" style="width:48px;height:48px;object-fit:cover;border-radius:4px;border:1px solid #eee;">`
                : '<div style="width:48px;height:48px;background:#f5f5f5;border-radius:4px;display:flex;align-items:center;justify-content:center;color:#ccc;font-size:18px;">📷</div>'}
            </td>
            <td style="font-size:12px;">${escapeHtml(spuId || '-')}</td>
            <td style="max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${escapeHtml(p.spu_name || '-')}">${escapeHtml(p.spu_name || '-')}</td>
            <td style="text-align:left;padding:8px;">${renderLivePhotoCheckStatus(p)}</td>
          </tr>
        `; }).join('')}
      </tbody>
    </table>
  `;

  container.innerHTML = html;
  const livePhotoChecks = () => Array.from(container.querySelectorAll('.lp-product-checkbox'));

  // 全选
  document.getElementById('lpSelectAll')?.addEventListener('change', (e) => {
    livePhotoChecks().forEach(cb => {
      cb.checked = e.target.checked;
      const spu = String(cb.dataset.spu || '').trim();
      const product = state.lpProducts.find(item => getLivePhotoProductSpuId(item) === spu);
      if (e.target.checked) {
        if (!state.lpSelectedProducts.includes(spu)) state.lpSelectedProducts.push(spu);
        if (product) rememberLivePhotoProductSelection(product);
      } else {
        state.lpSelectedProducts = state.lpSelectedProducts.filter(id => id !== spu);
        forgetLivePhotoProductSelection(spu);
      }
    });
    updateLpSelectedCountInfo();
  });

  // 单选
  livePhotoChecks().forEach(cb => {
    // 恢复已选状态
    const spu = String(cb.dataset.spu || '').trim();
    cb.checked = state.lpSelectedProducts.map(String).includes(spu);

    cb.addEventListener('change', () => {
      const product = state.lpProducts.find(item => getLivePhotoProductSpuId(item) === spu);
      if (cb.checked) {
        if (!state.lpSelectedProducts.includes(spu)) state.lpSelectedProducts.push(spu);
        if (product) rememberLivePhotoProductSelection(product);
      } else {
        state.lpSelectedProducts = state.lpSelectedProducts.filter(id => id !== spu);
        forgetLivePhotoProductSelection(spu);
      }
      // 更新全选状态
      const allCb = document.getElementById('lpSelectAll');
      const allCbs = livePhotoChecks();
      if (allCb) {
        allCb.checked = allCbs.length > 0 && allCbs.every(c => c.checked);
        allCb.indeterminate = allCbs.some(c => c.checked) && !allCb.checked;
      }
      updateLpSelectedCountInfo();
    });
  });

  updateLpSelectedCountInfo();
}

function updateLpSelectedCountInfo() {
  const el = document.getElementById('lpSelectedCountInfo');
  if (!el) return;
  if (state.lpSelectedProducts.length > 0) {
    el.style.display = 'block';
    el.innerHTML = `已选择 <strong>${state.lpSelectedProducts.length}</strong> 个商品`;
  } else {
    el.style.display = 'none';
  }
}

function renderLivePhotoPagination() {
  const info = document.getElementById('lpPaginationInfo');
  const btns = document.getElementById('lpPaginationButtons');
  const container = document.getElementById('lpPaginationContainer');
  if (!info || !btns) return;
  const currentKeyword = String(state.lpSearchQuery || '').trim();
  const currentSearchIds = uniqueStringList(state.lpSearchIds || []);
  const refreshPage = (pageNum) => refreshLivePhotoProductList({
    pageNum,
    keyword: currentKeyword,
    spuIdList: currentSearchIds,
    skipServerSnapshotFallback: Boolean(currentKeyword || currentSearchIds.length)
  });

  // 有数据时显示分页容器
  if (state.livePhotoTotal > 0 && container) {
    container.style.display = 'flex';
  }

  if (state.livePhotoTotal > 0) {
    const start = (state.livePhotoPageNum - 1) * state.livePhotoPageSize + 1;
    const end = Math.min(state.livePhotoPageNum * state.livePhotoPageSize, state.livePhotoTotal);
    info.innerHTML = `${currentKeyword || currentSearchIds.length ? `筛选结果 ` : ''}共 <strong>${state.livePhotoTotal}</strong> 条，显示 <strong>${start}-${end}</strong>，第 <strong>${state.livePhotoPageNum}</strong>/<strong>${state.livePhotoTotalPages}</strong> 页`;
  } else {
    info.innerHTML = currentKeyword || currentSearchIds.length ? '筛选结果共 0 条' : '共 0 条';
  }

  btns.innerHTML = '';
  if (state.livePhotoTotalPages <= 1) return;

  // 上一页
  if (state.livePhotoPageNum > 1) {
    const btn = document.createElement('button');
    btn.className = 'btn btn-default btn-sm'; btn.textContent = '上一页';
    btn.addEventListener('click', () => refreshPage(state.livePhotoPageNum - 1));
    btns.appendChild(btn);
  }

  // 页码按钮（最多显示 5 个）
  const maxBtns = 5;
  let s = Math.max(1, state.livePhotoPageNum - Math.floor(maxBtns / 2));
  let e = Math.min(state.livePhotoTotalPages, s + maxBtns - 1);
  if (e - s < maxBtns - 1) s = Math.max(1, e - maxBtns + 1);

  for (let i = s; i <= e; i++) {
    const btn = document.createElement('button');
    btn.className = i === state.livePhotoPageNum ? 'btn btn-primary btn-sm' : 'btn btn-default btn-sm';
    btn.textContent = i;
    if (i !== state.livePhotoPageNum) {
      btn.addEventListener('click', () => refreshPage(i));
    }
    btns.appendChild(btn);
  }

  // 下一页
  if (state.livePhotoPageNum < state.livePhotoTotalPages) {
    const btn = document.createElement('button');
    btn.className = 'btn btn-default btn-sm'; btn.textContent = '下一页';
    btn.addEventListener('click', () => refreshPage(state.livePhotoPageNum + 1));
    btns.appendChild(btn);
  }

  // 跳转到指定页
  if (state.livePhotoTotalPages > 5) {
    const jumpWrap = document.createElement('span');
    jumpWrap.style.cssText = 'display:flex;align-items:center;gap:4px;margin-left:8px;font-size:12px;';
    jumpWrap.innerHTML = '跳至<input type="number" min="1" max="' + state.livePhotoTotalPages + '" style="width:50px;padding:2px 4px;border:1px solid #d9d9d9;border-radius:4px;font-size:12px;text-align:center;">页';
    const jumpInput = jumpWrap.querySelector('input');
    jumpInput.value = state.livePhotoPageNum;
    jumpInput.addEventListener('keydown', (ev) => {
      if (ev.key === 'Enter') {
        const p = parseInt(jumpInput.value);
        if (p >= 1 && p <= state.livePhotoTotalPages) {
          refreshPage(p);
        }
      }
    });
    btns.appendChild(jumpWrap);
  }
}

async function startLivePhotoUpload() {
  if (selectedLivePhotoFiles.length === 0) { showToast('请先选择图片', 'error'); return; }

  if ((!state.mallId || !state.antiContent) && isWebsiteBridgeMode()) {
    await restoreConnectedShopFromStorage({ updateUi: true, detectIfMissing: true }).catch(() => {});
  }
  if (isWebsiteBridgeMode() && !getBridgeRuntimeState().ready) {
    syncWebsiteBridgeUi();
    await new Promise(resolve => setTimeout(resolve, 800));
  }

  // 根据模式获取 SPU 列表
  let spuList = [];
  let uploadTargets = [];
  if (state.lpProductSelectMode === 'list') {
    // 从商品列表选择
    uploadTargets = getSelectedLivePhotoUploadTargets();
    spuList = uploadTargets.map(item => item.spuId);
    if (spuList.length === 0) { showToast('请先从商品列表勾选商品', 'error'); return; }
  } else {
    // 手动输入
    const spuText = document.getElementById('livePhotoSpuList')?.value?.trim() || '';
    if (!spuText) { showToast('请输入 SPU ID', 'error'); return; }
    spuList = spuText.split(/[\n,，\s]+/).map(s => s.trim()).filter(s => s && /^\d+$/.test(s)).map(s => parseInt(s));
    uploadTargets = spuList.map(spuId => ({ spuId, productInfo: null }));
  }

  if (spuList.length === 0) { showToast('未找到有效的 SPU ID', 'error'); return; }

  const progressSection = document.getElementById('livePhotoProgressSection');
  const fill = document.getElementById('livePhotoProgressFill');
  const text = document.getElementById('livePhotoProgressText');
  const taskList = document.getElementById('livePhotoTaskList');
  if (!progressSection || !fill || !text || !taskList) {
    showToast('实拍图上传界面未初始化完成，请刷新插件页后重试', 'error');
    return;
  }
  progressSection.style.display = 'block';
  fill.style.width = '0%'; fill.className = 'progress-fill blue';
  text.textContent = '正在读取图片...';
  taskList.innerHTML = '';

  const uploadBtn = document.getElementById('startLivePhotoUploadBtn');
  if (uploadBtn) {
    uploadBtn.disabled = true;
    uploadBtn.textContent = '⏳ 上传中...';
  }

  try {
    // 读取图片
    const images = [];
    for (let i = 0; i < selectedLivePhotoFiles.length; i++) {
      text.textContent = `读取图片 ${i + 1}/${selectedLivePhotoFiles.length}...`;
      images.push({
        filename: selectedLivePhotoFiles[i].file.name,
        imageData: selectedLivePhotoFiles[i].dataUrl
      });
      fill.style.width = `${((i + 1) / selectedLivePhotoFiles.length) * 20}%`;
    }

    text.textContent = `图片就绪，开始上传 ${spuList.length} 个 SPU...`;
    let successCount = 0, failCount = 0;

    for (let i = 0; i < spuList.length; i++) {
      const target = uploadTargets[i] || { spuId: spuList[i], productInfo: null };
      const spuId = target.spuId;
      const pct = 20 + ((i + 1) / spuList.length) * 80;
      text.textContent = `正在上传 SPU ${spuId}（${i + 1}/${spuList.length}）...`;
      fill.style.width = `${pct}%`;

      const result = await sendRuntimeMessageWithRetry({
        action: 'uploadLivePhotosBatch',
        params: { spuId, images, productInfo: target.productInfo || undefined }
      }, { success: false, error: '实拍图上传请求失败' }, 45000, 1);

      const cls = result.success ? 'success' : 'fail';
      const icon = result.success ? '✅' : '❌';
      const msg = result.success ? `上传成功（${result.imageCount || 0}张图）` : (result.error || '上传失败');

      const item = document.createElement('div');
      item.className = `result-item ${cls}`;
      item.innerHTML = `<span class="icon">${icon}</span><span class="reason"><strong>SPU ${spuId}</strong> — ${escapeHtml(msg)}</span>`;
      taskList.appendChild(item);

      if (result.success) successCount++; else failCount++;
      await new Promise(r => setTimeout(r, 500));
    }

    fill.style.width = '100%';
    // 最终颜色
    if (failCount === 0) fill.className = 'progress-fill green';
    else if (successCount === 0) fill.className = 'progress-fill red';
    else fill.className = 'progress-fill blue';

    text.textContent = `上传完成！✅ ${successCount} 成功, ❌ ${failCount} 失败`;
    showToast(`上传完成！成功: ${successCount}, 失败: ${failCount}`, successCount > 0 ? 'success' : 'error');
    if (successCount > 0) {
      try {
        await refreshLivePhotoProductList({ pageNum: state.livePhotoPageNum || 1, pageSize: state.livePhotoPageSize || 10 });
      } catch (refreshErr) {
        console.warn('[小青蛙电商助手] 实拍图上传后回读列表失败，已保留上传结果摘要:', refreshErr);
        await saveTemuSnapshot('livephotos', {
          products: Array.isArray(state.lpProducts) ? state.lpProducts : [],
          pageNum: state.livePhotoPageNum,
          pageSize: state.livePhotoPageSize,
          total: state.livePhotoTotal,
          totalPages: state.livePhotoTotalPages,
          lastUpload: {
            spuList,
            imageCount: images.length,
            successCount,
            failCount,
            finishedAt: Date.now()
          }
        }, { itemCount: Array.isArray(state.lpProducts) ? state.lpProducts.length : 0 });
      }
    }
  } catch (error) {
    text.textContent = `上传失败: ${error.message}`;
    fill.className = 'progress-fill red';
    showToast(`上传失败: ${error.message}`, 'error');
  } finally {
    if (uploadBtn) {
      uploadBtn.disabled = false;
      uploadBtn.textContent = '🚀 开始上传';
    }
  }
}

// ===== 全局函数（供 onclick） =====
window.saveAsTemplate = async (spuId) => {
  showToast(`正在保存商品 ${spuId} 为模板...`, 'info');
  try {
    const result = await safeRuntimeSendMessage({
      action: 'saveAsTemplateDirect',
      data: { spuId, templateName: `模板-${spuId}` }
    }, { success: false, error: '保存模板失败' }, 45000);
    if (result && result.success) {
      showToast('保存成功：模板已保存', 'success');
      await loadTemplates();
    } else {
      showToast(result?.error || '保存模板失败', 'error');
    }
  } catch (err) {
    showToast(`保存失败: ${err.message}`, 'error');
  }
};

function compactComplianceTemplatePayload(data = {}) {
  return data;
}

window.editTemplate = (templateId) => {
  const tpl = state.templates.find(t => t.id === templateId);
  if (!tpl) { showToast('模板不存在', 'error'); return; }

  const complianceData = tpl.data || {};
  const fieldCount = Object.keys(complianceData).length;
  let dataHtml = '';
  if (fieldCount > 0) {
    dataHtml = Object.entries(complianceData).map(([k, v]) => {
      let dv = '';
      if (typeof v === 'object' && v.value) { dv = v.value; if (v.status) dv = `[${v.status}] ${dv}`; }
      else { dv = v; }
      const trunc = dv && dv.length > 100 ? dv.substring(0, 100) + '...' : (dv || '(空)');
      return `<div style="margin-bottom:6px;"><strong>${k}:</strong> ${escapeHtml(trunc)}</div>`;
    }).join('');
  } else {
    dataHtml = '<div style="color:#999;">⚠️ 模板中没有保存任何数据</div>';
  }

  const modal = document.createElement('div');
  modal.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:10000;';
  modal.innerHTML = `
    <div style="background:white;padding:24px;border-radius:8px;max-width:600px;max-height:80vh;overflow-y:auto;box-shadow:0 4px 12px rgba(0,0,0,0.15);">
      <h2 style="margin:0 0 16px;">📋 模板: ${escapeHtml(tpl.name)}</h2>
      <div style="margin-bottom:16px;color:#666;font-size:13px;">创建时间: ${tpl.createdAt ? new Date(tpl.createdAt).toLocaleString() : '-'}<br>字段数: ${fieldCount}</div>
      <div style="background:#f5f5f5;padding:16px;border-radius:4px;max-height:300px;overflow-y:auto;">
        <h3 style="margin:0 0 12px;font-size:15px;">保存的数据:</h3>
        ${dataHtml}
      </div>
      <div style="text-align:right;margin-top:16px;">
        <button id="closeModalBtn" class="btn btn-primary">关闭</button>
      </div>
    </div>`;
  document.body.appendChild(modal);
  document.getElementById('closeModalBtn').addEventListener('click', () => document.body.removeChild(modal));
  modal.addEventListener('click', (e) => { if (e.target === modal) document.body.removeChild(modal); });
};

// ===== 模板存储辅助 =====
async function getTemplates() {
  return await getComplianceTemplatesForCurrentMall();
}

async function saveTemplates(templates) {
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) {
    console.warn('[小青蛙电商助手] 未检测到当前店铺，无法保存模板');
    return false;
  }
  const key = getTemplateKey(mallId);
  const normalized = templates.map(normalizeComplianceTemplateForStorage);
  if (await safeStorageSet({ [key]: normalized }, '合规模板')) {
    return true;
  }
  const compacted = normalized.slice(-20).map(compactComplianceTemplateForStorage);
  if (await safeStorageSet({ [key]: compacted }, '合规模板降级')) {
    return true;
  }
  console.warn('[小青蛙电商助手] 模板压缩后仍超出配额，继续降级保存');
  await safeStorageSet({ [key]: compacted.slice(-10) }, '合规模板最终降级');
  return true;
}

function cloneComplianceValueForStorage(value) {
  if (value === undefined) return undefined;
  try {
    return JSON.parse(JSON.stringify(value));
  } catch (_) {
    return value;
  }
}

function normalizeComplianceTemplateDataForStorage(data = {}) {
  const sourceData = data?.data?.template_edit_request_list ? data.data : data;
  const editList = Array.isArray(sourceData.template_edit_request_list) ? sourceData.template_edit_request_list : [];
  return {
    ...(sourceData.spu_id !== undefined ? { spu_id: sourceData.spu_id } : {}),
    ...(sourceData.goods_id !== undefined ? { goods_id: sourceData.goods_id } : {}),
    ...(sourceData.cat_id !== undefined ? { cat_id: sourceData.cat_id } : {}),
    group_sku_by_same_info: sourceData.group_sku_by_same_info !== undefined ? sourceData.group_sku_by_same_info : true,
    template_edit_request_list: editList.map(item => {
      const normalized = {};
      [
        'task_id',
        'task_status',
        'task_type',
        'template_id',
        'task_name',
        'is_not_required',
        'status',
        'punish_time',
        'properties',
        'input_text',
        'images',
        'rep_detail_list',
        'sku_group_multi_detail_list'
      ].forEach(key => {
        if (item[key] !== undefined) normalized[key] = cloneComplianceValueForStorage(item[key]);
      });
      return normalized;
    })
  };
}

function hasCompactedComplianceValueForStorage(value) {
  if (value === null || value === undefined) return false;
  if (typeof value === 'string') return value.includes('…');
  if (Array.isArray(value)) return value.some(hasCompactedComplianceValueForStorage);
  if (typeof value === 'object') return Object.values(value).some(hasCompactedComplianceValueForStorage);
  return false;
}

function normalizeComplianceTemplateForStorage(tpl = {}) {
  const data = normalizeComplianceTemplateDataForStorage(tpl.data || {});
  const editList = Array.isArray(data.template_edit_request_list) ? data.template_edit_request_list : [];
  return {
    ...tpl,
    data,
    taskCount: tpl.taskCount || editList.length,
    taskTypes: tpl.taskTypes || editList.map(item => item.task_type).filter(Boolean),
    compacted: Boolean(tpl.compacted || hasCompactedComplianceValueForStorage(data))
  };
}

function compactComplianceValue(value, depth = 0) {
  if (value === null || value === undefined) return value;
  if (typeof value === 'string') return value.length > 180 ? `${value.slice(0, 180)}…` : value;
  if (typeof value === 'number' || typeof value === 'boolean') return value;
  if (Array.isArray(value)) {
    const maxItems = depth === 0 ? 10 : depth === 1 ? 5 : 3;
    return value.slice(0, maxItems).map(item => compactComplianceValue(item, depth + 1));
  }
  if (typeof value !== 'object') return value;

  const out = {};
  for (const [key, item] of Object.entries(value)) {
    if (/^(raw|html|base64|blob|buffer|file|content)$/i.test(key)) continue;
    if (typeof item === 'function') continue;
    const compacted = compactComplianceValue(item, depth + 1);
    if (compacted !== undefined) out[key] = compacted;
  }
  return out;
}

function compactComplianceTemplateForStorage(tpl = {}) {
  const normalizedTpl = normalizeComplianceTemplateForStorage(tpl);
  const data = normalizedTpl.data || {};
  const editList = Array.isArray(data.template_edit_request_list) ? data.template_edit_request_list : [];
  return {
    ...normalizedTpl,
    data: {
      spu_id: data.spu_id,
      goods_id: data.goods_id,
      cat_id: data.cat_id,
      group_sku_by_same_info: data.group_sku_by_same_info,
      template_edit_request_list: editList.map(item => ({
        task_type: item.task_type,
        task_id: item.task_id,
        task_status: item.task_status,
        template_id: item.template_id,
        task_name: item.task_name,
        is_not_required: item.is_not_required,
        properties: compactComplianceValue(item.properties || {}),
        input_text: compactComplianceValue(item.input_text || {}),
        images: compactComplianceValue(item.images || {}),
        rep_detail_list: compactComplianceValue(item.rep_detail_list || []),
        sku_group_multi_detail_list: compactComplianceValue(item.sku_group_multi_detail_list || [])
      }))
    },
    taskCount: normalizedTpl.taskCount || editList.length,
    taskTypes: normalizedTpl.taskTypes || editList.map(item => item.task_type).filter(Boolean),
    compacted: true
  };
}

// ===== 工具函数 =====
function escapeHtml(str) {
  if (str === null || str === undefined) return '';
  return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

function showToast(text, type = 'info') {
  const el = document.createElement('div');
  el.className = `toast toast-${type}`;
  el.textContent = text;
  document.body.appendChild(el);
  setTimeout(() => el.remove(), 3000);
}

// ========== 自动核价功能 ==========

// 核价状态
const pricingState = {
  list: [],          // 核价商品列表
  allList: [],       // 当前已加载的完整待核价商品列表
  total: 0,          // 总数
  pageNum: 1,        // 当前页
  pageSize: 10,      // 每页条数
  selected: new Set(), // 选中的行索引
  isRunning: false,  // 是否正在执行自动核价
  runTimer: null,    // 定时器
  retryCounts: {},   // priceOrderId -> 核价次数
  minPriceMap: {},   // skuId -> 阈值（最低价）
  refPriceMap: {},   // skuId -> 系统参考申报价（分）
  rule: null,         // 自动核价执行规则
};

const PRICING_RULE_STORAGE_KEY = 'xqPricingRule';
const DEFAULT_PRICING_RULE = {
  highAction: 'approveThreshold',
  lowAction: 'void',
  decreaseYuan: 0.01,
  maxResubmitTimes: 1
};

function normalizePricingRule(rule = {}) {
  const highAction = rule.highAction === 'autoApprove' ? 'autoApprove' : 'approveThreshold';
  const lowAction = rule.lowAction === 'resubmit' ? 'resubmit' : 'void';
  const decreaseYuan = Number(rule.decreaseYuan);
  const maxResubmitTimes = parseInt(rule.maxResubmitTimes, 10);
  return {
    highAction,
    lowAction,
    decreaseYuan: Number.isFinite(decreaseYuan) && decreaseYuan > 0 ? Math.round(decreaseYuan * 100) / 100 : DEFAULT_PRICING_RULE.decreaseYuan,
    maxResubmitTimes: Number.isFinite(maxResubmitTimes) && maxResubmitTimes > 0 ? Math.min(maxResubmitTimes, 20) : DEFAULT_PRICING_RULE.maxResubmitTimes
  };
}

function getPricingRule() {
  if (pricingState.rule) return normalizePricingRule(pricingState.rule);
  try {
    const stored = JSON.parse(localStorage.getItem(PRICING_RULE_STORAGE_KEY) || '{}');
    pricingState.rule = normalizePricingRule(stored);
  } catch {
    pricingState.rule = { ...DEFAULT_PRICING_RULE };
  }
  return normalizePricingRule(pricingState.rule);
}

function savePricingRule(rule) {
  pricingState.rule = normalizePricingRule(rule);
  localStorage.setItem(PRICING_RULE_STORAGE_KEY, JSON.stringify(pricingState.rule));
  return pricingState.rule;
}

function parsePricingYuanTextToFen(value) {
  if (value === undefined || value === null || value === '') return null;
  const text = String(value).replace(/[¥￥,\s]/g, '').trim();
  if (!text || /^[-—]+$/.test(text)) return null;
  const num = Number(text);
  return Number.isFinite(num) && num > 0 ? Math.round(num * 100) : null;
}

function getPricingSupplierPriceFen(sku = {}, skc = {}, item = {}) {
  return parsePricingYuanTextToFen(getPricingSupplierPriceText(sku, skc, item));
}

function formatPricingFen(fen) {
  const num = Number(fen);
  return Number.isFinite(num) ? (num / 100).toFixed(2) : '-';
}

function getPricingSkuId(sku = {}) {
  return String(sku.skuId || sku.goodsSkuId || sku.productSkuId || sku.skuIdStr || sku.id || '').trim();
}

function getPricingSkcId(skc = {}) {
  return String(skc.skcId || skc.goodsSkcId || skc.productSkcId || skc.skcIdStr || skc.id || '').trim();
}

function formatPricingPriceYuanText(value, options = {}) {
  if (value === undefined || value === null || value === '') return '';
  const asCent = Boolean(options.centValue);
  if (typeof value === 'string') {
    const text = value.trim();
    if (!text || /^[-—]+$/.test(text)) return '';
    const cleaned = text.replace(/[¥￥,\s]/g, '');
    const num = Number(cleaned);
    if (!Number.isFinite(num)) return text.replace(/[¥￥]/g, '').trim();
    if (asCent && cleaned.includes('.')) return num.toFixed(2);
    return (asCent ? num / 100 : num).toFixed(2);
  }
  const num = Number(value);
  if (!Number.isFinite(num)) return '';
  if (asCent && !Number.isInteger(num)) return num.toFixed(2);
  return (asCent ? num / 100 : num).toFixed(2);
}

function pickPricingPriceText(candidates = []) {
  for (const candidate of candidates) {
    const text = formatPricingPriceYuanText(candidate?.value, { centValue: candidate?.centValue });
    if (text) return text;
  }
  return '';
}

function pickPricingPriceFen(candidates = []) {
  for (const candidate of candidates) {
    const text = formatPricingPriceYuanText(candidate?.value, { centValue: candidate?.centValue });
    if (!text) continue;
    const num = Number(String(text).replace(/[,\s]/g, ''));
    if (Number.isFinite(num) && num > 0) return Math.round(num * 100);
  }
  return null;
}

function buildHalfManagedAdjustedDeclarePriceCandidates(reviewInfo = {}, sku = {}, site = {}) {
  const sources = [
    { prefix: 'reviewInfo', value: reviewInfo || {} },
    { prefix: 'sku', value: sku || {} },
    { prefix: 'site', value: site || {} }
  ];
  const capturedYuanKeys = [
    'xiaoqingwaCapturedReferencePrice',
    'xiaoqingwaCapturedReferencePriceText'
  ];
  const centKeys = [
    'adjustedDeclarePrice',
    'adjustedDeclarationPrice',
    'afterAdjustDeclarePrice',
    'afterAdjustedDeclarePrice',
    'adjustAfterDeclarePrice',
    'adjustedDeclarePriceCny',
    'adjustedDeclarationPriceCny',
    'afterAdjustDeclarePriceCny',
    'referenceDeclarePrice',
    'referenceDeclarationPrice',
    'suggestSupplyPrice',
    'targetSupplyPrice'
  ];
  const yuanKeys = [
    'adjustedDeclarePriceText',
    'adjustedDeclarationPriceText',
    'afterAdjustDeclarePriceText',
    'referenceDeclarePriceText',
    'referenceDeclarationPriceText',
    'suggestSupplyPriceText',
    'targetSupplyPriceText',
    'referencePrice',
    'referencePriceValue'
  ];
  const candidates = [];
  for (const source of sources) {
    for (const key of capturedYuanKeys) candidates.push({ value: source.value?.[key], key: `${source.prefix}.${key}` });
    for (const key of centKeys) candidates.push({ value: source.value?.[key], centValue: true, key: `${source.prefix}.${key}` });
    for (const key of yuanKeys) candidates.push({ value: source.value?.[key], key: `${source.prefix}.${key}` });
  }
  return candidates;
}

function getPricingReviewStatus(reviewInfo = {}, sku = {}) {
  const raw = reviewInfo?.priceReviewStatus ?? reviewInfo?.status ?? sku?.priceReviewStatus;
  const status = Number(raw);
  return Number.isFinite(status) ? status : null;
}

function isHalfManagedPricingReview(reviewInfo = {}, sku = {}) {
  return getPricingReviewStatus(reviewInfo, sku) === PRICING_HALF_MANAGED_PENDING_STATUS;
}

function pickPricingSiteSupplierPrice(sku = {}) {
  const sites = Array.isArray(sku.siteSupplierPriceList) ? sku.siteSupplierPriceList : [];
  return sites.find(site =>
    site && (site.supplierPrice !== undefined && site.supplierPrice !== null && site.supplierPrice !== '' ||
      site.supplierPriceValue !== undefined && site.supplierPriceValue !== null && site.supplierPriceValue !== '')
  ) || {};
}

function pickPricingSiteReferencePrice(sku = {}) {
  const sites = Array.isArray(sku.siteSupplierPriceList) ? sku.siteSupplierPriceList : [];
  return sites.find(site =>
    site && (
      site.xiaoqingwaCapturedReferencePrice !== undefined && site.xiaoqingwaCapturedReferencePrice !== null && site.xiaoqingwaCapturedReferencePrice !== '' ||
      site.adjustedDeclarePrice !== undefined && site.adjustedDeclarePrice !== null && site.adjustedDeclarePrice !== '' ||
      site.adjustedDeclarationPrice !== undefined && site.adjustedDeclarationPrice !== null && site.adjustedDeclarationPrice !== '' ||
      site.afterAdjustDeclarePrice !== undefined && site.afterAdjustDeclarePrice !== null && site.afterAdjustDeclarePrice !== '' ||
      site.afterAdjustedDeclarePrice !== undefined && site.afterAdjustedDeclarePrice !== null && site.afterAdjustedDeclarePrice !== '' ||
      site.adjustAfterDeclarePrice !== undefined && site.adjustAfterDeclarePrice !== null && site.adjustAfterDeclarePrice !== '' ||
      site.adjustedDeclarePriceCny !== undefined && site.adjustedDeclarePriceCny !== null && site.adjustedDeclarePriceCny !== '' ||
      site.adjustedDeclarationPriceCny !== undefined && site.adjustedDeclarationPriceCny !== null && site.adjustedDeclarationPriceCny !== '' ||
      site.afterAdjustDeclarePriceCny !== undefined && site.afterAdjustDeclarePriceCny !== null && site.afterAdjustDeclarePriceCny !== '' ||
      site.referenceDeclarePrice !== undefined && site.referenceDeclarePrice !== null && site.referenceDeclarePrice !== '' ||
      site.referenceDeclarationPrice !== undefined && site.referenceDeclarationPrice !== null && site.referenceDeclarationPrice !== '' ||
      site.suggestSupplyPrice !== undefined && site.suggestSupplyPrice !== null && site.suggestSupplyPrice !== '' ||
      site.targetSupplyPrice !== undefined && site.targetSupplyPrice !== null && site.targetSupplyPrice !== '' ||
      site.referencePriceValue !== undefined && site.referencePriceValue !== null && site.referencePriceValue !== '' ||
      site.referencePrice !== undefined && site.referencePrice !== null && site.referencePrice !== ''
    )
  ) || {};
}

function getPricingSupplierPriceText(sku = {}, skc = {}, item = {}) {
  const site = pickPricingSiteSupplierPrice(sku);
  return pickPricingPriceText([
    { value: sku.supplierPrice },
    { value: sku.supplierPriceValue, centValue: true },
    { value: site.supplierPrice },
    { value: site.supplierPriceValue, centValue: true },
    { value: skc.supplierPrice },
    { value: skc.supplierPriceValue, centValue: true },
    { value: item.supplierPrice },
    { value: item.supplierPriceValue, centValue: true }
  ]); 
}

function getHalfManagedAdjustedDeclarePriceText(reviewInfo = {}, sku = {}) {
  const site = pickPricingSiteReferencePrice(sku || {});
  return pickPricingPriceText(buildHalfManagedAdjustedDeclarePriceCandidates(reviewInfo, sku, site));
}

function getHalfManagedAdjustedDeclarePriceFen(reviewInfo = {}, sku = {}) {
  const site = pickPricingSiteReferencePrice(sku || {});
  return pickPricingPriceFen(buildHalfManagedAdjustedDeclarePriceCandidates(reviewInfo, sku, site));
}

function getPricingReferencePriceSourceKey(reviewInfo = {}, sku = {}) {
  const site = pickPricingSiteReferencePrice(sku || {});
  const candidates = isHalfManagedPricingReview(reviewInfo, sku)
    ? buildHalfManagedAdjustedDeclarePriceCandidates(reviewInfo, sku, site)
    : [
      { value: reviewInfo?.adjustedDeclarePrice, centValue: true, key: 'reviewInfo.adjustedDeclarePrice' },
      { value: reviewInfo?.adjustedDeclarationPrice, centValue: true, key: 'reviewInfo.adjustedDeclarationPrice' },
      { value: reviewInfo?.afterAdjustDeclarePrice, centValue: true, key: 'reviewInfo.afterAdjustDeclarePrice' },
      { value: reviewInfo?.referenceDeclarePrice, centValue: true, key: 'reviewInfo.referenceDeclarePrice' },
      { value: reviewInfo?.suggestSupplyPrice, centValue: true, key: 'reviewInfo.suggestSupplyPrice' },
      { value: reviewInfo?.referencePriceValue, centValue: true, key: 'reviewInfo.referencePriceValue' },
      { value: reviewInfo?.referencePrice, key: 'reviewInfo.referencePrice' },
      { value: sku?.adjustedDeclarePrice, centValue: true, key: 'sku.adjustedDeclarePrice' },
      { value: sku?.adjustedDeclarationPrice, centValue: true, key: 'sku.adjustedDeclarationPrice' },
      { value: sku?.afterAdjustDeclarePrice, centValue: true, key: 'sku.afterAdjustDeclarePrice' },
      { value: sku?.referenceDeclarePrice, centValue: true, key: 'sku.referenceDeclarePrice' },
      { value: sku?.suggestSupplyPrice, centValue: true, key: 'sku.suggestSupplyPrice' },
      { value: sku?.referencePriceValue, centValue: true, key: 'sku.referencePriceValue' },
      { value: sku?.referencePrice, key: 'sku.referencePrice' },
      { value: site?.targetSupplyPrice, centValue: true, key: 'site.targetSupplyPrice' },
      { value: site?.referencePriceValue, centValue: true, key: 'site.referencePriceValue' },
      { value: site?.referencePrice, key: 'site.referencePrice' }
    ];
  const found = candidates.find(candidate => formatPricingPriceYuanText(candidate?.value, { centValue: candidate?.centValue }));
  return found?.key || '';
}

function getPricingReferencePriceText(reviewInfo = {}, sku = {}) {
  reviewInfo = reviewInfo || {};
  sku = sku || {};
  if (isHalfManagedPricingReview(reviewInfo, sku)) {
    return getHalfManagedAdjustedDeclarePriceText(reviewInfo, sku);
  }
  const site = pickPricingSiteReferencePrice(sku);
  const direct = pickPricingPriceText([
    { value: reviewInfo.adjustedDeclarePrice, centValue: true },
    { value: reviewInfo.adjustedDeclarationPrice, centValue: true },
    { value: reviewInfo.afterAdjustDeclarePrice, centValue: true },
    { value: reviewInfo.referenceDeclarePrice, centValue: true },
    { value: reviewInfo.suggestSupplyPrice, centValue: true },
    { value: reviewInfo.referencePriceValue, centValue: true },
    { value: reviewInfo.referencePrice },
    { value: sku.adjustedDeclarePrice, centValue: true },
    { value: sku.adjustedDeclarationPrice, centValue: true },
    { value: sku.afterAdjustDeclarePrice, centValue: true },
    { value: sku.referenceDeclarePrice, centValue: true },
    { value: sku.suggestSupplyPrice, centValue: true },
    { value: sku.referencePriceValue, centValue: true },
    { value: sku.referencePrice },
    { value: site.targetSupplyPrice, centValue: true },
    { value: site.referencePriceValue, centValue: true },
    { value: site.referencePrice }
  ]);
  if (direct) return direct;
  return '';
}

function getPricingReferencePriceFen(reviewInfo = {}, sku = {}) {
  if (isHalfManagedPricingReview(reviewInfo, sku)) {
    return getHalfManagedAdjustedDeclarePriceFen(reviewInfo, sku);
  }
  const site = pickPricingSiteReferencePrice(sku || {});
  const direct = pickPricingPriceFen([
    { value: reviewInfo?.adjustedDeclarePrice, centValue: true },
    { value: reviewInfo?.adjustedDeclarationPrice, centValue: true },
    { value: reviewInfo?.afterAdjustDeclarePrice, centValue: true },
    { value: reviewInfo?.referenceDeclarePrice, centValue: true },
    { value: reviewInfo?.suggestSupplyPrice, centValue: true },
    { value: reviewInfo?.referencePriceValue, centValue: true },
    { value: reviewInfo?.referencePrice },
    { value: sku?.adjustedDeclarePrice, centValue: true },
    { value: sku?.adjustedDeclarationPrice, centValue: true },
    { value: sku?.afterAdjustDeclarePrice, centValue: true },
    { value: sku?.referenceDeclarePrice, centValue: true },
    { value: sku?.suggestSupplyPrice, centValue: true },
    { value: sku?.referencePriceValue, centValue: true },
    { value: sku?.referencePrice },
    { value: site?.targetSupplyPrice, centValue: true },
    { value: site?.referencePriceValue, centValue: true },
    { value: site?.referencePrice }
  ]);
  if (direct) return direct;
  const text = getPricingReferencePriceText(reviewInfo, sku);
  if (!text) return null;
  const num = Number(String(text).replace(/[,\s]/g, ''));
  return Number.isFinite(num) && num > 0 ? Math.round(num * 100) : null;
}

function pickPricingDebugFields(source = {}) {
  return Object.fromEntries(Object.entries(source || {}).filter(([key, value]) => {
    if (value && typeof value === 'object') return false;
    return /price|declare|declar|adjust|suggest|reference|bargain|review|status/i.test(key);
  }));
}

function getPricingReferencePriceLabel(reviewInfo = {}, sku = {}) {
  return isHalfManagedPricingReview(reviewInfo, sku) ? '调整后申报价格(CNY)' : '系统参考申报价';
}

function getPricingReferenceMissingMessage(priceOrderId) {
  return `核价单 ${priceOrderId}: 缺少系统参考申报价；半托管必须读取 TEMU 返回的“调整后申报价格(CNY)”，接口未返回时不能执行`;
}

function showPricingRuleModal() {
  const existing = document.getElementById('pricingRuleModal');
  if (existing) existing.remove();

  const rule = getPricingRule();
  const modal = document.createElement('div');
  modal.id = 'pricingRuleModal';
  modal.style.cssText = 'position:fixed;inset:0;z-index:10000;display:flex;align-items:center;justify-content:center;background:rgba(15,23,42,.46);padding:18px;';
  modal.innerHTML = `
    <div style="width:min(640px,96vw);background:#fff;border-radius:10px;box-shadow:0 24px 80px rgba(15,23,42,.28);overflow:hidden;">
      <div style="display:flex;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid #eef2f7;">
        <div>
          <div style="font-size:18px;font-weight:800;color:#111827;">设置核价规则</div>
          <div style="margin-top:4px;font-size:12px;color:#64748b;">规则保存后，“开始执行”会按该规则处理当前待核价单。</div>
        </div>
        <button type="button" class="btn btn-default" data-pricing-rule-close>关闭</button>
      </div>
      <div style="padding:18px;display:grid;gap:14px;">
        <div style="display:grid;grid-template-columns:150px 1fr;align-items:center;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;">
          <div style="height:100%;display:flex;align-items:center;justify-content:center;background:#f8fafc;font-weight:700;color:#334155;border-right:1px solid #e5e7eb;">系统参考申报价“高于”阈值时</div>
          <div style="display:flex;gap:10px;padding:14px;flex-wrap:wrap;">
            <label style="display:flex;align-items:center;gap:6px;font-size:13px;"><input type="radio" name="pricingHighAction" value="autoApprove" ${rule.highAction === 'autoApprove' ? 'checked' : ''}> 自动同意</label>
            <label style="display:flex;align-items:center;gap:6px;font-size:13px;"><input type="radio" name="pricingHighAction" value="approveThreshold" ${rule.highAction !== 'autoApprove' ? 'checked' : ''}> 按阈值同意</label>
          </div>
        </div>
        <div style="display:grid;grid-template-columns:150px 1fr;align-items:center;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;">
          <div style="height:100%;display:flex;align-items:center;justify-content:center;background:#f8fafc;font-weight:700;color:#334155;border-right:1px solid #e5e7eb;">系统参考申报价“低于”阈值时</div>
          <div style="display:flex;gap:10px;padding:14px;flex-wrap:wrap;">
            <label style="display:flex;align-items:center;gap:6px;font-size:13px;"><input type="radio" name="pricingLowAction" value="void" ${rule.lowAction !== 'resubmit' ? 'checked' : ''}> 直接拒绝</label>
            <label style="display:flex;align-items:center;gap:6px;font-size:13px;"><input type="radio" name="pricingLowAction" value="resubmit" ${rule.lowAction === 'resubmit' ? 'checked' : ''}> 重新提报</label>
          </div>
        </div>
        <div style="display:grid;grid-template-columns:150px 1fr;align-items:center;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;">
          <div style="height:100%;display:flex;align-items:center;justify-content:center;background:#f8fafc;font-weight:700;color:#334155;border-right:1px solid #e5e7eb;">重新提报条件</div>
          <div style="display:grid;gap:10px;padding:14px;font-size:13px;color:#334155;">
            <label style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">每次下调
              <input type="number" id="pricingRuleDecrease" value="${escapeHtml(rule.decreaseYuan)}" min="0.01" step="0.01" style="width:88px;padding:6px 8px;border:1px solid #d1d5db;border-radius:6px;">
              元，当重新提报价降到阈值或以下时“同意核价”
            </label>
            <label style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">提报
              <input type="number" id="pricingRuleMaxTimes" value="${escapeHtml(rule.maxResubmitTimes)}" min="1" max="20" step="1" style="width:88px;padding:6px 8px;border:1px solid #d1d5db;border-radius:6px;">
              次后达不到阈值则“拒绝”
            </label>
          </div>
        </div>
        <div style="border:1px solid #dbeafe;background:#eff6ff;border-radius:8px;padding:12px 14px;color:#1e3a8a;font-size:12px;line-height:1.75;">
          <div style="font-weight:800;margin-bottom:4px;color:#1d4ed8;">注意事项</div>
          <div>• 系统参考申报价高于或等于阈值时，按上方选择“自动同意”或“按阈值同意”执行。</div>
          <div>• 系统参考申报价低于阈值时，如选择“直接拒绝”，会放弃该核价单。</div>
          <div>• 如选择“重新提报”，系统按下调金额从当前申报价逐步降低；降到阈值或以下时提交同意。</div>
          <div>• 原申报价已经低于阈值、缺少系统参考申报价、缺少原申报价或达到最大提报次数仍未达标时，会跳过或拒绝。</div>
        </div>
      </div>
      <div style="display:flex;justify-content:flex-end;gap:10px;padding:14px 18px;border-top:1px solid #eef2f7;background:#f8fafc;">
        <button type="button" class="btn btn-default" data-pricing-rule-close>取消</button>
        <button type="button" class="btn btn-primary" id="pricingRuleSaveBtn">保存规则</button>
      </div>
    </div>
  `;

  const close = () => modal.remove();
  modal.querySelectorAll('[data-pricing-rule-close]').forEach(btn => btn.addEventListener('click', close));
  modal.addEventListener('click', event => { if (event.target === modal) close(); });
  modal.querySelector('#pricingRuleSaveBtn')?.addEventListener('click', () => {
    const highAction = modal.querySelector('input[name="pricingHighAction"]:checked')?.value || 'approveThreshold';
    const lowAction = modal.querySelector('input[name="pricingLowAction"]:checked')?.value || 'void';
    const decreaseYuan = Number(modal.querySelector('#pricingRuleDecrease')?.value);
    const maxResubmitTimes = parseInt(modal.querySelector('#pricingRuleMaxTimes')?.value, 10);
    if (!Number.isFinite(decreaseYuan) || decreaseYuan <= 0) {
      showToast('每次下调金额必须大于 0', 'error');
      return;
    }
    if (!Number.isFinite(maxResubmitTimes) || maxResubmitTimes <= 0) {
      showToast('提报次数必须大于 0', 'error');
      return;
    }
    const saved = savePricingRule({ highAction, lowAction, decreaseYuan, maxResubmitTimes });
    addPricingLog(`已保存核价规则：系统参考申报价高于阈值=${saved.highAction === 'autoApprove' ? '自动同意' : '按阈值同意'}；系统参考申报价低于阈值=${saved.lowAction === 'resubmit' ? `重新提报，每次下调${saved.decreaseYuan}元，最多${saved.maxResubmitTimes}次` : '直接拒绝'}`, 'success');
    showToast('核价规则已保存', 'success');
    close();
  });
  document.body.appendChild(modal);
}

function getPricingRowKey(priceOrderId, skuId) {
  return `${String(priceOrderId || '').trim()}::${String(skuId || '').trim()}`;
}

function getPricingRowKeyFromCheckbox(cb) {
  return getPricingRowKey(cb?.dataset?.poid, cb?.dataset?.sku);
}

function getPricingSearchTerms() {
  return String(document.getElementById('pricingSearchInput')?.value || '')
    .split(/[\s,，;；\n\r]+/)
    .map(value => value.trim())
    .filter(Boolean);
}

function pricingItemHasSkc(item = {}, terms = []) {
  if (!terms.length) return true;
  const lowerTerms = terms.map(term => term.toLowerCase());
  return (item.skcList || []).some((skc) => {
    const skcId = getPricingSkcId(skc).toLowerCase();
    return skcId && lowerTerms.some(term => skcId.includes(term));
  });
}

function rebuildPricingRefPriceMap(list = pricingState.list) {
  pricingState.refPriceMap = {};
  (list || []).forEach(item => {
    (item.skcList || []).forEach(skc => {
      (skc.supplierPriceReviewInfoList || []).forEach(reviewInfo => {
        (reviewInfo.productSkuList || []).forEach(ps => {
          const skuId = ps.skuId || ps.goodsSkuId;
          const refPriceValue = getPricingReferencePriceFen(reviewInfo, ps);
          if (skuId && refPriceValue !== null) pricingState.refPriceMap[skuId] = refPriceValue;
        });
      });
    });
  });
}

function selectAllVisiblePricingRows() {
  const cbs = Array.from(document.querySelectorAll('#pricingTableBody .pricing-cb'));
  pricingState.selected.clear();
  cbs.forEach((cb) => {
    cb.checked = true;
    pricingState.selected.add(getPricingRowKeyFromCheckbox(cb));
  });
  const selectAll = document.getElementById('pricingSelectAll');
  if (selectAll) selectAll.checked = cbs.length > 0;
}

function applyPricingSearchFilter({ autoSelect = false } = {}) {
  const terms = getPricingSearchTerms();
  const source = Array.isArray(pricingState.allList) && pricingState.allList.length
    ? pricingState.allList
    : pricingState.list;
  pricingState.list = source.filter(item => pricingItemHasSkc(item, terms));
  pricingState.total = pricingState.list.length;
  pricingState.pageNum = 1;
  pricingState.pageSize = pricingState.list.length || 100;
  pricingState.selected.clear();
  rebuildPricingRefPriceMap(pricingState.list);
  renderPricingTable();
  renderPricingPagination();
  if (autoSelect) selectAllVisiblePricingRows();
  if (terms.length) {
    showToast(`已筛选出 ${pricingState.list.length} 条匹配 SKC，并自动勾选`, pricingState.list.length ? 'success' : 'warning');
    addPricingLog(`按 SKC 查询：${terms.join(', ')}，匹配 ${pricingState.list.length} 条`, pricingState.list.length ? 'success' : 'warning');
  }
}

function collectPricingMissingReferenceRows() {
  const rows = [];
  (pricingState.list || []).forEach(item => {
    (item.skcList || []).forEach(skc => {
      const skcId = getPricingSkcId(skc);
      (skc.supplierPriceReviewInfoList || []).forEach(reviewInfo => {
        const priceOrderId = String(reviewInfo.priceOrderId || '');
        const productSkuList = Array.isArray(reviewInfo.productSkuList) ? reviewInfo.productSkuList : [];
        productSkuList.forEach(sku => {
          const skuId = getPricingSkuId(sku);
          const reference = getPricingReferencePriceText(reviewInfo, sku);
          if (reference) return;
          rows.push({
            priceOrderId,
            skcId,
            skuId,
            status: getPricingReviewStatus(reviewInfo, sku)
          });
        });
      });
    });
  });
  return rows;
}

function normalizePricingDiagnosticId(value) {
  const text = String(value ?? '').trim();
  if (!text) return '';
  const match = text.match(/\d{5,}/);
  return match ? match[0] : text;
}

function normalizePricingDiagnosticMoney(value) {
  if (value === undefined || value === null || value === '') return '';
  const text = String(value).replace(/[¥￥,\s]/g, '').trim();
  if (!text || /^[-—]+$/.test(text)) return '';
  const match = text.match(/\d+(?:\.\d+)?/);
  if (!match) return '';
  const num = Number(match[0]);
  return Number.isFinite(num) && num > 0 ? num.toFixed(2) : '';
}

function normalizePricingDiagnosticRow(row = {}) {
  return {
    ...row,
    priceOrderId: normalizePricingDiagnosticId(row.priceOrderId),
    skcId: normalizePricingDiagnosticId(row.skcId),
    skuId: normalizePricingDiagnosticId(row.skuId),
    price: normalizePricingDiagnosticMoney(row.price),
    candidateIds: Array.isArray(row.candidateIds)
      ? Array.from(new Set(row.candidateIds.map(normalizePricingDiagnosticId).filter(Boolean)))
      : [],
    capturedAt: Number(row.capturedAt || 0)
  };
}

function collectPricingDiagnosticRowsFromResult(result = {}) {
  const rows = [];
  (result.snapshots || []).forEach(snapshot => {
    (snapshot.rows || []).forEach(row => rows.push({
      ...row,
      sourceUrl: row.sourceUrl || snapshot.url || '',
      source: row.source || 'dom'
    }));
  });
  (result.capturedRows || []).forEach(row => rows.push(row));

  const byKey = new Map();
  rows.map(normalizePricingDiagnosticRow)
    .filter(row => row.price && (row.skuId || row.skcId || row.priceOrderId || row.candidateIds.length))
    .forEach(row => {
      const key = [
        row.priceOrderId || '',
        row.skcId || '',
        row.skuId || '',
        row.price || '',
        row.candidateIds.join(','),
        row.rowIndex ?? ''
      ].join('|');
      const existing = byKey.get(key);
      if (!existing || Number(row.capturedAt || 0) >= Number(existing.capturedAt || 0)) {
        byKey.set(key, row);
      }
    });
  return Array.from(byKey.values())
    .sort((a, b) => Number(b.capturedAt || 0) - Number(a.capturedAt || 0));
}

function pricingDiagnosticRowHasConflict(row = {}, target = {}) {
  return Boolean(
    (target.priceOrderId && row.priceOrderId && row.priceOrderId !== target.priceOrderId) ||
    (target.skcId && row.skcId && row.skcId !== target.skcId) ||
    (target.skuId && row.skuId && row.skuId !== target.skuId)
  );
}

function pricingDiagnosticRowMatches(row = {}, target = {}, options = {}) {
  row = normalizePricingDiagnosticRow(row);
  target = {
    priceOrderId: normalizePricingDiagnosticId(target.priceOrderId),
    skcId: normalizePricingDiagnosticId(target.skcId),
    skuId: normalizePricingDiagnosticId(target.skuId),
    skuCount: Number(target.skuCount || 0)
  };
  if (!row.price || pricingDiagnosticRowHasConflict(row, target)) return false;
  const candidates = Array.isArray(row.candidateIds) ? row.candidateIds : [];
  return Boolean(
    (target.skuId && row.skuId === target.skuId) ||
    (target.skuId && options.allowSingleSkuFallback && target.skuCount === 1 && !row.skuId && candidates.includes(target.skuId)) ||
    (options.allowSingleSkuFallback && target.skuCount === 1 && target.skcId && !row.skuId && row.skcId === target.skcId) ||
    (options.allowSingleSkuFallback && target.skuCount === 1 && target.priceOrderId && !row.skuId && row.priceOrderId === target.priceOrderId) ||
    (options.allowSingleSkuFallback && target.skuCount === 1 && target.skcId && !row.skuId && candidates.includes(target.skcId)) ||
    (options.allowSingleSkuFallback && target.skuCount === 1 && target.priceOrderId && !row.skuId && candidates.includes(target.priceOrderId))
  );
}

function summarizePricingDiagnosticRows(rows = [], missingRows = []) {
  return (rows || []).map(normalizePricingDiagnosticRow).filter(row => row.price).slice(0, 12).map((row, index) => {
    const matchedMissing = missingRows.find(missing => pricingDiagnosticRowMatches(row, { ...missing, skuCount: 1 }, { allowSingleSkuFallback: false }));
    const ids = [
      row.priceOrderId ? `核价单 ${row.priceOrderId}` : '',
      row.skcId ? `SKC ${row.skcId}` : '',
      row.skuId ? `SKU ${row.skuId}` : '',
      row.candidateIds?.length ? `候选ID ${row.candidateIds.slice(0, 8).join(',')}` : ''
    ].filter(Boolean).join('；') || '未识别到ID';
    return `${index + 1}. ¥${row.price || '-'} | ${ids} | ${matchedMissing ? `SKU精确匹配网站缺失行 ${matchedMissing.skuId || '-'}` : '暂未精确匹配网站缺失行'}`;
  });
}

function findCapturedPricingRowForTarget(rows = [], target = {}) {
  const normalizedTarget = {
    ...target,
    priceOrderId: normalizePricingDiagnosticId(target.priceOrderId),
    skcId: normalizePricingDiagnosticId(target.skcId),
    skuId: normalizePricingDiagnosticId(target.skuId),
    skuCount: Number(target.skuCount || 0)
  };
  const normalizedRows = rows.map(normalizePricingDiagnosticRow)
    .filter(row => row.price)
    .sort((a, b) => Number(b.capturedAt || 0) - Number(a.capturedAt || 0));
  return normalizedRows.find(row => pricingDiagnosticRowMatches(row, normalizedTarget, { allowSingleSkuFallback: false }))
    || normalizedRows.find(row => pricingDiagnosticRowMatches(row, normalizedTarget, { allowSingleSkuFallback: true }))
    || null;
}

function applyCapturedPricingReferenceRowsToCurrentList(rows = []) {
  const capturedRows = rows.map(normalizePricingDiagnosticRow).filter(row => row.price);
  if (!capturedRows.length) return { applied: 0, matched: 0, missing: collectPricingMissingReferenceRows().length };

  let applied = 0;
  let matched = 0;
  (pricingState.list || []).forEach(item => {
    (item.skcList || []).forEach(skc => {
      const skcId = getPricingSkcId(skc);
      (skc.supplierPriceReviewInfoList || []).forEach(reviewInfo => {
        const productSkuList = Array.isArray(reviewInfo.productSkuList) ? reviewInfo.productSkuList : [];
        const skuCount = productSkuList.length;
        productSkuList.forEach(sku => {
          const skuId = getPricingSkuId(sku);
          if (!skuId || getPricingReferencePriceText(reviewInfo, sku)) return;
          const captured = findCapturedPricingRowForTarget(capturedRows, {
            priceOrderId: reviewInfo.priceOrderId,
            skcId,
            skuId,
            skuCount
          });
          if (!captured?.price) return;
          matched++;
          sku.xiaoqingwaCapturedReferencePrice = captured.price;
          sku.xiaoqingwaCapturedReferencePriceText = captured.price;
          sku.xiaoqingwaCapturedReferencePriceSource = captured.source || 'captured';
          sku.xiaoqingwaCapturedReferencePriceAt = captured.capturedAt || Date.now();
          sku.xiaoqingwaCapturedReferencePriceOrderId = captured.priceOrderId || '';
          sku.xiaoqingwaCapturedReferencePriceSkcId = captured.skcId || '';
          sku.xiaoqingwaCapturedReferencePriceSkuId = captured.skuId || '';
          if (!reviewInfo.priceOrderId && captured.priceOrderId) reviewInfo.priceOrderId = captured.priceOrderId;
          applied++;
        });
      });
    });
  });

  if (applied) {
    rebuildPricingRefPriceMap(pricingState.list);
    renderPricingTable();
    renderPricingPagination();
  }
  return { applied, matched, missing: collectPricingMissingReferenceRows().length };
}

async function diagnosePricingReferenceCapture() {
  const btn = document.getElementById('pricingDiagnoseReferenceBtn');
  const originalText = btn?.textContent;
  if (btn) {
    btn.disabled = true;
    btn.textContent = '诊断中...';
  }
  try {
    const missingRows = collectPricingMissingReferenceRows();
    addPricingLog(`诊断开始：当前网站列表缺少参考申报价 ${missingRows.length} 行`, missingRows.length ? 'warning' : 'success');
    const result = await safeRuntimeSendMessage({
      action: 'diagnosePricingReference'
    }, { success: false, error: getRuntimeReconnectMessage('核价参考价诊断') }, 12000);

    if (!result?.success) {
      addPricingLog(`诊断失败：${result?.error || '未知错误'}`, 'error');
      showToast(`诊断失败：${result?.error || '未知错误'}`, 'error');
      return;
    }

    addPricingLog(`诊断结果：检测到 TEMU 后台标签页 ${result.sellerTabCount || 0} 个，插件缓存参考价 ${result.capturedCount || 0} 条`, 'info');
    const capturedRows = collectPricingDiagnosticRowsFromResult(result);
    (result.snapshots || []).forEach((snapshot, index) => {
      const status = snapshot.success
        ? `页面采集 ${snapshot.rowCount || 0} 条`
        : `采集失败：${snapshot.error || '未知错误'}`;
      addPricingLog(`后台页 ${index + 1}: ${status} | ${snapshot.url || '-'}`, snapshot.success ? 'info' : 'warning');
      summarizePricingDiagnosticRows(snapshot.rows || [], missingRows).forEach(line => addPricingLog(`页面采集 ${line}`, 'info'));
    });

    const cachedLines = summarizePricingDiagnosticRows(result.capturedRows || [], missingRows);
    if (cachedLines.length) {
      cachedLines.forEach(line => addPricingLog(`缓存采集 ${line}`, 'info'));
    } else {
      addPricingLog('缓存中没有系统参考申报价。请先刷新 TEMU 后台半托管核价页面，确保能看到 481.81 那一列，再点诊断。', 'warning');
    }

    const applyResult = applyCapturedPricingReferenceRowsToCurrentList(capturedRows);
    if (applyResult.applied > 0) {
      addPricingLog(`诊断判断：已按 SKU 精确规则应用 ${applyResult.applied} 个系统参考申报价，当前仍缺 ${applyResult.missing} 行。`, 'success');
      savePricingLocalCache({
        items: pricingState.list,
        allItems: pricingState.allList,
        total: pricingState.list.length,
        pageNum: pricingState.pageNum,
        pageSize: pricingState.pageSize,
        minPriceMap: pricingState.minPriceMap
      });
    } else if (capturedRows.length) {
      addPricingLog('诊断判断：已采到真实价格，但没有找到可安全应用的 SKU 精确匹配；同 SKC 多 SKU 不会串用价格。请把上面的采集日志发我继续补字段识别。', 'warning');
    } else {
      addPricingLog('诊断判断：插件没有从当前 TEMU 后台页采到参考申报价。需要停在显示 481.81 的后台页面再点诊断。', 'error');
    }
    showToast('诊断完成，查看执行日志', 'success');
  } catch (error) {
    addPricingLog(`诊断异常：${error?.message || String(error)}`, 'error');
    showToast(`诊断异常：${error?.message || String(error)}`, 'error');
  } finally {
    if (btn) {
      btn.disabled = false;
      btn.textContent = originalText || '诊断参考申报价';
    }
  }
}

async function loadAllPricingProducts({ applyCurrentSearch = true } = {}) {
  const btn = document.getElementById('pricingLoadAllBtn');
  const originalText = btn?.textContent;
  if (btn) {
    btn.disabled = true;
    btn.textContent = '加载中...';
  }
  const searchBtn = document.getElementById('pricingSearchBtn');
  const originalSearchText = searchBtn?.textContent;
  if (searchBtn) {
    searchBtn.disabled = true;
    searchBtn.textContent = '查询中...';
  }
  const container = document.getElementById('pricingTableBody');
  if (container) container.innerHTML = '<tr><td colspan="11" class="loading">正在加载所有待核价商品...</td></tr>';

  try {
    const allItems = [];
    let page = 1;
    const pageSize = 100;
    let total = 0;

    while (true) {
      const result = await safeRuntimeSendMessage({
        action: 'getPricingList',
        params: { pageNum: page, pageSize, priceReviewStatusList: PRICING_PENDING_STATUS_LIST }
      }, { success: false, error: getRuntimeReconnectMessage('核价列表加载') }, 30000);

      if (!result?.success || !result.list || result.list.length === 0) break;

      const filtered = result.list.filter(item =>
        item.skcList && item.skcList.length > 0 &&
        item.skcList.some(skc => skc.supplierPriceReviewInfoList && skc.supplierPriceReviewInfoList.length > 0)
      );

      allItems.push(...filtered);

      if (total === 0) total = result.total || 0;
      addPricingLog(`已加载第${page}页，本页${filtered.length}条，累计${allItems.length}条`, 'info');

      if (allItems.length >= total || result.list.length < pageSize) break;
      page++;
      await new Promise(r => setTimeout(r, 300));
    }

    pricingState.list = allItems;
    pricingState.allList = allItems.slice();
    pricingState.total = allItems.length;
    pricingState.pageNum = 1;
    pricingState.pageSize = allItems.length || 100;
    pricingState.selected.clear();

    rebuildPricingRefPriceMap(pricingState.list);

    renderPricingTable();
    renderPricingPagination();

    if (allItems.length > 0) {
      addPricingLog(`共${allItems.length}条商品，正在获取阈值...`, 'info');
      await fetchMinPricesForCurrentPage();
      addPricingLog(`阈值加载完毕，已设置${Object.keys(pricingState.minPriceMap).length}个SKU`, 'info');
      renderPricingTable();
    }

    if (applyCurrentSearch && getPricingSearchTerms().length) {
      applyPricingSearchFilter({ autoSelect: true });
    }

    savePricingLocalCache({
      items: allItems,
      allItems: pricingState.allList,
      total: allItems.length,
      pageNum: pricingState.pageNum,
      pageSize: pricingState.pageSize,
      minPriceMap: pricingState.minPriceMap
    });
    showToast(`✅ 加载完成，共${allItems.length}条商品`, 'success');
    return allItems;
  } catch (err) {
    showToast(`❌ 加载失败: ${err.message}`, 'error');
    addPricingLog(`加载失败: ${err.message}`, 'error');
    throw err;
  } finally {
    if (btn) {
      btn.disabled = false;
      btn.textContent = originalText || '📦 加载全部待核价商品';
    }
    if (searchBtn) {
      searchBtn.disabled = false;
      searchBtn.textContent = originalSearchText || '🔍 查询';
    }
  }
}

// 加载核价商品列表（只获取 TEMU 返回的待核价商品，系统参考申报价缺失时只展示不执行）
async function refreshPricingList() {
  const container = document.getElementById('pricingTableBody');
  if (!container) return;
  container.innerHTML = '<tr><td colspan="11" class="loading">加载中...</td></tr>';

  try {
    // 获取待核价商品；半托管“待卖家确认”和全托管“价格申报中”都需要纳入。
    const result = await safeRuntimeSendMessage({
      action: 'getPricingList',
      params: {
        pageNum: pricingState.pageNum,
        pageSize: pricingState.pageSize,
        priceReviewStatusList: PRICING_PENDING_STATUS_LIST,
        keyword: document.getElementById('pricingSearchInput')?.value.trim() || ''
      }
    }, { success: false, error: getRuntimeReconnectMessage('核价列表加载') }, 30000);

    if (!result) {
      container.innerHTML = '<tr><td colspan="11" class="empty">❌ 未收到响应，请检查插件是否已连接TEMU页面</td></tr>';
      console.error('[小青蛙电商助手] refreshPricingList: sendMessage 返回 null');
      return;
    }

    if (result && result.success) {
    // 过滤：只保留有核价需求的商品（supplierPriceReviewInfoList 不为空）
    let rawList = result.list || [];
    pricingState.list = rawList.filter(item => {
      if (!item.skcList || item.skcList.length === 0) return false;
      // 至少有一个 SKC 的 supplierPriceReviewInfoList 不为空
      return item.skcList.some(skc =>
        skc.supplierPriceReviewInfoList && skc.supplierPriceReviewInfoList.length > 0
      );
    });
    pricingState.allList = pricingState.list.slice();
    pricingState.total = result.total || rawList.length;
    // 如果过滤后为空但 total > 0，说明有数据但都不符合核价条件
    if (pricingState.list.length === 0 && pricingState.total > 0) {
      pricingState.total = 0; // 无有效核价数据
    }
    pricingState.selected.clear();

    console.log(`[小青蛙电商助手] 核价列表过滤: API返回${rawList.length}条, 过滤后${pricingState.list.length}条`);

    // 调试日志
    if (pricingState.list.length > 0) {
      const first = pricingState.list[0];
      console.log('[小青蛙电商助手] 核价列表第一条数据:', JSON.stringify(first).substring(0, 500));
      const firstSkc = first.skcList && first.skcList[0];
      if (firstSkc) {
        console.log('[小青蛙电商助手] 第一个SKC字段:', Object.keys(firstSkc).join(', '));
        const firstSku = firstSkc.skuList && firstSkc.skuList[0];
        if (firstSku) {
          console.log('[小青蛙电商助手] 第一个SKU字段:', Object.keys(firstSku).join(', '));
          console.log('[小青蛙电商助手] 第一个SKU数据:', JSON.stringify(firstSku).substring(0, 2000));
          console.log('[小青蛙电商助手] 第一个SKC的supplierPriceReviewInfoList:', JSON.stringify(firstSkc.supplierPriceReviewInfoList)?.substring(0, 1000));
          const firstReviewInfo = firstSkc.supplierPriceReviewInfoList?.[0] || {};
          const firstReviewSku = firstReviewInfo.productSkuList?.[0] || {};
          console.log('[小青蛙电商助手] 核价参考价候选字段:', JSON.stringify({
            reviewInfo: pickPricingDebugFields(firstReviewInfo),
            productSku: pickPricingDebugFields(firstReviewSku),
            skc: pickPricingDebugFields(firstSkc),
            sku: pickPricingDebugFields(firstSku)
          }).substring(0, 2000));
        }
      }
    }

    rebuildPricingRefPriceMap(pricingState.list);

    renderPricingTable();
    renderPricingPagination();
    savePricingLocalCache({
      items: pricingState.list,
      allItems: pricingState.allList,
      total: pricingState.list.length,
      pageNum: pricingState.pageNum,
      pageSize: pricingState.pageSize,
      minPriceMap: pricingState.minPriceMap
    });
    } else {
      // result 存在但 success 不为 true
      const errMsg = result?.error || '未知错误';
      container.innerHTML = `<tr><td colspan="11" class="empty">❌ 加载失败: ${escapeHtml(errMsg)}</td></tr>`;
      console.error('[小青蛙电商助手] refreshPricingList 失败:', errMsg);
    }
  } catch (err) {
    container.innerHTML = `<tr><td colspan="11" class="empty">❌ 加载异常: ${escapeHtml(err?.message || String(err))}</td></tr>`;
    console.error('[小青蛙电商助手] refreshPricingList 异常:', err);
  }
}

// 获取当前页所有 SKU 的最低价
async function fetchMinPricesForCurrentPage() {
  const allSkuIds = new Set();
  (pricingState.list || []).forEach(item => {
    (item.skcList || []).forEach(skc => {
      (skc.skuList || []).forEach(sku => {
        const skuId = getPricingSkuId(sku);
        if (skuId) allSkuIds.add(skuId);
      });
      (skc.supplierPriceReviewInfoList || []).forEach(reviewInfo => {
        (reviewInfo.productSkuList || []).forEach(sku => {
          const skuId = getPricingSkuId(sku);
          if (skuId) allSkuIds.add(skuId);
        });
      });
    });
  });

  const skuIds = Array.from(allSkuIds);
  if (skuIds.length === 0) return;

  // 最多 200 个 SKU 一次查询
  const chunks = [];
  for (let i = 0; i < skuIds.length; i += 200) {
    chunks.push(skuIds.slice(i, i + 200));
  }

  for (const chunk of chunks) {
    const result = await safeRuntimeSendMessage({
      action: 'getMinPrice',
      params: { skuIds: chunk }
    }, { success: false, error: getRuntimeReconnectMessage('阈值加载') }, 25000);
    if (result && result.success && result.minList) {
      // 打印第一条数据的字段名用于调试
      if (result.minList.length > 0) {
        console.log('[小青蛙电商助手] minList 第一条数据:', JSON.stringify(result.minList[0]).substring(0, 200));
      }
      result.minList.forEach(item => {
        pricingState.minPriceMap[item.skuid] = item.minprice;
      });
    }
  }
}

// 渲染核价表格（每个核价单一行，核价单 = supplierPriceReviewInfoList 中的 reviewInfo）
function renderPricingTable() {
  const tbody = document.getElementById('pricingTableBody');
  if (!tbody) { console.error('[小青蛙电商助手] 找不到 pricingTableBody'); return; }
  console.log('[小青蛙电商助手] renderPricingTable, list长度:', pricingState.list.length);

  if (pricingState.list.length === 0) {
    tbody.innerHTML = '<tr><td colspan="11" class="empty">📦 暂无待核价商品</td></tr>';
    return;
  }

  const rows = [];
  let globalIdx = 0;

  pricingState.list.forEach((item) => {
    const productName = item.productName || '-';
    const productId = item.productId || '-';
    (item.skcList || []).forEach(skc => {
      const skcId = skc.skcId || skc.goodsSkcId || '-';
      const mainImage = (skc.previewImgUrlList && skc.previewImgUrlList[0]) || '';
      (skc.supplierPriceReviewInfoList || []).forEach(reviewInfo => {
        const priceOrderId = String(reviewInfo.priceOrderId || '');  // 确保字符串
        const suggestPrice = getPricingReferencePriceText(reviewInfo);
        const times = reviewInfo.times || 0; // 已核价次数
        const productSkuList = reviewInfo.productSkuList || [];

        productSkuList.forEach((sku) => {
          const skuId = getPricingSkuId(sku) || '-';
          const specs = (sku.productPropertyList || []).map(p => p.value).join(' / ');
          const supplierPrice = getPricingSupplierPriceText(sku, skc, item) || '-';
          const refPriceYuan = getPricingReferencePriceText(reviewInfo, sku) || '-';
          const refPriceLabel = getPricingReferencePriceLabel(reviewInfo, sku);
          const refPriceSource = getPricingReferencePriceSourceKey(reviewInfo, sku);
          const skuImage = sku.skuPreviewImage || mainImage;
          const minPrice = pricingState.minPriceMap[skuId];
          const retryCount = pricingState.retryCounts[priceOrderId] || 0;

          const rowKey = getPricingRowKey(priceOrderId, skuId);
          rows.push(`
            <tr>
              <td><input type="checkbox" class="checkbox pricing-cb" data-key="${escapeHtml(rowKey)}" data-sku="${skuId}" data-skcid="${skcId}" data-poid="${priceOrderId}" ${pricingState.selected.has(rowKey) ? 'checked' : ''}></td>
              <td>${skuImage ? `<img src="${skuImage}" class="spu-img" style="width:44px;height:44px;object-fit:cover;border-radius:4px;border:1px solid #eee;">` : '<span style="color:#ccc;">无图</span>'}</td>
              <td style="font-size:11px;word-break:break-all;" title="${escapeHtml(skcId)}">${escapeHtml(skcId)}</td>
              <td style="font-size:11px;word-break:break-all;" title="${escapeHtml(skuId)}">${escapeHtml(skuId)}</td>
              <td style="font-size:12px;">${escapeHtml(specs || '-')}</td>
              <td style="font-size:12px;">¥${escapeHtml(supplierPrice)}</td>
              <td style="font-size:12px;" title="${escapeHtml([refPriceLabel, refPriceSource].filter(Boolean).join(' · '))}">${refPriceYuan === '-' ? '<span style="color:#ef4444;">未返回</span>' : `¥${escapeHtml(refPriceYuan)}`}</td>
              <td style="font-size:12px;">
                <input type="number" class="pricing-threshold-input" data-skuid="${skuId}" value="${minPrice !== undefined ? minPrice : ''}" placeholder="阈值" step="0.01" min="0" style="width:80px;padding:4px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:12px;">
              </td>
              <td style="font-size:12px;text-align:center;">${times}</td>
              <td style="font-size:10px;word-break:break-all;color:#888;" title="${escapeHtml(priceOrderId)}">${priceOrderId ? priceOrderId.substring(0, 10) + '...' : '-'}</td>
              <td style="font-size:12px;">
                <button class="btn btn-success btn-sm pricing-approve-btn" data-poid="${priceOrderId}" title="同意核价">✅</button>
                <button class="btn btn-danger btn-sm pricing-void-btn" data-poid="${priceOrderId}" title="拒绝核价">🗑️</button>
              </td>
            </tr>
          `);
          globalIdx++;
        });
      });
    });
  });

  console.log('[小青蛙电商助手] rows 数量:', rows.length);
  tbody.innerHTML = rows.join('');
  bindPricingTableEvents();
}

// 绑定核价表格事件
function bindPricingTableEvents() {
  // 全选
  document.getElementById('pricingSelectAll')?.addEventListener('change', (e) => {
    const cbs = document.querySelectorAll('#pricingTableBody .pricing-cb');
    cbs.forEach(cb => { cb.checked = e.target.checked; });
    pricingState.selected.clear();
    if (e.target.checked) {
      cbs.forEach(cb => pricingState.selected.add(getPricingRowKeyFromCheckbox(cb)));
    }
  });

  // 单选
  document.querySelectorAll('#pricingTableBody .pricing-cb').forEach((cb) => {
    cb.addEventListener('change', (e) => {
      const key = getPricingRowKeyFromCheckbox(e.target);
      if (e.target.checked) pricingState.selected.add(key);
      else pricingState.selected.delete(key);
      const cbs = Array.from(document.querySelectorAll('#pricingTableBody .pricing-cb'));
      const selectAll = document.getElementById('pricingSelectAll');
      if (selectAll) {
        selectAll.checked = cbs.length > 0 && cbs.every(item => item.checked);
        selectAll.indeterminate = cbs.some(item => item.checked) && !selectAll.checked;
      }
    });
  });

  // 阈值输入
  document.querySelectorAll('.pricing-threshold-input').forEach(input => {
    input.addEventListener('change', (e) => {
      const skuId = e.target.dataset.skuid;
      const val = parseFloat(e.target.value) || 0;
      pricingState.minPriceMap[skuId] = val;
    });
  });

  // 同意核价按钮（按 priceOrderId 匹配 reviewInfo，发送该核价单下所有 SKU）
  const handledApprove = new Set();
  document.querySelectorAll('.pricing-approve-btn').forEach(btn => {
    btn.addEventListener('click', async (e) => {
      const poid = e.target.dataset.poid;
      if (handledApprove.has(poid)) return;
      handledApprove.add(poid);

      // 从 supplierPriceReviewInfoList 中查找匹配的核价单
      let matchedReviewInfo = null;
      for (const item of pricingState.list) {
        for (const skc of (item.skcList || [])) {
          for (const ri of (skc.supplierPriceReviewInfoList || [])) {
            if (String(ri.priceOrderId) === String(poid)) {
              matchedReviewInfo = ri;
              break;
            }
          }
          if (matchedReviewInfo) break;
        }
        if (matchedReviewInfo) break;
      }
      if (!matchedReviewInfo) { handledApprove.delete(poid); return; }

      const productSkuList = matchedReviewInfo.productSkuList || [];
      const skuItems = productSkuList.map(s => {
        const skuId = getPricingSkuId(s);
        const referenceFen = getPricingReferencePriceFen(matchedReviewInfo, s);
        return skuId ? { ...s, skuId, productSkuId: s.productSkuId || skuId, suggestSupplyPrice: referenceFen } : null;
      }).filter(Boolean);

      // 收集每个 SKU 的阈值价格；未设置时使用 TEMU 返回的系统参考申报价直接接受。
      const customPrices = {};
      productSkuList.forEach(sku => {
        const skuId = getPricingSkuId(sku);
        if (skuId && pricingState.minPriceMap[skuId]) {
          customPrices[skuId] = pricingState.minPriceMap[skuId];
        }
      });
      const missingReferenceSku = productSkuList.find(sku => !getPricingReferencePriceFen(matchedReviewInfo, sku));
      if (missingReferenceSku && !customPrices[getPricingSkuId(missingReferenceSku)]) {
        showToast(getPricingReferenceMissingMessage(poid), 'error');
        addPricingLog(getPricingReferenceMissingMessage(poid), 'error');
        handledApprove.delete(poid);
        return;
      }

      const result = await safeRuntimeSendMessage({
        action: 'approvePricing',
        params: { priceOrderId: poid, items: skuItems, customPrices }
      }, { success: false, error: getRuntimeReconnectMessage('核价同意') }, 30000);
      showToast(result?.success ? '✅ 同意核价成功' : `❌ ${result?.error || '同意核价失败'}`, result?.success ? 'success' : 'error');
      if (result?.success) setTimeout(() => refreshPricingList(), 500);
      handledApprove.delete(poid);
    });
  });

  // 拒绝核价按钮（放弃调整报价，按 priceOrderId 去重）
  const handledVoid = new Set();
  document.querySelectorAll('.pricing-void-btn').forEach(btn => {
    btn.addEventListener('click', async (e) => {
      const poid = e.target.dataset.poid;
      if (handledVoid.has(poid)) return;
      handledVoid.add(poid);
      const result = await safeRuntimeSendMessage({
        action: 'voidPricing',
        params: { priceOrderId: poid }
      }, { success: false, error: getRuntimeReconnectMessage('核价拒绝') }, 30000);
      showToast(result?.success ? '🗑️ 拒绝核价成功（已放弃调整报价）' : `❌ ${result?.error || '拒绝核价失败'}`, result?.success ? 'success' : 'error');
      if (result?.success) setTimeout(() => refreshPricingList(), 500);
      handledVoid.delete(poid);
    });
  });
}

// 渲染核价分页
function renderPricingPagination() {
  const info = document.getElementById('pricingPaginationInfo');
  const btns = document.getElementById('pricingPaginationButtons');
  if (!info || !btns) return;

  const totalPages = Math.ceil(pricingState.total / pricingState.pageSize) || 1;
  info.textContent = `共 ${pricingState.total} 条，第 ${pricingState.pageNum}/${totalPages} 页`;

  let html = '';
  html += `<button class="btn btn-default btn-sm" ${pricingState.pageNum <= 1 ? 'disabled' : ''} data-page="${pricingState.pageNum - 1}">上一页</button>`;
  // 页码按钮（最多显示5个）
  const start = Math.max(1, pricingState.pageNum - 2);
  const end = Math.min(totalPages, start + 4);
  for (let i = start; i <= end; i++) {
    html += `<button class="btn ${i === pricingState.pageNum ? 'btn-primary' : 'btn-default'} btn-sm" data-page="${i}">${i}</button>`;
  }
  html += `<button class="btn btn-default btn-sm" ${pricingState.pageNum >= totalPages ? 'disabled' : ''} data-page="${pricingState.pageNum + 1}">下一页</button>`;

  btns.innerHTML = html;
  btns.querySelectorAll('button').forEach(btn => {
    btn.addEventListener('click', (e) => {
      const page = parseInt(e.target.dataset.page);
      if (page && page >= 1 && page <= totalPages) {
        pricingState.pageNum = page;
        refreshPricingList();
      }
    });
  });
}

// 开始自动核价执行
async function startPricingExecution() {
  if (pricingState.isRunning) {
    showToast('⚠️ 核价已在执行中，请先停止', 'warning');
    return;
  }

  // 检查是否已加载列表
  if (pricingState.list.length === 0) {
    showToast('⚠️ 正在加载待核价商品', 'warning');
    addPricingLog('列表为空，自动加载中...', 'info');
    await loadAllPricingProducts({ applyCurrentSearch: true }).catch(() => {});
    if (pricingState.list.length === 0) {
      showToast('❌ 没有待核价商品', 'error');
      addPricingLog('没有待核价商品，执行终止', 'error');
      return;
    }
  }

  // 先加载阈值
  await fetchMinPricesForCurrentPage();

  // 检查是否有阈值
  const hasAnyThreshold = Object.keys(pricingState.minPriceMap).length > 0;
  if (!hasAnyThreshold) {
    showToast('⚠️ 所有商品未设置阈值，请先设置阈值后再执行', 'warning');
    addPricingLog('未检测到阈值设置，请先设置阈值', 'warning');
    return;
  }

  const validation = validatePricingExecutionReadiness();
  if (!validation.ready) {
    showToast(validation.message, 'error');
    addPricingLog(validation.message, 'error');
    validation.details.slice(0, 8).forEach(detail => addPricingLog(detail, 'warning'));
    return;
  }

  pricingState.isRunning = true;
  updatePricingRunStatus();

  showToast('▶ 开始执行核价', 'success');
  addPricingLog(`开始执行核价 | 共${pricingState.list.length}条商品 | 阈值已设置${Object.keys(pricingState.minPriceMap).length}个SKU`, 'info');
  console.log('[小青蛙电商助手] 开始执行核价，列表长度:', pricingState.list.length);

  // 只执行一轮，完成后自动停止
  try {
    await runPricingCycle();
  } finally {
    // 一轮完成后自动停止
    stopPricingExecution();
    addPricingLog('✅ 一轮核价执行完毕，已自动停止', 'success');
    showToast('✅ 一轮核价执行完毕', 'success');
  }
}
// 执行一轮核价循环：按“设置核价规则”处理真实核价单数据。
async function runPricingCycle() {
  if (!pricingState.isRunning) return;

  addPricingLog('开始新一轮核价检查...', 'info');
  console.log('[小青蛙电商助手] runPricingCycle 开始, list长度:', pricingState.list.length);
  const selectedOrderIds = new Set(getSelectedPricingOrderIds());
  const rule = getPricingRule();
  if (selectedOrderIds.size) {
    addPricingLog(`本轮只处理已勾选的 ${selectedOrderIds.size} 个核价单`, 'info');
  }
  addPricingLog(`本轮规则：系统参考申报价高于阈值=${rule.highAction === 'autoApprove' ? '自动同意' : '按阈值同意'}；低于阈值=${rule.lowAction === 'resubmit' ? `重新提报，每次下调${rule.decreaseYuan}元，最多${rule.maxResubmitTimes}次` : '直接拒绝'}`, 'info');

  // 如果列表为空，先加载
  if (pricingState.list.length === 0) {
    addPricingLog('列表为空，重新加载...', 'info');
    await refreshPricingList();
    console.log('[小青蛙电商助手] 刷新后列表长度:', pricingState.list.length);
    if (pricingState.list.length === 0) {
      addPricingLog('没有待核价商品，本轮跳过', 'warning');
      return;
    }
  } else if (getPricingSearchTerms().length && Array.isArray(pricingState.allList) && pricingState.allList.length) {
    applyPricingSearchFilter({ autoSelect: false });
  } else {
    await refreshPricingList();
  }

  console.log('[小青蛙电商助手] 开始遍历核价单...');
  let processedCount = 0, acceptCount = 0, rejectCount = 0, resubmitCount = 0, skipCount = 0;

  // 三层遍历：item -> skc -> reviewInfo
  for (const item of pricingState.list) {
    if (!pricingState.isRunning) break;

    for (const skc of (item.skcList || [])) {
      if (!pricingState.isRunning) break;

      for (const reviewInfo of (skc.supplierPriceReviewInfoList || [])) {
        if (!pricingState.isRunning) break;

        const priceOrderId = reviewInfo.priceOrderId;
        if (!priceOrderId) {
          addPricingLog(`商品 ${item.productId || '?'} SKC ${skc.skcId || '?'}: 跳过（无核价单ID）`, 'warning');
          skipCount++;
          continue;
        }
        if (selectedOrderIds.size && !selectedOrderIds.has(String(priceOrderId))) {
          continue;
        }

        const productSkuList = reviewInfo.productSkuList || [];
        if (productSkuList.length === 0) continue;

        // 检查是否所有 SKU 都处于 TEMU 返回的待处理核价状态。
        const allPendingConfirm = productSkuList.every(sku => isPricingPendingReviewStatus(getPricingReviewStatus(reviewInfo, sku)));
        if (!allPendingConfirm) {
          addPricingLog(`核价单 ${priceOrderId}: 跳过（非卖家待确认状态）`, 'warning');
          skipCount++;
          continue;
        }

        const skuRows = [];
        let hasMissingThreshold = false;
        let hasMissingReference = false;
        let hasMissingSupplierPrice = false;
        for (const sku of productSkuList) {
          const skuId = getPricingSkuId(sku);
          const thresholdYuan = Number(pricingState.minPriceMap[skuId]);
          if (!skuId || !Number.isFinite(thresholdYuan) || thresholdYuan <= 0) {
            hasMissingThreshold = true;
            break;
          }
          const thresholdFen = Math.round(thresholdYuan * 100);
          const referenceFen = getPricingReferencePriceFen(reviewInfo, sku);
          const supplierFen = getPricingSupplierPriceFen(sku, skc, item);
          if (!Number.isFinite(referenceFen) || referenceFen <= 0) hasMissingReference = true;
          if (!Number.isFinite(supplierFen) || supplierFen <= 0) hasMissingSupplierPrice = true;
          skuRows.push({
            sku,
            skuId,
            thresholdYuan,
            thresholdFen,
            referenceFen,
            supplierFen
          });
        }
        if (hasMissingThreshold) {
          addPricingLog(`核价单 ${priceOrderId}: 跳过（未设置阈值）`, 'warning');
          skipCount++;
          continue;
        }
        if (hasMissingReference) {
          addPricingLog(getPricingReferenceMissingMessage(priceOrderId), 'warning');
          skipCount++;
          continue;
        }
        if (hasMissingSupplierPrice && rule.lowAction === 'resubmit') {
          addPricingLog(`核价单 ${priceOrderId}: 跳过（缺少原申报价，不能重新提报）`, 'warning');
          skipCount++;
          continue;
        }

        const skuItems = skuRows.map(row => ({
          ...row.sku,
          skuId: row.skuId,
          productSkuId: row.sku.productSkuId || row.skuId,
          suggestSupplyPrice: row.referenceFen,
          supplierPrice: row.supplierFen
        }));
        const minReferenceFen = Math.min(...skuRows.map(row => row.referenceFen));
        const minThresholdFen = Math.min(...skuRows.map(row => row.thresholdFen));
        const allReferenceAtLeastThreshold = skuRows.every(row => row.referenceFen >= row.thresholdFen);
        const orderTimes = Number(reviewInfo.times || pricingState.retryCounts[priceOrderId] || 0) || 0;

        if (allReferenceAtLeastThreshold) {
          const customPrices = {};
          skuRows.forEach(row => {
            customPrices[row.skuId] = rule.highAction === 'autoApprove'
              ? formatPricingFen(row.referenceFen)
              : row.thresholdYuan;
          });

          addPricingLog(`核价单 ${priceOrderId}: 系统参考申报价最低 ¥${formatPricingFen(minReferenceFen)} >= 阈值最低 ¥${formatPricingFen(minThresholdFen)} -> ${rule.highAction === 'autoApprove' ? '自动同意' : '按阈值同意'}(${skuItems.length}个SKU)`, 'success');
          const result = await safeRuntimeSendMessage({
            action: 'approvePricing',
            params: { priceOrderId, items: skuItems, customPrices }
          }, { success: false, error: getRuntimeReconnectMessage('核价同意') }, 30000);
          if (result?.success) {
            addPricingLog(`核价单 ${priceOrderId}: 同意核价成功`, 'success');
            acceptCount++;
          } else {
            addPricingLog(`核价单 ${priceOrderId}: 同意核价失败: ${result?.error || '未知错误'}`, 'error');
          }
        } else {
          if (rule.lowAction !== 'resubmit') {
            addPricingLog(`核价单 ${priceOrderId}: 系统参考申报价最低 ¥${formatPricingFen(minReferenceFen)} < 阈值最低 ¥${formatPricingFen(minThresholdFen)} -> 直接拒绝`, 'error');
            const result = await safeRuntimeSendMessage({
              action: 'voidPricing',
              params: { priceOrderId }
            }, { success: false, error: getRuntimeReconnectMessage('核价拒绝') }, 30000);
            if (result?.success) {
              addPricingLog(`核价单 ${priceOrderId}: 拒绝核价成功`, 'info');
              rejectCount++;
            } else {
              addPricingLog(`核价单 ${priceOrderId}: 拒绝核价失败: ${result?.error || '未知错误'}`, 'error');
            }
          } else {
            const decreaseFen = Math.max(1, Math.round(Number(rule.decreaseYuan || 0) * 100));
            const hasSupplierBelowThreshold = skuRows.some(row => row.supplierFen < row.thresholdFen);
            if (hasSupplierBelowThreshold) {
              addPricingLog(`核价单 ${priceOrderId}: 原申报价已低于阈值，不能通过下调达到阈值 -> 拒绝`, 'warning');
              const result = await safeRuntimeSendMessage({
                action: 'voidPricing',
                params: { priceOrderId }
              }, { success: false, error: getRuntimeReconnectMessage('核价拒绝') }, 30000);
              if (result?.success) {
                addPricingLog(`核价单 ${priceOrderId}: 拒绝核价成功`, 'info');
                rejectCount++;
              } else {
                addPricingLog(`核价单 ${priceOrderId}: 拒绝核价失败: ${result?.error || '未知错误'}`, 'error');
              }
            } else if (orderTimes >= rule.maxResubmitTimes) {
              addPricingLog(`核价单 ${priceOrderId}: 已提报 ${orderTimes} 次仍未达到阈值 -> 拒绝`, 'warning');
              const result = await safeRuntimeSendMessage({
                action: 'voidPricing',
                params: { priceOrderId }
              }, { success: false, error: getRuntimeReconnectMessage('核价拒绝') }, 30000);
              if (result?.success) {
                addPricingLog(`核价单 ${priceOrderId}: 拒绝核价成功`, 'info');
                rejectCount++;
              } else {
                addPricingLog(`核价单 ${priceOrderId}: 拒绝核价失败: ${result?.error || '未知错误'}`, 'error');
              }
            } else {
              const targetPrices = {};
              let allReachThreshold = true;
              skuRows.forEach(row => {
                const nextFen = Math.max(row.thresholdFen, row.supplierFen - decreaseFen);
                if (nextFen > row.thresholdFen) allReachThreshold = false;
                targetPrices[row.skuId] = formatPricingFen(nextFen);
              });

              if (allReachThreshold) {
                addPricingLog(`核价单 ${priceOrderId}: 按每次下调 ¥${formatPricingFen(decreaseFen)} 已到达阈值 -> 同意核价`, 'success');
                const result = await safeRuntimeSendMessage({
                  action: 'approvePricing',
                  params: { priceOrderId, items: skuItems, customPrices: targetPrices }
                }, { success: false, error: getRuntimeReconnectMessage('核价同意') }, 30000);
                if (result?.success) {
                  addPricingLog(`核价单 ${priceOrderId}: 阈值同意成功`, 'success');
                  acceptCount++;
                } else {
                  addPricingLog(`核价单 ${priceOrderId}: 阈值同意失败: ${result?.error || '未知错误'}`, 'error');
                }
              } else {
                addPricingLog(`核价单 ${priceOrderId}: 系统参考申报价最低 ¥${formatPricingFen(minReferenceFen)} < 阈值最低 ¥${formatPricingFen(minThresholdFen)} -> 重新提报第 ${orderTimes + 1}/${rule.maxResubmitTimes} 次，每次下调 ¥${formatPricingFen(decreaseFen)}`, 'warning');
                const result = await safeRuntimeSendMessage({
                  action: 'resubmitPricing',
                  params: { priceOrderId, items: skuItems, customPrices: targetPrices }
                }, { success: false, error: getRuntimeReconnectMessage('核价重新提报') }, 30000);
                if (result?.success) {
                  pricingState.retryCounts[priceOrderId] = orderTimes + 1;
                  addPricingLog(`核价单 ${priceOrderId}: 重新提报成功`, 'success');
                  resubmitCount++;
                } else {
                  addPricingLog(`核价单 ${priceOrderId}: 重新提报失败: ${result?.error || '未知错误'}`, 'error');
                }
              }
            }
          }
        }

        // 防止请求过快
        await new Promise(r => setTimeout(r, 1000));
        processedCount++;
      }
    }
  }

  addPricingLog(`本轮完成 | 处理${processedCount}条 | 同意${acceptCount} | 重新提报${resubmitCount} | 拒绝${rejectCount} | 跳过${skipCount}`, 'info');
  console.log('[小青蛙电商助手] 本轮核价完成, 处理:', processedCount, '同意:', acceptCount, '重新提报:', resubmitCount, '拒绝:', rejectCount, '跳过:', skipCount);
}

function validatePricingExecutionReadiness() {
  const selectedOrderIds = new Set(getSelectedPricingOrderIds());
  const details = [];
  let runnable = 0;
  let missingThreshold = 0;
  let missingOrderId = 0;
  let notPending = 0;
  const scopeText = selectedOrderIds.size ? `已勾选 ${selectedOrderIds.size} 个核价单` : '当前列表全部核价单';

  for (const item of pricingState.list || []) {
    for (const skc of item.skcList || []) {
      for (const reviewInfo of skc.supplierPriceReviewInfoList || []) {
        const priceOrderId = reviewInfo.priceOrderId;
        if (!priceOrderId) {
          missingOrderId++;
          details.push(`商品 ${item.productId || '-'} / SKC ${getPricingSkcId(skc) || '-'} 缺少核价单ID`);
          continue;
        }
        if (selectedOrderIds.size && !selectedOrderIds.has(String(priceOrderId))) continue;
        const productSkuList = reviewInfo.productSkuList || [];
        if (!productSkuList.length) {
          missingOrderId++;
          details.push(`核价单 ${priceOrderId} 缺少 SKU 明细`);
          continue;
        }
        if (!productSkuList.every(sku => isPricingPendingReviewStatus(getPricingReviewStatus(reviewInfo, sku)))) {
          notPending++;
          details.push(`核价单 ${priceOrderId} 不是卖家待确认状态`);
          continue;
        }
        const missingSku = productSkuList.find(sku => {
          const skuId = getPricingSkuId(sku);
          const value = Number(pricingState.minPriceMap[skuId]);
          return !Number.isFinite(value) || value <= 0;
        });
        if (missingSku) {
          missingThreshold++;
          details.push(`核价单 ${priceOrderId} / SKU ${getPricingSkuId(missingSku) || '-'} 未设置有效阈值`);
          continue;
        }
        runnable++;
      }
    }
  }

  if (selectedOrderIds.size && runnable === 0) {
    return {
      ready: false,
      message: `无法执行核价：${scopeText} 中没有可执行项，请检查阈值、核价单状态和勾选范围`,
      details
    };
  }
  if (!selectedOrderIds.size && runnable === 0) {
    return {
      ready: false,
      message: `无法执行核价：当前列表没有可执行核价单`,
      details
    };
  }
  if (missingThreshold || missingOrderId || notPending) {
    addPricingLog(`执行前校验：可执行 ${runnable} 个，缺阈值 ${missingThreshold} 个，缺核价单 ${missingOrderId} 个，非待确认 ${notPending} 个`, 'info');
  }
  return { ready: true, message: '', details };
}

// 停止自动核价
function stopPricingExecution() {
  pricingState.isRunning = false;
  if (pricingState.runTimer) {
    clearInterval(pricingState.runTimer);
    pricingState.runTimer = null;
  }
  updatePricingRunStatus();
  showToast('⏹ 自动核价已停止', 'warning');
  addPricingLog('自动核价已停止', 'warning');
}

// 更新执行状态显示
function updatePricingRunStatus() {
  const el = document.getElementById('pricingRunStatus');
  if (!el) return;
  if (pricingState.isRunning) {
    el.style.color = '#52c41a';
    el.textContent = `✅ 核价执行中 | ${new Date().toLocaleTimeString()}`;
  } else {
    el.style.color = '#ff4d4f';
    el.textContent = `核价已停止 | 最后执行时间: ${new Date().toLocaleString()}`;
  }
}

// 添加执行日志
function addPricingLog(message, type = 'info') {
  const section = document.getElementById('pricingLogSection');
  const list = document.getElementById('pricingLogList');
  if (!section || !list) return;

  section.style.display = 'block';
  const item = document.createElement('div');
  item.className = `result-item ${type === 'error' ? 'fail' : type === 'success' ? 'success' : ''}`;
  const icons = { info: 'ℹ️', success: '✅', error: '❌', warning: '⚠️' };
  item.innerHTML = `<span class="icon">${icons[type] || 'ℹ️'}</span><span class="reason">[${new Date().toLocaleTimeString()}] ${escapeHtml(message)}</span>`;
  list.prepend(item);

  // 最多保留 100 条日志
  while (list.children.length > 100) {
    list.removeChild(list.lastChild);
  }
}

// 批量设置阈值
async function batchSetThreshold() {
  const selectedChecks = Array.from(document.querySelectorAll('#pricingTableBody .pricing-cb:checked'));
  if (selectedChecks.length === 0) {
    showToast('请先勾选要设置阈值的商品', 'warning');
    return;
  }

  // 弹出输入框
  const threshold = prompt(`为 ${selectedChecks.length} 个商品设置统一的阈值（最低供货价）：`);
  if (threshold === null || isNaN(parseFloat(threshold))) return;

  const val = parseFloat(threshold);
  if (val <= 0) { showToast('阈值必须大于0', 'error'); return; }

  // 收集需要更新阈值的 SKU
  const updateItems = [];
  selectedChecks.forEach(cb => {
    const skuId = cb.dataset.sku;
    if (skuId) {
      pricingState.minPriceMap[skuId] = val;
      updateItems.push({
        mallId: '',
        skuId: skuId,
        skcId: cb.dataset.skcid,
        minPrice: val
      });
    }
  });

  if (updateItems.length === 0) return;

  showToast('正在保存阈值...', 'info');
  const result = await safeRuntimeSendMessage({
    action: 'batchSaveMinPrice',
    params: { items: updateItems }
  }, { success: false, error: getRuntimeReconnectMessage('阈值保存') }, 30000);

  if (result?.success) {
    showToast(`✅ 成功设置 ${updateItems.length} 个 SKU 的阈值`, 'success');
    renderPricingTable();
  } else {
    showToast(`❌ ${result?.error || '设置失败'}`, 'error');
  }
}

// 导出未审商品（Excel 格式，参考大白鹅）
function exportPricingData() {
  if (pricingState.list.length === 0) {
    showToast('暂无数据可导出', 'warning');
    return;
  }

  // 和核价表格保持同一数据口径：supplierPriceReviewInfoList -> productSkuList。
  const rows = [];
  pricingState.list.forEach(item => {
    const productName = item.productName || '-';
    const spuId = item.productId || '-';
    (item.skcList || []).forEach(skc => {
      const skcId = getPricingSkcId(skc) || '-';
      const skcCode = skc.extCode || skc.skcExtCode || '';

      (skc.supplierPriceReviewInfoList || []).forEach(reviewInfo => {
        const priceOrderId = String(reviewInfo.priceOrderId || '');
        const suggestPrice = getPricingReferencePriceText(reviewInfo);
        const times = reviewInfo.times || 0;

        (reviewInfo.productSkuList || []).forEach(sku => {
          const skuId = getPricingSkuId(sku) || '-';
          const specs = (sku.productPropertyList || [])
            .map(p => `${p.name || ''}${p.name ? ': ' : ''}${p.value || ''}`)
            .filter(Boolean)
            .join(', ');
          const skuCode = sku.extCode || sku.skuExtCode || '';
          const supplierPrice = getPricingSupplierPriceText(sku, skc, item);
          const currentThreshold = pricingState.minPriceMap[skuId];
          const threshold = currentThreshold !== undefined ? currentThreshold : '';

          rows.push({
            '商品名称': productName,
            'SPU': String(spuId),
            'SKC': String(skcId),
            'SKU': String(skuId),
            'SKU属性': specs,
            'SKC货号': skcCode || '—',
            'SKU货号': skuCode,
            '原申报价': supplierPrice,
            [getPricingReferencePriceLabel(reviewInfo, sku)]: getPricingReferencePriceText(reviewInfo, sku) || suggestPrice,
            '参考价': getPricingReferencePriceText(reviewInfo, sku) || suggestPrice,
            '参考价字段': getPricingReferencePriceLabel(reviewInfo, sku),
            '参考价来源': getPricingReferencePriceSourceKey(reviewInfo, sku),
            '阈值': threshold,
            '核价次数': times,
            '核价单ID': priceOrderId
          });
        });
      });
    });
  });

  if (!rows.length) {
    showToast('当前核价列表没有可导出的 SKU', 'warning');
    return;
  }

  const fileName = `核价商品_${new Date().toISOString().slice(0, 10)}.xlsx`;
  const fields = [
    { key: 'productName', label: '商品名称' },
    { key: 'spuId', label: 'SPU' },
    { key: 'skcId', label: 'SKC' },
    { key: 'skuId', label: 'SKU' },
    { key: 'skuSpecs', label: 'SKU属性' },
    { key: 'skcCode', label: 'SKC货号' },
    { key: 'skuCode', label: 'SKU货号' },
    { key: 'supplierPrice', label: '原申报价' },
    { key: 'suggestPrice', label: '参考价' },
    { key: 'referencePriceLabel', label: '参考价字段' },
    { key: 'referencePriceSource', label: '参考价来源' },
    { key: 'threshold', label: '阈值' },
    { key: 'times', label: '核价次数' },
    { key: 'priceOrderId', label: '核价单ID' }
  ];
  const xlsxRows = rows.map(row => ({
    productName: row['商品名称'],
    spuId: row['SPU'],
    skcId: row['SKC'],
    skuId: row['SKU'],
    skuSpecs: row['SKU属性'],
    skcCode: row['SKC货号'],
    skuCode: row['SKU货号'],
    supplierPrice: row['原申报价'],
    suggestPrice: row['参考价'],
    referencePriceLabel: row['参考价字段'],
    referencePriceSource: row['参考价来源'],
    threshold: row['阈值'],
    times: row['核价次数'],
    priceOrderId: row['核价单ID']
  }));
  const { blob } = createTemuXlsxBlob({ sheetName: '核价商品', fields, rows: xlsxRows });
  downloadBlobFile(fileName, blob);
  showToast(`📥 导出成功，共 ${rows.length} 个 SKU`, 'success');
}

async function handlePricingImport(e) {
  if (pricingState.list.length === 0) {
    showToast('请先加载核价商品列表', 'warning');
    e.target.value = '';
    return;
  }
  const file = e.target.files[0];
  if (!file) return;

  try {
    const XLSX = await ensureXlsxLoaded();
    const data = await file.arrayBuffer();
    const wb = XLSX.read(data, { type: 'array' });
    const ws = wb.Sheets[wb.SheetNames[0]];
    const rows = XLSX.utils.sheet_to_json(ws);

    if (rows.length === 0) {
      showToast('表格中没有数据', 'warning');
      return;
    }

    // 按 SKU 匹配阈值
    let matched = 0;
    rows.forEach(row => {
      const skuId = String(row['SKU'] || row['sku'] || '').trim();
      const threshold = parseFloat(row['阈值'] || row['threshold'] || 0);

      if (skuId && threshold > 0) {
        pricingState.minPriceMap[skuId] = threshold;
        matched++;
      }
    });

    if (matched > 0) {
      renderPricingTable();
      showToast(`✅ 成功导入 ${matched} 个 SKU 的阈值`, 'success');
    } else {
      showToast('未匹配到任何 SKU，请检查表格格式', 'warning');
    }
  } catch (err) {
    showToast(`导入失败: ${err.message || err}`, 'error');
    console.error('[小青蛙电商助手] 导入失败:', err);
  }

  // 清空 file input 以便再次选择同一文件
  e.target.value = '';
}

// 初始化核价 Tab 事件
function getSelectedPricingOrderIds() {
  const cbs = Array.from(document.querySelectorAll('#pricingTableBody .pricing-cb:checked'));
  return Array.from(new Set(cbs.map(cb => String(cb.dataset.poid || '').trim()).filter(Boolean)));
}

function getSelectedPricingRows() {
  return Array.from(document.querySelectorAll('#pricingTableBody .pricing-cb:checked'))
    .map(cb => ({
      priceOrderId: String(cb.dataset.poid || '').trim(),
      skuId: String(cb.dataset.sku || '').trim()
    }))
    .filter(row => row.priceOrderId && row.skuId && row.skuId !== '-');
}

function groupSelectedPricingRowsByOrder(rows = []) {
  const grouped = new Map();
  for (const row of rows) {
    if (!grouped.has(row.priceOrderId)) grouped.set(row.priceOrderId, new Set());
    grouped.get(row.priceOrderId).add(row.skuId);
  }
  return Array.from(grouped.entries()).map(([priceOrderId, skuIds]) => ({
    priceOrderId,
    skuIds: Array.from(skuIds)
  }));
}

function syncPricingThresholdInputsFromDom() {
  document.querySelectorAll('.pricing-threshold-input').forEach(input => {
    const skuId = String(input.dataset.skuid || '').trim();
    const price = Number(input.value);
    if (skuId && Number.isFinite(price) && price > 0) {
      pricingState.minPriceMap[skuId] = price;
    }
  });
}

function findPricingReviewInfoByOrderId(priceOrderId) {
  const target = String(priceOrderId || '');
  for (const item of pricingState.list || []) {
    for (const skc of item.skcList || []) {
      for (const reviewInfo of skc.supplierPriceReviewInfoList || []) {
        if (String(reviewInfo.priceOrderId || '') === target) return reviewInfo;
      }
    }
  }
  return null;
}

function buildPricingApprovalPayload(reviewInfo, selectedSkuIds = null) {
  const productSkuList = Array.isArray(reviewInfo?.productSkuList) ? reviewInfo.productSkuList : [];
  const selectedSet = selectedSkuIds ? new Set(selectedSkuIds.map(id => String(id || '').trim()).filter(Boolean)) : null;
  const skuItems = [];
  const customPrices = {};

  for (const sku of productSkuList) {
    const skuId = getPricingSkuId(sku);
    if (!skuId) continue;
    if (selectedSet && !selectedSet.has(skuId)) continue;
    const referenceFen = getPricingReferencePriceFen(reviewInfo, sku);
    skuItems.push({
      ...sku,
      skuId,
      productSkuId: sku.productSkuId || skuId,
      suggestSupplyPrice: referenceFen
    });
    const price = Number(pricingState.minPriceMap[skuId]);
    if (Number.isFinite(price) && price > 0) {
      customPrices[skuId] = price;
    }
  }

  return { skuItems, customPrices };
}

async function handleBatchApprovePricing(event) {
  event?.preventDefault?.();
  event?.stopImmediatePropagation?.();
  syncPricingThresholdInputsFromDom();

  const selectedGroups = groupSelectedPricingRowsByOrder(getSelectedPricingRows());
  if (!selectedGroups.length) {
    showToast('请先勾选要同意核价的商品', 'warning');
    return;
  }

  let success = 0;
  let fail = 0;
  for (const { priceOrderId, skuIds } of selectedGroups) {
    const reviewInfo = findPricingReviewInfoByOrderId(priceOrderId);
    if (!reviewInfo) {
      fail++;
      addPricingLog(`核价单 ${priceOrderId}: 未找到核价明细`, 'error');
      continue;
    }

    const { skuItems, customPrices } = buildPricingApprovalPayload(reviewInfo, skuIds);
    if (!skuItems.length) {
      fail++;
      addPricingLog(`核价单 ${priceOrderId}: 缺少SKU明细`, 'error');
      continue;
    }
    const missingReferenceItem = skuItems.find(item => !item.suggestSupplyPrice && !customPrices[item.skuId]);
    if (missingReferenceItem) {
      fail++;
      addPricingLog(getPricingReferenceMissingMessage(priceOrderId), 'error');
      continue;
    }

    const result = await safeRuntimeSendMessage({
      action: 'approvePricing',
      params: { priceOrderId, items: skuItems, customPrices }
    }, { success: false, error: getRuntimeReconnectMessage('核价批量同意') }, 30000);
    if (result?.success) {
      success++;
      addPricingLog(`核价单 ${priceOrderId}: 已按勾选同意 ${skuItems.length} 个SKU`, 'success');
    } else {
      fail++;
      addPricingLog(`核价单 ${priceOrderId}: 批量同意失败: ${result?.error || '未知错误'}`, 'error');
    }
    await new Promise(resolve => setTimeout(resolve, 500));
  }

  showToast(`批量同意完成：${success} 成功 / ${fail} 失败`, success ? 'success' : 'error');
  refreshPricingList();
}

async function handleBatchVoidPricing(event) {
  event?.preventDefault?.();
  event?.stopImmediatePropagation?.();

  const selectedGroups = groupSelectedPricingRowsByOrder(getSelectedPricingRows());
  if (!selectedGroups.length) {
    showToast('请先勾选要作废核价的商品', 'warning');
    return;
  }

  let success = 0;
  let fail = 0;
  for (const { priceOrderId, skuIds } of selectedGroups) {
    const result = await safeRuntimeSendMessage({
      action: 'voidPricing',
      params: { priceOrderId }
    }, { success: false, error: getRuntimeReconnectMessage('核价批量作废') }, 30000);
    if (result?.success) {
      success++;
      addPricingLog(`核价单 ${priceOrderId}: 批量拒绝成功（勾选 ${skuIds.length} 个SKU）`, 'success');
    } else {
      fail++;
      addPricingLog(`核价单 ${priceOrderId}: 批量拒绝失败: ${result?.error || '未知错误'}`, 'error');
    }
    await new Promise(resolve => setTimeout(resolve, 500));
  }

  showToast(`批量拒绝完成：${success} 成功 / ${fail} 失败`, success ? 'success' : 'error');
  refreshPricingList();
}

function initPricingTab() {
  const initialContainer = document.getElementById('pricingTableBody');
  if (initialContainer && !pricingState.list.length) {
    initialContainer.innerHTML = '<tr><td colspan="11" class="empty">打开核价 TAB 后会显示当前店铺本地临时缓存；如需最新数据，请点击“加载全部待核价商品”。</td></tr>';
  }
  document.getElementById('pricingBatchSetBtn')?.addEventListener('click', batchSetThreshold, true);
  document.getElementById('pricingBatchApproveBtn')?.addEventListener('click', handleBatchApprovePricing, true);
  document.getElementById('pricingBatchVoidBtn')?.addEventListener('click', handleBatchVoidPricing, true);
  document.getElementById('pricingRuleBtn')?.addEventListener('click', showPricingRuleModal, true);
  document.getElementById('pricingDiagnoseReferenceBtn')?.addEventListener('click', diagnosePricingReferenceCapture, true);
  // 加载全部（循环加载所有页，合并结果）
  document.getElementById('pricingLoadAllBtn')?.addEventListener('click', async () => {
    await loadAllPricingProducts({ applyCurrentSearch: true }).catch(() => {});
  });

  const runPricingSearch = async () => {
    if (!getPricingSearchTerms().length) {
      applyPricingSearchFilter({ autoSelect: false });
      return;
    }
    if (!Array.isArray(pricingState.allList) || pricingState.allList.length === 0) {
      showToast('正在先加载待核价商品，加载完成后自动筛选 SKC...', 'info');
      await loadAllPricingProducts({ applyCurrentSearch: true }).catch(() => {});
      return;
    }
    applyPricingSearchFilter({ autoSelect: true });
  };

  document.getElementById('pricingSearchBtn')?.addEventListener('click', runPricingSearch, true);
  document.getElementById('pricingSearchInput')?.addEventListener('keydown', (event) => {
    if (event.key !== 'Enter') return;
    event.preventDefault();
    runPricingSearch();
  });
  document.getElementById('pricingClearSearchBtn')?.addEventListener('click', () => {
    const input = document.getElementById('pricingSearchInput');
    if (input) input.value = '';
    applyPricingSearchFilter({ autoSelect: false });
  }, true);

  // 导出
  const pricingExportBtn = document.getElementById('pricingExportBtn');
  if (pricingExportBtn && pricingExportBtn.dataset.temuExportBound !== '1') {
    pricingExportBtn.dataset.temuExportBound = '1';
    pricingExportBtn.addEventListener('click', exportPricingData);
  }

  // 导入阈值
  const pricingImportBtn = document.getElementById('pricingImportBtn');
  if (pricingImportBtn && pricingImportBtn.dataset.temuImportBound !== '1') {
    pricingImportBtn.dataset.temuImportBound = '1';
    pricingImportBtn.addEventListener('click', (event) => {
      event.preventDefault();
      const input = document.getElementById('pricingImportFile');
      if (!input) return;
      input.focus({ preventScroll: true });
      input.value = '';
      input.click();
    });
  }
  const pricingImportFile = document.getElementById('pricingImportFile');
  if (pricingImportFile && pricingImportFile.dataset.temuImportChangeBound !== '1') {
    pricingImportFile.dataset.temuImportChangeBound = '1';
    pricingImportFile.addEventListener('change', handlePricingImport);
  }

  // 开始/停止执行
  document.getElementById('pricingStartBtn')?.addEventListener('click', startPricingExecution);
  document.getElementById('pricingStopBtn')?.addEventListener('click', stopPricingExecution);
}

function bindTemuImportExportFallbacks() {
  if (window.__XQW_TEMU_IMPORT_EXPORT_FALLBACKS_BOUND__) return;
  window.__XQW_TEMU_IMPORT_EXPORT_FALLBACKS_BOUND__ = true;
  document.addEventListener('click', (event) => {
    const pricingExport = event.target?.closest?.('#pricingExportBtn');
    if (pricingExport) {
      if (pricingExport.dataset.temuExportBound === '1') return;
      event.preventDefault();
      try { exportPricingData(); }
      catch (error) {
        console.error('[小青蛙电商助手] 核价导出兜底触发失败:', error);
        showToast(`导出失败：${error?.message || error}`, 'error');
      }
      return;
    }

    const pricingImport = event.target?.closest?.('#pricingImportBtn');
    if (pricingImport) {
      if (pricingImport.dataset.temuImportBound === '1') return;
      event.preventDefault();
      const input = document.getElementById('pricingImportFile');
      if (!input) {
        showToast('导入文件选择器未初始化，请刷新页面后重试', 'error');
        return;
      }
      input.value = '';
      input.click();
      return;
    }

    const salesExport = event.target?.closest?.('#salesExportBtn');
    if (salesExport) {
      if (salesExport.dataset.temuSalesExportBound === '1') return;
      event.preventDefault();
      void exportSalesData();
    }
  }, true);

  document.addEventListener('change', (event) => {
    if (event.target?.id !== 'pricingImportFile') return;
    if (event.target.dataset.temuImportChangeBound === '1') return;
    void handlePricingImport(event);
  }, true);
}

// ========== 定制品信息填写功能 ==========

// 定制品商品列表状态
const customizedProductState = {
  products: [],
  allProducts: [],
  viewProducts: [],
  pageNum: 1,
  pageSize: 20,
  totalPages: 0,
  total: 0,
  sourceTotal: 0,
  cacheLoaded: false,
  syncedAt: '',
  statusCounts: {},
  cacheKey: '',
  cacheVersion: 11,
  selectedKeys: new Set()
};

function getCustomizedSearchQuery() {
  return document.getElementById('customizedProductSearch')?.value?.trim() || '';
}

function getCustomizedStatusFilter() {
  return document.getElementById('customizedProductStatusFilter')?.value || '';
}

function getCustomizedStatusTopStatus(value = getCustomizedStatusFilter()) {
  const raw = String(value ?? '').trim();
  if (!raw) return null;
  const num = Number(raw);
  if ([0, 100, 200, 300].includes(num)) return num;
  if (raw.includes('在售')) return 100;
  if (raw.includes('未发布')) return 0;
  if (raw.includes('下架') || raw.includes('终止')) return 200;
  if (raw.includes('删除')) return 300;
  return null;
}

function getCustomizedStatusLabel(topStatus) {
  const map = {
    0: '未发布到站点',
    100: '在售中',
    200: '已下架/已终止',
    300: '已删除'
  };
  return map[Number(topStatus)] || '全部';
}

function collectTemuUploadTemplateOuterPackageImagesFromSources(sources = []) {
  const rows = [];
  const push = (item) => {
    const url = getTemuUploadTemplateImageUrlFromValue(item);
    if (!url) return;
    if (rows.some(row => row.imageUrl === url || row.signedImageUrl === url)) return;
    const row = item && typeof item === 'object' && !Array.isArray(item)
      ? cloneTemuUploadJsonSafe(item)
      : {};
    rows.push({
      ...row,
      imageUrl: row.imageUrl || url,
      signedImageUrl: row.signedImageUrl || url
    });
  };
  const pushList = (source) => {
    if (!source) return;
    (Array.isArray(source) ? source : [source]).forEach(push);
  };
  sources.forEach(pushList);
  return rows;
}

function getCustomizedProductCacheKey(mallId = state.mallId) {
  return mallId ? `temuCustomizedProductCache_${mallId}` : 'temuCustomizedProductCache';
}

function buildCustomizedStatusBadgesFromRecord(product = {}) {
  const badges = [];
  const isTruthyFlag = value => value === true || value === 1 || value === '1' || value === 'true';
  const isOnSale = Number(product.skcTopStatus) === 100;
  const orangeBadgeStyle = { color: '#ff6800', backgroundColor: '#fff4e6', borderColor: '#ff6800' };
  const blueBadgeStyle = { color: '#0071f3', backgroundColor: '#e6f6ff', borderColor: '#0071f3' };
  const redBadgeStyle = { color: '#dc2626', backgroundColor: '#fee2e2', borderColor: '#dc2626' };
  const skuList = Array.isArray(product.skuList) ? product.skuList : [];
  const hasCustomizationTemplate = skuList.some(sku => {
    const tmpl = sku?.productSkuCustomizationTmpl || sku?.productSkuSaleExtAttr?.productSkuCustomizationTmpl;
    return Boolean(tmpl && (!Array.isArray(tmpl.templateIds) || tmpl.templateIds.length > 0));
  });
  const personalization = product.productPersonalization || {};
  const hasPersonalizationEntry = Number(personalization.personalizationSwitch) === 1 || hasCustomizationTemplate;
  const autoDeleteDays = Number(product.autoDeleteRemainingDays);

  if (Number.isFinite(autoDeleteDays) && autoDeleteDays > 0) {
    badges.push({ text: `${autoDeleteDays}天后自动废弃`, ...orangeBadgeStyle });
  } else if (Number(product.productCertAuditStatus) === 1 || isTruthyFlag(product.offSaleByNoCert) || isTruthyFlag(product.noCertPunishRecord)) {
    badges.push({ text: '资质待上传', ...orangeBadgeStyle });
  }

  if (isOnSale) {
    badges.push({ text: '在售', ...blueBadgeStyle });
  }

  if (hasPersonalizationEntry) {
    badges.push({ text: '定制商品', ...(hasCustomizationTemplate ? blueBadgeStyle : orangeBadgeStyle) });
  }

  if (Number(product.skcTopStatus) === 200 && !(Number.isFinite(autoDeleteDays) && autoDeleteDays > 0)) {
    badges.push({ text: '已下架/已终止', ...redBadgeStyle });
  }

  if (Number(product.skcTopStatus) === 300) {
    badges.push({ text: '已删除', ...redBadgeStyle });
  }

  return badges;
}

function normalizeCustomizedProductRecord(product = {}) {
  const topStatus = Number(product.skcTopStatus ?? product.topStatus ?? '');
  const statusName = getCustomizedStatusLabel(topStatus);
  const skuList = Array.isArray(product.skuList) ? product.skuList : [];
  const skuQuantityDetailList = Array.isArray(product.skuQuantityDetailList) ? product.skuQuantityDetailList : [];
  const raw = product.raw && typeof product.raw === 'object' ? product.raw : {};
  const productWhExtAttr = product.productWhExtAttr || product.productWhExtAttrReq || raw.productWhExtAttr || raw.productWhExtAttrReq || null;
  const productOuterPackage = product.productOuterPackage || product.productOuterPackageReq || raw.productOuterPackage || raw.productOuterPackageReq || productWhExtAttr?.productOuterPackage || productWhExtAttr?.outerPackage || null;
  const outerPackageImages = collectTemuUploadTemplateOuterPackageImagesFromSources([
    product.outerPackageImages,
    product.productOuterPackageImages,
    product.productOuterPackageImageReqs,
    product.productOuterPackageImageList,
    product.outerPackageImageReqs,
    product.outerPackageImageList,
    product.packageImageList,
    raw.outerPackageImages,
    raw.productOuterPackageImages,
    raw.productOuterPackageImageReqs,
    raw.productOuterPackageImageList,
    raw.outerPackageImageReqs,
    raw.outerPackageImageList,
    raw.packageImageList,
    productWhExtAttr?.outerPackageImages,
    productWhExtAttr?.productOuterPackageImages,
    productWhExtAttr?.productOuterPackageImageReqs,
    productWhExtAttr?.productOuterPackageImageList,
    productWhExtAttr?.outerPackageImageReqs,
    productWhExtAttr?.outerPackageImageList,
    productWhExtAttr?.packageImageList,
    productOuterPackage?.outerPackageImages,
    productOuterPackage?.productOuterPackageImages,
    productOuterPackage?.productOuterPackageImageReqs,
    productOuterPackage?.productOuterPackageImageList,
    productOuterPackage?.outerPackageImageReqs,
    productOuterPackage?.outerPackageImageList,
    productOuterPackage?.packageImageList,
    productOuterPackage?.packageImages,
    productOuterPackage?.imageList,
    productOuterPackage?.images
  ]);
  return {
    goodsId: product.goodsId || product.spuId || '',
    productId: product.productId || product.skcId || '',
    productName: product.productName || '-',
    mainImage: product.mainImage || '',
    productStatus: product.productStatus ?? '',
    skcStatus: Number(product.skcStatus ?? 0),
    skcSiteStatus: Number(product.skcSiteStatus ?? 0),
    removeStatus: Number(product.removeStatus ?? 0),
    skcTopStatus: Number.isFinite(topStatus) ? topStatus : 0,
    lifecycleStage: Number(product.lifecycleStage ?? topStatus ?? 0),
    lifecycleStageName: product.lifecycleStageName || statusName,
    productStatusName: product.productStatusName || statusName,
    statusName,
    price: product.price || '',
    skcId: product.skcId || '',
    skcExtCode: product.skcExtCode || '',
    skuCount: Number(product.skuCount || skuList.length || skuQuantityDetailList.length || 0),
    categoryId: product.categoryId || '',
    categoryName: product.categoryName || '',
    createTime: product.createTime || '',
    updateTime: product.updateTime || '',
    onlineTime: product.onlineTime || '',
    score: product.score || '',
    skuDisplayName: product.skuDisplayName || '',
    skuCode: product.skuCode || '',
    productPropertyList: Array.isArray(product.productPropertyList) ? product.productPropertyList : [],
    skuName: product.skuName || product.productName || '-',
    skuSpecText: product.skuSpecText || '',
    saleStatusText: product.saleStatusText || '',
    onSalesDuration: Number(product.onSalesDuration || 0),
    productCertAuditStatus: Number(product.productCertAuditStatus || 0),
    offSaleByNoCert: product.offSaleByNoCert ?? '',
    noCertPunishRecord: product.noCertPunishRecord ?? '',
    productPersonalization: product.productPersonalization || {},
    ...(productWhExtAttr && typeof productWhExtAttr === 'object' && !Array.isArray(productWhExtAttr) ? { productWhExtAttr: cloneTemuUploadJsonSafe(productWhExtAttr) } : {}),
    ...(productOuterPackage && typeof productOuterPackage === 'object' && !Array.isArray(productOuterPackage) ? { productOuterPackage: cloneTemuUploadJsonSafe(productOuterPackage) } : {}),
    ...(outerPackageImages.length ? { outerPackageImages: cloneTemuUploadJsonSafe(outerPackageImages) } : {}),
    ...(product.raw ? { raw: cloneTemuUploadJsonSafe(product.raw) } : {}),
    autoDeleteRemainingDays: product.autoDeleteRemainingDays ?? null,
    skuList,
    skuQuantityDetailList,
    warehouseInfoList: Array.isArray(product.warehouseInfoList) ? product.warehouseInfoList : [],
    statusBadges: Array.isArray(product.statusBadges) ? product.statusBadges : []
  };
}

function mergeCustomizedExactBadges(products = [], exactProducts = []) {
  const exactMap = new Map();
  exactProducts.forEach(product => {
    const normalized = normalizeCustomizedProductRecord(product);
    const key = String(normalized.goodsId || '').trim();
    if (key && Array.isArray(normalized.statusBadges)) {
      exactMap.set(key, normalized);
    }
  });

  return products.map(product => {
    const key = String(product.goodsId || '').trim();
    const exact = exactMap.get(key);
    if (!exact) return product;
    return {
      ...product,
      mainImage: exact.mainImage || product.mainImage,
      productName: exact.productName || product.productName,
      statusBadges: exact.statusBadges || [],
      badgeSource: 'temu-goods-list-dom'
    };
  });
}

function findCustomizedTemplateDataLocally(spuId) {
  const product = (customizedProductState.allProducts || []).find(item => (
    String(item.goodsId || '') === String(spuId)
    || String(item.productId || '') === String(spuId)
    || String(item.skcId || '') === String(spuId)
  ));
  if (!product) return null;
  const skuList = Array.isArray(product.skuList) ? product.skuList : [];
  const allSkuIds = skuList.map(sku => sku.skuId || sku.productSkuId || '').filter(Boolean);
  const foundSkus = skuList
    .map(sku => {
      const tmpl = sku.productSkuCustomizationTmpl || sku.productSkuSaleExtAttr?.productSkuCustomizationTmpl || null;
      const templateIds = Array.isArray(tmpl?.templateIds)
        ? tmpl.templateIds
        : [tmpl?.templateId, tmpl?.template_id, tmpl?.id, sku.templateId].filter(Boolean);
      return {
        productSkuId: sku.skuId || sku.productSkuId || '',
        templateId: templateIds[0] || '',
        templateIds,
        specInfo: sku.skuSpec || sku.specInfo || ''
      };
    })
    .filter(sku => sku.productSkuId && (sku.templateId || sku.templateIds.length));
  if (!allSkuIds.length && !foundSkus.length) return null;
  return {
    productId: product.productId || product.skcId || product.goodsId || spuId,
    goodsId: product.goodsId || spuId,
    allSkuIds,
    foundSkus
  };
}

function computeCustomizedStatusCounts(products = []) {
  const counts = { all: products.length, 0: 0, 100: 0, 200: 0, 300: 0 };
  products.forEach(product => {
    const topStatus = Number(product.skcTopStatus ?? product.topStatus ?? 0);
    if (topStatus === 0 || topStatus === 100 || topStatus === 200 || topStatus === 300) {
      counts[topStatus] += 1;
    }
  });
  return counts;
}

function renderCustomizedStatusTabCounts() {
  const counts = customizedProductState.statusCounts || {};
  const tabs = document.querySelectorAll('#customizedProductStatusTabs .status-tab');
  tabs.forEach(tab => {
    const label = tab.dataset.label || tab.textContent.replace(/\s*\d+$/, '').trim();
    const stage = tab.dataset.stage || '';
    const count = stage === ''
      ? Number(counts.all || customizedProductState.sourceTotal || customizedProductState.allProducts.length || 0)
      : Number(counts[stage] || 0);
    tab.textContent = `${label}${count ? ` ${count}` : ''}`;
  });
}

function compactCustomizedCacheText(value, maxLength = 160) {
  const text = String(value ?? '').trim();
  return text.length > maxLength ? text.slice(0, maxLength) : text;
}

function compactCustomizedProductForCache(product = {}) {
  const skuList = Array.isArray(product.skuList) ? product.skuList : [];
  return {
    goodsId: compactCustomizedCacheText(product.goodsId, 40),
    productId: compactCustomizedCacheText(product.productId, 40),
    productName: compactCustomizedCacheText(product.productName, 180),
    mainImage: compactCustomizedCacheText(product.mainImage, 260),
    imageList: Array.isArray(product.imageList)
      ? product.imageList.slice(0, 8).map(url => compactCustomizedCacheText(url, 360)).filter(Boolean)
      : [],
    productSkcImageExtList: Array.isArray(product.productSkcImageExtList)
      ? product.productSkcImageExtList.slice(0, 8).map(item => ({
        imageUrl: compactCustomizedCacheText(item?.imageUrl || item?.url || item?.imgUrl, 360)
      })).filter(item => item.imageUrl)
      : [],
    productPropertyList: Array.isArray(product.productPropertyList)
      ? product.productPropertyList.slice(0, 80).map(item => {
        if (!item || typeof item !== 'object') return item;
        return Object.fromEntries(Object.entries(item).map(([key, value]) => [
          key,
          typeof value === 'string' ? compactCustomizedCacheText(value, 260) : value
        ]));
      })
      : [],
    skcTopStatus: Number(product.skcTopStatus || 0),
    lifecycleStageName: compactCustomizedCacheText(product.lifecycleStageName || product.statusName, 40),
    productStatusName: compactCustomizedCacheText(product.productStatusName || product.statusName, 40),
    statusName: compactCustomizedCacheText(product.statusName, 40),
    skuCount: Number(product.skuCount || skuList.length || 0),
    createTime: compactCustomizedCacheText(product.createTime, 40),
    statusBadges: (Array.isArray(product.statusBadges) ? product.statusBadges : []).slice(0, 6).map(badge => ({
      text: compactCustomizedCacheText(badge?.text, 30),
      color: compactCustomizedCacheText(badge?.color, 40),
      backgroundColor: compactCustomizedCacheText(badge?.backgroundColor, 40),
      borderColor: compactCustomizedCacheText(badge?.borderColor, 40)
    })),
    skuList: skuList.slice(0, 20).map(sku => ({
      skuId: compactCustomizedCacheText(sku?.skuId, 40),
      skuSpec: compactCustomizedCacheText(sku?.skuSpec, 120),
      skuImage: compactCustomizedCacheText(sku?.skuImage, 360),
      imageUrl: compactCustomizedCacheText(sku?.imageUrl, 360),
      thumbUrl: compactCustomizedCacheText(sku?.thumbUrl, 360),
      productSkuImage: compactCustomizedCacheText(sku?.productSkuImage, 360),
      price: compactCustomizedCacheText(sku?.price, 40),
      stockText: compactCustomizedCacheText(sku?.stockText, 40),
      showStockText: compactCustomizedCacheText(sku?.showStockText, 40),
      createTime: compactCustomizedCacheText(sku?.createTime || product.createTime, 40)
    }))
  };
}

async function persistCustomizedProductCache() {
  const key = getCustomizedProductCacheKey();
  const payload = {
    badgeRuleVersion: customizedProductState.cacheVersion || 2,
    syncedAt: customizedProductState.syncedAt || '',
    sourceTotal: customizedProductState.sourceTotal || 0,
    statusCounts: customizedProductState.statusCounts || {},
    items: (customizedProductState.allProducts || []).map(compactCustomizedProductForCache)
  };
  try {
    await safeStorageSet({ [key]: payload }, '定制品缓存');
    await saveTemuSnapshot('customized', payload, {
      itemCount: (customizedProductState.allProducts || []).length
    });
    return true;
  } catch (err) {
    console.warn('[小青蛙电商助手] 定制品缓存超过浏览器配额，改为只保存同步摘要:', err);
    await saveTemuSnapshot('customized', payload, {
      itemCount: (customizedProductState.allProducts || []).length
    });
    try {
      await chrome.storage.local.remove(key);
      await safeStorageSet({
        [key]: {
          badgeRuleVersion: customizedProductState.cacheVersion || 2,
          syncedAt: customizedProductState.syncedAt || '',
          sourceTotal: customizedProductState.sourceTotal || 0,
          statusCounts: customizedProductState.statusCounts || {},
          items: []
        }
      }, '定制品同步摘要');
    } catch (summaryErr) {
      console.warn('[小青蛙电商助手] 定制品同步摘要保存失败，本次仅保留内存数据:', summaryErr);
    }
    return false;
  }
}

async function loadCustomizedProductCache() {
  const key = getCustomizedProductCacheKey();
  const serverCache = await fetchTemuSnapshot('customized');
  if (serverCache && Array.isArray(serverCache.items) && Number(serverCache.badgeRuleVersion || 0) === Number(customizedProductState.cacheVersion || 2)) {
    customizedProductState.cacheLoaded = true;
    customizedProductState.cacheKey = key;
    customizedProductState.allProducts = serverCache.items.map(normalizeCustomizedProductRecord);
    customizedProductState.products = customizedProductState.allProducts.slice();
    customizedProductState.viewProducts = customizedProductState.allProducts.slice();
    customizedProductState.sourceTotal = Number(serverCache.sourceTotal || customizedProductState.allProducts.length || 0);
    customizedProductState.syncedAt = serverCache.syncedAt || '';
    customizedProductState.statusCounts = serverCache.statusCounts || computeCustomizedStatusCounts(customizedProductState.allProducts);
    customizedProductState.total = customizedProductState.viewProducts.length;
    customizedProductState.totalPages = Math.max(1, Math.ceil(customizedProductState.total / Math.max(1, customizedProductState.pageSize || 20)));
    updateTemuSnapshotSyncMeta('customized', serverCache.syncedAt);
    return true;
  }
  customizedProductState.cacheLoaded = true;
  customizedProductState.cacheKey = key;
  customizedProductState.allProducts = [];
  customizedProductState.products = [];
  customizedProductState.viewProducts = [];
  customizedProductState.sourceTotal = 0;
  customizedProductState.syncedAt = '';
  customizedProductState.statusCounts = {};
  customizedProductState.total = 0;
  customizedProductState.totalPages = 1;
  updateTemuSnapshotSyncMeta('customized', null);
  return false;
}

function matchesCustomizedProduct(product = {}, query = '') {
  if (!query) return true;
  const terms = parseTemuSearchTerms(query);
  const statusBadgeText = Array.isArray(product.statusBadges)
    ? product.statusBadges.map(b => b.text).join(' ')
    : '';
  const skuList = Array.isArray(product.skuList) ? product.skuList : [];
  return matchesAnyTemuSearchTerm([
    product.goodsId,
    product.productId,
    product.skcId,
    product.productName,
    product.skuName,
    product.skuId,
    product.skuSpec,
    product.skuCode,
    product.statusName,
    product.lifecycleStageName,
    product.skcTopStatus,
    product.skcStatus,
    product.skcSiteStatus,
    product.removeStatus,
    statusBadgeText,
    product.mainImage,
    product.skuCount,
    product.createTime,
    ...skuList.flatMap(sku => [
      sku?.skuId,
      sku?.productSkuId,
      sku?.skuCode,
      sku?.extCode,
      sku?.skuSpec,
      sku?.specInfo
    ])
  ], terms);
}

function getCustomizedFilteredProducts() {
  const source = Array.isArray(customizedProductState.viewProducts)
    ? customizedProductState.viewProducts
    : (Array.isArray(customizedProductState.allProducts) ? customizedProductState.allProducts : customizedProductState.products);
  return source || [];
}

function getCustomizedDisplayProducts() {
  const source = getCustomizedFilteredProducts();
  const pageNum = Math.max(1, customizedProductState.pageNum || 1);
  const pageSize = Math.max(1, customizedProductState.pageSize || 20);
  const start = (pageNum - 1) * pageSize;
  return source.slice(start, start + pageSize);
}

const CUSTOMIZED_EXPORT_FIELDS = [
  { key: 'productName', label: '商品名称' },
  { key: 'spuId', label: 'SPU ID' },
  { key: 'skcImage', label: 'SKC图片' },
  { key: 'skcImageUrl', label: 'SKC图片URL' },
  { key: 'skcImageStatus', label: 'SKC图片状态' },
  { key: 'skcId', label: 'SKC ID' },
  { key: 'skcCode', label: 'SKC货号' },
  { key: 'skuImage', label: 'SKU图片' },
  { key: 'skuImageUrl', label: 'SKU图片URL' },
  { key: 'skuImageStatus', label: 'SKU图片状态' },
  { key: 'skuId', label: 'SKU ID', default: true },
  { key: 'skuCode', label: 'SKU货号' },
  { key: 'skuSpec', label: '商品规格' },
  { key: 'category', label: '类目' },
  { key: 'stock', label: '库存' },
  { key: 'price', label: '申报价格' },
  { key: 'status', label: '商品状态' },
  { key: 'onlineTime', label: '加站时间' },
  { key: 'createTime', label: '创建时间' }
];

const CUSTOMIZED_EXPORT_IMAGE_FIELDS = new Set(['skcImage', 'skuImage']);
const CUSTOMIZED_EXPORT_IMAGE_URL_FIELDS = new Set(['skcImageUrl', 'skuImageUrl']);
const CUSTOMIZED_EXPORT_IMAGE_STATUS_FIELDS = new Set(['skcImageStatus', 'skuImageStatus']);

function normalizeCustomizedImageUrl(value = '') {
  const text = String(value || '').trim();
  if (!text) return '';
  if (/^data:image\//i.test(text)) return text;
  if (text.startsWith('//')) return `https:${text}`;
  if (text.startsWith('http://')) return `https://${text.slice(7)}`;
  return /^https?:\/\//i.test(text) ? text : '';
}

function collectCustomizedImageCandidates(source, output = [], depth = 0) {
  if (!source || depth > 3) return output;
  if (typeof source === 'string') {
    const url = normalizeCustomizedImageUrl(source);
    if (url) output.push(url);
    return output;
  }
  if (Array.isArray(source)) {
    source.forEach(item => collectCustomizedImageCandidates(item, output, depth + 1));
    return output;
  }
  if (typeof source !== 'object') return output;

  [
    'url',
    'src',
    'image',
    'imageUrl',
    'imgUrl',
    'mainImage',
    'mainImageUrl',
    'goodsImg',
    'goodsImage',
    'productSkcPicture',
    'productSkcImage',
    'colorImageUrl',
    'materialImgUrl',
    'thumbUrl',
    'skuImage',
    'skuPreviewImage',
    'productSkuImage',
    'productImgUrl',
    'previewImgUrl',
    'previewImageUrl'
  ].forEach(key => collectCustomizedImageCandidates(source[key], output, depth + 1));

  [
    'imageList',
    'images',
    'goodsImages',
    'skcImages',
    'thumbUrlList',
    'previewImgUrlList',
    'previewImgUrls',
    'carouselImageUrls',
    'productSkcImageExtList',
    'productSkuThumbUrlI18nVOList'
  ].forEach(key => collectCustomizedImageCandidates(source[key], output, depth + 1));

  return output;
}

function scoreCustomizedImageCandidate(url = '') {
  const text = String(url || '').toLowerCase();
  let score = /^data:image\//.test(text) ? 100 : 0;
  if (/main|skc|goods|product|color|preview/.test(text)) score += 8;
  if (/thumb|thumbnail|small|tiny/.test(text)) score -= 8;
  const dimensionMatches = Array.from(text.matchAll(/(?:^|[^\d])(\d{2,4})[x*_](\d{2,4})(?:[^\d]|$)/g));
  for (const match of dimensionMatches) {
    const w = Number(match[1]);
    const h = Number(match[2]);
    if (!Number.isFinite(w) || !Number.isFinite(h)) continue;
    const max = Math.max(w, h);
    const min = Math.min(w, h);
    if (max >= 800) score += 16;
    else if (max >= 400) score += 9;
    else if (max >= 240) score += 3;
    if (min > 0 && min <= 160) score -= 12;
  }
  return score;
}

function pickBestCustomizedImage(...sources) {
  const candidates = [];
  sources.forEach(source => collectCustomizedImageCandidates(source, candidates));
  const unique = Array.from(new Set(candidates.map(normalizeCustomizedImageUrl).filter(Boolean)));
  unique.sort((a, b) => scoreCustomizedImageCandidate(b) - scoreCustomizedImageCandidate(a));
  return unique[0] || '';
}

function getCustomizedImageStatus(url = '') {
  if (!url) return '缺失图片';
  if (/^data:image\//i.test(url)) return '已嵌入';
  if (/^https?:\/\//i.test(url)) return '待嵌入/可打开URL';
  return '无效图片地址';
}

function getCustomizedSkuImage(sku = {}, product = {}) {
  return pickBestCustomizedImage(
    sku.skuImage,
    sku.imageUrl,
    sku.thumbUrl,
    sku.productSkuImage,
    sku.skuPreviewImage,
    sku.productSkuThumbUrlI18nVOList,
    sku.mainImage,
    product.mainImage,
    product.imageList,
    product.productSkcImageExtList
  );
}

function getCustomizedSkuCode(sku = {}, product = {}) {
  return sku.skuCode || sku.extCode || sku.productSkuCode || sku.skuExtCode || product.skuCode || '';
}

function getCustomizedExportRows(products = []) {
  const rows = [];
  products.forEach(product => {
    const skuList = Array.isArray(product.skuList) && product.skuList.length ? product.skuList : [null];
    const skcImage = pickBestCustomizedImage(
      product.mainImage,
      product.imageList,
      product.productSkcImageExtList,
      product.productSkcPicture,
      product.goodsImg,
      product.imageUrl
    );
    skuList.forEach(sku => {
      const rowSku = sku || {};
      const skuImage = getCustomizedSkuImage(rowSku, product);
      rows.push({
        product,
        sku: rowSku,
        values: {
          productName: product.productName || '',
          spuId: product.goodsId || product.spuId || '',
          skcImage,
          skcImageUrl: normalizeCustomizedImageUrl(skcImage),
          skcImageStatus: getCustomizedImageStatus(skcImage),
          skcId: product.productId || product.skcId || '',
          skcCode: product.skcExtCode || product.skuCode || '',
          skuImage,
          skuImageUrl: normalizeCustomizedImageUrl(skuImage),
          skuImageStatus: getCustomizedImageStatus(skuImage),
          skuId: rowSku.skuId || rowSku.productSkuId || '',
          skuCode: getCustomizedSkuCode(rowSku, product),
          skuSpec: rowSku.skuSpec || rowSku.specInfo || product.skuSpecText || '',
          category: product.categoryName || product.categoryId || '',
          stock: rowSku.stockText || rowSku.showStockText || '',
          price: rowSku.price || product.price || '',
          status: product.lifecycleStageName || product.productStatusName || product.statusName || '',
          onlineTime: product.onlineTime || '',
          createTime: rowSku.createTime || product.createTime || ''
        }
      });
    });
  });
  return rows;
}

function csvEscape(value) {
  const text = String(value ?? '').replace(/\r?\n/g, ' ').trim();
  return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
}

function triggerBrowserDownload(filename, blob) {
  if (!(blob instanceof Blob)) {
    throw new Error('导出文件生成失败');
  }
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  link.rel = 'noopener';
  link.style.display = 'none';
  document.body.appendChild(link);
  link.click();
  window.setTimeout(() => {
    link.remove();
    URL.revokeObjectURL(url);
  }, 1500);
}

function downloadTextFile(filename, content, mime = 'text/csv;charset=utf-8') {
  const blob = new Blob([content], { type: mime });
  triggerBrowserDownload(filename, blob);
}

function createTemuXlsxBlob({ sheetName = '数据', fields = [], rows = [], imageFields = new Set(), imageLayoutByField = {} }) {
  const defaultImageLayout = { width: 120, height: 120, columnWidth: 18, rowHeightPt: 96, name: '图片' };
  const getImageLayout = key => imageLayoutByField[key] || defaultImageLayout;
  const splitDataImage = value => {
    const match = String(value || '').trim().match(/^data:(image\/[a-z0-9.+-]+);base64,([\s\S]+)$/i);
    if (!match) return null;
    return { mimeType: match[1].toLowerCase(), base64: match[2].replace(/\s+/g, '') };
  };
  const xmlEscape = value => String(value ?? '')
    .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/g, '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');
  const columnName = index => {
    let name = '';
    let n = index + 1;
    while (n > 0) {
      const rem = (n - 1) % 26;
      name = String.fromCharCode(65 + rem) + name;
      n = Math.floor((n - 1) / 26);
    }
    return name;
  };
  const textEncoder = new TextEncoder();
  const toBytes = value => value instanceof Uint8Array ? value : textEncoder.encode(String(value));
  const base64ToBytes = base64 => {
    const binary = atob(String(base64 || ''));
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
    return bytes;
  };
  const crcTable = (() => {
    const table = new Uint32Array(256);
    for (let i = 0; i < 256; i += 1) {
      let c = i;
      for (let k = 0; k < 8; k += 1) c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1);
      table[i] = c >>> 0;
    }
    return table;
  })();
  const crc32 = bytes => {
    let crc = 0xffffffff;
    for (let i = 0; i < bytes.length; i += 1) crc = crcTable[(crc ^ bytes[i]) & 0xff] ^ (crc >>> 8);
    return (crc ^ 0xffffffff) >>> 0;
  };
  const write16 = (bytes, offset, value) => {
    bytes[offset] = value & 0xff;
    bytes[offset + 1] = (value >>> 8) & 0xff;
  };
  const write32 = (bytes, offset, value) => {
    bytes[offset] = value & 0xff;
    bytes[offset + 1] = (value >>> 8) & 0xff;
    bytes[offset + 2] = (value >>> 16) & 0xff;
    bytes[offset + 3] = (value >>> 24) & 0xff;
  };
  const dosTimestamp = date => {
    const year = Math.max(1980, date.getFullYear());
    return {
      time: (date.getHours() << 11) | (date.getMinutes() << 5) | Math.floor(date.getSeconds() / 2),
      date: ((year - 1980) << 9) | ((date.getMonth() + 1) << 5) | date.getDate()
    };
  };
  const createZipBlob = files => {
    const localParts = [];
    const centralParts = [];
    const now = dosTimestamp(new Date());
    let offset = 0;
    for (const file of files) {
      const nameBytes = toBytes(file.name);
      const dataBytes = toBytes(file.data);
      const crc = crc32(dataBytes);
      const local = new Uint8Array(30 + nameBytes.length);
      write32(local, 0, 0x04034b50);
      write16(local, 4, 20);
      write16(local, 6, 0x0800);
      write16(local, 8, 0);
      write16(local, 10, now.time);
      write16(local, 12, now.date);
      write32(local, 14, crc);
      write32(local, 18, dataBytes.length);
      write32(local, 22, dataBytes.length);
      write16(local, 26, nameBytes.length);
      write16(local, 28, 0);
      local.set(nameBytes, 30);
      localParts.push(local, dataBytes);

      const central = new Uint8Array(46 + nameBytes.length);
      write32(central, 0, 0x02014b50);
      write16(central, 4, 20);
      write16(central, 6, 20);
      write16(central, 8, 0x0800);
      write16(central, 10, 0);
      write16(central, 12, now.time);
      write16(central, 14, now.date);
      write32(central, 16, crc);
      write32(central, 20, dataBytes.length);
      write32(central, 24, dataBytes.length);
      write16(central, 28, nameBytes.length);
      write16(central, 30, 0);
      write16(central, 32, 0);
      write16(central, 34, 0);
      write16(central, 36, 0);
      write32(central, 38, 0);
      write32(central, 42, offset);
      central.set(nameBytes, 46);
      centralParts.push(central);
      offset += local.length + dataBytes.length;
    }
    const centralSize = centralParts.reduce((sum, part) => sum + part.length, 0);
    const end = new Uint8Array(22);
    write32(end, 0, 0x06054b50);
    write16(end, 8, files.length);
    write16(end, 10, files.length);
    write32(end, 12, centralSize);
    write32(end, 16, offset);
    write16(end, 20, 0);
    return new Blob([...localParts, ...centralParts, end], {
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
  };
  const imageExtForMime = mimeType => {
    const mime = String(mimeType || '').toLowerCase();
    if (mime.includes('png')) return 'png';
    if (mime.includes('gif')) return 'gif';
    if (mime.includes('bmp')) return 'bmp';
    if (mime.includes('webp')) return 'webp';
    return 'jpeg';
  };
  const imageContentType = ext => ({
    png: 'image/png',
    gif: 'image/gif',
    bmp: 'image/bmp',
    webp: 'image/webp'
  }[ext] || 'image/jpeg');
  const images = [];
  const cellXml = (value, rowNumber, columnIndex) => {
    const ref = `${columnName(columnIndex)}${rowNumber}`;
    const text = String(value ?? '');
    const formulaMatch = text.match(/^=IMAGE\("([^"]+)"\)$/i);
    if (formulaMatch) {
      return `<c r="${ref}"><f>${xmlEscape(text.slice(1))}</f></c>`;
    }
    return `<c r="${ref}" t="inlineStr"><is><t>${xmlEscape(text)}</t></is></c>`;
  };
  const sheetRows = [];
  sheetRows.push(`<row r="1" ht="24" customHeight="1">${fields.map((field, index) => cellXml(field.label, 1, index)).join('')}</row>`);
  rows.forEach((row, rowIndex) => {
    const rowNumber = rowIndex + 2;
    const cells = fields.map((field, columnIndex) => {
      const value = row[field.key] || '';
      const dataImage = imageFields.has(field.key) ? splitDataImage(value) : null;
      if (dataImage) {
        const ext = imageExtForMime(dataImage.mimeType);
        const imageIndex = images.length + 1;
        images.push({
          rowIndex: rowNumber - 1,
          columnIndex,
          fieldKey: field.key,
          ext,
          bytes: base64ToBytes(dataImage.base64),
          name: `image${imageIndex}.${ext}`
        });
        return cellXml('', rowNumber, columnIndex);
      }
      return cellXml(value, rowNumber, columnIndex);
    }).join('');
    const rowImageHeightPt = fields.reduce((height, field) => {
      const value = row[field.key] || '';
      return imageFields.has(field.key) && splitDataImage(value)
        ? Math.max(height, getImageLayout(field.key).rowHeightPt)
        : height;
    }, 28);
    sheetRows.push(`<row r="${rowNumber}" ht="${rowImageHeightPt}" customHeight="1">${cells}</row>`);
  });

  const colsXml = `<cols>${fields.map((field, index) => {
    const width = imageFields.has(field.key)
      ? getImageLayout(field.key).columnWidth
      : Math.min(42, Math.max(12, String(field.label || '').length * 2 + 4));
    return `<col min="${index + 1}" max="${index + 1}" width="${width}" customWidth="1"/>`;
  }).join('')}</cols>`;
  const sheetDrawingXml = images.length ? '<drawing r:id="rId1"/>' : '';
  const worksheetXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <sheetViews><sheetView workbookViewId="0"/></sheetViews>
  <sheetFormatPr defaultRowHeight="18"/>
  ${colsXml}
  <sheetData>${sheetRows.join('')}</sheetData>
  <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
  ${sheetDrawingXml}
</worksheet>`;
  const mediaDefaults = Array.from(new Set(images.map(image => image.ext)))
    .map(ext => `<Default Extension="${ext}" ContentType="${imageContentType(ext)}"/>`)
    .join('');
  const drawingOverride = images.length
    ? '<Override PartName="/xl/drawings/drawing1.xml" ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"/>'
    : '';
  const contentTypesXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
  <Default Extension="xml" ContentType="application/xml"/>
  ${mediaDefaults}
  <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
  <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
  <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
  <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
  <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
  ${drawingOverride}
</Types>`;
  const rootRelsXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
  <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
  <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
</Relationships>`;
  const workbookXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <sheets><sheet name="${xmlEscape(sheetName).slice(0, 31) || '数据'}" sheetId="1" r:id="rId1"/></sheets>
</workbook>`;
  const workbookRelsXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
  <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
</Relationships>`;
  const stylesXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  <fonts count="1"><font><sz val="11"/><name val="Microsoft YaHei"/></font></fonts>
  <fills count="1"><fill><patternFill patternType="none"/></fill></fills>
  <borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>
  <cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>
  <cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs>
  <cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>
</styleSheet>`;
  const nowIso = new Date().toISOString();
  const coreXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <dc:creator>小青蛙电商助手</dc:creator>
  <cp:lastModifiedBy>小青蛙电商助手</cp:lastModifiedBy>
  <dcterms:created xsi:type="dcterms:W3CDTF">${nowIso}</dcterms:created>
  <dcterms:modified xsi:type="dcterms:W3CDTF">${nowIso}</dcterms:modified>
</cp:coreProperties>`;
  const appXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
  <Application>小青蛙电商助手</Application>
</Properties>`;
  const files = [
    { name: '[Content_Types].xml', data: contentTypesXml },
    { name: '_rels/.rels', data: rootRelsXml },
    { name: 'docProps/core.xml', data: coreXml },
    { name: 'docProps/app.xml', data: appXml },
    { name: 'xl/workbook.xml', data: workbookXml },
    { name: 'xl/_rels/workbook.xml.rels', data: workbookRelsXml },
    { name: 'xl/styles.xml', data: stylesXml },
    { name: 'xl/worksheets/sheet1.xml', data: worksheetXml }
  ];
  if (images.length) {
    const sheetRelsXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" Target="../drawings/drawing1.xml"/>
</Relationships>`;
    const drawingRelsXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
${images.map((image, index) => `  <Relationship Id="rId${index + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/${xmlEscape(image.name)}"/>`).join('\n')}
</Relationships>`;
    const drawingXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
${images.map((image, index) => {
  const pictureId = index + 1;
  const layout = getImageLayout(image.fieldKey);
  return `  <xdr:oneCellAnchor>
    <xdr:from><xdr:col>${image.columnIndex}</xdr:col><xdr:colOff>45720</xdr:colOff><xdr:row>${image.rowIndex}</xdr:row><xdr:rowOff>45720</xdr:rowOff></xdr:from>
    <xdr:ext cx="${layout.width * 9525}" cy="${layout.height * 9525}"/>
    <xdr:pic>
      <xdr:nvPicPr><xdr:cNvPr id="${pictureId}" name="${xmlEscape(layout.name)}${pictureId}"/><xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr>
      <xdr:blipFill><a:blip r:embed="rId${pictureId}"/><a:stretch><a:fillRect/></a:stretch></xdr:blipFill>
      <xdr:spPr><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr>
    </xdr:pic>
    <xdr:clientData/>
  </xdr:oneCellAnchor>`;
}).join('\n')}
</xdr:wsDr>`;
    files.push(
      { name: 'xl/worksheets/_rels/sheet1.xml.rels', data: sheetRelsXml },
      { name: 'xl/drawings/drawing1.xml', data: drawingXml },
      { name: 'xl/drawings/_rels/drawing1.xml.rels', data: drawingRelsXml },
      ...images.map(image => ({ name: `xl/media/${image.name}`, data: image.bytes }))
    );
  }
  return { blob: createZipBlob(files), embeddedImageCount: images.length };
}

function downloadBlobFile(filename, blob) {
  triggerBrowserDownload(filename, blob);
}

function buildCustomizedExportFilename(date = new Date()) {
  const pad = n => String(n).padStart(2, '0');
  return `定制品导出_${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}_${pad(date.getHours())}${pad(date.getMinutes())}.xlsx`;
}

async function writeBlobToFileHandle(fileHandle, blob) {
  if (!fileHandle || typeof fileHandle.createWritable !== 'function') {
    throw new Error('浏览器未返回有效的保存文件句柄');
  }
  const writable = await fileHandle.createWritable();
  try {
    await writable.write(blob);
  } finally {
    await writable.close();
  }
}

function expandCustomizedExportFields(selectedFieldKeys = []) {
  const keys = new Set(selectedFieldKeys);
  if (keys.has('skcImage')) {
    keys.add('skcImageUrl');
    keys.add('skcImageStatus');
  }
  if (keys.has('skuImage')) {
    keys.add('skuImageUrl');
    keys.add('skuImageStatus');
  }
  return CUSTOMIZED_EXPORT_FIELDS.filter(field => keys.has(field.key));
}

function buildCustomizedExportXlsxRows(rows = [], fields = []) {
  return rows.map(row => {
    const output = {};
    fields.forEach(field => {
      const value = row.values[field.key] || '';
      if (CUSTOMIZED_EXPORT_IMAGE_FIELDS.has(field.key)) {
        const url = normalizeCustomizedImageUrl(value);
        if (/^data:image\//i.test(value)) {
          output[field.key] = value;
        } else if (/^https?:\/\//i.test(url)) {
          output[field.key] = `=IMAGE("${url.replace(/"/g, '%22')}")`;
        } else {
          output[field.key] = '缺失图片';
        }
      } else {
        output[field.key] = value;
      }
    });
    return output;
  });
}

async function hydrateCustomizedExportImages(rows = []) {
  const payloadRows = rows.map(row => ({
    skcImage: normalizeCustomizedImageUrl(row.values.skcImage),
    skuImage: normalizeCustomizedImageUrl(row.values.skuImage)
  }));
  const result = await safeRuntimeSendMessage(
    { action: 'embedCustomizedProductExportImages', rows: payloadRows },
    { success: false, rows: payloadRows, error: '当前插件版本暂不支持图片嵌入' },
    45000
  );
  if (!result?.success || !Array.isArray(result.rows)) {
    console.warn('[小青蛙电商助手] 定制品导出图片嵌入失败，使用URL公式兜底:', result?.error || result);
    return { rows, embeddedCount: 0, failedCount: payloadRows.filter(row => row.skcImage || row.skuImage).length };
  }
  let embeddedCount = 0;
  let failedCount = 0;
  const nextRows = rows.map((row, index) => {
    const imageRow = result.rows[index] || {};
    const values = { ...row.values };
    ['skcImage', 'skuImage'].forEach(key => {
      const urlKey = `${key}Url`;
      const statusKey = `${key}Status`;
      if (/^data:image\//i.test(imageRow[key] || '')) {
        values[key] = imageRow[key];
        values[urlKey] = imageRow[urlKey] || values[urlKey] || normalizeCustomizedImageUrl(row.values[key]);
        values[statusKey] = '已嵌入';
        embeddedCount += 1;
      } else if (imageRow[urlKey] || imageRow[key]) {
        values[urlKey] = imageRow[urlKey] || imageRow[key] || values[urlKey];
        values[statusKey] = imageRow[statusKey] || '图片抓取失败，已保留URL';
        failedCount += 1;
      }
    });
    return { ...row, values };
  });
  return {
    rows: nextRows,
    embeddedCount: Number(result.embeddedCount || embeddedCount || 0),
    failedCount: Number(result.failedCount || failedCount || 0)
  };
}

async function exportCustomizedProducts(selectedFieldKeys = [], options = {}) {
  const fields = CUSTOMIZED_EXPORT_FIELDS.filter(field => selectedFieldKeys.includes(field.key));
  if (!fields.length) {
    showToast('请至少选择一个导出项', 'error');
    return false;
  }
  const checkedKeys = getCustomizedSelectedSpuIds();
  const sourceProducts = getCustomizedFilteredProducts();
  const products = checkedKeys.length
    ? sourceProducts.filter(product => checkedKeys.includes(getCustomizedProductKey(product)))
    : sourceProducts;
  if (!products.length) {
    showToast('暂无可导出的定制品数据，请先同步商品列表', 'error');
    return false;
  }
  const selectedFields = expandCustomizedExportFields(selectedFieldKeys);
  const rawRows = getCustomizedExportRows(products);
  const imageWanted = selectedFieldKeys.some(key => CUSTOMIZED_EXPORT_IMAGE_FIELDS.has(key));
  showToast(imageWanted ? '正在生成带图片的 Excel 文件...' : '正在生成 Excel 文件...', 'info');
  const hydrated = imageWanted
    ? await hydrateCustomizedExportImages(rawRows)
    : { rows: rawRows, embeddedCount: 0, failedCount: 0 };
  const xlsxRows = buildCustomizedExportXlsxRows(hydrated.rows, selectedFields);
  const missingSkcImages = hydrated.rows.filter(row => !normalizeCustomizedImageUrl(row.values.skcImage)).length;
  const missingSkuImages = hydrated.rows.filter(row => !normalizeCustomizedImageUrl(row.values.skuImage)).length;
  const { blob, embeddedImageCount } = createTemuXlsxBlob({
    sheetName: '定制品导出',
    fields: selectedFields,
    rows: xlsxRows,
    imageFields: CUSTOMIZED_EXPORT_IMAGE_FIELDS,
    imageLayoutByField: {
      skcImage: { width: 112, height: 112, columnWidth: 18, rowHeightPt: 92, name: 'SKC图片' },
      skuImage: { width: 112, height: 112, columnWidth: 18, rowHeightPt: 92, name: 'SKU图片' }
    }
  });
  const filename = options.filename || buildCustomizedExportFilename();
  downloadBlobFile(filename, blob);
  const warning = missingSkcImages || missingSkuImages || hydrated.failedCount
    ? `；缺SKC图${missingSkcImages}行，缺SKU图${missingSkuImages}行，抓图失败${hydrated.failedCount || 0}张`
    : '';
  showToast(`已导出 ${rawRows.length} 行定制品数据，嵌入图片 ${embeddedImageCount || hydrated.embeddedCount || 0} 张${warning}`, warning ? 'warning' : 'success');
  return true;
}

async function handleCustomizedExportConfirm(event) {
  const button = event?.target?.closest?.('#customizedExportConfirmBtn') || event?.currentTarget;
  const modal = button?.closest?.('#customizedExportModal');
  if (!button || !modal || button.dataset.exporting === '1') return;
  event?.preventDefault?.();
  event?.stopPropagation?.();
  event?.stopImmediatePropagation?.();
  const fields = Array.from(modal.querySelectorAll('.customized-export-field'));
  const selected = fields.filter(input => input.checked).map(input => input.value);
  const oldText = button.textContent;
  button.dataset.exporting = '1';
  button.disabled = true;
  button.textContent = '导出中...';
  try {
    const exported = await exportCustomizedProducts(selected, { filename: buildCustomizedExportFilename() });
    if (exported && modal.parentNode) modal.parentNode.removeChild(modal);
  } catch (error) {
    console.error('[小青蛙电商助手] 定制品导出失败', error);
    showToast(`导出失败：${error?.message || error}`, 'error');
  } finally {
    if (modal.parentNode) {
      button.disabled = false;
      button.textContent = oldText || '导出';
      delete button.dataset.exporting;
    }
  }
}

function showCustomizedExportDialog(event) {
  event?.preventDefault?.();
  event?.stopPropagation?.();
  const existingModal = document.getElementById('customizedExportModal');
  if (existingModal) return;
  const modal = document.createElement('div');
  modal.className = 'modal';
  modal.id = 'customizedExportModal';
  modal.style.cssText = 'position:fixed;inset:0;z-index:10000;display:flex;align-items:center;justify-content:center;padding:18px;background:rgba(15,23,42,.45);overflow:auto;';
  modal.innerHTML = `
    <div class="modal-content" style="width:min(860px,96vw);max-height:86vh;overflow:auto;">
      <h2 style="margin:0 0 14px;font-size:16px;">导出项选择</h2>
      <label class="customized-export-option" style="margin-bottom:4px;">
        <input type="checkbox" id="customizedExportSelectAll">
        <span>全部选择</span>
      </label>
      <div class="customized-export-grid">
        ${CUSTOMIZED_EXPORT_FIELDS.map(field => `
          <label class="customized-export-option">
            <input type="checkbox" class="customized-export-field" value="${escapeHtml(field.key)}" ${field.default ? 'checked' : ''}>
            <span>${escapeHtml(field.label)}</span>
          </label>
        `).join('')}
      </div>
      <div style="display:flex;justify-content:flex-end;gap:8px;margin-top:18px;position:sticky;bottom:0;background:#fff;padding-top:12px;">
        <button type="button" class="btn btn-default" id="customizedExportCancelBtn">取消</button>
        <button type="button" class="btn btn-primary" id="customizedExportConfirmBtn">导出</button>
      </div>
    </div>
  `;
  document.body.appendChild(modal);
  const close = () => {
    if (modal.parentNode) modal.parentNode.removeChild(modal);
  };
  const selectAll = modal.querySelector('#customizedExportSelectAll');
  const fields = Array.from(modal.querySelectorAll('.customized-export-field'));
  const syncSelectAll = () => {
    const checkedCount = fields.filter(input => input.checked).length;
    selectAll.checked = checkedCount === fields.length;
    selectAll.indeterminate = checkedCount > 0 && checkedCount < fields.length;
  };
  fields.forEach(input => input.addEventListener('change', syncSelectAll));
  selectAll.addEventListener('change', () => {
    fields.forEach(input => { input.checked = selectAll.checked; });
    syncSelectAll();
  });
  modal.querySelector('#customizedExportCancelBtn')?.addEventListener('click', close);
  modal.addEventListener('click', (e) => { if (e.target === modal) close(); });
  modal.querySelector('#customizedExportConfirmBtn')?.addEventListener('click', async (event) => {
    const button = event.currentTarget;
    const selected = fields.filter(input => input.checked).map(input => input.value);
    const filename = buildCustomizedExportFilename();
    let fileHandle = null;
    if (typeof window.showSaveFilePicker === 'function') {
      try {
        fileHandle = await window.showSaveFilePicker({
          suggestedName: filename,
          types: [{
            description: 'Excel 文件',
            accept: { 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'] }
          }]
        });
      } catch (error) {
        if (error?.name === 'AbortError') return;
        console.warn('[小青蛙电商助手] 系统保存窗口不可用，改用浏览器下载:', error);
      }
    }
    button.disabled = true;
    const oldText = button.textContent;
    button.textContent = '导出中...';
    try {
      await exportCustomizedProducts(selected, { filename, fileHandle });
      close();
    } catch (error) {
      console.error('[小青蛙电商助手] 定制品导出失败:', error);
      showToast(`导出失败：${error?.message || error}`, 'error');
      button.disabled = false;
      button.textContent = oldText || '导出';
    }
  });
  syncSelectAll();
}

if (!window.__CUSTOMIZED_EXPORT_CLICK_BOUND__) {
  window.__CUSTOMIZED_EXPORT_CLICK_BOUND__ = true;
  document.addEventListener('click', (event) => {
    if (event.target?.closest?.('#customizedExportConfirmBtn')) {
      handleCustomizedExportConfirm(event);
      return;
    }
    const exportBtn = event.target?.closest?.('#customizedExportBtn');
    if (!exportBtn) return;
    showCustomizedExportDialog(event);
  }, true);
}

function bindCustomizedExportButton() {
  const exportBtn = document.getElementById('customizedExportBtn');
  if (!exportBtn || exportBtn.dataset.customizedExportBound === '1') return;
  exportBtn.dataset.customizedExportBound = '1';
  exportBtn.addEventListener('click', showCustomizedExportDialog, true);
  exportBtn.onclick = showCustomizedExportDialog;
}

bindCustomizedExportButton();
document.addEventListener('DOMContentLoaded', bindCustomizedExportButton);
window.setTimeout(bindCustomizedExportButton, 500);
window.setTimeout(bindCustomizedExportButton, 1500);

async function fetchCustomizedProductsAllViaBackground(params = {}) {
  const pageSize = Math.max(1, params.pageSize || customizedProductState.pageSize || 20);
  const lifecycleStage = params.lifecycleStage || '';
  let pageNo = 1;
  let totalPages = 1;
  let total = 0;
  const products = [];

  while (pageNo <= totalPages) {
    const result = await safeRuntimeSendMessage({
      action: 'fetchCustomizedProductList',
      params: {
        pageNo,
        pageSize,
        lifecycleStage: lifecycleStage || undefined
      }
    }, { success: false, error: '获取商品管理列表失败' }, 30000);

    if (!result?.success) {
      throw new Error(result?.error || '获取商品管理列表失败');
    }

    const pageProducts = Array.isArray(result.products) ? result.products : [];
    products.push(...pageProducts);
    total = result.total || total || products.length;
    totalPages = Math.max(1, result.totalPages || Math.ceil(total / pageSize));
    pageNo += 1;
  }

  return {
    success: true,
    products,
    total,
    totalPages
  };
}

async function fetchCustomizedProductsFromTemuListPage(params = {}) {
  return await safeRuntimeSendMessage(
    { action: 'fetchCustomizedProductsFromTemuListPage', params },
    { success: false, error: '读取 TEMU 商品管理列表失败' },
    20000
  );
}

function parseSpuIdList(raw = '') {
  return String(raw || '')
    .split(/[,，\s]+/)
    .map(s => s.trim())
    .filter(s => s && /^\d+$/.test(s));
}

function parseTemuSearchTerms(raw = '') {
  return uniqueStringList(String(raw || '')
    .split(/[,，\s]+/)
    .map(s => s.trim())
    .filter(Boolean));
}

function isTemuIdSearchTerm(value = '') {
  return /^\d{5,}$/.test(String(value || '').trim());
}

function getTemuMultiIdSearchTerms(raw = '') {
  return parseTemuSearchTerms(raw).filter(isTemuIdSearchTerm);
}

function matchesAnyTemuSearchTerm(values = [], terms = []) {
  const normalizedTerms = parseTemuSearchTerms(terms.join(' ')).map(term => term.toLowerCase());
  if (!normalizedTerms.length) return true;
  const haystack = (Array.isArray(values) ? values : [values])
    .map(value => String(value ?? '').toLowerCase())
    .join(' ');
  return normalizedTerms.some(term => haystack.includes(term));
}

function uniqueStringList(list = []) {
  const seen = new Set();
  const result = [];
  list.forEach(item => {
    const value = String(item || '').trim();
    if (!value || seen.has(value)) return;
    seen.add(value);
    result.push(value);
  });
  return result;
}

function getCustomizedProductSpu(product = {}) {
  return String(product.goodsId || product.spuId || product.productId || product.skcId || '').trim();
}

function getCustomizedProductKey(product = {}) {
  return String(product.goodsId || product.spuId || product.productId || product.skcId || '').trim();
}

function getCustomizedCheckboxKey(cb) {
  return String(cb?.dataset?.key || cb?.dataset?.spu || cb?.dataset?.skc || '').trim();
}

function setCustomizedSelectedKey(key, selected) {
  const normalized = String(key || '').trim();
  if (!normalized) return;
  if (!customizedProductState.selectedKeys) customizedProductState.selectedKeys = new Set();
  if (selected) customizedProductState.selectedKeys.add(normalized);
  else customizedProductState.selectedKeys.delete(normalized);
}

function syncCustomizedSelectedKeysFromDom(container = document) {
  const checks = Array.from(container.querySelectorAll?.('#customizedProductListContainer .customized-product-checkbox') || []);
  checks.forEach(cb => setCustomizedSelectedKey(getCustomizedCheckboxKey(cb), cb.checked));
}

function isCustomizedProductPending(product = {}) {
  const topStatus = Number(product.skcTopStatus);
  if (topStatus === 0) return true;
  if ([200, 300].includes(topStatus)) return false;

  const badgeText = (Array.isArray(product.statusBadges) ? product.statusBadges : [])
    .map(badge => String(badge?.text || ''))
    .join(' ');
  if (/待|未配置|资质待上传|即将废弃/.test(badgeText)) return true;

  const skuList = Array.isArray(product.skuList) ? product.skuList : [];
  if (!skuList.length) return false;
  return !skuList.some(sku => {
    const tmpl = sku?.productSkuCustomizationTmpl || sku?.productSkuSaleExtAttr?.productSkuCustomizationTmpl;
    const templateIds = Array.isArray(tmpl?.templateIds)
      ? tmpl.templateIds
      : [tmpl?.templateId, tmpl?.template_id, tmpl?.id, sku.templateId].filter(Boolean);
    return Boolean(tmpl && templateIds.length);
  });
}

function getCustomizedSelectedSpuIds() {
  syncCustomizedSelectedKeysFromDom();
  const checkedKeys = Array.from(document.querySelectorAll('#customizedProductListContainer .customized-product-checkbox:checked'))
    .map(getCustomizedCheckboxKey);
  return uniqueStringList([
    ...Array.from(customizedProductState.selectedKeys || []),
    ...checkedKeys
  ]);
}

function resolveCustomizedTargetSpuIds(scope = 'selected') {
  const manualSpuIds = parseSpuIdList(document.getElementById('customizedTargetSpuId')?.value || '');
  if (manualSpuIds.length) return uniqueStringList(manualSpuIds);

  if (scope === 'selected') {
    return getCustomizedSelectedSpuIds();
  }

  const products = Array.isArray(customizedProductState.allProducts) ? customizedProductState.allProducts : [];
  const scopedProducts = scope === 'pending'
    ? products.filter(isCustomizedProductPending)
    : products;
  return uniqueStringList(scopedProducts.map(getCustomizedProductSpu));
}

function applyCustomizedProductFilter() {
  const query = getCustomizedSearchQuery();
  const statusTop = getCustomizedStatusTopStatus();
  const source = Array.isArray(customizedProductState.allProducts) ? customizedProductState.allProducts : [];

  const filtered = source.filter(product => {
    if (!matchesCustomizedProduct(product, query)) return false;
    if (statusTop === null) return true;
    return Number(product.skcTopStatus ?? product.topStatus ?? -1) === statusTop;
  });

  customizedProductState.products = source.slice();
  customizedProductState.viewProducts = filtered;
  customizedProductState.total = filtered.length;
  customizedProductState.totalPages = Math.max(1, Math.ceil(filtered.length / Math.max(1, customizedProductState.pageSize || 20)));
  if (customizedProductState.pageNum > customizedProductState.totalPages) customizedProductState.pageNum = 1;
  renderCustomizedProductTable();
  renderCustomizedProductPagination();
  renderCustomizedStatusTabCounts();
}

async function syncCustomizedProducts(params = {}) {
  const container = document.getElementById('customizedProductListContainer');
  const loadBtn = document.getElementById('customizedProductLoadBtn');
  const originalText = loadBtn?.textContent || '';
  if (loadBtn) {
    loadBtn.disabled = true;
    loadBtn.textContent = '同步中...';
  }
  container.innerHTML = '<div class="loading">同步中...</div>';

  try {
    const pageSize = Math.max(100, customizedProductState.pageSize || 20);
    const result = await fetchCustomizedProductsAllViaBackground({ pageSize });
    if (!result?.success || !Array.isArray(result.products)) {
      throw new Error(result?.error || '获取商品列表失败');
    }
    let normalized = result.products.map(normalizeCustomizedProductRecord);
    try {
      const exactListResult = await fetchCustomizedProductsFromTemuListPage({});
      if (exactListResult?.success && Array.isArray(exactListResult.products)) {
        normalized = mergeCustomizedExactBadges(normalized, exactListResult.products);
      }
    } catch (exactErr) {
      console.warn('[小青蛙电商助手] 未能读取 TEMU 商品列表 DOM 标签，保持 API 数据不伪造标签:', exactErr);
    }
    customizedProductState.allProducts = normalized;
    customizedProductState.products = normalized.slice();
    customizedProductState.selectedKeys.clear();
    customizedProductState.sourceTotal = Number(result.sourceTotal || result.total || normalized.length || 0);
    customizedProductState.syncedAt = new Date().toLocaleString('zh-CN', { hour12: false });
    customizedProductState.statusCounts = computeCustomizedStatusCounts(normalized);
    customizedProductState.cacheLoaded = true;
    customizedProductState.cacheKey = getCustomizedProductCacheKey();
    customizedProductState.pageNum = 1;

    try {
      const countRes = await safeRuntimeSendMessage({ action: 'fetchCustomizedProductStatusCount', params: {} }, { success: false, error: '获取定制品状态统计失败' }, 15000);
      const list = countRes?.result?.skcTopStatusCountList || countRes?.skcTopStatusCountList || [];
      if (Array.isArray(list) && list.length) {
        const counts = { all: customizedProductState.sourceTotal || normalized.length || 0, 0: 0, 100: 0, 200: 0, 300: 0 };
        list.forEach(entry => {
          const topStatus = Number(entry.skcTopStatus);
          if ([0, 100, 200, 300].includes(topStatus)) counts[topStatus] = Number(entry.skcTotal || 0);
        });
        customizedProductState.statusCounts = counts;
      }
    } catch (countErr) {
      console.warn('[小青蛙电商助手] 定制品状态统计获取失败，使用本地计算:', countErr);
    }

    applyCustomizedProductFilter();
    await persistCustomizedProductCache();
  } catch (err) {
    console.error('[小青蛙电商助手] syncCustomizedProducts 异常:', err);
    container.innerHTML = `<div class="empty"><div class="empty-icon">❌</div><p>同步失败: ${escapeHtml(err?.message || String(err))}</p></div>`;
  } finally {
    if (loadBtn) {
      loadBtn.disabled = false;
      loadBtn.textContent = originalText || '🔄 同步商品列表';
    }
  }
}

async function loadCustomizedProducts(params = {}) {
  const cacheKey = getCustomizedProductCacheKey();
  if (!customizedProductState.cacheLoaded || customizedProductState.cacheKey !== cacheKey) {
    await loadCustomizedProductCache();
  }
  if (typeof params.pageNum === 'number' && params.pageNum > 0) {
    customizedProductState.pageNum = params.pageNum;
  }
  if (typeof params.pageSize === 'number' && params.pageSize > 0) {
    customizedProductState.pageSize = params.pageSize;
  }
  if (!customizedProductState.allProducts.length) {
    const container = document.getElementById('customizedProductListContainer');
    container.innerHTML = '<div class="empty"><div class="empty-icon">📦</div><p>暂无已同步的定制品数据，请点击「同步商品列表」</p></div>';
    renderCustomizedProductPagination();
    renderCustomizedStatusTabCounts();
    return;
  }
  applyCustomizedProductFilter();
}

// 渲染定制品商品列表
function renderCustomizedProductTable() {
  const container = document.getElementById('customizedProductListContainer');
  const products = getCustomizedDisplayProducts();

  if (products.length === 0) {
    container.innerHTML = '<div class="empty"><div class="empty-icon">📦</div><p>暂无商品数据</p></div>';
    return;
  }

  const escapeAttr = value => escapeHtml(value ?? '');
  const fallbackBadgeStyles = {
    warn: { color: '#ff6800', backgroundColor: '#fff4e6', borderColor: '#ff6800' },
    danger: { color: '#dc2626', backgroundColor: '#fee2e2', borderColor: '#dc2626' },
    info: { color: '#0071f3', backgroundColor: '#e6f6ff', borderColor: '#0071f3' }
  };

  container.innerHTML = `
    <div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;padding:8px 10px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;">
      <label style="display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:700;color:#334155;cursor:pointer;">
        <input type="checkbox" class="checkbox" id="customizedSelectPage">
        <span>全选本页</span>
      </label>
      <span id="customizedPageSelectInfo" style="font-size:12px;color:#64748b;">本页 ${products.length} 条</span>
    </div>
    <div class="customized-card-list">
      ${products.map(p => {
        const imgHtml = p.mainImage
          ? `<img src="${escapeAttr(p.mainImage)}" class="customized-product-image img-fallback" data-fallback-size="72">`
          : '<div class="customized-product-image" style="display:flex;align-items:center;justify-content:center;background:#f8fafc;color:#cbd5e1;font-size:20px;">📷</div>';
        const badges = Array.isArray(p.statusBadges) ? p.statusBadges : [];
        const skuList = Array.isArray(p.skuList) ? p.skuList : [];
        const productKey = getCustomizedProductKey(p);
        const checkedAttr = customizedProductState.selectedKeys.has(productKey) ? 'checked' : '';

        return `
          <div class="customized-product-card">
            <div class="customized-product-select">
              <input type="checkbox" class="checkbox product-checkbox customized-product-checkbox" data-key="${escapeAttr(productKey)}" data-spu="${escapeAttr(p.goodsId || p.spuId || '')}" data-skc="${escapeAttr(p.productId || p.skcId || '')}" ${checkedAttr}>
            </div>
            <div class="customized-product-left">
              ${imgHtml}
              <div class="customized-product-meta">
                <div class="customized-product-title" title="${escapeAttr(p.productName || '-') }">${escapeHtml(p.productName || '-')}</div>
                <div class="customized-badge-row">
                  ${badges.map((badge, idx) => {
                    const fallback = /资质待上传/.test(badge.text)
                      ? fallbackBadgeStyles.warn
                      : /在售|定制商品/.test(badge.text)
                        ? fallbackBadgeStyles.info
                        : /下架|终止|删除/.test(badge.text)
                          ? fallbackBadgeStyles.danger
                          : fallbackBadgeStyles.info;
                    const style = `background:${badge.backgroundColor || fallback.backgroundColor};color:${badge.color || fallback.color};border:1px solid ${badge.borderColor || fallback.borderColor};`;
                    return `<span class="customized-badge" style="${style}">${escapeHtml(badge.text)}</span>`;
                  }).join('')}
                </div>
                <div class="customized-meta-row">
                  <span class="customized-meta-label">SPU ID</span>
                  <span class="customized-meta-value">${escapeHtml(p.goodsId || '—')}</span>
                </div>
                <div class="customized-meta-row">
                  <span class="customized-meta-label">SKC ID</span>
                  <span class="customized-meta-value">${escapeHtml(p.productId || '—')}</span>
                </div>
                <div class="customized-meta-row">
                  <span class="customized-meta-label">SKU数</span>
                  <span class="customized-meta-value">${escapeHtml(String(p.skuCount || skuList.length || 0))}</span>
                </div>
              </div>
            </div>
            <div class="customized-sku-panel">
              <div class="customized-sku-head">
                <span>SKU ID</span>
                <span>商品规格</span>
                <span>申报价格(CNY)</span>
                <span>库存</span>
                <span>创建时间</span>
              </div>
              ${skuList.length > 0 ? skuList.map(sku => {
                return `
                  <div class="customized-sku-row">
                    <span class="customized-sku-cell customized-sku-id" title="${escapeAttr(sku.skuId || '—')}">${escapeHtml(sku.skuId || '—')}</span>
                    <span class="customized-sku-cell customized-sku-spec" title="${escapeAttr(sku.skuSpec || '—')}">${escapeHtml(sku.skuSpec || '—')}</span>
                    <span class="customized-sku-cell customized-sku-price" title="${sku.price ? `¥${escapeAttr(sku.price)}` : '—'}">${sku.price ? `¥${escapeHtml(sku.price)}` : '—'}</span>
                    <span class="customized-sku-cell customized-sku-stock" title="${escapeAttr(sku.stockText || sku.showStockText || '—')}">${escapeHtml(sku.stockText || sku.showStockText || '—')}</span>
                    <span class="customized-sku-cell" title="${escapeAttr(sku.createTime || p.createTime || '—')}">${escapeHtml(sku.createTime || p.createTime || '—')}</span>
                  </div>
                `;
              }).join('') : `
                <div class="customized-sku-row" style="grid-template-columns:1fr;">
                  <span class="customized-sku-cell" style="color:#94a3b8;">暂无 SKU 明细</span>
                </div>
              `}
            </div>
          </div>
        `;
      }).join('')}
    </div>
  `;

  const pageSelect = document.getElementById('customizedSelectPage');
  const pageInfo = document.getElementById('customizedPageSelectInfo');
  const pageChecks = Array.from(container.querySelectorAll('.customized-product-checkbox'));
  const syncPageSelect = () => {
    const checked = pageChecks.filter(cb => cb.checked).length;
    if (pageSelect) {
      pageSelect.checked = pageChecks.length > 0 && checked === pageChecks.length;
      pageSelect.indeterminate = checked > 0 && checked < pageChecks.length;
    }
    if (pageInfo) {
      pageInfo.textContent = `本页 ${products.length} 条，已选 ${customizedProductState.selectedKeys.size} 条`;
    }
  };
  pageSelect?.addEventListener('change', () => {
    pageChecks.forEach(cb => {
      cb.checked = pageSelect.checked;
      setCustomizedSelectedKey(getCustomizedCheckboxKey(cb), pageSelect.checked);
    });
    syncPageSelect();
  });
  pageChecks.forEach(cb => cb.addEventListener('change', (event) => {
    setCustomizedSelectedKey(getCustomizedCheckboxKey(event.currentTarget), event.currentTarget.checked);
    syncPageSelect();
  }));
  container.querySelectorAll('.customized-product-select').forEach(selectCell => {
    selectCell.addEventListener('click', (event) => {
      const checkbox = selectCell.querySelector('.customized-product-checkbox');
      if (!checkbox || event.target === checkbox) return;
      checkbox.checked = !checkbox.checked;
      checkbox.dispatchEvent(new Event('change', { bubbles: true }));
    });
  });
  syncPageSelect();
}

// 渲染定制品商品列表分页
function renderCustomizedProductPagination() {
  const info = document.getElementById('customizedProductPaginationInfo');
  const btns = document.getElementById('customizedProductPaginationButtons');
  const container = document.getElementById('customizedProductPaginationContainer');
  if (!info || !btns) return;

  const filteredProducts = getCustomizedFilteredProducts();
  const totalPages = Math.max(1, Math.ceil(filteredProducts.length / Math.max(1, customizedProductState.pageSize || 20)));
  if (container) container.style.display = filteredProducts.length > 0 ? 'flex' : 'none';

  const s = customizedProductState;
  if (filteredProducts.length > 0) {
    const hasFilter = Boolean(getCustomizedSearchQuery()) || getCustomizedStatusTopStatus() !== null;
    const totalLabel = hasFilter
      ? `共 <strong>${filteredProducts.length}</strong> 条`
      : `共 <strong>${Number(s.sourceTotal || filteredProducts.length || 0)}</strong> 条`;
    info.innerHTML = `${totalLabel}，第 <strong>${s.pageNum}</strong>/<strong>${totalPages}</strong> 页${s.syncedAt ? `，上次同步时间 <strong>${escapeHtml(s.syncedAt)}</strong>` : ''}`;
  } else {
    info.innerHTML = s.syncedAt ? `暂无数据，上次同步时间 <strong>${escapeHtml(s.syncedAt)}</strong>` : '暂无数据';
  }

  // 分页按钮
  btns.innerHTML = '';
  if (s.pageNum > 1) {
    const prev = document.createElement('button');
    prev.className = 'btn btn-default btn-sm';
    prev.textContent = '上一页';
    prev.addEventListener('click', () => loadCustomizedProducts({ pageNum: s.pageNum - 1 }));
    btns.appendChild(prev);
  }
  if (s.pageNum < totalPages) {
    const next = document.createElement('button');
    next.className = 'btn btn-default btn-sm';
    next.textContent = '下一页';
    next.addEventListener('click', () => loadCustomizedProducts({ pageNum: s.pageNum + 1 }));
    btns.appendChild(next);
  }

  if (filteredProducts.length > 0) {
    const jumpWrap = document.createElement('div');
    jumpWrap.style.display = 'flex';
    jumpWrap.style.alignItems = 'center';
    jumpWrap.style.gap = '6px';
    jumpWrap.style.marginLeft = '8px';
    jumpWrap.innerHTML = `
      <span style="font-size:12px;color:#64748b;">跳转</span>
      <input type="number" min="1" max="${totalPages}" value="${s.pageNum}" style="width:72px;padding:4px 8px;border:1px solid #cbd5e1;border-radius:8px;font-size:12px;">
      <button class="btn btn-default btn-sm" type="button">确定</button>
    `;
    const input = jumpWrap.querySelector('input');
    const button = jumpWrap.querySelector('button');
    button.addEventListener('click', () => {
      const pageNum = Math.max(1, Math.min(totalPages, parseInt(input.value, 10) || 1));
      loadCustomizedProducts({ pageNum });
    });
    btns.appendChild(jumpWrap);
  }
}

const customizedState = {
  templates: [],       // 定制品模板列表
  isRunning: false,
  isDelisting: false,
  isPublishing: false
};

function normalizeCustomizedTemplateRecord(template = {}) {
  const templateIds = [...new Set(
    (Array.isArray(template.templateIds) ? template.templateIds : [template.templateId, template.template_id])
      .map(item => String(item || '').trim())
      .filter(Boolean)
  )];
  const surfaces = Array.isArray(template.surfaces)
    ? template.surfaces.filter(item => item && typeof item === 'object')
    : [];
  const primaryTemplateId = String(template.templateId || template.template_id || templateIds[0] || '').trim();
  const skuId = String(template.skuId || template.productSkuId || '').trim();
  const spuId = String(template.spuId || template.goodsId || '').trim();
  const createdAt = template.createdAt || '';

  return {
    ...template,
    id: String(template.id || (template.serverId ? `server_customized_${template.serverId}` : `${primaryTemplateId || 'template'}__${skuId || 'sku'}__${spuId || 'spu'}__${createdAt || 'legacy'}`)),
    templateId: primaryTemplateId,
    templateIds: templateIds.length ? templateIds : (primaryTemplateId ? [primaryTemplateId] : []),
    surfaces,
    skuId,
    spuId,
    sourceProductId: String(template.sourceProductId || template.productId || template.goodsId || '').trim(),
    sourceGoodsId: String(template.sourceGoodsId || template.goodsId || template.spuId || '').trim(),
    sourceTemplateSkuId: String(template.sourceTemplateSkuId || template.skuId || template.productSkuId || '').trim()
  };
}

function normalizeServerCustomizedTemplate(row = {}) {
  const data = row.data && typeof row.data === 'object' ? row.data : {};
  return normalizeCustomizedTemplateRecord({
    ...data,
    id: data.id || `server_customized_${row.id}`,
    serverId: row.id,
    name: data.name || row.name || '未命名模板',
    createdAt: data.createdAt || row.createdAt || row.updatedAt || ''
  });
}

function mergeCustomizedTemplates(...lists) {
  const merged = new Map();
  lists.flat().forEach(template => {
    if (!template || typeof template !== 'object') return;
    const normalized = normalizeCustomizedTemplateRecord(template);
    const fingerprintParts = [
      (normalized.templateIds || [normalized.templateId]).join('|'),
      normalized.sourceTemplateSkuId || normalized.skuId || '',
      normalized.spuId || '',
      normalized.name || ''
    ].map(item => String(item || '').trim()).filter(Boolean);
    const fingerprint = fingerprintParts.length ? `fp:${fingerprintParts.join('::')}` : '';
    const key = normalized.serverId ? `server:${normalized.serverId}` : (fingerprint || `local:${normalized.id}`);
    const existing = fingerprint ? merged.get(fingerprint) : null;
    if (existing && !existing.serverId && normalized.serverId) {
      merged.delete(fingerprint);
    } else if (existing && existing.serverId && !normalized.serverId) {
      return;
    }
    merged.set(key, normalized);
    if (fingerprint && key !== fingerprint && !merged.has(fingerprint)) merged.set(fingerprint, normalized);
  });
  return Array.from(new Map(Array.from(merged.values()).map(item => [item.serverId ? `server:${item.serverId}` : item.id, item])).values());
}

// 定制品模板 storage key
function getCustomizedTemplateKey(mallId) {
  return mallId ? `temuCustomizedTemplates_${mallId}` : 'temuCustomizedTemplates';
}

// 初始化定制品 Tab
function initCustomizedTab() {
  document.querySelectorAll('#customized button').forEach(btn => {
    if (!btn.getAttribute('type')) btn.setAttribute('type', 'button');
  });
  bindCustomizedExportButton();
  // 加载模板
  loadCustomizedTemplates();

  // 状态Tab点击
  document.getElementById('customizedProductStatusTabs')?.addEventListener('click', (e) => {
    const tab = e.target.closest('.status-tab');
    if (!tab) return;
    const stage = tab.dataset.stage;
    // 切换active样式
    document.querySelectorAll('#customizedProductStatusTabs .status-tab').forEach(t => {
      t.style.background = '#f1f5f9';
      t.style.color = '#64748b';
      t.classList.remove('active');
    });
    tab.style.background = 'var(--primary)';
    tab.style.color = 'white';
    tab.classList.add('active');
    // 同步下拉框
    const statusFilter = document.getElementById('customizedProductStatusFilter');
    if (statusFilter) statusFilter.value = stage;
    customizedProductState.pageNum = 1;
    loadCustomizedProducts();
  });

  // 商品列表
  document.getElementById('customizedProductLoadBtn')?.addEventListener('click', (event) => {
    event.preventDefault();
    void syncCustomizedProducts();
  });
  document.getElementById('customizedExportBtn')?.addEventListener('click', showCustomizedExportDialog);
  document.getElementById('customizedProductSearchBtn')?.addEventListener('click', (event) => {
    event.preventDefault();
    customizedProductState.pageNum = 1;
    loadCustomizedProducts();
  });
  document.getElementById('customizedProductSearch')?.addEventListener('keydown', (e) => {
    if (e.key !== 'Enter') return;
    e.preventDefault();
    customizedProductState.pageNum = 1;
    loadCustomizedProducts();
  });
  document.getElementById('customizedProductPageSize')?.addEventListener('change', () => {
    customizedProductState.pageSize = parseInt(document.getElementById('customizedProductPageSize')?.value) || 20;
    customizedProductState.pageNum = 1;
    loadCustomizedProducts();
  });
  document.getElementById('customizedProductStatusFilter')?.addEventListener('change', () => {
    customizedProductState.pageNum = 1;
    loadCustomizedProducts();
  });

  // 切换到定制品tab时自动加载商品列表
  document.querySelector('[data-tab="customized"]')?.addEventListener('click', (event) => {
    event.preventDefault();
    if (customizedProductState.products.length || customizedProductState.allProducts.length) {
      loadCustomizedProducts();
      return;
    }
    loadCustomizedProductCache().then((loaded) => {
      if (loaded) loadCustomizedProducts();
    }).catch(err => console.warn('[小青蛙电商助手] 切换定制品时读取服务器缓存失败:', err));
  });

  // 新增模板
  document.getElementById('customizedAddTemplateBtn')?.addEventListener('click', (event) => {
    event.preventDefault();
    showAddCustomizedTemplateDialog(event);
  });

  document.getElementById('customizedTemplateSelect')?.addEventListener('change', (event) => {
    renderCustomizedTemplates();
  });
  document.getElementById('refreshCustomizedTemplatesBtn')?.addEventListener('click', refreshCustomizedTemplatesFromButton);

  document.getElementById('customizedApplyStock')?.addEventListener('change', (event) => {
    const input = document.getElementById('customizedStockQuantity');
    if (!input) return;
    input.disabled = !event.target.checked;
    input.style.background = event.target.checked ? 'var(--surface)' : '#f8fafc';
    if (event.target.checked) input.focus();
  });

  document.getElementById('customizedStartBtn')?.addEventListener('click', startCustomizedApply);
  document.getElementById('customizedDelistBtn')?.addEventListener('click', startCustomizedBatchDelist);
  document.getElementById('customizedDelistClearBtn')?.addEventListener('click', (event) => {
    event.preventDefault();
    const input = document.getElementById('customizedDelistSpuIds');
    if (input) input.value = '';
  });
  document.getElementById('customizedPublishBtn')?.addEventListener('click', startCustomizedBatchPublish);
  document.getElementById('customizedPublishClearBtn')?.addEventListener('click', (event) => {
    event.preventDefault();
    const input = document.getElementById('customizedPublishSpuIds');
    if (input) input.value = '';
  });

  const initialContainer = document.getElementById('customizedProductListContainer');
  if (initialContainer && !customizedProductState.cacheLoaded) {
    initialContainer.innerHTML = '<div class="empty"><div class="empty-icon">📦</div><p>打开定制品 TAB 后将显示服务器上次同步数据；如需最新数据，请点击“同步商品列表”。</p></div>';
  }
}

async function deleteSelectedCustomizedTemplate() {
    const select = document.getElementById('customizedTemplateSelect');
    const templateKey = select?.value;
    if (!templateKey) { showToast('请先选择要删除的模板', 'error'); return; }
    const template = customizedState.templates.find(t => t.id === templateKey);
    if (!template) return;
    if (!confirm(`确定删除模板「${template.name}」吗？`)) return;

    if (template.serverId) {
      const result = await deleteUserTemplate(template.serverId);
      if (!result.success) {
        showToast(`删除失败：${result.error || '服务器删除失败'}`, 'error');
        return;
      }
    }

    customizedState.templates = customizedState.templates.filter(t => t.id !== templateKey);
    const mallId = await resolveCurrentMallIdForStorage();
    await safeStorageSet({ [getCustomizedTemplateKey(mallId)]: customizedState.templates }, '定制品模板');
    renderCustomizedTemplates();
    showToast(`模板「${template.name}」已删除`, 'info');
}

// 加载定制品模板
async function loadCustomizedTemplates() {
  const mallId = await resolveCurrentMallIdForStorage();
  const key = getCustomizedTemplateKey(mallId);
  const [localResult, serverResult] = await Promise.all([
    chrome.storage.local.get(key),
    fetchUserTemplates('customized', mallId).catch(err => ({ success: false, error: err.message }))
  ]);
  const localTemplates = (Array.isArray(localResult[key]) ? localResult[key] : []).map(normalizeCustomizedTemplateRecord);
  const serverTemplates = serverResult?.success
    ? serverResult.templates.map(normalizeServerCustomizedTemplate)
    : [];
  customizedState.templates = mergeCustomizedTemplates(serverTemplates, localTemplates);
  if (customizedState.templates.length) {
    await safeStorageSet({ [key]: customizedState.templates }, '定制品模板');
  }
  renderCustomizedTemplates();
}

async function refreshCustomizedTemplatesFromButton(event) {
  const btn = event?.currentTarget;
  const oldText = btn?.textContent || '刷新';
  try {
    if (btn) {
      btn.disabled = true;
      btn.textContent = '刷新中...';
    }
    await loadCustomizedTemplates();
    showToast(`定制品模板已刷新，共 ${customizedState.templates.length} 个`, 'success');
  } catch (error) {
    showToast(`刷新定制品模板失败：${error?.message || error || '未知错误'}`, 'error');
  } finally {
    if (btn) {
      btn.disabled = false;
      btn.textContent = oldText;
    }
  }
}

// 渲染模板列表
function renderCustomizedTemplates() {
  const selectEl = document.getElementById('customizedTemplateSelect');
  const inlineList = document.getElementById('customizedTemplateInlineList');
  if (!selectEl) return;

  // 更新下拉选择
  const currentVal = selectEl.value;
  selectEl.innerHTML = '<option value="">-- 请选择模板 --</option>';
  if (inlineList) inlineList.innerHTML = '';
  customizedState.templates.forEach(tpl => {
    const opt = document.createElement('option');
    opt.value = tpl.id;
    opt.textContent = `${tpl.name}（SKU: ${tpl.skuId}）`;
    if (tpl.id === currentVal) opt.selected = true;
    selectEl.appendChild(opt);

    if (inlineList) {
      const item = document.createElement('div');
      item.tabIndex = 0;
      item.setAttribute('role', 'button');
      item.className = 'template-inline-item';
      item.dataset.templateId = String(tpl.id);
      item.title = tpl.name;
      if (tpl.id === currentVal) item.classList.add('active');

      const name = document.createElement('span');
      name.className = 'template-inline-name';
      name.textContent = `${tpl.name}（SKU: ${tpl.skuId}）`;

      const remove = document.createElement('button');
      remove.type = 'button';
      remove.className = 'template-inline-delete';
      remove.textContent = 'X';
      remove.title = `删除模板「${tpl.name}」`;
      remove.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        selectEl.value = String(tpl.id);
        void deleteSelectedCustomizedTemplate();
      });

      item.append(name, remove);
      item.addEventListener('click', () => {
        selectEl.value = String(tpl.id);
        renderCustomizedTemplates();
      });
      item.addEventListener('keydown', (event) => {
        if (event.key !== 'Enter' && event.key !== ' ') return;
        event.preventDefault();
        selectEl.value = String(tpl.id);
        renderCustomizedTemplates();
      });
      inlineList.appendChild(item);
    }
  });
}

function showCenterConfirm({ title = '确认操作', message = '', confirmText = '确认', cancelText = '取消' } = {}) {
  return new Promise((resolve) => {
    const modal = document.createElement('div');
    modal.className = 'center-confirm-mask';
    modal.innerHTML = `
      <div class="center-confirm-card">
        <div class="center-confirm-title">${escapeHtml(title)}</div>
        <div class="center-confirm-body">${escapeHtml(message)}</div>
        <div class="center-confirm-actions">
          <button class="btn btn-default btn-sm" type="button" data-action="cancel">${escapeHtml(cancelText)}</button>
          <button class="btn btn-primary btn-sm" type="button" data-action="confirm">${escapeHtml(confirmText)}</button>
        </div>
      </div>
    `;
    document.body.appendChild(modal);
    const close = (value) => {
      if (modal.parentNode) modal.parentNode.removeChild(modal);
      resolve(value);
    };
    modal.addEventListener('click', (event) => {
      if (event.target === modal) return close(false);
      const action = event.target?.closest?.('button[data-action]')?.dataset?.action;
      if (action === 'cancel') close(false);
      if (action === 'confirm') close(true);
    });
  });
}

// 显示新增模板弹窗
function showAddCustomizedTemplateDialog() {
  const modal = document.createElement('div');
  modal.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:10000;';
  modal.innerHTML = `
    <div style="background:white;padding:24px;border-radius:12px;width:460px;max-width:90vw;max-height:80vh;overflow-y:auto;box-shadow:0 8px 24px rgba(0,0,0,0.2);">
      <h2 style="margin:0 0 16px;font-size:16px;">➕ 新增定制品模板</h2>
      <div style="margin-bottom:14px;">
        <label style="display:block;margin-bottom:6px;font-size:13px;font-weight:500;">SPU ID（商品 ID）<span style="color:#ff4d4f;">*</span></label>
        <input type="text" id="newTemplateSpuId" placeholder="输入商品的 SPU ID（商品 ID）" style="width:100%;padding:8px 12px;border:1px solid #d9d9d9;border-radius:6px;font-size:14px;" autofocus>
        <div style="font-size:11px;color:#999;margin-top:4px;">💡 在 TEMU 商品列表的「商品ID」列可以找到。该商品下至少有一个 SKU 已配置定制品信息。</div>
      </div>
      <div style="margin-bottom:14px;">
        <label style="display:block;margin-bottom:6px;font-size:13px;font-weight:500;">模板名称 <span style="color:#ff4d4f;">*</span></label>
        <input type="text" id="newTemplateName" placeholder="例如：宠物照片毯-模板1" style="width:100%;padding:8px 12px;border:1px solid #d9d9d9;border-radius:6px;font-size:14px;" value="">
      </div>
      <div id="templateSkuList" style="display:none;margin-bottom:14px;">
        <label style="display:block;margin-bottom:6px;font-size:13px;font-weight:500;">选择已配置定制品的 SKU <span style="color:#ff4d4f;">*</span></label>
        <div id="templateSkuOptions" style="max-height:200px;overflow-y:auto;border:1px solid #d9d9d9;border-radius:6px;padding:4px;"></div>
      </div>
      <div style="display:flex;gap:10px;justify-content:flex-end;">
        <button class="btn btn-default" id="closeTemplateDialogBtn">取消</button>
        <button class="btn btn-primary" id="queryTemplateBtn">🔍 查询已配置的 SKU</button>
        <button class="btn btn-primary" id="confirmAddTemplateBtn" style="display:none;">✅ 确认保存模板</button>
      </div>
    </div>
  `;
  document.body.appendChild(modal);

  const close = () => document.body.removeChild(modal);
  modal.querySelector('#closeTemplateDialogBtn').addEventListener('click', close);
  modal.addEventListener('click', (e) => { if (e.target === modal) close(); });

  let foundSkus = []; // 存储查到的已配置定制品的 SKU
  let templateSourceData = null;

  // 查询按钮
  modal.querySelector('#queryTemplateBtn').addEventListener('click', async () => {
    const spuId = modal.querySelector('#newTemplateSpuId')?.value?.trim() || '';
    const name = modal.querySelector('#newTemplateName')?.value?.trim() || '';

    if (!spuId || !/^\d+$/.test(spuId)) { showToast('请输入有效的 SPU ID', 'error'); return; }
    if (!name) { showToast('请输入模板名称', 'error'); return; }

    const queryBtn = modal.querySelector('#queryTemplateBtn');
    queryBtn.disabled = true;
    queryBtn.textContent = '⏳ 查询中...';

    try {
      let data = findCustomizedTemplateDataLocally(spuId);
      if (!data?.foundSkus?.length) {
        const listResult = await safeRuntimeSendMessage({
          action: 'fetchCustomizedProductList',
          params: { pageNo: 1, pageSize: 100, productId: parseInt(spuId) }
        }, { success: false, error: '后台查询商品失败' });
        if (listResult?.success && Array.isArray(listResult.products) && listResult.products.length > 0) {
          const match = listResult.products.find(item => (
            String(item.goodsId || '') === String(spuId)
            || String(item.productId || '') === String(spuId)
            || String(item.skcId || '') === String(spuId)
          )) || listResult.products[0];
          const skuList = Array.isArray(match?.skuList) ? match.skuList : [];
          const allSkuIds = skuList.map(sku => sku.skuId || sku.productSkuId || '').filter(Boolean);
          const foundSkus = skuList
            .map(sku => {
              const tmpl = sku.productSkuCustomizationTmpl || sku.productSkuSaleExtAttr?.productSkuCustomizationTmpl || null;
              const templateIds = Array.isArray(tmpl?.templateIds)
                ? tmpl.templateIds
                : [tmpl?.templateId, tmpl?.template_id, tmpl?.id, sku.templateId].filter(Boolean);
              return {
                productSkuId: sku.skuId || sku.productSkuId || '',
                templateId: templateIds[0] || '',
                templateIds,
                specInfo: sku.skuSpec || sku.specInfo || ''
              };
            })
            .filter(sku => sku.productSkuId && (sku.templateId || sku.templateIds.length));
          data = allSkuIds.length || foundSkus.length ? {
            productId: match.productId || match.skcId || match.goodsId || spuId,
            goodsId: match.goodsId || spuId,
            allSkuIds,
            foundSkus
          } : null;
        }
      }

      queryBtn.disabled = false;
      queryBtn.textContent = '🔍 查询已配置的 SKU';

      if (data && data.foundSkus) {
        foundSkus = Array.isArray(data.foundSkus) ? data.foundSkus : [];
        templateSourceData = data;

        if (foundSkus.length === 0) {
          showToast('该商品下没有 SKU 配置了定制品信息', 'error');
          return;
        }

        // 显示 SKU 选择列表
        const skuListDiv = modal.querySelector('#templateSkuList');
        const skuOptionsDiv = modal.querySelector('#templateSkuOptions');
        if (!skuListDiv || !skuOptionsDiv) throw new Error('模板弹窗未初始化完成');
        skuListDiv.style.display = 'block';

        skuOptionsDiv.innerHTML = foundSkus.map((sku, idx) => `
          <label style="display:flex;align-items:center;gap:8px;padding:8px;border-radius:4px;cursor:pointer;${idx === 0 ? 'background:#e6f7ff;' : ''}" class="sku-option-label">
            <input type="radio" name="selectedSkuForTemplate" value="${idx}" ${idx === 0 ? 'checked' : ''}>
            <div style="flex:1;">
              <div style="font-size:13px;font-weight:500;">SKU ID: ${escapeHtml(String(sku.productSkuId || '-'))}</div>
              <div style="font-size:11px;color:#999;">规格: ${escapeHtml(String(sku.specInfo || '-'))} | 模板ID: ${escapeHtml(String((sku.templateIds || [sku.templateId]).filter(Boolean).join(' / ') || '-'))}</div>
            </div>
          </label>
        `).join('');

        // 选中高亮
        skuOptionsDiv.querySelectorAll('.sku-option-label').forEach(label => {
          label.addEventListener('click', () => {
            skuOptionsDiv.querySelectorAll('.sku-option-label').forEach(l => l.style.background = 'none');
            label.style.background = '#e6f7ff';
          });
        });

        // 显示确认按钮
        modal.querySelector('#confirmAddTemplateBtn').style.display = 'inline-block';

        showToast(`找到 ${foundSkus.length} 个已配置定制品的 SKU`, 'success');
      } else {
        throw new Error('未找到该商品已配置定制品的 SKU，请先同步定制品商品列表');
      }
    } catch (err) {
      queryBtn.disabled = false;
      queryBtn.textContent = '🔍 查询已配置的 SKU';
      showToast(`查询失败: ${err?.message || String(err)}`, 'error');
    }
  });

  // 确认保存
  modal.querySelector('#confirmAddTemplateBtn').addEventListener('click', async () => {
    const name = modal.querySelector('#newTemplateName')?.value?.trim() || '';
    const spuId = modal.querySelector('#newTemplateSpuId')?.value?.trim() || '';

    // 获取选中的 SKU
    const selectedIdx = modal.querySelector('input[name="selectedSkuForTemplate"]:checked');
    if (!selectedIdx) { showToast('请选择一个 SKU', 'error'); return; }

    const selectedSku = foundSkus[parseInt(selectedIdx.value)];
    if (!selectedSku) { showToast('选择的 SKU 无效', 'error'); return; }

    const newTemplate = {
      id: `customized_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
      templateId: String(selectedSku.templateId || selectedSku.templateIds?.[0] || ''),
      skuId: String(selectedSku.productSkuId),
      spuId: spuId,
      name: name,
      specInfo: selectedSku.specInfo || '',
      templateIds: selectedSku.templateIds || [selectedSku.templateId].filter(Boolean),
      surfaces: Array.isArray(selectedSku.surfaces) ? selectedSku.surfaces : [],
      sourceProductId: String(templateSourceData?.productId || ''),
      sourceGoodsId: String(templateSourceData?.goodsId || ''),
      sourceTemplateSkuId: String(selectedSku.productSkuId || ''),
      createdAt: new Date().toISOString()
    };

    const serverResult = await saveUserTemplate('customized', newTemplate);
    if (!serverResult.success) {
      showToast(`模板保存失败：${serverResult.error || '服务器保存失败'}`, 'error');
      return;
    }
    if (serverResult.id) newTemplate.serverId = serverResult.id;

    customizedState.templates.push(normalizeCustomizedTemplateRecord(newTemplate));
    const mallId = await resolveCurrentMallIdForStorage();
    await safeStorageSet({
      [getCustomizedTemplateKey(mallId)]: customizedState.templates
    }, '定制品模板');

    renderCustomizedTemplates();
    close();
    showToast(`保存成功：模板「${name}」已保存，来源 SKU: ${selectedSku.productSkuId}`, 'success');
  });
}

function getCustomizedBatchActionTargetSkcIds(inputId) {
  const fromInput = document.getElementById(inputId)?.value || '';
  const checkedKeys = getCustomizedSelectedSpuIds();
  const products = Array.isArray(customizedProductState.allProducts) ? customizedProductState.allProducts : [];
  const fromChecked = products
    .filter(product => checkedKeys.includes(getCustomizedProductKey(product)))
    .map(product => String(product.productId || product.skcId || product.goodsId || product.spuId || '').trim())
    .filter(Boolean);
  const fromTyped = fromInput
    .split(/[,，\s\n]+/)
    .map(spu => spu.trim())
    .filter(spu => /^\d+$/.test(spu));
  return Array.from(new Set([...fromChecked, ...fromTyped]));
}

function getCustomizedDelistTargetSkcIds() {
  return getCustomizedBatchActionTargetSkcIds('customizedDelistSpuIds');
}

function getCustomizedPublishTargetSkcIds() {
  return getCustomizedBatchActionTargetSkcIds('customizedPublishSpuIds');
}

const customizedBatchActionProgressTimers = new Map();

function createCustomizedBatchActionTaskId(action) {
  return `customized-${action}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}

function getCustomizedBatchActionProgressElements(action) {
  const prefix = action === 'publish' ? 'customizedPublish' : 'customizedDelist';
  return {
    section: document.getElementById(`${prefix}ProgressSection`),
    fill: document.getElementById(`${prefix}ProgressFill`),
    text: document.getElementById(`${prefix}ProgressText`),
    list: document.getElementById(`${prefix}ResultList`)
  };
}

function resetCustomizedBatchActionProgress(action, total) {
  const els = getCustomizedBatchActionProgressElements(action);
  if (!els.section || !els.fill || !els.text || !els.list) return;
  els.section.style.display = 'block';
  els.fill.style.width = '0%';
  els.fill.className = 'progress-fill blue';
  els.text.textContent = `共 ${total} 个 SKC，等待后台开始处理...`;
  els.list.innerHTML = '';
}

function renderCustomizedBatchActionProgress(action, task = {}) {
  const els = getCustomizedBatchActionProgressElements(action);
  if (!els.section || !els.fill || !els.text || !els.list) return;
  const total = Math.max(Number(task.total || 0), 0);
  const completed = Math.min(Math.max(Number(task.completed || 0), 0), total || Number(task.completed || 0));
  const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
  const successCount = Number(task.successCount || 0);
  const failCount = Number(task.failCount || 0);
  const verb = action === 'publish' ? '上架' : '下架';
  const status = String(task.status || 'running');

  els.section.style.display = 'block';
  els.fill.style.width = `${Math.max(0, Math.min(100, percent))}%`;
  if (status === 'failed') {
    els.fill.className = 'progress-fill red';
  } else if (status === 'done' && failCount === 0) {
    els.fill.className = 'progress-fill green';
  } else {
    els.fill.className = 'progress-fill blue';
  }

  const currentText = task.currentSkcId ? `，当前 SKC ${task.currentSkcId}` : '';
  const errorText = task.error ? `，错误：${task.error}` : '';
  els.text.textContent = `${verb}进度：${completed}/${total || '?'}，成功 ${successCount}，失败 ${failCount}${currentText}${errorText}`;

  const rows = Array.isArray(task.results) ? task.results : [];
  if (rows.length) {
    els.list.innerHTML = rows.slice(-30).map(item => {
      const ok = Boolean(item?.success);
      const skcId = escapeHtml(item?.skcId || '');
      const productName = item?.productName ? ` · ${escapeHtml(item.productName)}` : '';
      const error = item?.error ? ` · ${escapeHtml(item.error)}` : '';
      return `<div class="result-item ${ok ? 'success' : 'fail'}">${ok ? '成功' : '失败'}：SKC ${skcId}${productName}${error}</div>`;
    }).join('');
  }
}

function stopCustomizedBatchActionProgressPolling(taskId) {
  const timer = customizedBatchActionProgressTimers.get(taskId);
  if (timer) {
    clearInterval(timer);
    customizedBatchActionProgressTimers.delete(taskId);
  }
}

function startCustomizedBatchActionProgressPolling(action, taskId) {
  stopCustomizedBatchActionProgressPolling(taskId);
  const poll = async () => {
    const result = await safeRuntimeSendMessage({
      action: 'getCustomizedBatchActionProgress',
      taskId
    }, null, 6000);
    if (result?.success && result.task) {
      renderCustomizedBatchActionProgress(action, result.task);
      if (['done', 'failed'].includes(String(result.task.status || ''))) {
        stopCustomizedBatchActionProgressPolling(taskId);
      }
    }
  };
  poll();
  const timer = setInterval(poll, 500);
  customizedBatchActionProgressTimers.set(taskId, timer);
}

function renderCustomizedBatchActionResult(action, taskId, skcIds, result, error = null) {
  stopCustomizedBatchActionProgressPolling(taskId);
  const results = Array.isArray(result?.results) ? result.results : [];
  const successCount = Number(result?.successCount || results.filter(item => item?.success).length || 0);
  const failCount = Number(result?.failCount || Math.max(0, (result?.total || skcIds.length) - successCount));
  renderCustomizedBatchActionProgress(action, {
    status: error || !result?.success ? 'failed' : 'done',
    total: Number(result?.total || skcIds.length),
    completed: Number(result?.total || skcIds.length),
    successCount,
    failCount,
    results,
    error: error?.message || result?.error || ''
  });
}

async function startCustomizedBatchDelist() {
  const event = arguments[0];
  if (event?.preventDefault) event.preventDefault();
  if (event?.stopPropagation) event.stopPropagation();

  if (customizedState.isDelisting) {
    showToast('批量下架正在执行，请稍候', 'info');
    return;
  }

  const skcIds = getCustomizedDelistTargetSkcIds();
  if (skcIds.length === 0) {
    showToast('请先勾选要下架的商品，或手动输入 SKC', 'error');
    return;
  }

  const preview = skcIds.slice(0, 10).join('、');
  const confirmed = await showCenterConfirm({
    title: '确认批量下架',
    message: `将尝试下架 ${skcIds.length} 个 SKC：\n\n${preview}${skcIds.length > 10 ? ' ...' : ''}`,
    confirmText: '确认下架',
    cancelText: '取消'
  });
  if (!confirmed) {
    return;
  }

  customizedState.isDelisting = true;
  const progressTaskId = createCustomizedBatchActionTaskId('delist');
  resetCustomizedBatchActionProgress('delist', skcIds.length);
  startCustomizedBatchActionProgressPolling('delist', progressTaskId);
  const delistBtn = document.getElementById('customizedDelistBtn');
  const originalText = delistBtn?.textContent || '⛔ 批量下架';
  if (delistBtn) {
    delistBtn.disabled = true;
    delistBtn.textContent = '⏳ 执行中...';
  }

  try {
    const result = await safeRuntimeSendMessage({
      action: 'batchDelistCustomizedSkc',
      params: { skcIds, progressTaskId }
    }, { success: false, error: '后台下架请求失败' }, Math.max(45000, skcIds.length * 4000));

    renderCustomizedBatchActionResult('delist', progressTaskId, skcIds, result);
    if (result?.success) {
      const failText = result.failCount ? `，失败 ${result.failCount} 个` : '';
      showToast(`已提交下架申请：成功 ${result.successCount || skcIds.length} 个${failText}`, result.failCount ? 'warning' : 'success');
    } else {
      const submitError = new Error(result?.error || '未能完成下架操作');
      submitError.progressResult = result;
      throw submitError;
    }
  } catch (error) {
    renderCustomizedBatchActionResult('delist', progressTaskId, skcIds, error?.progressResult || null, error);
    showToast(`批量下架失败：${error.message}`, 'error');
  } finally {
    stopCustomizedBatchActionProgressPolling(progressTaskId);
    customizedState.isDelisting = false;
    if (delistBtn) {
      delistBtn.disabled = false;
      delistBtn.textContent = originalText;
    }
  }
}

async function startCustomizedBatchPublish() {
  const event = arguments[0];
  if (event?.preventDefault) event.preventDefault();
  if (event?.stopPropagation) event.stopPropagation();

  if (customizedState.isPublishing) {
    showToast('批量上架正在执行，请稍候', 'info');
    return;
  }

  const skcIds = getCustomizedPublishTargetSkcIds();
  if (skcIds.length === 0) {
    showToast('请先勾选要上架的商品，或手动输入 SKC', 'error');
    return;
  }

  const preview = skcIds.slice(0, 10).join('、');
  const confirmed = await showCenterConfirm({
    title: '确认批量上架',
    message: `将尝试上架 ${skcIds.length} 个 SKC：\n\n${preview}${skcIds.length > 10 ? ' ...' : ''}`,
    confirmText: '确认上架',
    cancelText: '取消'
  });
  if (!confirmed) {
    return;
  }

  customizedState.isPublishing = true;
  const progressTaskId = createCustomizedBatchActionTaskId('publish');
  resetCustomizedBatchActionProgress('publish', skcIds.length);
  startCustomizedBatchActionProgressPolling('publish', progressTaskId);
  const publishBtn = document.getElementById('customizedPublishBtn');
  const originalText = publishBtn?.textContent || '✅ 批量上架';
  if (publishBtn) {
    publishBtn.disabled = true;
    publishBtn.textContent = '⏳ 执行中...';
  }

  try {
    const result = await safeRuntimeSendMessage({
      action: 'batchPublishCustomizedSkc',
      params: { skcIds, progressTaskId }
    }, { success: false, error: '后台上架请求失败' }, Math.max(45000, skcIds.length * 4000));

    renderCustomizedBatchActionResult('publish', progressTaskId, skcIds, result);
    if (result?.success) {
      const failText = result.failCount ? `，失败 ${result.failCount} 个` : '';
      showToast(`已提交上架申请：成功 ${result.successCount || skcIds.length} 个${failText}`, result.failCount ? 'warning' : 'success');
    } else {
      const submitError = new Error(result?.error || '未能完成上架操作');
      submitError.progressResult = result;
      throw submitError;
    }
  } catch (error) {
    renderCustomizedBatchActionResult('publish', progressTaskId, skcIds, error?.progressResult || null, error);
    showToast(`批量上架失败：${error.message}`, 'error');
  } finally {
    stopCustomizedBatchActionProgressPolling(progressTaskId);
    customizedState.isPublishing = false;
    if (publishBtn) {
      publishBtn.disabled = false;
      publishBtn.textContent = originalText;
    }
  }
}
// 开始批量填写定制品信息
async function startCustomizedApply() {
  const event = arguments[0];
  if (event?.preventDefault) event.preventDefault();
  if (event?.stopPropagation) event.stopPropagation();

  if (customizedState.isRunning) { showToast('正在填写中，请稍候', 'info'); return; }

  const templateSelect = document.getElementById('customizedTemplateSelect');
  const selectedTemplateKey = templateSelect?.value || '';
  if (!selectedTemplateKey) { showToast('请先选择一个定制品模板', 'error'); return; }

  const template = customizedState.templates.find(t => t.id === selectedTemplateKey);
  if (!template) { showToast('模板不存在', 'error'); return; }

  const uploadScope = document.getElementById('customizedUploadScope')?.value || 'selected';
  const targetSpuIds = resolveCustomizedTargetSpuIds(uploadScope);
  if (targetSpuIds.length === 0) {
    const hasSyncedProducts = Array.isArray(customizedProductState.allProducts) && customizedProductState.allProducts.length > 0;
    if (uploadScope === 'selected') {
      showToast('请先在左侧勾选商品，或手动输入 SPU ID', 'warning');
    } else if (!hasSyncedProducts) {
      showToast('请先点击「同步商品列表」，再按上传范围批量填写', 'warning');
    } else if (uploadScope === 'pending') {
      showToast('当前没有待处理商品', 'warning');
    } else {
      showToast('当前没有可填写的商品', 'warning');
    }
    return;
  }

  const shouldApplyStock = Boolean(document.getElementById('customizedApplyStock')?.checked);
  const stockInputValue = document.getElementById('customizedStockQuantity')?.value?.trim() || '';
  let stockQuantity = null;
  if (shouldApplyStock) {
    if (stockInputValue === '') {
      showToast('请输入商品库存数量', 'error');
      return;
    }
    stockQuantity = Number(stockInputValue);
    if (!Number.isInteger(stockQuantity) || stockQuantity < 0) {
      showToast('商品库存必须是大于等于 0 的整数', 'error');
      return;
    }
  }

  customizedState.isRunning = true;
  const startBtn = document.getElementById('customizedStartBtn');
  if (startBtn) {
    startBtn.disabled = true;
    startBtn.textContent = '⏳ 填写中...';
  }

  const progressSection = document.getElementById('customizedProgressSection');
  const fill = document.getElementById('customizedProgressFill');
  const text = document.getElementById('customizedProgressText');
  const list = document.getElementById('customizedResultList');
  if (!progressSection || !fill || !text || !list) {
    customizedState.isRunning = false;
    if (startBtn) {
      startBtn.disabled = false;
      startBtn.textContent = '🚀 开始自动填写';
    }
    showToast('定制品填写界面未初始化完成，请刷新插件页后重试', 'error');
    return;
  }
  progressSection.style.display = 'block';
  fill.style.width = '0%'; fill.className = 'progress-fill blue';
  text.textContent = `共 ${targetSpuIds.length} 个商品，开始填写...`;
  list.innerHTML = '';

  let successCount = 0, failCount = 0;
  const delay = parseInt(document.getElementById('uploadDelay')?.value) || 1000;

  try {
    for (let i = 0; i < targetSpuIds.length; i++) {
      const spuId = targetSpuIds[i];
      const pct = Math.round(((i + 1) / targetSpuIds.length) * 100);
      fill.style.width = `${pct}%`;
      text.textContent = `正在处理 SPU ${spuId}（${i + 1}/${targetSpuIds.length}）...`;

      try {
      const selectedTemplateIds = (template.templateIds || [template.templateId].filter(Boolean))
        .map(id => String(id || '').trim())
        .filter(Boolean);
      if (selectedTemplateIds.length === 0) {
        throw new Error('模板中没有可用的 templateId');
      }

      // 提交前由后台实时查询模板 SKU 和目标商品 SKU，避免使用本地缓存导致 TEMU 报 SKU 数据变更。
      const editResult = await safeRuntimeSendMessage({
        action: 'applyCustomizedTemplateToSkus',
        params: {
          productId: spuId,
          templateId: selectedTemplateIds[0],
          templateIds: selectedTemplateIds,
          templateSkuId: template.sourceTemplateSkuId || template.skuId,
          sourceTemplateSkuId: template.sourceTemplateSkuId || template.skuId,
          applyMode: 'all',
          applyStock: shouldApplyStock,
          stockQuantity: shouldApplyStock ? stockQuantity : null
        }
      }, { success: false, error: '后台填写定制品失败' }, 45000);

        if (editResult && editResult.success) {
          const cls = 'success';
          const icon = '✅';
          const appliedCount = Number(editResult.appliedSkuCount || editResult.editCount || 0);
          const msg = `${appliedCount || '目标商品'} 个 SKU 填写成功`;
          const item = document.createElement('div');
          item.className = `result-item ${cls}`;
          item.innerHTML = `<span class="icon">${icon}</span><span class="reason"><strong>SPU ${spuId}</strong> — ${msg}</span>`;
          list.appendChild(item);
          if (editResult.stockError) {
            const stockItem = document.createElement('div');
            stockItem.className = 'result-item warning';
            stockItem.innerHTML = `<span class="icon">⚠️</span><span class="reason"><strong>SPU ${spuId}</strong> — ${escapeHtml(editResult.stockError)}</span>`;
            list.appendChild(stockItem);
          } else if (editResult.stockApplied === true) {
            const stockItem = document.createElement('div');
            stockItem.className = 'result-item success';
            const stockModeText = editResult.stockMode === 'semiManaged' ? '半托管' : editResult.stockMode === 'fullManaged' ? '全托管' : '';
            const noChangeText = editResult.stockNoChange ? '，库存无需修改' : '';
            stockItem.innerHTML = `<span class="icon">✅</span><span class="reason"><strong>SPU ${spuId}</strong> — ${stockModeText ? `${stockModeText} ` : ''}库存 ${escapeHtml(editResult.stockQuantity ?? stockQuantity)} 已同步${noChangeText}</span>`;
            list.appendChild(stockItem);
          } else if (shouldApplyStock) {
            const stockItem = document.createElement('div');
            stockItem.className = 'result-item warning';
            stockItem.innerHTML = `<span class="icon">⚠️</span><span class="reason"><strong>SPU ${spuId}</strong> — 库存接口未返回成功，请在 TEMU 后台核对</span>`;
            list.appendChild(stockItem);
          }
          successCount++;
        } else {
          throw new Error(editResult?.error || '填写失败');
        }
      } catch (err) {
        const item = document.createElement('div');
        item.className = 'result-item fail';
        item.innerHTML = `<span class="icon">❌</span><span class="reason"><strong>SPU ${spuId}</strong> — ${escapeHtml(err.message)}</span>`;
        list.appendChild(item);
        failCount++;
      }

      if (i < targetSpuIds.length - 1) await new Promise(r => setTimeout(r, delay));
    }
  } catch (err) {
    const item = document.createElement('div');
    item.className = 'result-item fail';
    item.innerHTML = `<span class="icon">❌</span><span class="reason"><strong>任务中断</strong> — ${escapeHtml(err.message || String(err))}</span>`;
    list.appendChild(item);
    failCount++;
  } finally {
    fill.style.width = '100%';
    if (failCount === 0) fill.className = 'progress-fill green';
    else if (successCount === 0) fill.className = 'progress-fill red';
    else fill.className = 'progress-fill blue';
    text.textContent = `填写完成！✅ ${successCount} 成功, ❌ ${failCount} 失败`;

    customizedState.isRunning = false;
    if (startBtn) {
      startBtn.disabled = false;
      startBtn.textContent = '🚀 开始自动填写';
    }
    if (successCount > 0) {
      text.textContent += '，正在回读最新定制品数据...';
      try {
        await syncCustomizedProducts();
      } catch (refreshErr) {
        console.warn('[小青蛙电商助手] 定制品填写后回读最新列表失败:', refreshErr);
      }
    }
    showToast(`定制品信息填写完成！成功: ${successCount}, 失败: ${failCount}`, successCount > 0 ? 'success' : 'error');
  }
}

// ========== 销售数据 Tab ==========
const salesDataState = {
  pageNum: 1,
  pageSize: 20,
  totalPages: 0,
  total: 0,
  products: [],
  viewProducts: null,
  isExporting: false,
  allProducts: [],  // 所有商品（全量数据）
  loading: false,
  lastSyncAt: 0,
  sortBy: 'todayDesc'
};

function resetSalesDataState() {
  salesDataState.mallId = null;
  salesDataState.pageNum = 1;
  salesDataState.totalPages = 0;
  salesDataState.total = 0;
  salesDataState.products = [];
  salesDataState.viewProducts = null;
  salesDataState.allProducts = [];
  salesDataState.loading = false;
  salesDataState.lastSyncAt = 0;
  clearDashboardSalesVisuals();
}

function getSalesCacheKey() {
  return state.mallId ? `temuSalesDataCache_${state.mallId}` : 'temuSalesDataCache';
}

async function saveSalesDataCache() {
  const mallId = state.mallId || salesDataState.mallId || null;
  const cacheProducts = (Array.isArray(salesDataState.allProducts) ? salesDataState.allProducts : [])
    .filter(hasSalesProductSales)
    .map(product => {
      const skuRows = getSalesProductSkuRows(product);
      return {
        mallId,
        productId: product.productId || '',
        productCNName: product.productCNName || product.productName || '',
        productName: product.productName || product.productCNName || '',
        mainImage: product.mainImage || '',
        skcId: product.skcId || '',
        skcExtCode: product.skcExtCode || '',
        onSalesDuration: product.onSalesDuration || 0,
        createTime: product.createTime || '',
        score: product.score ?? '',
        todaySaleVolume: Number(product.todaySaleVolume || 0),
        sevenDaysSaleVolume: Number(product.sevenDaysSaleVolume || 0),
        thirtyDaysSaleVolume: Number(product.thirtyDaysSaleVolume || 0),
        skuQuantityDetailList: skuRows.map(row => ({
          productSkuId: row.skuId,
          className: row.skuName,
          skuExtCode: row.skuCode,
          supplierPrice: row.amountValue !== null && row.amountValue !== undefined ? Math.round(Number(row.amountValue) * 100) : null,
          todaySaleVolume: Number(row.todaySaleVolume || 0),
          lastSevenDaysSaleVolume: Number(row.sevenDaysSaleVolume || 0),
          lastThirtyDaysSaleVolume: Number(row.thirtyDaysSaleVolume || 0),
          availableSaleDays: row.availableSaleDays,
          stockDays: row.stockDays,
          adviceQuantity: row.adviceQuantity,
          purchaseConfig: row.purchaseConfig,
          warehouseAvailableSaleDays: row.availableSaleDays,
          shippedInventoryNum: Number(row.shippedInventoryNum || 0),
          inventoryNumInfo: {
            warehouseInventoryNum: Number(row.availableInventoryNum || 0),
            expectedOccupiedInventoryNum: Number(row.reservedInventoryNum || 0),
            unavailableWarehouseInventoryNum: Number(row.unavailableInventoryNum || 0),
            shippedInventoryNum: Number(row.shippedInventoryNum || 0)
          },
          warehouseInfoList: []
        }))
      };
    });
  const payload = {
    mallId,
    mallName: state.mallName || getCachedShopName(mallId) || '',
    products: cacheProducts,
    total: cacheProducts.length,
    sourceTotal: cacheProducts.length,
    pageSize: salesDataState.pageSize,
    lastSyncAt: salesDataState.lastSyncAt,
    syncedAt: salesDataState.lastSyncAt,
    sortBy: salesDataState.sortBy || 'todayDesc'
  };
  const legacyKey = mallId ? `temuSalesCache_${mallId}` : 'temuSalesCache';
  const modernKey = mallId ? `temuSalesDataCache_${mallId}` : getSalesCacheKey();
  await chrome.storage.local.remove([modernKey]);
  const saved = await safeStorageSet({ [legacyKey]: payload }, '销量缓存');
  if (!saved) {
    await safeStorageSet({
      [legacyKey]: {
        ...payload,
        products: cacheProducts.slice(0, 200),
        total: Math.min(cacheProducts.length, 200),
        sourceTotal: cacheProducts.length
      }
    }, '销量缓存摘要');
  }
  await saveTemuSnapshot('sales', payload, { mallId, itemCount: cacheProducts.length });
}

async function loadSalesDataCache() {
  const stored = await chrome.storage.local.get(['temuAuth', 'temuLocalMallId']);
  const currentMallId = state.mallId || stored?.temuAuth?.mallId || stored?.temuLocalMallId || null;
  const serverCache = currentMallId ? await fetchTemuSnapshot('sales', currentMallId) : null;
  const cache = serverCache && Array.isArray(serverCache.products) ? serverCache : null;

  if (!cache || !Array.isArray(cache.products)) {
    resetSalesDataState();
    updateSalesSyncDisplay();
    renderSalesDataTable();
    return false;
  }
  salesDataState.mallId = cache.mallId || currentMallId || null;
  salesDataState.allProducts = cache.products;
  salesDataState.products = cache.products;
  salesDataState.total = cache.total || cache.sourceTotal || cache.products.length;
  salesDataState.pageSize = cache.pageSize || salesDataState.pageSize;
  salesDataState.lastSyncAt = cache.lastSyncAt || cache.syncedAt || 0;
  salesDataState.sortBy = cache.sortBy || 'todayDesc';
  salesDataState.pageNum = 1;
  sortSalesProducts();
  applySalesSearchFilter();
  updateSalesSyncDisplay();
  const sortEl = document.getElementById('salesSortSelect');
  if (sortEl) sortEl.value = salesDataState.sortBy;
  const pageSizeEl = document.getElementById('salesPageSizeSelect');
  if (pageSizeEl) pageSizeEl.value = String(salesDataState.pageSize);
  updateSalesDashboardAll(cache.products);
  return true;
}

// 后台全量加载所有页面，更新仪表盘汇总（照抄大白鹅：逐页加载汇总）
async function loadAllPagesForDashboard(requestParams) {
  // 如果有搜索条件（搜SPU/名称），不需要全量加载
  if (requestParams.productSpuId || requestParams.productName) return;

  const total = salesDataState.total || 0;
  // 少于50条不需要全量加载，当前页就是全部
  if (total <= 50) return;

  // 显示加载提示
  const hint = document.getElementById('salesAllLoadingHint');
  if (hint) hint.style.display = 'block';

  try {
    const result = await safeRuntimeSendMessage({
      action: 'fetchSalesDataAll',
      params: { pageSize: 40 }  // 大白鹅用40
    }, { success: false, error: '全量销量数据加载失败' }, 60000);

    if (result?.success && result.products?.length > 0) {
      salesDataState.allProducts = result.products;
      updateSalesDashboardAll(result.products);
      // 同时更新柱状图（用全量数据）
      renderSalesBarChart(result.products);
      if (getSalesSearchQuery()) {
        applySalesSearchFilter();
      }
    }
  } catch (err) {
    console.error('[小青蛙电商助手] 全量加载失败:', err);
  } finally {
    if (hint) hint.style.display = 'none';
  }
}

// 用全量数据更新仪表盘汇总
function calculateSalesSummary(products = []) {
  let todayTotal = 0, sevenDayTotal = 0, thirtyDayTotal = 0;
  let todayAmount = 0, sevenDayAmount = 0, thirtyDayAmount = 0;
  (products || []).forEach(p => {
    const amountSummary = getSalesProductAmountSummary(p);
    todayTotal += Number(p.todaySaleVolume || 0);
    sevenDayTotal += Number(p.sevenDaysSaleVolume || 0);
    thirtyDayTotal += Number(p.thirtyDaysSaleVolume || 0);
    todayAmount += amountSummary.todayAmount || 0;
    sevenDayAmount += amountSummary.sevenDayAmount || 0;
    thirtyDayAmount += amountSummary.thirtyDayAmount || 0;
  });
  return { todayTotal, sevenDayTotal, thirtyDayTotal, todayAmount, sevenDayAmount, thirtyDayAmount };
}

function renderSalesSummaryToTab(summary = {}) {
  const {
    todayTotal = 0,
    sevenDayTotal = 0,
    thirtyDayTotal = 0,
    todayAmount = 0,
    sevenDayAmount = 0,
    thirtyDayAmount = 0
  } = summary;

  // 更新销量卡片（销量查询 Tab）
  const todayEl = document.getElementById('salesTodayCount');
  const sevenEl = document.getElementById('salesSevenDayCount');
  const thirtyEl = document.getElementById('salesThirtyDayCount');
  if (todayEl) todayEl.textContent = todayTotal.toLocaleString();
  if (sevenEl) sevenEl.textContent = sevenDayTotal.toLocaleString();
  if (thirtyEl) thirtyEl.textContent = thirtyDayTotal.toLocaleString();

  // 更新销售额（销量查询 Tab）
  const todayAmtEl = document.getElementById('salesTodayAmount');
  const sevenAmtEl = document.getElementById('salesSevenDayAmount');
  const thirtyAmtEl = document.getElementById('salesThirtyDayAmount');
  if (todayAmtEl) todayAmtEl.textContent = '\u00a5' + formatAmount(todayAmount);
  if (sevenAmtEl) sevenAmtEl.textContent = '\u00a5' + formatAmount(sevenDayAmount);
  if (thirtyAmtEl) thirtyAmtEl.textContent = '\u00a5' + formatAmount(thirtyDayAmount);

  // 更新独立面板（销量查询 Tab）
  const todayAmtStand = document.getElementById('salesTodayAmountStandalone');
  const sevenAmtStand = document.getElementById('salesSevenDayAmountStandalone');
  const thirtyAmtStand = document.getElementById('salesThirtyDayAmountStandalone');
  if (todayAmtStand) todayAmtStand.textContent = '¥' + formatAmount(todayAmount);
  if (sevenAmtStand) sevenAmtStand.textContent = '¥' + formatAmount(sevenDayAmount);
  if (thirtyAmtStand) thirtyAmtStand.textContent = '¥' + formatAmount(thirtyDayAmount);
}

function syncDashboardFromSalesSummary(summary = {}, products = [], source = 'sales-query-summary') {
  setDashboardSalesSummary({
    todayTotal: summary.todayTotal || 0,
    sevenDayTotal: summary.sevenDayTotal || 0,
    thirtyTotal: summary.thirtyDayTotal || 0,
    todayAmount: summary.todayAmount || 0,
    sevenDayAmount: summary.sevenDayAmount || 0,
    thirtyAmount: summary.thirtyDayAmount || 0,
    source
  });
  updateDashboardSales(
    summary.todayTotal || 0,
    summary.sevenDayTotal || 0,
    summary.todayAmount || 0,
    summary.sevenDayAmount || 0,
    products || [],
    source
  );
  unlockDashboardThirty(summary.thirtyDayTotal || 0, summary.thirtyDayAmount || 0);
}

function updateSalesDashboardAll(products) {
  if (!products || products.length === 0) return;

  const summary = calculateSalesSummary(products);
  renderSalesSummaryToTab(summary);
  syncDashboardFromSalesSummary(summary, products, 'sales-query-all');
}

// 更新销售统计卡片（当前页数据）
function updateSalesDashboard() {
  const products = salesDataState.products;
  if (!products || products.length === 0) return;

  const summary = calculateSalesSummary(products);
  renderSalesSummaryToTab(summary);
  renderSalesBarChart(products);
  syncDashboardFromSalesSummary(summary, products, 'sales-query-summary');
}
function formatAmount(amount) {
  if (!amount || amount === 0) return '0.00';
  const abs = Math.abs(amount);
  if (abs >= 10000) return (amount / 10000).toFixed(2) + '\u4e07';
  return amount.toFixed(2);
}

function normalizeSalesText(value) {
  if (value === null || value === undefined) return '';
  const text = String(value).trim();
  return text;
}

function normalizeSalesNumber(value) {
  if (value === null || value === undefined || value === '') return null;
  if (typeof value === 'number') {
    return Number.isFinite(value) ? value : null;
  }
  const text = String(value).trim();
  if (!text) return null;
  const cleaned = text.replace(/,/g, '').replace(/[^\d.-]/g, '');
  if (!cleaned || cleaned === '-' || cleaned === '.' || cleaned === '-.') return null;
  const num = Number(cleaned);
  return Number.isFinite(num) ? num : null;
}

function pickSalesField(source, fields) {
  if (!source || !fields) return '';
  for (const field of fields) {
    const value = source[field];
    if (value !== null && value !== undefined && String(value).trim() !== '') {
      return value;
    }
  }
  return '';
}

function parseSalesTimestamp(value) {
  if (value === null || value === undefined || value === '') return null;
  if (value instanceof Date) {
    return Number.isNaN(value.getTime()) ? null : value;
  }
  if (typeof value === 'number') {
    const ms = value < 1e12 ? value * 1000 : value;
    const date = new Date(ms);
    return Number.isNaN(date.getTime()) ? null : date;
  }

  const text = String(value).trim();
  if (!text) return null;
  if (/^\d{10}$/.test(text)) {
    const date = new Date(Number(text) * 1000);
    return Number.isNaN(date.getTime()) ? null : date;
  }
  if (/^\d{13}$/.test(text)) {
    const date = new Date(Number(text));
    return Number.isNaN(date.getTime()) ? null : date;
  }

  const date = new Date(text);
  return Number.isNaN(date.getTime()) ? null : date;
}

function getSalesDurationSeconds(product = {}) {
  const normalizeDurationSeconds = (value) => {
    const num = normalizeSalesNumber(value);
    if (num === null || num < 0) return null;
    // TEMU 销量接口里该字段常直接返回“天数”
    if (num > 0 && num < 1000) return num * 86400;
    return num;
  };
  const numericFields = [
    'onSalesDuration',
    'onSalesDurationOffline',
    'on_sales_duration',
    'onlineDuration',
    'inShopDuration',
    'joinSiteDuration',
    'siteDuration',
    'duration',
    'shelfDuration',
    'shopDuration'
  ];

  for (const field of numericFields) {
    const num = normalizeDurationSeconds(product[field]);
    if (num !== null && num >= 0) return num;
  }

  const dateFields = [
    'createTime',
    'createDate',
    'publishTime',
    'joinTime',
    'addTime',
    'firstOnlineTime',
    'shelveTime',
    'onlineTime',
    'onShelvesTime'
  ];
  const now = Date.now();
  for (const field of dateFields) {
    const date = parseSalesTimestamp(product[field]);
    if (!date) continue;
    const seconds = Math.floor((now - date.getTime()) / 1000);
    if (seconds >= 0) return seconds;
  }
  return 0;
}

function getSalesSearchQuery() {
  return document.getElementById('salesTableSearchInput')?.value?.trim() || '';
}

function getSalesSkuId(sku = {}) {
  return normalizeSalesText(pickSalesField(sku, [
    'skuId',
    'productSkuId',
    'goodsSkuId',
    'id',
    'sku_id',
    'skuCodeId'
  ]));
}

function getSalesSkuName(sku = {}) {
  return normalizeSalesText(pickSalesField(sku, [
    'className',
    'class_name',
    'skuClassName',
    'sku_class_name',
    'skuDisplayName',
    'skuName',
    'skuSpecText',
    'specText',
    'specification',
    'propValue'
  ]));
}

function getSalesSkuCode(sku = {}) {
  return normalizeSalesText(pickSalesField(sku, [
    'skuCode',
    'sku_code',
    'productSkuCode',
    'goodsSkuCode',
    'skuNo',
    'skuNoText',
    'itemNo',
    'code'
  ]));
}

function getSalesSkuAmountValue(sku = {}) {
  const fields = [
    'supplierPrice',
    'skuPrice',
    'priceValue',
    'declaredPrice',
    'amount',
    'price',
    'salesPrice',
    'salePrice',
    'currentPrice',
    'unitPrice',
    'sellingPrice',
    'retailPrice',
    'marketPrice',
    'goodsPrice',
    'productPrice',
    'minPrice',
    'maxPrice',
    'linePrice',
    'sale_price',
    'current_price',
    'line_price',
    'min_price'
  ];
  const centBasedFields = new Set([
    'supplierPrice',
    'skuPrice',
    'priceValue',
    'declaredPrice',
    'salesPrice',
    'salePrice',
    'currentPrice',
    'unitPrice',
    'sellingPrice',
    'retailPrice',
    'marketPrice',
    'goodsPrice',
    'productPrice',
    'minPrice',
    'maxPrice',
    'linePrice',
    'sale_price',
    'current_price',
    'line_price',
    'min_price'
  ]);

  for (const field of fields) {
    const raw = sku?.[field];
    const num = normalizeSalesNumber(raw);
    if (num === null || num <= 0) continue;
    if (centBasedFields.has(field) && num >= 100) return num / 100;
    return num;
  }
  return null;
}

function hasSalesSkuAmount(sku = {}) {
  if (sku && typeof sku === 'object' && sku.amountValue !== undefined && sku.amountValue !== null) {
    return Number(sku.amountValue) > 0;
  }
  return getSalesSkuAmountValue(sku) !== null;
}

function getSalesSkuWarehouseList(sku = {}) {
  const list = [];
  const sources = [
    sku.warehouseInfoList,
    sku.warehouseList,
    sku.stockList,
    sku.inventoryList,
    sku.warehouseInventoryList,
    sku.inventoryInfoList,
    sku.warehouseInventoryInfoList,
    sku.stockInfoList
  ];

  sources.forEach(source => {
    if (!Array.isArray(source)) return;
    source.forEach(item => {
      if (item && typeof item === 'object') list.push(item);
    });
  });

  return list;
}

function getSalesInventorySummary(sku = {}) {
  const warehouses = getSalesSkuWarehouseList(sku);
  const directValues = source => normalizeSalesNumber(pickSalesField(sku, source)) || 0;
  const warehouseValues = source => warehouses.reduce((sum, warehouse) => {
    const data = warehouse && typeof warehouse === 'object' && warehouse.inventoryNumInfo && typeof warehouse.inventoryNumInfo === 'object'
      ? warehouse.inventoryNumInfo
      : warehouse;
    const value = normalizeSalesNumber(pickSalesField(data, source));
    return sum + (value || 0);
  }, 0);
  const sumOrDirect = (warehouseFields, directFields) => {
    const total = warehouseValues(warehouseFields);
    return total || directValues(directFields);
  };

  return {
    availableInventoryNum: sumOrDirect([
      'warehouseInventoryNum',
      'availableInventoryNum',
      'availableNum',
      'stockQuantity'
    ], [
      'availableInventoryNum',
      'inWarehouseAvailableInventoryNum',
      'warehouseInventoryNum',
      'stockQuantity'
    ]),
    reservedInventoryNum: sumOrDirect([
      'expectedOccupiedInventoryNum',
      'preOccupyInventoryNum',
      'reservedInventoryNum',
      'inWarehouseReservedInventoryNum',
      'waitDeliveryInventoryNum'
    ], [
      'reservedInventoryNum',
      'inWarehouseReservedInventoryNum',
      'preOccupyInventoryNum',
      'expectedOccupiedInventoryNum',
      'waitDeliveryInventoryNum'
    ]),
    unavailableInventoryNum: sumOrDirect([
      'unavailableWarehouseInventoryNum',
      'inWarehouseUnavailableInventoryNum',
      'waitQcNum',
      'waitOnShelfNum',
      'waitInStock',
      'waitReceiveNum',
      'waitApproveInventoryNum'
    ], [
      'unavailableInventoryNum',
      'inWarehouseUnavailableInventoryNum',
      'unavailableWarehouseInventoryNum'
    ]),
    shippedInventoryNum: sumOrDirect([
      'shippedInventoryNum',
      'shippedNum',
      'sentInventoryNum',
      'deliveredInventoryNum'
    ], [
      'shippedInventoryNum',
      'shippedNum',
      'sentInventoryNum',
      'deliveredInventoryNum'
    ])
  };
}

function normalizeSalesWarehouseName(name, index) {
  const text = normalizeSalesText(name);
  if (!text) return '';
  if (text === '仓库1' || text === '仓库 1') return '';
  if (/^仓库\s*1$/.test(text)) return '';
  return text;
}

function getSalesInventoryHtml(sku = {}) {
  const summary = getSalesInventorySummary(sku);
  const warehouses = getSalesSkuWarehouseList(sku);
  const formatCard = (label, value, bg, color) => `
    <div style="padding:8px 10px;border-radius:10px;background:${bg};border:1px solid rgba(148,163,184,0.18);min-width:0;">
      <div style="font-size:11px;line-height:1.2;color:#64748b;margin-bottom:4px;">${label}</div>
      <div style="font-size:14px;line-height:1.2;font-weight:800;color:${color};word-break:break-all;">${Number(value || 0).toLocaleString()}</div>
    </div>
  `;
  return `
    <div style="margin-top:8px;padding:10px 10px 9px;border-radius:12px;border:1px solid #e8edf5;background:linear-gradient(180deg,#fbfdff 0%,#f8fafc 100%);box-shadow:0 1px 2px rgba(15,23,42,0.03);">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;">
        <div style="font-size:11px;font-weight:700;color:#475569;">库存数据</div>
        <div style="font-size:11px;color:#94a3b8;">${warehouses.length ? `${warehouses.length} 个仓库明细` : '暂无仓库明细'}</div>
      </div>
      <div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;">
        ${formatCard('仓内可用库存', summary.availableInventoryNum, '#eff6ff', '#1d4ed8')}
        ${formatCard('仓内预占用库存', summary.reservedInventoryNum, '#fff7ed', '#c2410c')}
        ${formatCard('仓内暂不可用库存', summary.unavailableInventoryNum, '#fdf2f8', '#be185d')}
        ${formatCard('已发货库存', summary.shippedInventoryNum, '#ecfdf5', '#047857')}
      </div>
    </div>
  `;
}

function normalizeSalesSkuRow(sku = {}, fallbackIndex = 0) {
  const skuId = getSalesSkuId(sku);
  const skuName = getSalesSkuName(sku);
  const skuCode = getSalesSkuCode(sku);
  const amountValue = getSalesSkuAmountValue(sku);
  const inventorySummary = getSalesInventorySummary(sku);

  const todaySaleVolume = normalizeSalesNumber(pickSalesField(sku, [
    'todaySaleVolume',
    'todaySalesVolume',
    'todaySales',
    'todaySale',
    'todayPlatformPurchaseNum',
    'todayPlatformSalesNum',
    'dailySaleVolume',
    'saleVolumeToday'
  ])) || 0;
  const sevenDaysSaleVolume = normalizeSalesNumber(pickSalesField(sku, [
    'lastSevenDaysSaleVolume',
    'sevenDaysSaleVolume',
    'sevenDaySaleVolume',
    'lastSevenDaysSale',
    'last7DaysSaleVolume',
    'sevenDaysSalesVolume',
    'recent7DaysSaleVolume',
    'sevenSalesVolume',
    'weekSaleVolume'
  ])) || 0;
  const thirtyDaysSaleVolume = normalizeSalesNumber(pickSalesField(sku, [
    'lastThirtyDaysSaleVolume',
    'thirtyDaysSaleVolume',
    'thirtyDaySaleVolume',
    'lastThirtyDaysSale',
    'last30DaysSaleVolume',
    'recent30DaysSaleVolume',
    'thirtyDaysSalesVolume',
    'monthSaleVolume'
  ])) || 0;

  const warehouses = getSalesSkuWarehouseList(sku);
  const stockFromWarehouses = warehouses.reduce((sum, warehouse) => {
    const qty = normalizeSalesNumber(pickSalesField(warehouse, [
      'warehouseInventoryNum',
      'inventoryNum',
      'quantity',
      'stockQuantity'
    ]));
    return sum + (qty || 0);
  }, 0);

  const stockQuantity = stockFromWarehouses || normalizeSalesNumber(pickSalesField(sku, [
    'stockQuantity',
    'inventory',
    'stock',
    'warehouseInventoryNum'
  ])) || 0;

  const availableSaleDays = normalizeSalesNumber(pickSalesField(sku, [
    'availableSaleDays',
    'availableSaleDaysFromInventory',
    'warehouseAvailableSaleDays',
    'predictSaleAvailableDays',
    'predictSaleInventoryAvailableDays',
    'predictSaleWarehouseAvailableDays',
    'safeInventoryDays',
    'stockDays'
  ]));
  const stockDays = normalizeSalesNumber(pickSalesField(sku, [
    'stockDays',
    'safeInventoryDays',
    'availableSaleDaysFromInventory',
    'warehouseAvailableSaleDays'
  ]));
  const adviceQuantity = normalizeSalesNumber(pickSalesField(sku, [
    'adviceQuantity',
    'predictSaleAdviceQuantity',
    'adviceProduceNum',
    'availableProduceNum',
    'lackQuantity'
  ]));
  const purchaseConfig = normalizeSalesText(pickSalesField(sku, [
    'purchaseConfig',
    'purchaseLabel'
  ]));

  return {
    raw: sku,
    skuId: skuId || `sku-${fallbackIndex + 1}`,
    skuName: skuName || skuCode || `SKU ${fallbackIndex + 1}`,
    skuClassName: skuName,
    skuCode,
    amountValue,
    todaySaleVolume,
    sevenDaysSaleVolume,
    thirtyDaysSaleVolume,
    availableSaleDays,
    stockDays,
    adviceQuantity,
    purchaseConfig,
    stockQuantity: stockQuantity || inventorySummary.availableInventoryNum || inventorySummary.reservedInventoryNum || inventorySummary.unavailableInventoryNum || inventorySummary.shippedInventoryNum || 0,
    ...inventorySummary,
    warehouseInfoList: warehouses
  };
}

function getSalesProductSkuRows(product = {}) {
  const sources = [
    product.skuQuantityDetailList,
    product.skuList,
    product.skuListInfo,
    product.skuInfoList,
    product.skuDetails
  ];

  const rows = [];
  const seen = new Set();

  sources.forEach(source => {
    if (!Array.isArray(source)) return;
    source.forEach((item, index) => {
      if (!item || typeof item !== 'object') return;
      const row = normalizeSalesSkuRow(item, rows.length + index);
      const dedupeKey = [row.skuId, row.skuName, row.amountValue, row.todaySaleVolume, row.sevenDaysSaleVolume, row.thirtyDaysSaleVolume].join('|');
      if (seen.has(dedupeKey)) return;
      seen.add(dedupeKey);
      rows.push(row);
    });
  });

  return rows;
}

function getSalesVisibleSkuRows(product = {}) {
  return getSalesProductSkuRows(product)
    .filter(row => row.amountValue !== null && row.amountValue !== undefined && row.amountValue > 0)
    .sort((a, b) => {
      const salesA = (a.todaySaleVolume || 0) + (a.sevenDaysSaleVolume || 0) + (a.thirtyDaysSaleVolume || 0);
      const salesB = (b.todaySaleVolume || 0) + (b.sevenDaysSaleVolume || 0) + (b.thirtyDaysSaleVolume || 0);
      if (salesB !== salesA) return salesB - salesA;
      return (b.amountValue || 0) - (a.amountValue || 0);
    });
}

function getSalesProductSortMetric(product = {}) {
  const skuRows = getSalesProductSkuRows(product);
  const sumBy = field => skuRows.reduce((sum, row) => sum + Number(row[field] || 0), 0);
  const today = Math.max(Number(product.todaySaleVolume || 0), sumBy('todaySaleVolume'));
  const seven = Math.max(Number(product.sevenDaysSaleVolume || 0), sumBy('sevenDaysSaleVolume'));
  const thirty = Math.max(Number(product.thirtyDaysSaleVolume || 0), sumBy('thirtyDaysSaleVolume'));
  return { today, seven, thirty };
}

function getSalesProductAmountSummary(product = {}) {
  const skuRows = getSalesProductSkuRows(product);
  const sumByPeriod = (field) => skuRows.reduce((sum, row) => {
    const unitPrice = Number(row.amountValue || 0);
    const volume = Number(row[field] || 0);
    return sum + (unitPrice > 0 && volume > 0 ? unitPrice * volume : 0);
  }, 0);
  const directTodayAmount = normalizeSalesNumber(pickSalesField(product, [
    'todaySaleAmount',
    'todaySalesAmount',
    'dailySaleAmount',
    'saleAmountToday',
    'todayTurnover',
    'todayGmv',
    'todayRevenue'
  ])) || 0;
  const directSevenAmount = normalizeSalesNumber(pickSalesField(product, [
    'sevenDaysSaleAmount',
    'sevenDaySaleAmount',
    'lastSevenDaysSaleAmount',
    'last7DaysSaleAmount',
    'sevenDayAmount',
    'sevenTurnover',
    'sevenGmv',
    'sevenRevenue'
  ])) || 0;
  const directThirtyAmount = normalizeSalesNumber(pickSalesField(product, [
    'thirtyDaysSaleAmount',
    'thirtyDaySaleAmount',
    'lastThirtyDaysSaleAmount',
    'last30DaysSaleAmount',
    'thirtyDayAmount',
    'thirtyTurnover',
    'thirtyGmv',
    'thirtyRevenue'
  ])) || 0;
  const fallbackUnitPrice = getSalesSkuAmountValue(product) || 0;
  const fallbackToday = fallbackUnitPrice > 0 ? fallbackUnitPrice * Number(product.todaySaleVolume || 0) : 0;
  const fallbackSeven = fallbackUnitPrice > 0 ? fallbackUnitPrice * Number(product.sevenDaysSaleVolume || 0) : 0;
  const fallbackThirty = fallbackUnitPrice > 0 ? fallbackUnitPrice * Number(product.thirtyDaysSaleVolume || 0) : 0;
  const skuTodayAmount = sumByPeriod('todaySaleVolume');
  const skuSevenAmount = sumByPeriod('sevenDaysSaleVolume');
  const skuThirtyAmount = sumByPeriod('thirtyDaysSaleVolume');
  return {
    todayAmount: directTodayAmount || skuTodayAmount || fallbackToday || 0,
    sevenDayAmount: directSevenAmount || skuSevenAmount || fallbackSeven || 0,
    thirtyDayAmount: directThirtyAmount || skuThirtyAmount || fallbackThirty || 0
  };
}

function sortSalesProducts() {
  const source = Array.isArray(salesDataState.allProducts) ? [...salesDataState.allProducts] : [];
  const sortBy = salesDataState.sortBy || 'todayDesc';
  source.sort((a, b) => {
    const sa = getSalesProductSortMetric(a);
    const sb = getSalesProductSortMetric(b);
    if (sortBy === 'todayAsc') return sa.today - sb.today || sa.seven - sb.seven || sa.thirty - sb.thirty;
    if (sortBy === 'sevenDesc') return sb.seven - sa.seven || sb.today - sa.today || sb.thirty - sa.thirty;
    if (sortBy === 'sevenAsc') return sa.seven - sb.seven || sa.today - sb.today || sa.thirty - sb.thirty;
    if (sortBy === 'thirtyDesc') return sb.thirty - sa.thirty || sb.seven - sa.seven || sb.today - sa.today;
    if (sortBy === 'thirtyAsc') return sa.thirty - sb.thirty || sa.seven - sb.seven || sa.today - sb.today;
    if (sortBy === 'default') return 0;
    return sb.today - sa.today || sb.seven - sa.seven || sb.thirty - sa.thirty;
  });
  salesDataState.allProducts = source;
  salesDataState.products = source;
}

function hasSalesProductSales(product = {}) {
  if (Number(product.todaySaleVolume || 0) > 0) return true;
  if (Number(product.sevenDaysSaleVolume || 0) > 0) return true;
  if (Number(product.thirtyDaysSaleVolume || 0) > 0) return true;
  return getSalesProductSkuRows(product).some(row =>
    Number(row.todaySaleVolume || 0) > 0
    || Number(row.sevenDaysSaleVolume || 0) > 0
    || Number(row.thirtyDaysSaleVolume || 0) > 0
  );
}

function formatSalesSyncTime(value) {
  if (!value) return '—';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return '—';
  const pad = n => String(n).padStart(2, '0');
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
}

function updateSalesSyncDisplay() {
  const el = document.getElementById('salesLastSyncTime');
  if (!el) return;
  el.textContent = `上次同步时间：${formatSalesSyncTime(salesDataState.lastSyncAt)}`;
}

async function getSalesAuth() {
  if (state.mallId && state.antiContent) {
    const auth = normalizeTemuAuth({ mallId: state.mallId, antiContent: state.antiContent });
    state.mallId = auth.mallId;
    state.antiContent = auth.antiContent;
    return auth;
  }
  const stored = await chrome.storage.local.get('temuAuth');
  const auth = normalizeTemuAuth(stored?.temuAuth || {});
  if (auth.mallId && auth.antiContent) return auth;
  return null;
}

function buildSalesProductFromApi(item = {}) {
  const product = {
    productId: item.productId || item.product_id || item.goodsId || item.goods_id || item.spuId || '',
    productCNName: item.productCNName || item.productName || item.product_name || item.goodsName || item.name || '',
    productName: item.productName || item.productCNName || item.goodsName || item.name || '',
    mainImage: item.productSkcPicture || item.goodsImg || item.mainImageUrl || item.mainImage || item.imageUrl || '',
    skcId: item.productSkcId || item.skcId || item.goodsSkcId || '',
    skcExtCode: item.skcExtCode || item.skcCode || item.productSkcCode || '',
    onSalesDuration: item.onSalesDurationOffline || item.onSalesDuration || item.on_sales_duration || item.productCycleDays || item.productDays || 0,
    createTime: item.createTime || item.createDate || item.publishTime || item.joinTime || item.addTime || item.firstOnlineTime || item.shelveTime || '',
    score: item.score ?? item.asfScore ?? item.goodsScore ?? item.productDsrScore ?? item.rating ?? item.reviewScore ?? item.starRating ?? item.scoreValue ?? item.mallDeliverRate ?? item.qualityAfterSalesRate ?? '',
    skuList: Array.isArray(item.skuList) ? item.skuList : [],
    skuQuantityDetailList: Array.isArray(item.skuQuantityDetailList) && item.skuQuantityDetailList.length > 0
      ? item.skuQuantityDetailList
      : (Array.isArray(item.skuQuantityTotalInfo?.skuQuantityDetailList) ? item.skuQuantityTotalInfo.skuQuantityDetailList : []),
    warehouseInfoList: Array.isArray(item.warehouseInfoList) ? item.warehouseInfoList : [],
    raw: item
  };
  const skuRows = getSalesProductSkuRows(product);
  const sumBy = field => skuRows.reduce((sum, row) => sum + Number(row[field] || 0), 0);
  product.todaySaleVolume = Math.max(sumBy('todaySaleVolume'), normalizeSalesNumber(pickSalesField(item, [
    'todaySaleVolume',
    'todaySale',
    'todayPlatformPurchaseNum',
    'todayPlatformSalesNum',
    'saleVolumeToday',
    'todaySalesVolume',
    'todayOrderNum',
    'todaySales'
  ])) || 0);
  product.sevenDaysSaleVolume = Math.max(sumBy('sevenDaysSaleVolume'), normalizeSalesNumber(pickSalesField(item, [
    'lastSevenDaysSaleVolume',
    'sevenDaysSaleVolume',
    'lastSevenDaysSale',
    'sevenDaySaleVolume',
    'last7DaysSaleVolume',
    'sevenDaysSalesVolume',
    'weekSaleVolume',
    'recent7DaysSaleVolume'
  ])) || 0);
  product.thirtyDaysSaleVolume = Math.max(sumBy('thirtyDaysSaleVolume'), normalizeSalesNumber(pickSalesField(item, [
    'lastThirtyDaysSaleVolume',
    'thirtyDaysSaleVolume',
    'lastThirtyDaysSale',
    'thirtyDaySaleVolume',
    'last30DaysSaleVolume',
    'thirtyDaysSalesVolume',
    'monthSaleVolume',
    'recent30DaysSaleVolume'
  ])) || 0);
  return product;
}

async function fetchSalesDataAllDirect() {
  const auth = await getSalesAuth();
  if (!auth?.mallId || !auth?.antiContent) {
    throw new Error('请先登录 TEMU 店铺并完成认证');
  }

  const pageSize = 100;
  const backgroundResult = await safeRuntimeSendMessage({
    action: 'fetchSalesDataAll',
    params: {
      mallId: auth.mallId,
      antiContent: auth.antiContent,
      pageSize,
      maxPages: 80
    }
  }, null, 70000);

  if (backgroundResult?.success) {
    return {
      success: true,
      products: Array.isArray(backgroundResult.products) ? backgroundResult.products : [],
      total: backgroundResult.total || backgroundResult.products?.length || 0,
      totalPages: backgroundResult.totalPages || 1
    };
  }

  const backgroundError = backgroundResult?.error || backgroundResult?.message || '';
  const products = [];
  let pageNo = 1;
  let total = 0;
  let totalPages = 1;
  while (pageNo <= totalPages && pageNo <= 80) {
    let response;
    try {
      response = await fetch('https://agentseller.temu.com/mms/venom/api/supplier/sales/management/listWarehouse', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          mallid: String(auth.mallId),
          'anti-content': auth.antiContent
        },
        credentials: 'include',
        body: JSON.stringify({
          pageNo,
          pageSize,
          priceAdjustRecentDays: 7
        })
      });
    } catch (error) {
      if (isExtensionContextInvalidatedError(backgroundError)) {
        throw new Error(getRuntimeReconnectMessage('销量查询'));
      }
      throw new Error(backgroundError || error?.message || '销量查询请求失败，请确认执行插件已连接 TEMU 店铺');
    }

    const text = await response.text();
    let data = {};
    try {
      data = text ? JSON.parse(text) : {};
    } catch (error) {
      throw new Error(backgroundError || '销量查询接口返回异常，请刷新 TEMU 后台并重新连接店铺');
    }
    if (!response.ok || !data?.success || !data?.result) {
      throw new Error(data?.error_msg || data?.message || backgroundError || `销量查询接口请求失败 ${response.status}`);
    }

    const result = data.result || {};
    const list = result.subOrderList || result.productList || result.list || result.items || result.records || [];
    const pageProducts = Array.isArray(list) ? list.map(buildSalesProductFromApi) : [];
    products.push(...pageProducts);
    total = result.total || result.totalCount || total || products.length;
    totalPages = Math.max(1, Math.ceil(total / pageSize));
    const syncEl = document.getElementById('salesLastSyncTime');
    if (syncEl) {
      syncEl.textContent = `正在同步销量：第 ${pageNo}/${totalPages} 页，已读取 ${products.length} 条`;
    }
    if (!pageProducts.length || pageProducts.length < pageSize) break;
    pageNo += 1;
    await new Promise(resolve => setTimeout(resolve, 80));
  }

  return {
    success: true,
    products,
    total: total || products.length,
    totalPages
  };
}

function renderSalesInventoryCell(row = {}) {
  const pairs = [
    `可用 ${Number(row.availableInventoryNum || 0).toLocaleString()}`,
    `预占 ${Number(row.reservedInventoryNum || 0).toLocaleString()}`,
    `暂不可用 ${Number(row.unavailableInventoryNum || 0).toLocaleString()}`,
    `已发货 ${Number(row.shippedInventoryNum || 0).toLocaleString()}`
  ];
  const bgColors = ['#eff6ff', '#ecfeff', '#fefce8', '#f1f5f9'];
  const borderColors = ['#bfdbfe', '#a5f3fc', '#fde68a', '#cbd5e1'];
  return `<div style="display:flex;flex-wrap:wrap;gap:6px 8px;align-items:flex-start;min-width:0;white-space:normal;font-size:12px;color:#334155;font-weight:700;line-height:1.35;">${pairs.map((text, index) => `<span style="display:inline-flex;align-items:center;white-space:nowrap;padding:2px 7px;border-radius:999px;background:${bgColors[index]};border:1px solid ${borderColors[index]};">${text}</span>`).join('')}</div>`;
}

function matchesSalesProduct(product = {}, query = '') {
  if (!query) return true;
  const key = query.toLowerCase();
  const fields = [
    product.productId,
    product.productCNName,
    product.productName,
    product.goodsName,
    product.skuClassName,
    product.skuDisplayName,
    product.skuName,
    product.skuCode,
    product.skuId,
    product.skcId,
    product.skcExtCode
  ];

  if (fields.some(value => normalizeSalesText(value).toLowerCase().includes(key))) {
    return true;
  }

  const skuRows = getSalesProductSkuRows(product);
  return skuRows.some(row => [
    row.skuId,
    row.skuName,
    row.skuClassName,
    row.skuCode
  ].some(value => normalizeSalesText(value).toLowerCase().includes(key)));
}

function getSalesDisplayProducts() {
  const source = getSalesFilteredProducts();
  const pageNum = Math.max(1, salesDataState.pageNum || 1);
  const pageSize = Math.max(1, salesDataState.pageSize || 20);
  const start = (pageNum - 1) * pageSize;
  return source.slice(start, start + pageSize);
}

function getSalesFilteredProducts() {
  const source = Array.isArray(salesDataState.viewProducts)
    ? salesDataState.viewProducts
    : (Array.isArray(salesDataState.allProducts) && salesDataState.allProducts.length > 0
      ? salesDataState.allProducts
      : salesDataState.products);
  return source.filter(hasSalesProductSales);
}

function applySalesSearchFilter() {
  const query = getSalesSearchQuery();
  const source = salesDataState.allProducts && salesDataState.allProducts.length > 0
    ? salesDataState.allProducts
    : salesDataState.products;

  const filtered = query
    ? source.filter(product => matchesSalesProduct(product, query))
    : salesDataState.products;

  salesDataState.viewProducts = filtered.filter(hasSalesProductSales);
  const visibleCount = salesDataState.viewProducts.length;
  const totalPages = Math.max(1, Math.ceil(visibleCount / Math.max(1, salesDataState.pageSize || 20)));
  if (salesDataState.pageNum > totalPages) salesDataState.pageNum = 1;
  renderSalesDataTable();
  renderSalesDataPagination();
}

function goToSalesPage(pageNum = 1) {
  const totalPages = Math.max(1, Math.ceil(getSalesFilteredProducts().length / Math.max(1, salesDataState.pageSize || 20)));
  salesDataState.pageNum = Math.min(Math.max(1, pageNum), totalPages);
  renderSalesDataTable();
  renderSalesDataPagination();
}

// ========== TEMU AI 商品诊断 ==========
function setTemuAiStatus(message, tone = 'info') {
  const el = document.getElementById('temuAiStatus');
  if (!el) return;
  el.textContent = message;
  el.style.color = tone === 'error' ? '#dc2626' : tone === 'success' ? '#059669' : '#64748b';
}

function getTemuAiNumberInput(id) {
  const value = document.getElementById(id)?.value;
  if (value === null || value === undefined || String(value).trim() === '') return null;
  const num = Number(value);
  return Number.isFinite(num) ? num : null;
}

function initTemuAiDiagnosisTab() {
  const sourceSelect = document.getElementById('temuAiSourceSelect');
  const loadBtn = document.getElementById('temuAiLoadProductsBtn');
  const runBtn = document.getElementById('temuAiRunBtn');
  sourceSelect?.addEventListener('change', () => {
    const manual = sourceSelect.value === 'manual';
    const form = document.getElementById('temuAiManualForm');
    if (form) form.style.display = manual ? 'grid' : 'none';
    prepareTemuAiDiagnosisProducts();
  });
  loadBtn?.addEventListener('click', () => prepareTemuAiDiagnosisProducts());
  runBtn?.addEventListener('click', runTemuAiProductDiagnosis);
}

function getTemuAiSalesSourceProducts(sourceMode = '') {
  const fromSearch = Array.isArray(salesDataState.viewProducts) && salesDataState.viewProducts.length
    ? salesDataState.viewProducts
    : [];
  const fromAll = Array.isArray(salesDataState.allProducts) && salesDataState.allProducts.length
    ? salesDataState.allProducts
    : salesDataState.products;
  const source = sourceMode === 'salesSearch' && fromSearch.length ? fromSearch : fromAll;
  return Array.isArray(source) ? source.filter(hasSalesProductSales) : [];
}

function compactTemuProductForDiagnosis(product = {}, index = 0) {
  const skuRows = getSalesProductSkuRows(product);
  const metrics = getSalesProductSortMetric(product);
  const prices = skuRows.map(row => Number(row.amountValue || 0)).filter(value => value > 0);
  const stockFields = ['stockQuantity', 'availableInventoryNum', 'warehouseInventoryNum', 'inventory', 'stock'];
  const hasStockValue = skuRows.some(row => {
    const raw = row.raw && typeof row.raw === 'object' ? row.raw : {};
    if (pickSalesField(raw, stockFields) !== '') return true;
    return (Array.isArray(row.warehouseInfoList) ? row.warehouseInfoList : []).some(warehouse => {
      const source = warehouse?.inventoryNumInfo && typeof warehouse.inventoryNumInfo === 'object' ? warehouse.inventoryNumInfo : warehouse;
      return pickSalesField(source || {}, ['warehouseInventoryNum', 'inventoryNum', 'quantity', 'stockQuantity', 'availableInventoryNum', 'availableNum']) !== '';
    });
  });
  const stockTotal = hasStockValue
    ? skuRows.reduce((sum, row) => sum + Number(row.stockQuantity || row.availableInventoryNum || 0), 0)
    : null;
  const stockDaysValues = skuRows.map(row => row.stockDays ?? row.availableSaleDays).filter(value => value !== null && value !== undefined && Number(value) >= 0);
  const avgPrice = prices.length ? prices.reduce((sum, value) => sum + value, 0) / prices.length : 0;
  const defaultCarouselCount = getTemuAiNumberInput('temuAiCarouselCountInput');
  const defaultDetailCount = getTemuAiNumberInput('temuAiDetailImageCountInput');
  const mainImage = product.mainImage || product.productSkcPicture || product.goodsImg || '';
  return {
    key: String(product.productId || product.spuId || `product-${index + 1}`),
    spuId: String(product.productId || product.spuId || ''),
    skcId: String(product.skcId || product.productSkcId || ''),
    title: product.productCNName || product.productName || product.spuName || product.name || '',
    mainImage,
    mainImageKnown: Boolean(mainImage),
    carouselImageCount: defaultCarouselCount,
    detailImageCount: defaultDetailCount,
    skuCount: skuRows.length,
    skcCount: product.skcId || product.productSkcId ? 1 : null,
    sales7: metrics.seven,
    sales30: metrics.thirty,
    todaySales: metrics.today,
    avgPrice,
    totalStock: stockTotal,
    stockKnown: hasStockValue,
    stockDays: stockDaysValues.length ? Math.min(...stockDaysValues.map(Number)) : null,
    rating: product.score || product.rating || null,
    evidenceSource: 'sales-cache',
    notes: '',
    skus: skuRows.slice(0, 24).map(row => ({
      skuId: row.skuId,
      name: row.skuName,
      price: row.amountValue,
      sales7: row.sevenDaysSaleVolume,
      sales30: row.thirtyDaysSaleVolume,
      stockQuantity: row.stockQuantity,
      stockDays: row.stockDays ?? row.availableSaleDays
    }))
  };
}

function buildManualTemuDiagnosisProduct() {
  const mainImage = document.getElementById('temuAiManualImageInput')?.value?.trim() || '';
  const totalStock = getTemuAiNumberInput('temuAiManualStockInput');
  return {
    key: document.getElementById('temuAiManualSpuInput')?.value?.trim() || 'manual-product',
    spuId: document.getElementById('temuAiManualSpuInput')?.value?.trim() || '',
    skcId: document.getElementById('temuAiManualSkcInput')?.value?.trim() || '',
    title: document.getElementById('temuAiManualTitleInput')?.value?.trim() || '',
    mainImage,
    mainImageKnown: Boolean(mainImage),
    carouselImageCount: getTemuAiNumberInput('temuAiCarouselCountInput'),
    detailImageCount: getTemuAiNumberInput('temuAiDetailImageCountInput'),
    skuCount: getTemuAiNumberInput('temuAiManualSkuCountInput'),
    sales7: getTemuAiNumberInput('temuAiManualSales7Input') || 0,
    sales30: 0,
    todaySales: 0,
    avgPrice: getTemuAiNumberInput('temuAiManualPriceInput') || 0,
    totalStock,
    stockKnown: totalStock !== null,
    evidenceSource: 'manual',
    notes: document.getElementById('temuAiManualNotesInput')?.value?.trim() || '',
    skus: []
  };
}

function prepareTemuAiDiagnosisProducts(options = {}) {
  const sourceMode = document.getElementById('temuAiSourceSelect')?.value || 'salesTop';
  const limit = Number(document.getElementById('temuAiLimitSelect')?.value || 5);
  if (sourceMode === 'manual') {
    const product = buildManualTemuDiagnosisProduct();
    state.temuAiDiagnosisProducts = [product];
    state.temuAiDiagnosisSelected = [product.key];
    renderTemuAiDiagnosisProductList();
    setTemuAiStatus('已准备 1 个手动商品样本', 'success');
    return;
  }

  const source = getTemuAiSalesSourceProducts(sourceMode);
  if (!source.length) {
    state.temuAiDiagnosisProducts = [];
    state.temuAiDiagnosisSelected = [];
    renderTemuAiDiagnosisProductList();
    if (!options.silent) setTemuAiStatus('没有可用销量缓存，请先在“销量查询”同步数据。', 'error');
    return;
  }
  const products = source
    .map(compactTemuProductForDiagnosis)
    .sort((a, b) => Number(b.sales7 || 0) - Number(a.sales7 || 0))
    .slice(0, Math.max(1, limit));
  state.temuAiDiagnosisProducts = products;
  state.temuAiDiagnosisSelected = products.map(item => item.key);
  renderTemuAiDiagnosisProductList();
  setTemuAiStatus(`已准备 ${products.length} 个商品样本，来源：${sourceMode === 'salesSearch' ? '当前搜索结果' : '销量缓存'}`, 'success');
}

function renderTemuAiDiagnosisProductList() {
  const el = document.getElementById('temuAiProductList');
  if (!el) return;
  const products = state.temuAiDiagnosisProducts || [];
  if (!products.length) {
    el.innerHTML = '<div class="empty">暂无商品样本，请先同步销量数据或手动输入商品。</div>';
    return;
  }
  el.innerHTML = products.map(product => {
    const checked = state.temuAiDiagnosisSelected.includes(product.key) ? 'checked' : '';
    const imageHtml = product.mainImage
      ? `<img class="temu-ai-thumb" src="${escapeHtml(product.mainImage)}" alt="">`
      : '<div class="temu-ai-thumb" style="display:flex;align-items:center;justify-content:center;color:#94a3b8;">图</div>';
    return `
      <label class="temu-ai-product-card">
        <input type="checkbox" class="checkbox temu-ai-product-checkbox" data-key="${escapeHtml(product.key)}" ${checked}>
        ${imageHtml}
        <span>
          <span class="temu-ai-product-title">${escapeHtml(product.title || `SPU ${product.spuId || product.key}`)}</span>
          <span class="temu-ai-meta">
            <span class="temu-ai-chip">SPU ${escapeHtml(product.spuId || '-')}</span>
            <span class="temu-ai-chip">SKC ${escapeHtml(product.skcId || '-')}</span>
            <span class="temu-ai-chip">SKU ${Number(product.skuCount || 0)}</span>
            <span class="temu-ai-chip">7日 ${Number(product.sales7 || 0)}</span>
          </span>
        </span>
      </label>
    `;
  }).join('');
  el.querySelectorAll('.temu-ai-product-checkbox').forEach(input => {
    input.addEventListener('change', () => {
      const key = input.dataset.key;
      if (input.checked) {
        if (!state.temuAiDiagnosisSelected.includes(key)) state.temuAiDiagnosisSelected.push(key);
      } else {
        state.temuAiDiagnosisSelected = state.temuAiDiagnosisSelected.filter(item => item !== key);
      }
    });
  });
}

function renderTemuAiLocalDiagnostics(localDiagnostics = []) {
  return localDiagnostics.map(item => {
    const riskClass = `temu-ai-risk-${item.riskLevel || 'low'}`;
    const findings = Array.isArray(item.findings) ? item.findings : [];
    const actions = Array.isArray(item.recommendedActions) ? item.recommendedActions : [];
    const uncertain = Array.isArray(item.uncertainItems) ? item.uncertainItems : [];
    return `
      <div class="temu-ai-diagnosis-card">
        <div class="temu-ai-diagnosis-head">
          <div>
            <div class="temu-ai-product-title">${escapeHtml(item.title || `SPU ${item.spuId || item.key}`)}</div>
            <div class="temu-ai-meta">
              <span class="temu-ai-chip">SPU ${escapeHtml(item.spuId || '-')}</span>
              <span class="temu-ai-chip">SKC ${escapeHtml(item.skcId || '-')}</span>
              <span class="temu-ai-chip">风险 ${escapeHtml(item.riskLevel || '-')}</span>
            </div>
          </div>
          <div class="temu-ai-score ${riskClass}">${Number(item.score || 0)}分</div>
        </div>
        <strong style="font-size:12px;">问题与证据</strong>
        <div class="temu-ai-list">
          ${(findings.length ? findings : [{ message: '本地规则未发现明确高风险项。', evidenceLevel: 'A' }]).map(finding => `<div>${escapeHtml(finding.message || '')} <b>证据等级 ${escapeHtml(finding.evidenceLevel || 'A')}</b></div>`).join('')}
        </div>
        <strong style="font-size:12px;margin-top:10px;display:block;">建议动作</strong>
        <div class="temu-ai-list">
          ${(actions.length ? actions : ['保持监控，补充曝光、点击率、转化率后再做更精确判断。']).map(action => `<div>${escapeHtml(action)}</div>`).join('')}
        </div>
        <strong style="font-size:12px;margin-top:10px;display:block;">不可确认项</strong>
        <div class="temu-ai-list">
          ${(uncertain.length ? uncertain : ['暂无不可确认项。']).map(text => `<div>${escapeHtml(text)}</div>`).join('')}
        </div>
      </div>
    `;
  }).join('');
}

const TEMU_AI_EVIDENCE_FIELD_LABELS = {
  sales7: '近7日销量',
  sales14: '近14日销量',
  sales30: '近30日销量',
  todaySales: '今日销量',
  todaySaleVolume: '今日销量',
  sevenDaysSaleVolume: '近7日销量',
  thirtyDaysSaleVolume: '近30日销量',
  totalStock: '总库存',
  stock: '库存',
  stockDays: '可售天数',
  availableSaleDays: '可售天数',
  mainImage: '主图',
  image: '商品图片',
  title: '标题',
  spuId: 'SPU商品ID',
  productId: 'SPU商品ID',
  skcId: 'SKC货品ID',
  skuCount: 'SKU数量',
  carouselImageCount: '轮播图数量',
  detailImageCount: '详情图数量',
  priceMin: '最低售价',
  priceMax: '最高售价',
  supplierPrice: '供货价',
  impressions: '曝光量',
  exposure: '曝光量',
  clicks: '点击量',
  ctr: '点击率',
  conversionRate: '转化率',
  adSpend: '广告花费',
  roas: '广告ROAS',
  activity: '活动',
  notes: '补充说明',
  rating: '评分',
  reviews: '评价数'
};

function getTemuAiEvidenceFieldLabel(field = '') {
  const raw = String(field || '').trim();
  if (!raw) return '证据项';
  return TEMU_AI_EVIDENCE_FIELD_LABELS[raw] || raw;
}

function formatTemuAiEvidenceValue(field = '', value = '') {
  const key = String(field || '').trim();
  if (key === 'mainImage' || key === 'image') {
    if (['A', 'B', 'C'].includes(String(value || '').trim().toUpperCase())) return '未返回具体值';
    if (!value) return '未同步到图片';
    return /^https?:\/\//i.test(String(value)) ? '已同步图片' : String(value);
  }
  if (key === 'totalStock' && (value === null || typeof value === 'undefined' || value === '')) return '未同步库存数据';
  if ((key === 'sales7' || key === 'sales14' || key === 'sales30' || key === 'todaySales' || key === 'todaySaleVolume' || key === 'sevenDaysSaleVolume' || key === 'thirtyDaysSaleVolume') && value !== null && typeof value !== 'undefined' && value !== '') {
    return `${value} 单`;
  }
  if (typeof value === 'boolean') return value ? '是' : '否';
  if (value === null || typeof value === 'undefined' || value === '') return '未返回具体值';
  return String(value);
}

function getTemuAiEvidenceLevelText(level = '') {
  const normalized = String(level || '').trim().toUpperCase();
  if (normalized === 'A') return '证据等级A：来自已同步数据';
  if (normalized === 'B') return '证据等级B：来自规则推断或间接数据';
  if (normalized === 'C') return '证据等级C：证据不足，仅供观察';
  return normalized ? `证据等级${normalized}` : '证据等级未标注';
}

function renderTemuAiEvidenceList(evidence = []) {
  const rows = Array.isArray(evidence) ? evidence : [];
  if (!rows.length) return '<div>AI 未返回证据项，请补充数据后重新诊断。</div>';
  return rows.map(ev => {
    const field = ev?.field || '';
    const label = getTemuAiEvidenceFieldLabel(field);
    const value = formatTemuAiEvidenceValue(field, ev?.value);
    const level = getTemuAiEvidenceLevelText(ev?.evidenceLevel);
    return `<div><strong>${escapeHtml(label)}：</strong>${escapeHtml(value)} <span style="color:#64748b;">（${escapeHtml(level)}）</span></div>`;
  }).join('');
}

function renderTemuAiTextList(title, rows = [], emptyText = '暂无。') {
  const list = Array.isArray(rows) ? rows.filter(Boolean) : [];
  return `
    <strong style="font-size:12px;margin-top:10px;display:block;">${escapeHtml(title)}</strong>
    <div class="temu-ai-list">${list.length ? list.map(item => {
      if (item && typeof item === 'object') {
        const label = item.label || item.day || item.status || item.type || '';
        const text = item.text || item.action || item.reason || item.item || item.value || '';
        const evidence = item.evidence || item.condition || item.dataNeeded || '';
        return `<div>${label ? `<strong>${escapeHtml(label)}：</strong>` : ''}${escapeHtml(text || JSON.stringify(item))}${evidence ? `<br><span style="color:#64748b;">依据/条件：${escapeHtml(evidence)}</span>` : ''}</div>`;
      }
      return `<div>${escapeHtml(item)}</div>`;
    }).join('') : `<div>${escapeHtml(emptyText)}</div>`}</div>
  `;
}

function renderTemuAiModelResult(result = {}) {
  const parsed = result.parsed && typeof result.parsed === 'object' ? result.parsed : null;
  if (parsed?.summary || Array.isArray(parsed?.items)) {
    const items = Array.isArray(parsed.items) ? parsed.items : [];
    return `
      <div class="temu-ai-summary">${escapeHtml(parsed.summary || 'AI 已返回结构化诊断。')}</div>
      ${items.map(item => `
        <div class="temu-ai-diagnosis-card">
          <div class="temu-ai-diagnosis-head">
            <div>
              <div class="temu-ai-product-title">${escapeHtml(item.key || '商品诊断')}</div>
              <div class="temu-ai-meta">
                <span class="temu-ai-chip">${escapeHtml(item.priority || 'P2')}</span>
                <span class="temu-ai-chip">风险 ${escapeHtml(item.risk || '-')}</span>
                <span class="temu-ai-chip">观察 ${escapeHtml(item.observeDays || 7)} 天</span>
              </div>
            </div>
          </div>
          <div class="temu-ai-summary" style="background:#eef2ff;color:#312e81;border-color:#c7d2fe;">${escapeHtml(item.diagnosis || '')}</div>
          <strong style="font-size:12px;">证据</strong>
          <div class="temu-ai-list">${renderTemuAiEvidenceList(item.evidence || [])}</div>
          ${renderTemuAiTextList('可疑原因分级', item.causeCandidates || [], '暂无可确认原因。')}
          ${renderTemuAiTextList('7天急救动作', item.sevenDayPlan || item.actions || [], '暂无动作。')}
          ${renderTemuAiTextList('需要补充的数据', item.dataNeeded || item.uncertainItems || [], '暂无。')}
          ${renderTemuAiTextList('暂时不要做', item.doNotDo || [], '暂无。')}
        </div>
      `).join('')}
    `;
  }
  return result.text ? `<div class="temu-ai-summary">${escapeHtml(result.text)}</div>` : '';
}

async function runTemuAiProductDiagnosis() {
  if (!state.temuAiDiagnosisProducts?.length) prepareTemuAiDiagnosisProducts();
  const selected = (state.temuAiDiagnosisProducts || []).filter(item => state.temuAiDiagnosisSelected.includes(item.key));
  if (!selected.length) {
    showToast('请先选择要诊断的商品', 'warning');
    return;
  }
  const resultEl = document.getElementById('temuAiResult');
  const runBtn = document.getElementById('temuAiRunBtn');
  if (runBtn) {
    runBtn.disabled = true;
    runBtn.textContent = '诊断中...';
  }
  setTemuAiStatus('正在生成诊断，所有结论会绑定证据等级...');
  if (resultEl) resultEl.innerHTML = '<div class="temu-ai-summary">正在分析商品数据，请稍候...</div>';
  try {
    const result = await aiRequest('/temu/product-diagnosis', {
      method: 'POST',
      body: JSON.stringify({
        products: selected,
        context: {
          source: document.getElementById('temuAiSourceSelect')?.value || '',
          mallId: state.mallId || '',
          rule: 'strict-evidence'
        }
      })
    });
    const modelHtml = renderTemuAiModelResult(result);
    const localHtml = result.mode === 'ai' ? renderTemuAiLocalDiagnostics(result.localDiagnostics || []) : '';
    if (resultEl) {
      resultEl.innerHTML = `
        ${modelHtml}
        ${localHtml ? '<div class="temu-ai-summary" style="background:#f8fafc;color:#334155;border-color:#e2e8f0;">规则校验：以下校验只基于已同步字段，缺失数据已列入不可确认项。</div>' : ''}
        ${localHtml}
      `;
    }
    setTemuAiStatus('AI诊断完成，已附带规则校验。', 'success');
  } catch (err) {
    console.error('[小青蛙电商助手] TEMU AI商品诊断失败:', err);
    const message = err.message || '大模型API未连接，请先在管理后台配置并启用大模型API';
    if (resultEl) resultEl.innerHTML = `<div class="temu-ai-summary" style="background:#fef2f2;color:#991b1b;border-color:#fecaca;">${escapeHtml(message)}</div>`;
    setTemuAiStatus(message, 'error');
  } finally {
    if (runBtn) {
      runBtn.disabled = false;
      runBtn.textContent = '开始诊断';
    }
  }
}

// ===== 首页概览相关函数 =====

let SERVER_URL = 'https://xiaoqingwa.vip/api';
const PROD_API_BASE = 'https://xiaoqingwa.vip/api';
const DEFAULT_LOCAL_API_BASE = 'http://127.0.0.1:3001/api';
const LOGIN_REQUEST_TIMEOUT_MS = 12000;

function isLocalApiBase(base) {
  return /^https?:\/\/(127\.0\.0\.1|localhost)(:\d+)?/i.test(String(base || '').trim());
}

function normalizeApiBase(base) {
  return String(base || '').trim().replace(/\/$/, '');
}

function isLocalDevPage() {
  const protocol = window.location?.protocol || '';
  const hostname = window.location?.hostname || '';
  return protocol === 'file:' || hostname === '127.0.0.1' || hostname === 'localhost';
}

function getQueryApiBase() {
  try {
    return new URLSearchParams(window.location.search).get('apiBase');
  } catch {
    return '';
  }
}

function isAllowedApiBase(base) {
  const normalized = normalizeApiBase(base);
  if (normalized === PROD_API_BASE) return true;
  return isLocalDevPage() && isLocalApiBase(normalized);
}

function describeApiFetchError(base, err) {
  if (err?.name === 'AbortError') return '服务器响应超时，请稍后重试';
  const normalized = normalizeApiBase(base);
  if (isLocalDevPage() && normalized === PROD_API_BASE) {
    return '本地打开页面不能直连正式登录接口。请访问 https://xiaoqingwa.vip 登录，或启动本地后端后使用 ?apiBase=http://127.0.0.1:3001/api';
  }
  if (isLocalApiBase(normalized)) {
    return '无法连接本地后端服务，请确认本地服务器已启动，并且地址为 http://127.0.0.1:3001/api';
  }
  return err?.message === 'Failed to fetch'
    ? '无法连接登录服务器，请检查网络或后端接口地址'
    : (err?.message || String(err));
}

function getApiBaseCandidates(preferCurrent = true) {
  const seen = new Set();
  const bases = [];
  const push = (base) => {
    if (!base) return;
    const normalized = normalizeApiBase(base);
    if (!isAllowedApiBase(normalized)) return;
    if (seen.has(normalized)) return;
    seen.add(normalized);
    bases.push(normalized);
  };
  const queryBase = getQueryApiBase();
  push(queryBase);
  const storedBase = localStorage.getItem('temuApiBase');
  if (isLocalDevPage()) {
    if (storedBase && isLocalApiBase(storedBase)) push(storedBase);
    push(DEFAULT_LOCAL_API_BASE);
    if (preferCurrent && isLocalApiBase(SERVER_URL)) push(SERVER_URL);
    return bases.length ? bases : [DEFAULT_LOCAL_API_BASE];
  }
  push(storedBase);
  if (preferCurrent) push(SERVER_URL);
  push(PROD_API_BASE);
  return bases;
}

async function postJsonWithFallback(path, payload, preferCurrent = true) {
  const bases = getApiBaseCandidates(preferCurrent);
  let lastError = null;
  const timeoutMs = path === '/user/login' ? LOGIN_REQUEST_TIMEOUT_MS : 8000;
  for (const base of bases) {
    const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
    const timeoutId = controller ? window.setTimeout(() => controller.abort(), timeoutMs) : null;
    try {
      const resp = await fetch(`${base}${path}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
        signal: controller?.signal
      });
      const result = await resp.json().catch(() => ({}));
      if (resp.ok && result.ok) {
        return { ok: true, base, result };
      }
      lastError = { status: resp.status, message: result.error || result.message || `HTTP ${resp.status}` };
      if (![401, 403].includes(resp.status) && resp.status < 500) {
        return { ok: false, base, result, error: lastError.message };
      }
    } catch (err) {
      lastError = { message: describeApiFetchError(base, err) };
    } finally {
      if (timeoutId) window.clearTimeout(timeoutId);
    }
  }
  return { ok: false, error: lastError?.message || '请求失败' };
}

function warmServerApi() {
  fetch(`${SERVER_URL}/health`, {
    cache: 'no-store',
    keepalive: true
  }).catch(() => {});
}

async function resolveServerUrl() {
  const queryBase = getQueryApiBase();
  if (queryBase && isAllowedApiBase(queryBase)) {
    SERVER_URL = normalizeApiBase(queryBase);
    localStorage.setItem('temuApiBase', SERVER_URL);
    return;
  }

  const storedBase = localStorage.getItem('temuApiBase');
  if (isLocalDevPage()) {
    if (storedBase && isLocalApiBase(storedBase)) {
      SERVER_URL = normalizeApiBase(storedBase);
      return;
    }
    SERVER_URL = DEFAULT_LOCAL_API_BASE;
    localStorage.setItem('temuApiBase', SERVER_URL);
    return;
  }

  if (storedBase && isAllowedApiBase(storedBase)) {
    SERVER_URL = normalizeApiBase(storedBase);
    return;
  }
  if (storedBase) {
    localStorage.removeItem('temuApiBase');
  }

  SERVER_URL = PROD_API_BASE;
  localStorage.setItem('temuApiBase', SERVER_URL);
}
let dashThirtyUnlocked = false;
const dashboardSalesState = {
  todayTotal: 0,
  sevenDayTotal: 0,
  thirtyTotal: 0,
  todayAmount: 0,
  sevenDayAmount: 0,
  thirtyAmount: 0,
  source: 'unknown'
};

function setDashboardSalesSummary(summary = {}) {
  if (Number.isFinite(Number(summary.todayTotal))) dashboardSalesState.todayTotal = Number(summary.todayTotal);
  if (Number.isFinite(Number(summary.sevenDayTotal))) dashboardSalesState.sevenDayTotal = Number(summary.sevenDayTotal);
  if (Number.isFinite(Number(summary.thirtyTotal))) dashboardSalesState.thirtyTotal = Number(summary.thirtyTotal);
  if (Number.isFinite(Number(summary.todayAmount))) dashboardSalesState.todayAmount = Number(summary.todayAmount);
  if (Number.isFinite(Number(summary.sevenDayAmount))) dashboardSalesState.sevenDayAmount = Number(summary.sevenDayAmount);
  if (Number.isFinite(Number(summary.thirtyAmount))) dashboardSalesState.thirtyAmount = Number(summary.thirtyAmount);
  if (summary.source) dashboardSalesState.source = summary.source;
  syncDingHuiTemuHome();
}

function getDashboardMetricElement(...ids) {
  for (const id of ids) {
    const el = document.getElementById(id);
    if (el) return el;
  }
  return null;
}

function syncDingHuiTemuHome() {
  const hasHome = Boolean(document.getElementById('dhTemuMallName'));
  if (!hasHome) return;
  const connected = Boolean(state.mallId && state.antiContent);
  const currentServerUser = getCurrentServerUser();
  const admin = isAdminUser(currentServerUser);
  const currentShop = connected ? findBoundShopForMall(state.boundShops || [], state.mallId) : null;
  const currentShopBound = Boolean(currentShop);
  const currentShopAuthorized = admin || hasValidShopAccess(currentShop);
  const pickName = (...values) => values
    .map(value => String(value ?? '').trim())
    .find(value => value && value !== '当前店铺' && value !== '当前店铺 ');
  const shopName = connected
    ? (pickName(state.mallName, getCachedShopName(state.mallId)) || `店铺 ${state.mallId}`)
    : '未连接店铺';
  const fullShopName = connected && state.mallId ? `${shopName} (${state.mallId})` : shopName;
  const setText = (id, value) => {
    const el = document.getElementById(id);
    if (el) el.textContent = value;
  };
  const setBadge = (targetId, sourceId, fallbackText, fallbackClass) => {
    const target = document.getElementById(targetId);
    if (!target) return;
    const source = document.getElementById(sourceId);
    target.textContent = source?.textContent || fallbackText;
    target.className = source?.className || fallbackClass;
    target.style.display = 'inline-flex';
  };
  const bridge = getBridgeRuntimeState();
  const connectionText = connected ? '已连接' : (bridge.active && bridge.ready ? '助手已连接' : '未连接助手');
  const connectionClass = connected
    ? 'status-badge status-connected'
    : `status-badge ${bridge.active && bridge.ready ? 'status-bridge-ready' : 'status-disconnected'}`;

  setText('dhTemuMallName', shopName);
  setText('dhTemuMallInfo', fullShopName);
  setBadge('dhTemuConnectionStatus', 'connectionStatus', connectionText, connectionClass);
  setBadge('dhTemuTopAuthStatus', 'topAuthStatus', currentServerUser ? '已登录' : '未绑定', currentServerUser ? 'status-badge status-authorized' : 'status-badge status-disconnected');
  setText('dhTemuDashAuthStatus', document.getElementById('dashAuthStatus')?.textContent || document.getElementById('loginAuthStatus')?.textContent || '未登录');
  const bindBtn = document.getElementById('dhTemuBindCurrentShopBtn');
  if (bindBtn) {
    const shouldShowBind = connected && !admin && !currentShopBound;
    bindBtn.hidden = !shouldShowBind;
    bindBtn.textContent = '绑定店铺';
    bindBtn.className = 'btn btn-primary btn-sm';
    bindBtn.title = '将当前连接的 TEMU 店铺绑定到账号，绑定后会在店铺列表显示为未授权';
  }

  const sourceResult = document.getElementById('dashAuthResult');
  const targetResult = document.getElementById('dhTemuDashAuthResult');
  if (targetResult) {
    targetResult.textContent = sourceResult?.textContent || '';
    targetResult.style.color = sourceResult?.style?.color || '#667085';
    targetResult.style.display = sourceResult?.style?.display === 'block' && sourceResult?.textContent ? 'block' : 'none';
  }

  const todayTotal = Number(dashboardSalesState.todayTotal || 0);
  const sevenDayTotal = Number(dashboardSalesState.sevenDayTotal || 0);
  const todayAmount = Number(dashboardSalesState.todayAmount || 0);
  const sevenDayAmount = Number(dashboardSalesState.sevenDayAmount || 0);
  setText('dhTemuSalesToday', Number.isFinite(todayTotal) ? todayTotal.toLocaleString() : '0');
  setText('dhTemuSalesSeven', Number.isFinite(sevenDayTotal) ? sevenDayTotal.toLocaleString() : '0');
  setText('dhTemuSalesAmount', `¥${formatAmount(sevenDayAmount)}`);
  setText('dhTemuSalesCost', `¥${formatAmount(todayAmount)}`);
  renderTemuShopSalesDistribution({ shopName, todayTotal, sevenDayTotal, connected });
}

const temuShopSalesSnapshotState = {
  loadedAt: 0,
  snapshots: [],
  pendingPromise: null
};

function getSalesSnapshotSummary(snapshot = {}) {
  const products = Array.isArray(snapshot.products) ? snapshot.products : [];
  const summary = snapshot.summary && typeof snapshot.summary === 'object'
    ? snapshot.summary
    : calculateSalesSummary(products);
  return {
    todayTotal: Number(summary.todayTotal || 0),
    sevenDayTotal: Number(summary.sevenDayTotal || 0),
    thirtyDayTotal: Number(summary.thirtyDayTotal || summary.thirtyTotal || 0),
    todayAmount: Number(summary.todayAmount || 0),
    sevenDayAmount: Number(summary.sevenDayAmount || 0),
    thirtyAmount: Number(summary.thirtyDayAmount || summary.thirtyAmount || 0),
    products
  };
}

async function getTemuShopSalesSnapshots(force = false) {
  const now = Date.now();
  if (!force && temuShopSalesSnapshotState.loadedAt && now - temuShopSalesSnapshotState.loadedAt < 60000) {
    return temuShopSalesSnapshotState.snapshots;
  }
  if (temuShopSalesSnapshotState.pendingPromise) return temuShopSalesSnapshotState.pendingPromise;
  temuShopSalesSnapshotState.pendingPromise = fetchAllTemuSnapshots('sales')
    .then((snapshots) => {
      temuShopSalesSnapshotState.loadedAt = Date.now();
      temuShopSalesSnapshotState.snapshots = Array.isArray(snapshots) ? snapshots : [];
      return temuShopSalesSnapshotState.snapshots;
    })
    .finally(() => {
      temuShopSalesSnapshotState.pendingPromise = null;
    });
  return temuShopSalesSnapshotState.pendingPromise;
}

function renderTemuShopSalesRows(shops = []) {
  const listEl = document.getElementById('dhTemuShopSalesList');
  const summaryEl = document.getElementById('dhTemuShopSalesSummary');
  const donutEl = document.getElementById('dhTemuShopSalesDonut');
  if (!listEl) return;
  const sorted = (Array.isArray(shops) ? shops : [])
    .sort((a, b) => Number(b.today || 0) - Number(a.today || 0)
      || Number(b.seven || 0) - Number(a.seven || 0)
      || String(a.name || '').localeCompare(String(b.name || ''), 'zh-Hans-CN'));
  const totalToday = sorted.reduce((sum, shop) => sum + Math.max(0, Number(shop.today || 0)), 0);
  if (summaryEl) summaryEl.textContent = `${sorted.length} 个店铺 · 按今日销量`;
  if (donutEl) {
    if (totalToday <= 0) {
      donutEl.style.background = 'conic-gradient(#e5e7eb 0 100%)';
      donutEl.setAttribute('title', '暂无当日销量数据');
    } else {
      const colors = ['#2563eb', '#10b981', '#f59e0b', '#ef4444', '#06b6d4', '#8b5cf6', '#84cc16', '#f97316'];
      let cursor = 0;
      const segments = sorted.map((shop, index) => {
        const start = cursor;
        const pct = Math.max(0, Number(shop.today || 0)) / totalToday * 100;
        cursor += pct;
        return `${colors[index % colors.length]} ${start.toFixed(2)}% ${cursor.toFixed(2)}%`;
      }).join(', ');
      donutEl.style.background = `conic-gradient(${segments})`;
      donutEl.setAttribute('title', `今日总销量 ${totalToday.toLocaleString()}`);
    }
  }
  listEl.innerHTML = sorted.map((shop, index) => `
    <div class="dh-shop-sales-row" title="${escapeHtml(shop.name)}">
      <div class="dh-shop-sales-rank">${index + 1}</div>
      <div class="dh-shop-sales-main">
        <div class="dh-shop-sales-name">${escapeHtml(shop.name)}</div>
        <div class="dh-shop-sales-meta">${shop.synced ? `今日 ${Number(shop.today).toLocaleString()} · 7日 ${Number(shop.seven).toLocaleString()}` : '未同步销量数据'}</div>
      </div>
      <div class="dh-shop-sales-value">${Number(shop.today || 0).toLocaleString()}</div>
    </div>
  `).join('');
}

function renderTemuShopSalesDistribution({ shopName = '未连接店铺', todayTotal = 0, sevenDayTotal = 0, connected = false } = {}) {
  const listEl = document.getElementById('dhTemuShopSalesList');
  const summaryEl = document.getElementById('dhTemuShopSalesSummary');
  if (!listEl) return;

  const currentMallId = String(state.mallId || '').trim();
  const shopMap = new Map();
  const addShop = (id, name, today = 0, seven = 0, synced = false) => {
    const key = String(id || name || `shop-${shopMap.size}`).trim();
    if (!key) return;
    shopMap.set(key, {
      id: String(id || '').trim(),
      name: name || (id ? `店铺 ${id}` : '未连接店铺'),
      today: Number(today || 0),
      seven: Number(seven || 0),
      synced
    });
  };

  if (connected) addShop(currentMallId, shopName, todayTotal, sevenDayTotal, true);
  (Array.isArray(state.boundShops) ? state.boundShops : []).forEach((shop) => {
    const mallId = String(shop?.mallId || '').trim();
    const name = pickShopName(shop?.shopName, shop?.mallName, shop?.storeName, shop?.name, getCachedShopName(mallId), mallId ? `店铺 ${mallId}` : '绑定店铺');
    if (mallId && mallId === currentMallId && connected) {
      addShop(mallId, shopName || name, todayTotal, sevenDayTotal, true);
    } else {
      addShop(mallId, name, 0, 0, false);
    }
  });
  if (shopMap.size === 0) addShop('', '未连接店铺', 0, 0, false);

  renderTemuShopSalesRows(Array.from(shopMap.values()));
  getTemuShopSalesSnapshots(false).then((snapshots) => {
    snapshots.forEach((snapshot) => {
      const mallId = normalizeTemuMallId(snapshot.mallId || '');
      if (!mallId) return;
      const summary = getSalesSnapshotSummary(snapshot);
      const name = pickShopName(snapshot.mallName, getCachedShopName(mallId), mallId ? `店铺 ${mallId}` : '绑定店铺');
      addShop(mallId, name, summary.todayTotal, summary.sevenDayTotal, true);
    });
    renderTemuShopSalesRows(Array.from(shopMap.values()));
  }).catch(() => {
    if (summaryEl) summaryEl.textContent = `${shopMap.size} 个店铺`;
  });
}

function pickTrendDateValue(item = {}) {
  const dateValue = pickSalesField(item, [
    'date',
    'saleDate',
    'salesDate',
    'day',
    'statDate',
    'time',
    'statTime',
    'dateStr',
    'dateString',
    'sale_date',
    'sale_time'
  ]);
  if (!dateValue) return '';
  const text = normalizeSalesText(dateValue);
  if (!text) return '';
  const date = new Date(text);
  if (!Number.isNaN(date.getTime())) {
    const pad = n => String(n).padStart(2, '0');
    return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
  }
  return text.slice(0, 10);
}

function pickTrendVolumeValue(item = {}) {
  return normalizeSalesNumber(pickSalesField(item, [
    'saleVolume',
    'salesVolume',
    'volume',
    'count',
    'num',
    'saleNum',
    'salesNum',
    'orderNum',
    'todaySaleVolume',
    'sevenDaysSaleVolume',
    'thirtyDaysSaleVolume',
    'dailySaleVolume'
  ])) || 0;
}

function getTrendPointArray(node, depth = 0) {
  if (!node || typeof node !== 'object' || depth > 3) return null;
  const candidateKeys = [
    'salesTrendList',
    'saleTrendList',
    'dailyTrendList',
    'dailySalesTrendList',
    'dailySaleTrendList',
    'trendList',
    'dayTrendList',
    'dailyList',
    'saleList',
    'chartList',
    'dateList',
    'salesList'
  ];
  for (const key of candidateKeys) {
    const value = node[key];
    if (Array.isArray(value) && value.some(item => item && typeof item === 'object')) {
      return value;
    }
  }
  for (const value of Object.values(node)) {
    if (Array.isArray(value) && value.some(item => item && typeof item === 'object')) {
      const hasTrendShape = value.some(item => {
        const date = pickTrendDateValue(item);
        const sale = pickTrendVolumeValue(item);
        return Boolean(date) && Number.isFinite(sale);
      });
      if (hasTrendShape) return value;
    }
  }
  for (const value of Object.values(node)) {
    if (value && typeof value === 'object') {
      const nested = getTrendPointArray(value, depth + 1);
      if (nested) return nested;
    }
  }
  return null;
}

function extractSalesTrendSeries(products = []) {
  const series = new Map();
  for (const product of products) {
    const source = product?.raw || product;
    const points = Array.isArray(source) ? source : getTrendPointArray(source);
    if (!Array.isArray(points) || points.length === 0) continue;
    for (const item of points) {
      if (!item || typeof item !== 'object') continue;
      const date = pickTrendDateValue(item);
      const value = pickTrendVolumeValue(item);
      if (!date || !Number.isFinite(value)) continue;
      series.set(date, (series.get(date) || 0) + value);
    }
  }

  const ordered = Array.from(series.entries())
    .map(([date, value]) => ({ date, value: Number(value) || 0 }))
    .sort((a, b) => a.date.localeCompare(b.date));

  return ordered;
}

function buildSevenDayTrendBars(products = []) {
  const trendSeries = extractSalesTrendSeries(products);
  if (trendSeries.length === 0) return [];
  const valueByDate = new Map(trendSeries.map(item => [item.date, Number(item.value) || 0]));
  const lastDate = new Date(trendSeries[trendSeries.length - 1].date);
  const baseDate = Number.isNaN(lastDate.getTime()) ? new Date() : lastDate;
  const pad = n => String(n).padStart(2, '0');
  const bars = [];
  for (let offset = 6; offset >= 0; offset--) {
    const date = new Date(baseDate);
    date.setDate(date.getDate() - offset);
    const key = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
    bars.push({ date: key, value: valueByDate.get(key) || 0 });
  }
  return bars;
}

function renderTrendBarChartMarkup(bars = []) {
  const labels = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
  const maxValue = Math.max(...bars.map(item => Number(item.value) || 0), 1);
  return bars.map(item => {
    const date = new Date(item.date);
    const label = Number.isNaN(date.getTime())
      ? item.date
      : `${labels[date.getDay()]}<br><span class="bar-chart-date">${date.getMonth() + 1}/${date.getDate()}</span>`;
    const heightPercent = Math.max(8, Math.round((Number(item.value) / maxValue) * 100));
    return `<div class="bar-chart-item"><div class="bar-chart-value">${Number(item.value).toLocaleString()}</div><div class="bar-chart-bar" style="height:${heightPercent}%;"><div class="bar-chart-fill" style="height:100%;"></div></div><div class="bar-chart-label">${label}</div></div>`;
  }).join('');
}

const dashboardStockReminderState = {
  mallId: '',
  loading: false,
  loadedAt: 0,
  payload: null,
  error: '',
  pendingPromise: null
};

function pickStockReminderField(source, keys = []) {
  if (!source || typeof source !== 'object') return null;
  for (const key of keys) {
    const value = source[key];
    if (value !== undefined && value !== null && value !== '') return value;
  }
  return null;
}

function parseStockReminderTime(value) {
  if (value === null || value === undefined || value === '') return null;
  if (value instanceof Date) {
    const time = value.getTime();
    return Number.isNaN(time) ? null : time;
  }
  if (typeof value === 'number' && Number.isFinite(value)) {
    return value > 1e12 ? value : value * 1000;
  }
  const text = String(value).trim();
  if (!text) return null;
  const numeric = Number(text);
  if (Number.isFinite(numeric)) {
    return numeric > 1e12 ? numeric : numeric * 1000;
  }
  let parsed = new Date(text);
  let time = parsed.getTime();
  if (Number.isNaN(time)) {
    parsed = new Date(text.replace(/-/g, '/'));
    time = parsed.getTime();
  }
  return Number.isNaN(time) ? null : time;
}

function formatStockReminderCountdown(ms) {
  if (!Number.isFinite(ms)) return '—';
  if (ms <= 0) return '已超时';
  const totalMinutes = Math.ceil(ms / 60000);
  const days = Math.floor(totalMinutes / 1440);
  const hours = Math.floor((totalMinutes % 1440) / 60);
  const minutes = totalMinutes % 60;
  const parts = [];
  if (days > 0) parts.push(`${days}天`);
  if (hours > 0 || days > 0) parts.push(`${hours}小时`);
  parts.push(`${minutes}分钟`);
  return parts.join('');
}

function formatStockReminderDate(value) {
  const time = parseStockReminderTime(value);
  if (!time) return '—';
  const date = new Date(time);
  return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
}

function buildStockReminderItem(row, type, deadline, thresholdHours) {
  const remainingMs = deadline - Date.now();
  const productName = pickStockReminderField(row, [
    'productName',
    'goodsName',
    'productTitle',
    'skuName',
    'productSkcName',
    'title'
  ]) || '备货单';
  const skuText = pickStockReminderField(row, ['productSkcId', 'skcId', 'skc', 'skuId']) || '—';
  const orderSn = pickStockReminderField(row, ['subPurchaseOrderSn', 'subOrderSn', 'purchaseOrderSn', 'orderSn']) || '—';
  const image = pickStockReminderField(row, ['productSkcPicture', 'img', 'imageUrl', 'mainImage']) || '';
  const typeLabel = type === 'shipping' ? '发货超时预警' : '到货超时预警';
  const tone = type === 'shipping'
    ? remainingMs <= 6 * 3600000 ? 'danger' : 'warning'
    : remainingMs <= 12 * 3600000 ? 'danger' : 'warning';

  return {
    id: `${type}:${orderSn}:${skuText}:${deadline}`,
    type,
    typeLabel,
    tone,
    productName,
    skuText,
    orderSn,
    statusText: pickStockReminderField(row, ['statusText', 'orderStatusName', 'statusName', 'orderStatus', 'status']) || '',
    image,
    deadline,
    deadlineText: formatStockReminderDate(deadline),
    remainingMs,
    remainingText: formatStockReminderCountdown(remainingMs),
    thresholdHours
  };
}

function readDashboardStockReminderStatusText(row = {}) {
  const value = pickStockReminderField(row, [
    'statusText',
    'orderStatusName',
    'statusName',
    'subOrderStatusName',
    'purchaseStatusName',
    'productStatusName',
    'orderStatus',
    'subOrderStatus',
    'purchaseStatus',
    'status',
    'state'
  ]);
  if (value === null || value === undefined || value === '') return '';
  const text = String(value).trim();
  if (!/^-?\d+$/.test(text)) return text;
  const statusLabelMap = {
    0: '待创建',
    1: '待发货',
    2: '已送货',
    3: '已收货',
    4: '抽检全部退回',
    5: '已验收',
    6: '已验收',
    7: '已入库',
    8: '已作废',
    9: '已取消',
    10: '已超时'
  };
  return statusLabelMap[Number(text)] || text;
}

function isDashboardStockReminderFinishedStatus(row = {}) {
  return /已收货|已验收|已入库|已作废|已取消|取消|作废|关闭|完结|完成/.test(readDashboardStockReminderStatusText(row));
}

function isDashboardStockReminderShippingStatus(row = {}) {
  return /待发货|待送货|待揽收|待履约/.test(readDashboardStockReminderStatusText(row));
}

function isDashboardStockReminderArrivalStatus(row = {}) {
  const statusText = readDashboardStockReminderStatusText(row);
  if (!statusText || isDashboardStockReminderFinishedStatus(row)) return false;
  return /待收货|待到货|运输中|配送中|已发货|已送货|送货中/.test(statusText);
}

function buildDashboardStockReminderItems(rows = []) {
  const shipping = [];
  const arrival = [];
  const now = Date.now();
  const shippingThresholdMs = 24 * 3600000;
  const arrivalThresholdMs = 48 * 3600000;

  for (const row of Array.isArray(rows) ? rows : []) {
    const shippingDeadline = parseStockReminderTime(pickStockReminderField(row, [
      'expectLatestDeliverTime',
      'expectLatestDeliveryTime',
      'latestDeliverTime',
      'deliverDeadline',
      'deliveryDeadline'
    ]));
    if (shippingDeadline) {
      const remaining = shippingDeadline - now;
      if (remaining > 0 && remaining <= shippingThresholdMs && isDashboardStockReminderShippingStatus(row)) {
        shipping.push(buildStockReminderItem(row, 'shipping', shippingDeadline, 24));
      }
    }

    const arrivalDeadline = parseStockReminderTime(pickStockReminderField(row, [
      'expectLatestArriveTime',
      'expectLatestReceiveTime',
      'latestArriveTime',
      'arriveDeadline',
      'receiveDeadline'
    ]));
    if (arrivalDeadline) {
      const remaining = arrivalDeadline - now;
      if (remaining > 0 && remaining <= arrivalThresholdMs && isDashboardStockReminderArrivalStatus(row)) {
        arrival.push(buildStockReminderItem(row, 'arrival', arrivalDeadline, 48));
      }
    }
  }

  const sorter = (a, b) => a.remainingMs - b.remainingMs || a.productName.localeCompare(b.productName, 'zh-Hans-CN');
  shipping.sort(sorter);
  arrival.sort(sorter);
  return { shipping, arrival };
}

function renderDashboardStockReminder(payload = {}) {
  const summaryEl = document.getElementById('dashStockReminderSummary');
  const listEl = document.getElementById('dashStockReminderList');
  if (!summaryEl || !listEl) return;

  const shipping = Array.isArray(payload.shipping) ? payload.shipping : [];
  const arrival = Array.isArray(payload.arrival) ? payload.arrival : [];
  const loading = Boolean(payload.loading);
  const error = String(payload.error || '').trim();
  const message = String(payload.message || '').trim();
  const updatedAt = payload.updatedAt ? formatStockReminderDate(payload.updatedAt) : '';
  const total = shipping.length + arrival.length;

  if (loading) {
    summaryEl.textContent = '正在加载备货单提醒...';
    listEl.innerHTML = '<div style="color:#999;text-align:center;padding:16px;">正在从 TEMU 读取备货单数据</div>';
    return;
  }

  if (error) {
    summaryEl.textContent = error;
    listEl.innerHTML = `<div style="color:#dc2626;text-align:center;padding:16px;">${escapeHtml(error)}</div>`;
    return;
  }

  if (total > 0) {
    summaryEl.textContent = `发货预警 ${shipping.length} 条 · 到货预警 ${arrival.length} 条${updatedAt ? ` · 更新于 ${updatedAt}` : ''}`;
  } else {
    summaryEl.textContent = message || '暂无即将超时的备货单';
  }

  const renderSection = (title, items, tone) => {
    if (!items || items.length === 0) return '';
    const headerBg = tone === 'danger' ? '#fff1f2' : '#eff6ff';
    const headerColor = tone === 'danger' ? '#be123c' : '#1d4ed8';
    return `
      <div style="display:grid;gap:10px;">
        <div style="display:flex;justify-content:space-between;align-items:center;gap:12px;padding:10px 12px;border-radius:12px;background:${headerBg};border:1px solid ${tone === 'danger' ? '#fecdd3' : '#dbeafe'};">
          <div style="font-weight:800;color:${headerColor};">${escapeHtml(title)}</div>
          <div style="font-size:12px;color:${headerColor};font-weight:700;">${items.length} 条</div>
        </div>
        <div style="display:flex;flex-direction:column;gap:10px;">
          ${items.map(item => {
            const severityBg = item.tone === 'danger' ? '#fff1f2' : '#f8fbff';
            const severityBorder = item.tone === 'danger' ? '#fecdd3' : '#dbeafe';
            const severityColor = item.tone === 'danger' ? '#be123c' : '#1d4ed8';
            return `
              <div style="display:grid;grid-template-columns:56px minmax(0,1fr) auto;gap:12px;padding:12px;border-radius:14px;background:${severityBg};border:1px solid ${severityBorder};align-items:center;">
                <div style="width:56px;height:56px;border-radius:12px;overflow:hidden;background:#fff;display:flex;align-items:center;justify-content:center;border:1px solid rgba(148,163,184,0.25);">
                  ${item.image ? `<img src="${escapeHtml(item.image)}" alt="" data-fallback-size="56" style="width:56px;height:56px;object-fit:cover;">` : '<div style="font-size:20px;color:#cbd5e1;">备</div>'}
                </div>
                <div style="min-width:0;">
                  <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
                    <span style="font-weight:800;color:#0f172a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;" title="${escapeHtml(item.productName)}">${escapeHtml(item.productName)}</span>
                    <span style="display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;background:#fff;color:${severityColor};font-size:11px;font-weight:800;border:1px solid ${severityBorder};">${escapeHtml(item.typeLabel)}</span>
                  </div>
                  <div style="margin-top:4px;color:#475569;font-size:12px;line-height:1.5;display:flex;flex-wrap:wrap;gap:10px;">
                    <span>备货单号：${escapeHtml(item.orderSn)}</span>
                    <span>SKC ID：${escapeHtml(item.skuText)}</span>
                    <span>截止时间：${escapeHtml(item.deadlineText)}</span>
                    <span>剩余：${escapeHtml(item.remainingText)}</span>
                  </div>
                </div>
                <div style="text-align:right;min-width:88px;">
                  <div style="font-size:12px;color:#64748b;">${escapeHtml(item.typeLabel)}</div>
                  <div style="margin-top:4px;font-size:13px;font-weight:800;color:${severityColor};">${escapeHtml(item.remainingText)}</div>
                </div>
              </div>
            `;
          }).join('')}
        </div>
      </div>
    `;
  };

  if (total === 0) {
    listEl.innerHTML = '<div style="color:#999;text-align:center;padding:16px;">当前没有即将超时的备货单</div>';
    return;
  }

  listEl.innerHTML = [renderSection('马上要超时发货的备货单（剩余24小时内）', shipping, 'danger'), renderSection('马上要到货超时的备货单（剩余48小时内）', arrival, 'warning')].filter(Boolean).join('<div style="height:12px;"></div>');
}

function clearDashboardStockReminder(message = '请先登录并连接店铺') {
  renderDashboardStockReminder({
    loading: false,
    shipping: [],
    arrival: [],
    message
  });
}

async function refreshDashboardStockReminder(force = false) {
  const summaryEl = document.getElementById('dashStockReminderSummary');
  const listEl = document.getElementById('dashStockReminderList');
  if (!summaryEl || !listEl) return null;

  if (!state.mallId || !state.antiContent) {
    dashboardStockReminderState.mallId = '';
    dashboardStockReminderState.payload = null;
    dashboardStockReminderState.error = '';
    if (await loadStockReminderSnapshot()) return dashboardStockReminderState.payload;
    clearDashboardStockReminder('请先连接店铺后再查看备货单提醒');
    return null;
  }

  if (!force && await loadStockReminderSnapshot()) return dashboardStockReminderState.payload;

  if (!force
    && dashboardStockReminderState.payload
    && dashboardStockReminderState.mallId === state.mallId
    && Date.now() - dashboardStockReminderState.loadedAt < 5 * 60 * 1000) {
    renderDashboardStockReminder(dashboardStockReminderState.payload);
    return dashboardStockReminderState.payload;
  }

  if (dashboardStockReminderState.loading) return dashboardStockReminderState.pendingPromise || null;

  dashboardStockReminderState.loading = true;
  dashboardStockReminderState.error = '';
  dashboardStockReminderState.pendingPromise = (async () => {
    renderDashboardStockReminder({ loading: true });
    try {
      const result = await safeRuntimeSendMessage({
        action: 'getStockReminderData',
        data: {
          mallId: state.mallId
        }
      }, { success: false, error: '备货单提醒加载失败' }, 30000);

      if (!result || !result.success) {
        const error = result?.error || '备货单提醒加载失败';
        dashboardStockReminderState.payload = null;
        dashboardStockReminderState.error = error;
        renderDashboardStockReminder({ error });
        return null;
      }

      const payload = {
        shipping: Array.isArray(result.shipping) ? result.shipping : [],
        arrival: Array.isArray(result.arrival) ? result.arrival : [],
        updatedAt: result.updatedAt || Date.now(),
        message: result.message || ''
      };
      dashboardStockReminderState.mallId = state.mallId;
      dashboardStockReminderState.loadedAt = Date.now();
      dashboardStockReminderState.payload = payload;
      dashboardStockReminderState.error = '';
      renderDashboardStockReminder(payload);
      await saveTemuSnapshot('stock_reminders', {
        ...payload,
        shippingThresholdHours: 24,
        arrivalThresholdHours: 48
      }, { itemCount: payload.shipping.length + payload.arrival.length });
      return payload;
    } catch (error) {
      const message = error?.message || String(error) || '备货单提醒加载失败';
      dashboardStockReminderState.payload = null;
      dashboardStockReminderState.error = message;
      renderDashboardStockReminder({ error: message });
      return null;
    } finally {
      dashboardStockReminderState.loading = false;
      dashboardStockReminderState.pendingPromise = null;
    }
  })();

  return dashboardStockReminderState.pendingPromise;
}

function clearDashboardSalesVisuals() {
  dashThirtyUnlocked = false;
  const dToday = document.getElementById('dashTodayCount');
  const dSeven = document.getElementById('dashSevenDayCount');
  const dTodayAmt = document.getElementById('dashTodayAmount');
  const dSevenAmt = document.getElementById('dashSevenDayAmount');
  const dTodayStand = getDashboardMetricElement('dashTodaySalesCountDisplay', 'dashTodayAmountStandalone');
  const dSevenStand = getDashboardMetricElement('dashSevenDaySalesCountDisplay', 'dashSevenDayAmountStandalone');
  const dThirtyCard = document.getElementById('dashThirtyCard');
  const dThirtyCount = document.getElementById('dashThirtyDayCount');
  const dThirtyAmount = document.getElementById('dashThirtyDayAmount');
  const dThirtyRow = document.getElementById('dashThirtyRow');
  const dThirtyStand = document.getElementById('dashThirtyDayAmountStandalone');
  const dashChart = document.getElementById('dashBarChart');
  const dashDailyChart = document.getElementById('dashDailySalesBarChart');

  if (dToday) dToday.textContent = '0';
  if (dSeven) dSeven.textContent = '0';
  if (dTodayAmt) { dTodayAmt.textContent = '¥0.00'; dTodayAmt.style.display = ''; }
  if (dSevenAmt) { dSevenAmt.textContent = '¥0.00'; dSevenAmt.style.display = ''; }
  if (dTodayStand) { dTodayStand.style.display = ''; dTodayStand.textContent = '0'; }
  if (dSevenStand) { dSevenStand.style.display = ''; dSevenStand.textContent = '0'; }
  if (dThirtyCard) { dThirtyCard.style.opacity = '0.4'; dThirtyCard.style.cursor = 'pointer'; dThirtyCard.title = '点击刷新同步当月销量'; }
  if (dThirtyCount) dThirtyCount.textContent = '—';
  if (dThirtyAmount) dThirtyAmount.textContent = '点击刷新';
  if (dThirtyRow) { dThirtyRow.style.opacity = '0.4'; dThirtyRow.style.cursor = 'pointer'; dThirtyRow.title = '点击刷新同步当月销售额'; }
  if (dThirtyStand) dThirtyStand.textContent = '🔥 点击刷新';
  if (dashChart) dashChart.innerHTML = '<div class="bar-chart-empty">暂无真实日趋势数据</div>';
  if (dashDailyChart) dashDailyChart.innerHTML = '<div class="bar-chart-empty">点击同步数据后显示每日销量</div>';
  clearDashboardStockReminder('请先登录并连接店铺');
  syncDingHuiTemuHome();
}

// 更新首页销量概览（今日+7日，不含30天）
function updateDashboardSales(todayTotal, sevenDayTotal, todayAmount, sevenDayAmount, products, source = 'sales-query-summary') {
  setDashboardSalesSummary({
    todayTotal,
    sevenDayTotal,
    todayAmount,
    sevenDayAmount,
    source
  });
  const dToday = document.getElementById('dashTodayCount');
  const dSeven = document.getElementById('dashSevenDayCount');
  const dTodayAmt = document.getElementById('dashTodayAmount');
  const dSevenAmt = document.getElementById('dashSevenDayAmount');
  const dTodayStand = getDashboardMetricElement('dashTodaySalesCountDisplay', 'dashTodayAmountStandalone');
  const dSevenStand = getDashboardMetricElement('dashSevenDaySalesCountDisplay', 'dashSevenDayAmountStandalone');

  if (dToday) dToday.textContent = Number.isFinite(Number(todayTotal)) ? Number(todayTotal).toLocaleString() : '0';
  if (dSeven) dSeven.textContent = Number.isFinite(Number(sevenDayTotal)) ? Number(sevenDayTotal).toLocaleString() : '0';
  if (dTodayAmt) { dTodayAmt.textContent = `¥${formatAmount(todayAmount)}`; dTodayAmt.style.display = ''; }
  if (dSevenAmt) { dSevenAmt.textContent = `¥${formatAmount(sevenDayAmount)}`; dSevenAmt.style.display = ''; }
  if (dTodayStand) {
    dTodayStand.style.display = '';
    dTodayStand.textContent = Number.isFinite(Number(todayTotal)) ? Number(todayTotal).toLocaleString() : '0';
  }
  if (dSevenStand) {
    dSevenStand.style.display = '';
    dSevenStand.textContent = Number.isFinite(Number(sevenDayTotal)) ? Number(sevenDayTotal).toLocaleString() : '0';
  }

  // 更新首页柱状图
  const dashChart = document.getElementById('dashBarChart');
  const dashDailyChart = document.getElementById('dashDailySalesBarChart');
  if (dashChart || dashDailyChart) {
    const bars = buildSevenDayTrendBars(products || []);
    const chartHtml = bars.length > 0
      ? `<div class="bar-chart-row">${renderTrendBarChartMarkup(bars)}</div>`
      : '<div class="bar-chart-empty">暂无真实日趋势数据</div>';
    if (bars.length > 0) {
      if (dashChart) dashChart.innerHTML = chartHtml;
      if (dashDailyChart) dashDailyChart.innerHTML = chartHtml;
    } else {
      if (dashChart) dashChart.innerHTML = chartHtml;
      if (dashDailyChart) dashDailyChart.innerHTML = chartHtml;
    }
  }

  // 更新账号信息
  updateDashboardAccount();
  syncDingHuiTemuHome();
}

let lastDashboardHomeSalesWarnText = '';
let lastDashboardHomeSalesWarnAt = 0;

function warnDashboardHomeSalesFailure(message) {
  const text = String(message || '后台首页销量读取失败');
  const now = Date.now();
  if (text === lastDashboardHomeSalesWarnText && now - lastDashboardHomeSalesWarnAt < 60000) return;
  lastDashboardHomeSalesWarnText = text;
  lastDashboardHomeSalesWarnAt = now;
  console.info('[小青蛙电商助手] 首页销量暂不可用，已保持概览默认值:', text);
}

async function refreshDashboardHomeSales(options = {}) {
  const allowRemoteFetch = options.allowRemoteFetch === true;
  if (!state.mallId || !state.antiContent) return null;
  try {
    let products = Array.isArray(salesDataState.allProducts) && salesDataState.allProducts.length
      ? salesDataState.allProducts
      : [];
    if (!products.length) {
      const loaded = await loadSalesDataCache();
      products = loaded && Array.isArray(salesDataState.allProducts) ? salesDataState.allProducts : [];
    }
    if (!products.length) {
      if (!allowRemoteFetch) {
        warnDashboardHomeSalesFailure('暂无服务器销量缓存');
        return null;
      }
      const result = await fetchSalesDataAllDirect({ pageSize: 100 });
      if (!result?.success || !Array.isArray(result.products)) {
        warnDashboardHomeSalesFailure(result?.error || '销量查询数据同步失败');
        return null;
      }
      products = result.products;
      salesDataState.products = products;
      salesDataState.allProducts = products;
      salesDataState.viewProducts = null;
      salesDataState.total = result.total || products.length || 0;
      salesDataState.totalPages = Math.max(1, Math.ceil(products.length / Math.max(1, salesDataState.pageSize || 20)));
      salesDataState.lastSyncAt = Date.now();
      sortSalesProducts();
      await saveSalesDataCache();
      updateSalesSyncDisplay();
    }
    const summary = calculateSalesSummary(products);
    renderSalesSummaryToTab(summary);
    syncDashboardFromSalesSummary(summary, products, 'sales-query-home');
    renderSalesBarChart(products);
    return { success: true, ...summary, source: 'sales-query-home' };
  } catch (error) {
    console.info('[小青蛙电商助手] 首页销量读取异常，已保持缓存/默认值:', error?.message || error);
    warnDashboardHomeSalesFailure(error?.message || error);
    return null;
  }
}

// 解锁首页当月销量数据
function unlockDashboardThirty(thirtyTotal, thirtyAmount) {
  dashThirtyUnlocked = true;
  setDashboardSalesSummary({
    thirtyTotal,
    thirtyAmount,
    source: 'sales-query-summary'
  });
  const card = document.getElementById('dashThirtyCard');
  const count = document.getElementById('dashThirtyDayCount');
  const amount = document.getElementById('dashThirtyDayAmount');
  const row = document.getElementById('dashThirtyRow');
  const stand = document.getElementById('dashThirtyDayAmountStandalone');
  if (card) { card.style.opacity = '1'; card.style.cursor = 'pointer'; card.title = '点击刷新同步当月销量'; }
  if (count) count.textContent = thirtyTotal.toLocaleString();
  if (amount) { amount.textContent = '点击刷新同步'; amount.style.display = ''; }
  if (row) { row.style.opacity = '1'; row.style.cursor = 'pointer'; row.title = '点击刷新同步当月销售额'; }
  if (stand) { stand.style.display = ''; stand.textContent = `¥${formatAmount(thirtyAmount)}`; }
  syncDingHuiTemuHome();
}

// 更新首页账号信息
function updateDashboardAccount() {
  // 账号卡片由 renderDashboardAccessPanels 负责整块渲染，这里保留接口避免旧调用报错。
}

function mergeDashboardTrendCardIntoSalesCard() {
  if (document.getElementById('dashTrendMergedMarker')) return;
  const salesCard = document.querySelector('.sales-summary-left');
  const trendCard = document.querySelector('.dashboard-trend-card');
  const chartContainer = trendCard?.querySelector('.chart-container');
  if (!salesCard || !trendCard || !chartContainer) return;

  const marker = document.createElement('span');
  marker.id = 'dashTrendMergedMarker';
  marker.style.display = 'none';
  chartContainer.appendChild(marker);
  salesCard.appendChild(chartContainer);
  trendCard.style.display = 'none';
}

function compareSemverVersion(left, right) {
  const parse = (value) => String(value || '')
    .split('.')
    .map(part => Number.parseInt(part.replace(/\D.*$/, ''), 10) || 0);
  const a = parse(left);
  const b = parse(right);
  const length = Math.max(a.length, b.length, 3);
  for (let i = 0; i < length; i += 1) {
    const diff = (a[i] || 0) - (b[i] || 0);
    if (diff !== 0) return diff > 0 ? 1 : -1;
  }
  return 0;
}

async function loadExtensionVersionStatus() {
  const latestEl = document.getElementById('dashExtensionLatestVersion');
  const currentEl = document.getElementById('dashExtensionCurrentVersion');
  const noticeEl = document.getElementById('dashExtensionUpdateNotice');
  const downloadBtn = document.getElementById('dashExtensionDownloadBtn');
  const kpiLatestEl = document.getElementById('dashKpiExtensionLatestVersion');
  const kpiCurrentEl = document.getElementById('dashKpiExtensionCurrentVersion');
  const kpiDownloadBtn = document.getElementById('dashKpiExtensionDownloadBtn');
  if (!latestEl && !currentEl && !noticeEl && !kpiLatestEl && !kpiCurrentEl && !kpiDownloadBtn) return;

  if (latestEl) latestEl.textContent = LATEST_EXTENSION_VERSION;
  if (kpiLatestEl) kpiLatestEl.textContent = LATEST_EXTENSION_VERSION;
  if (downloadBtn) downloadBtn.href = EXTENSION_DOWNLOAD_URL;
  if (kpiDownloadBtn) kpiDownloadBtn.href = EXTENSION_DOWNLOAD_URL;
  bindExtensionDownloadButtons();

  const result = await safeRuntimeSendMessage(
    { action: 'getExtensionInfo' },
    { success: false, error: '未检测到插件' },
    1800
  );
  const currentVersion = result?.success ? String(result.version || '').trim() : '';
  if (currentEl) currentEl.textContent = currentVersion || '未检测到插件';
  if (kpiCurrentEl) kpiCurrentEl.textContent = currentVersion || '未检测到插件';

  const needsUpdate = !currentVersion || compareSemverVersion(currentVersion, LATEST_EXTENSION_VERSION) < 0;
  if (noticeEl) noticeEl.style.display = needsUpdate ? 'block' : 'none';
  if (kpiDownloadBtn) {
    kpiDownloadBtn.style.display = 'inline-flex';
    kpiDownloadBtn.textContent = needsUpdate ? '下载最新版插件' : '重新下载插件';
  }
  bindExtensionDownloadButtons();
}

function setDashboardLockedState(locked) {
  document.body.classList.toggle('auth-locked', locked);
  setDashboardAuthPanelVisible(Boolean(locked));
}

function setFeatureAccessState(locked, reason = '') {
  const user = getCurrentServerUser();
  const admin = isAdminUser(user);
  featureAccessState.locked = admin ? false : Boolean(locked);
  featureAccessState.reason = admin ? '' : (reason || (locked ? '当前账号暂无可用授权店铺，请先绑定并完成卡密授权' : ''));
  applyRoleFeatureVisibility(user);
  document.querySelectorAll('.tab').forEach((tab) => {
    const tabId = tab.dataset.tab;
    if (tabId === 'dashboard') {
      tab.classList.remove('locked');
      tab.disabled = false;
      return;
    }
    if (ADMIN_ONLY_TABS.has(tabId) && !admin) {
      tab.classList.remove('locked');
      tab.disabled = true;
      tab.hidden = true;
      return;
    }
    tab.classList.toggle('locked', featureAccessState.locked);
    tab.disabled = featureAccessState.locked;
  });
}

function formatDashboardDate(value, includeTime = true) {
  if (!value) return '未授权';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return '未授权';
  return includeTime
    ? date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
    : date.toLocaleDateString('zh-CN');
}

function focusDashboardAuthorizationSection(mallId = '', focusTarget = 'code') {
  const renewCard = document.getElementById('dashRenewSection');
  const codeEl = document.getElementById('dashCardInput');
  if (renewCard) {
    renewCard.style.display = 'block';
    renewCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
  }
  requestAnimationFrame(() => {
    codeEl?.focus();
    codeEl?.select?.();
  });
}

function setDashboardShopActionResult(message, tone = 'info') {
  const resultEl = document.getElementById('dashShopActionResult');
  if (!resultEl) return;
  resultEl.style.display = 'block';
  resultEl.textContent = message;
  resultEl.style.color = tone === 'success' ? '#16a34a' : tone === 'error' ? '#dc2626' : '#475569';
}

function notifyDashboardShopAction(message, tone = 'info') {
  setDashboardShopActionResult(message, tone);
  if (typeof showToast === 'function') {
    showToast(message, tone === 'error' ? 'error' : tone === 'success' ? 'success' : 'info');
  }
}

function setDashboardAuthPanelVisible(visible) {
  const authGate = document.getElementById('authGate');
  if (!authGate) return;
  authGate.style.display = visible ? 'flex' : 'none';
}

function findBoundShopForMall(shops = [], mallId = '') {
  const targetMallId = String(mallId || '').trim();
  if (!targetMallId) return null;
  return (Array.isArray(shops) ? shops : []).find((shop) => String(shop?.mallId || '').trim() === targetMallId) || null;
}

function hasValidShopAccess(shop = null) {
  if (!shop || typeof shop !== 'object') return false;
  if (shop.isMember === true || shop.membership?.isMember === true) return true;
  const expireAt = shop.expireAt || shop.expiresAt || shop.membershipExpireAt || shop.membership?.expireAt;
  if (!expireAt) return false;
  const expireTime = Date.parse(expireAt);
  return Number.isNaN(expireTime) || expireTime > Date.now();
}

function isCurrentShopAllowed(currentShopStatus, shops = [], mallId = '') {
  if (isAdminUser(getCurrentServerUser())) return true;
  if (!mallId) return (Array.isArray(shops) ? shops : []).some(hasValidShopAccess);
  if (currentShopStatus) {
    return Boolean(currentShopStatus.shopBound && currentShopStatus.isMember);
  }
  return hasValidShopAccess(findBoundShopForMall(shops, mallId));
}

async function submitDashboardShopAccess({ mallId, mallName = '', code = '', redeem = true }) {
  const currentMallId = String(mallId || '').trim();
  if (!currentMallId) {
    notifyDashboardShopAction('未找到店铺ID，无法继续', 'error');
    return false;
  }

  try {
    const token = await getServerAuthToken();
    if (!token) {
      notifyDashboardShopAction('请先登录会员账号', 'error');
      return false;
    }

    notifyDashboardShopAction(redeem ? `正在为店铺「${mallName || currentMallId}」续费...` : `正在绑定店铺「${mallName || currentMallId}」...`);

    const bindResp = await fetch(`${SERVER_URL}/user/bind-shop`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
      body: JSON.stringify({ mallId: currentMallId, mallName: mallName || '' })
    });
    const bindResult = await bindResp.json().catch(() => ({}));
    if (!bindResp.ok || !bindResult.ok) {
      throw new Error(bindResult.error || bindResult.message || '店铺绑定失败');
    }

    if (!redeem) {
      notifyDashboardShopAction(bindResult.message || `店铺「${mallName || currentMallId}」绑定成功`, 'success');
      await loadDashboardMembership();
      return true;
    }

    if (!code) {
      throw new Error('请输入店铺卡密');
    }

    const redeemResp = await fetch(`${SERVER_URL}/membership/shop-redeem`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
      body: JSON.stringify({ mallId: currentMallId, code })
    });
    const redeemResult = await redeemResp.json().catch(() => ({}));
    if (!redeemResp.ok || !redeemResult.ok) {
      throw new Error(redeemResult.error || redeemResult.message || '店铺授权失败');
    }

    notifyDashboardShopAction(redeemResult.message || `店铺「${mallName || currentMallId}」续费成功`, 'success');
    await loadDashboardMembership();
    return true;
  } catch (err) {
    console.error('[小青蛙电商助手] 店铺绑定/续费失败:', err);
    notifyDashboardShopAction(err?.message || '店铺绑定/续费失败', 'error');
    return false;
  }
}

async function bindCurrentDashboardShop() {
  const currentMallId = String(state.mallId || '').trim();
  if (!currentMallId) {
    notifyDashboardShopAction('当前未识别到店铺ID，请先打开卖家店铺页面', 'error');
    return false;
  }
  const currentShop = Array.isArray(state.boundShops)
    ? state.boundShops.find((shop) => String(shop.mallId) === String(currentMallId))
    : null;
  return await submitDashboardShopAccess({
    mallId: currentMallId,
    mallName: currentShop?.mallName || state.mallName || '',
    redeem: false
  });
}

async function handleCurrentDashboardShopBindClick(event = null) {
  event?.preventDefault?.();
  event?.stopPropagation?.();
  const button = event?.currentTarget?.id === 'dhTemuBindCurrentShopBtn'
    ? event.currentTarget
    : document.getElementById('dhTemuBindCurrentShopBtn');
  const originalText = button?.textContent || '绑定店铺';
  if (button) {
    button.disabled = true;
    button.textContent = '处理中...';
  }
  try {
    if (!state.mallId) {
      await reconnectCurrentShop();
    }
    const currentMallId = String(state.mallId || '').trim();
    if (!currentMallId) {
      notifyDashboardShopAction('当前未识别到店铺ID，请先打开 TEMU 卖家店铺页面后重新连接', 'error');
      return false;
    }
    const currentShop = findBoundShopForMall(state.boundShops || [], currentMallId);
    if (currentShop && !hasValidShopAccess(currentShop) && !isAdminUser(getCurrentServerUser())) {
      const mallName = currentShop.shopName || currentShop.mallName || state.mallName || currentMallId;
      const code = window.prompt(`请输入店铺「${mallName}」的卡密完成授权`, '')?.trim();
      if (!code) {
        notifyDashboardShopAction('已取消输入卡密授权', 'info');
        return false;
      }
      return await submitDashboardShopAccess({ mallId: currentMallId, mallName, code, redeem: true });
    }
    return await bindCurrentDashboardShop();
  } finally {
    if (button) {
      button.disabled = false;
      button.textContent = originalText;
      syncDingHuiTemuHome();
    }
  }
}

function bindShopRedeemAction() {
  const btn = document.getElementById('dashRedeemBtn');
  if (!btn) return;
  const runAction = async (shouldRedeem = true) => {
    const mallId = document.getElementById('dashBindMallId')?.value?.trim() || state.mallId || '';
    const code = document.getElementById('dashCardInput')?.value?.trim();
    const resultEl = document.getElementById('dashRedeemResult');
    if (!mallId) {
      if (resultEl) {
        resultEl.style.display = 'block';
        resultEl.style.color = '#ff4d4f';
        resultEl.textContent = '请输入店铺ID';
      }
      return;
    }
    if (shouldRedeem && !code) {
      if (resultEl) {
        resultEl.style.display = 'block';
        resultEl.style.color = '#ff4d4f';
        resultEl.textContent = '请输入店铺授权码';
      }
      return;
    }
    try {
      const token = await getServerAuthToken();
      if (!token) {
        if (resultEl) {
          resultEl.style.display = 'block';
          resultEl.style.color = '#ff4d4f';
          resultEl.textContent = '请先登录会员账号';
        }
        return;
      }

      if (resultEl) {
        resultEl.style.display = 'block';
        resultEl.style.color = '#666';
        resultEl.textContent = shouldRedeem ? '正在绑定并授权...' : '正在绑定店铺...';
      }

      const bindResp = await fetch(`${SERVER_URL}/user/bind-shop`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
        body: JSON.stringify({ mallId })
      });
      const bindResult = await bindResp.json();
      if (!bindResp.ok || !bindResult.ok) {
        throw new Error(bindResult.error || bindResult.message || '店铺绑定失败');
      }

      if (!shouldRedeem) {
        if (resultEl) {
          resultEl.style.display = 'block';
          resultEl.style.color = '#52c41a';
          resultEl.textContent = bindResult.message || '店铺绑定成功，请继续输入授权码完成开通';
        }
        await loadDashboardMembership();
        return;
      }

      const redeemResp = await fetch(`${SERVER_URL}/membership/shop-redeem`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
        body: JSON.stringify({ mallId, code })
      });
      const redeemResult = await redeemResp.json();
      if (!redeemResp.ok || !redeemResult.ok) {
        throw new Error(redeemResult.error || redeemResult.message || '店铺授权失败');
      }

      if (resultEl) {
        resultEl.style.display = 'block';
        resultEl.style.color = '#52c41a';
        resultEl.textContent = redeemResult.message || '店铺绑定并授权成功';
      }
      await loadDashboardMembership();
    } catch (err) {
      if (resultEl) {
        resultEl.style.display = 'block';
        resultEl.style.color = '#ff4d4f';
        resultEl.textContent = err?.message || '店铺绑定/授权失败';
      }
    }
  };
  btn.onclick = () => runAction(true);
}

function renderDashboardAccessPanels(user, membership, shops, currentShopStatus = null) {
  const dashboard = document.getElementById('dashboard');
  if (!dashboard) return;
  const accountCard = dashboard.querySelector('#dashAccountPanel');
  const shopCard = dashboard.querySelector('#dashShopPanel');
  const renewCard = document.getElementById('dashRenewSection');
  const admin = isAdminUser(user);
  const shopList = (Array.isArray(shops) ? shops : []).map(hydrateShopNameFromCache);
  rememberShopNames(shopList, {
    mallId: currentShopStatus?.mallId || state.mallId,
    mallName: currentShopStatus?.shopName || currentShopStatus?.mallName || state.mallName
  });
  const boundCount = shopList.length;
  const authorizedCount = admin ? shopList.length : shopList.filter(hasValidShopAccess).length;
  const hasCurrentShop = Boolean(currentShopStatus?.mallId || state.mallId);
  const currentShopId = currentShopStatus?.mallId || state.mallId || '';
  const currentBoundShop = hasCurrentShop ? shopList.find((shop) => String(shop.mallId) === String(currentShopId)) : null;
  const currentShopName = hasCurrentShop
    ? (pickShopName(
        currentShopStatus?.shopName,
        currentShopStatus?.mallName,
        currentBoundShop?.shopName,
        currentBoundShop?.mallName,
        state.mallName,
        getCachedShopName(currentShopId)
      ) || `店铺 ${currentShopId}`)
    : getCurrentShopDisplayName();
  const currentShopBound = admin ? true : (hasCurrentShop ? Boolean(currentShopStatus?.shopBound || currentBoundShop) : boundCount > 0);
  const currentShopAuthorized = admin ? true : (hasCurrentShop
    ? Boolean(currentShopStatus?.isMember || hasValidShopAccess(currentBoundShop))
    : authorizedCount > 0);
  const currentShopTone = currentShopAuthorized ? '#16a34a' : currentShopBound ? '#d97706' : '#dc2626';
  const currentShopBg = currentShopAuthorized ? '#ecfdf5' : currentShopBound ? '#fff7ed' : '#fef2f2';
  const currentShopText = !currentShopBound
    ? '当前账号暂无绑定店铺'
    : currentShopAuthorized
      ? (hasCurrentShop
        ? (admin ? '管理员可直接使用' : `${currentShopStatus?.membership?.planName || currentBoundShop?.planName || '店铺会员'} · 剩余${Math.max(0, currentShopStatus?.membership?.daysLeft ?? currentBoundShop?.daysLeft ?? 0)}天`)
        : (admin ? '管理员已登录，执行前连接 TEMU 店铺' : '服务器店铺授权已加载，执行前连接 TEMU 店铺'))
      : '已绑定店铺，待授权';
  const currentShopBadgeText = admin
    ? '管理员已登录'
    : (currentShopAuthorized ? '账号已授权' : currentShopBound ? '待授权' : '未绑定');
  const currentShopStatusBadge = currentShopAuthorized
    ? `<span style="display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;background:#dcfce7;color:#166534;font-size:12px;font-weight:800;">${escapeHtml(currentShopBadgeText)}</span>`
    : `<span style="display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;background:#fff7ed;color:#c2410c;font-size:12px;font-weight:800;">${escapeHtml(currentShopBadgeText)}</span>`;
  const shouldShowReconnectBindButton = !admin && (!hasCurrentShop || !currentShopBound);
  const topAuthStatusEl = document.getElementById('topAuthStatus');
  if (topAuthStatusEl) {
    topAuthStatusEl.textContent = currentShopBadgeText;
    topAuthStatusEl.className = `status-badge ${currentShopAuthorized ? 'status-authorized' : 'status-disconnected'}`;
    topAuthStatusEl.style.display = user ? 'inline-flex' : 'none';
  }
  const roleText = user?.role === 'admin' ? '管理员' : user?.role === 'operator' ? '运营人员' : user?.role ? String(user.role) : '—';
  const statusText = user?.status === 'active' ? '正常' : user?.status === 'inactive' ? '停用' : user?.status ? String(user.status) : '—';
  const shopRows = shopList.length > 0
    ? shopList.map((shop, index) => {
        const hasAuth = admin || hasValidShopAccess(shop);
        const isCurrentShop = state.mallId && String(shop.mallId) === String(state.mallId);
        const daysLeft = shop.daysLeft != null ? Math.max(0, shop.daysLeft) : (shop.expireAt ? Math.max(0, Math.ceil((new Date(shop.expireAt) - new Date()) / 86400000)) : null);
        const statusLabel = hasAuth
          ? (daysLeft === 0 && shop.expireAt ? '已过期' : '已授权')
          : '未授权';
        const statusBg = statusLabel === '已授权'
          ? '#dcfce7'
          : statusLabel === '已过期'
            ? '#fee2e2'
            : '#fef3c7';
        const statusColor = statusLabel === '已授权'
          ? '#166534'
          : statusLabel === '已过期'
            ? '#b91c1c'
            : '#b45309';
        const rowShopName = pickShopName(shop.shopName, shop.mallName, shop.storeName, shop.name, isCurrentShop ? state.mallName : null, getCachedShopName(shop.mallId))
          || (shop.mallId ? `店铺 ${shop.mallId}` : '未命名店铺');
        const expireText = admin
          ? '管理员账号可直接使用'
          : hasAuth
            ? `授权至 ${formatDashboardDate(shop.expireAt)}${daysLeft != null ? ` · 剩余 ${daysLeft} 天` : ''}`
            : '该店铺暂未完成授权';
        return `
          <tr class="account-shop-row${isCurrentShop ? ' is-current' : ''}">
            <td class="account-shop-index">${index + 1}</td>
            <td>
              <div class="account-shop-name">
                <span>${escapeHtml(rowShopName)}</span>
                ${isCurrentShop ? '<span style="font-size:12px;font-weight:800;padding:2px 8px;border-radius:999px;background:#dbeafe;color:#2563eb;">当前连接</span>' : ''}
              </div>
              <div class="account-shop-meta">店铺ID：${escapeHtml(shop.mallId || '—')}</div>
            </td>
            <td>
              <span style="display:inline-flex;align-items:center;padding:3px 9px;border-radius:999px;background:${statusBg};color:${statusColor};font-size:12px;font-weight:800;">${escapeHtml(statusLabel)}</span>
              <div class="account-shop-expire">${escapeHtml(expireText)}</div>
            </td>
            <td>
              <div class="account-shop-actions">
                ${admin ? `<span style="font-size:12px;color:#64748b;">无需卡密授权</span><button class="btn btn-default btn-sm" type="button" data-shop-action="unbind" data-shop-id="${escapeHtml(shop.mallId || '')}" data-shop-name="${escapeHtml(rowShopName)}">解绑店铺</button>` : `<button class="btn btn-primary btn-sm" type="button" data-shop-action="${hasAuth ? 'renew' : 'card-renew'}" data-shop-id="${escapeHtml(shop.mallId || '')}" data-shop-name="${escapeHtml(rowShopName)}">${hasAuth ? '续费' : '输入卡密授权'}</button><button class="btn btn-default btn-sm" type="button" data-shop-action="unbind" data-shop-id="${escapeHtml(shop.mallId || '')}" data-shop-name="${escapeHtml(rowShopName)}">解绑店铺</button>`}
              </div>
            </td>
          </tr>
        `;
      }).join('')
    : `
      <tr>
        <td colspan="4" style="padding:24px 14px;text-align:center;color:#64748b;">
          暂无绑定店铺，连接 TEMU 店铺后可在这里完成绑定和授权。
        </td>
      </tr>
    `;

  const shopListCardHtml = `
    <div class="account-shop-list-card" style="margin:0;border:0;padding:0;box-shadow:none;background:transparent;">
      <div class="account-shop-list-head">
        <div class="account-shop-list-title">店铺列表</div>
        <span class="account-shop-list-count">共 ${boundCount} 个</span>
      </div>
      <div class="account-shop-table-wrap" style="max-height:205px;overflow:auto;">
        <table class="account-shop-table">
          <thead>
            <tr>
              <th style="width:48px;text-align:center;">序号</th>
              <th>店铺</th>
              <th>授权状态</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>${shopRows}</tbody>
        </table>
      </div>
    </div>
  `;

  if (accountCard) {
    accountCard.style.display = 'block';
    accountCard.innerHTML = `
      <div class="account-current-card">
        <div class="account-hero-row">
          <div class="account-current-main">
            <div class="account-current-label">当前 TEMU 店铺</div>
            <div id="mallInfo" class="account-current-title">${escapeHtml(currentShopName)}${currentShopId ? ` (${escapeHtml(currentShopId)})` : ''}</div>
            <div class="account-current-badges">
              <span id="connectionStatus" class="status-badge status-disconnected">未连接</span>
              <span id="topAuthStatus" class="status-badge ${currentShopAuthorized ? 'status-authorized' : 'status-disconnected'}" style="display:inline-flex;">${escapeHtml(currentShopBadgeText)}</span>
              <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:${currentShopBg};color:${currentShopTone};font-size:12px;font-weight:800;">${escapeHtml(currentShopText)}</span>
            </div>
          </div>
          <div class="account-current-actions">
            <button class="btn btn-primary btn-sm" id="reconnectBtn" type="button" title="重新检测店铺连接">🔄 重新连接</button>
            <button class="btn btn-default btn-sm" id="visitStoreBtn" type="button" title="在浏览器新标签页打开店铺页面">🏪 一键访问店铺</button>
            <button id="dashAuthLogoutBtn" class="btn btn-default btn-sm" type="button">退出登录</button>
            ${shouldShowReconnectBindButton ? `<button class="btn btn-primary btn-sm" type="button" id="dashBindCurrentShopBtn">${hasCurrentShop ? '绑定当前店铺' : '重新连接并绑定当前店铺'}</button>` : ''}
          </div>
        </div>
        <div class="account-current-side">
          <div class="account-mini-cell">
            <div class="account-mini-label">手机号</div>
            <div id="dashLoginPhone" class="account-mini-value">${escapeHtml(user?.phone || '—')}</div>
          </div>
          <div class="account-mini-cell">
            <div class="account-mini-label">运营人员</div>
            <div id="dashLoginNickname" class="account-mini-value">${escapeHtml(user?.nickname || user?.realName || user?.name || '—')}</div>
          </div>
          <div class="account-mini-cell">
            <div class="account-mini-label">店铺授权</div>
            <div class="account-mini-value">${boundCount} 个 / 已授权 ${authorizedCount} 个</div>
          </div>
          <div id="dashExtensionUpdateNotice" class="account-mini-cell account-update-card">
            <div class="account-mini-label">插件提示</div>
            <div class="account-mini-value">发现插件版本不是最新。</div>
          </div>
          <div class="account-mini-cell">
            <div class="account-mini-label">插件版本</div>
            <div class="account-mini-value">当前 <span id="dashExtensionCurrentVersion">检测中...</span> / 最新 <span id="dashExtensionLatestVersion">${escapeHtml(LATEST_EXTENSION_VERSION)}</span></div>
            <div class="account-plugin-actions">
              <a id="dashExtensionDownloadBtn" class="btn btn-primary btn-sm" href="${escapeHtml(EXTENSION_DOWNLOAD_URL)}" download style="justify-content:center;text-decoration:none;">下载最新版插件</a>
            </div>
          </div>
        </div>
      </div>

      ${shopListCardHtml}
      <div id="dashShopActionResult" style="display:none;margin-top:10px;font-size:13px;"></div>
      <div id="dashAuthStatus" style="display:none;">已登录</div>
      <div id="dashAuthResult" style="margin-top:8px;font-size:13px;display:none;"></div>
    `;
    loadExtensionVersionStatus();
    const dashShopListCard = document.getElementById('dashShopListCard');
    if (dashShopListCard) dashShopListCard.innerHTML = shopListCardHtml;
    const handleShopPanelClick = async (event) => {
      const logoutBtn = event.target.closest('#dashAuthLogoutBtn');
      if (logoutBtn) {
        await logoutDashboardAuth();
        return;
      }
      const bindCurrentBtn = event.target.closest('#dashBindCurrentShopBtn');
      if (bindCurrentBtn) {
        await handleCurrentDashboardShopBindClick();
        return;
      }
      const reconnectBtn = event.target.closest('#reconnectBtn');
      if (reconnectBtn) {
        await reconnectCurrentShop();
        return;
      }
      const visitStoreBtn = event.target.closest('#visitStoreBtn');
      if (visitStoreBtn) {
        visitCurrentStore();
        return;
      }
      const button = event.target.closest('button[data-shop-action]');
      if (!button) return;
      const action = button.dataset.shopAction;
      const mallId = button.dataset.shopId || '';
      const mallName = button.dataset.shopName || '';
      if (action === 'renew') {
        const code = window.prompt(`请输入店铺「${mallName || mallId || '当前店铺'}」的卡密进行续费`, '')?.trim();
        if (!code) return;
        await submitDashboardShopAccess({ mallId, mallName, code, redeem: true });
        return;
      }
      if (action === 'card-renew') {
        const code = window.prompt(`请输入店铺「${mallName || mallId || '当前店铺'}」的卡密完成授权`, '')?.trim();
        if (!code) return;
        await submitDashboardShopAccess({ mallId, mallName, code, redeem: true });
        return;
      }
      if (action === 'unbind') {
        if (!mallId) {
          setDashboardShopActionResult('未找到店铺ID，无法解绑', 'error');
          return;
        }
        if (!window.confirm(`确定要解绑店铺「${mallName || mallId}」吗？`)) return;
        try {
          const token = await getServerAuthToken();
          if (!token) {
            setDashboardShopActionResult('请先登录会员账号', 'error');
            return;
          }
          setDashboardShopActionResult(`正在解绑店铺「${mallName || mallId}」...`);
          const resp = await fetch(`${SERVER_URL}/user/unbind-shop/${encodeURIComponent(mallId)}`, {
            method: 'DELETE',
            headers: { Authorization: `Bearer ${token}` }
          });
          const result = await resp.json().catch(() => ({}));
          if (!resp.ok || !result.ok) {
            throw new Error(result.error || result.message || '解绑失败');
          }
          setDashboardShopActionResult(`店铺「${mallName || mallId}」已解绑`, 'success');
          await loadDashboardMembership();
        } catch (err) {
          setDashboardShopActionResult(err?.message || '解绑失败', 'error');
        }
      }
    };
    accountCard.onclick = handleShopPanelClick;
    if (dashShopListCard) dashShopListCard.onclick = handleShopPanelClick;
    updateConnectionStatus(Boolean(state.mallId && state.antiContent), state.mallName);
  }

  if (shopCard) {
    shopCard.style.display = 'none';
    shopCard.innerHTML = '';
    shopCard.onclick = null;
  }

  if (renewCard) {
    renewCard.style.display = 'none';
    renewCard.style.marginTop = '14px';
    renewCard.style.border = '1px solid #e2e8f0';
    renewCard.style.borderRadius = '14px';
    renewCard.style.borderLeft = '4px solid #f59e0b';
    renewCard.style.boxShadow = 'none';
    renewCard.style.background = '#fff';
    bindShopRedeemAction();
  }
}

// 加载会员状态和绑定店铺
let dashAuthCooldownTimer = null;
let authCaptchaValue = '';

function setDashAuthResult(message, tone = 'info') {
  const resultEls = [
    document.getElementById('loginAuthResult'),
    document.getElementById('dashAuthResult')
  ].filter(Boolean);
  if (!resultEls.length) return;
  resultEls.forEach((resultEl) => {
    resultEl.style.display = 'block';
    resultEl.textContent = message;
    resultEl.style.color = tone === 'success' ? '#52c41a' : tone === 'error' ? '#ff4d4f' : '#666';
  });
  const dhResult = document.getElementById('dhTemuDashAuthResult');
  if (dhResult) {
    dhResult.style.display = message ? 'block' : 'none';
    dhResult.textContent = message || '';
    dhResult.style.color = tone === 'success' ? '#52c41a' : tone === 'error' ? '#ff4d4f' : '#667085';
  }
  syncDingHuiTemuHome();
}

function getTopbarAccountLabel(user) {
  if (!user) return '未登录';
  return user.nickname || user.realName || user.name || user.phone || '已登录账号';
}

function getTopbarAccountInitials(user) {
  if (!user) return 'XQ';
  const selected = getSelectedAuthPlatform();
  if (selected === 'ozon') return 'OZ';
  if (selected === 'temu') return 'TM';
  const label = getTopbarAccountLabel(user);
  return String(label).trim().slice(0, 2).toUpperCase() || 'XQ';
}

function renderTopbarAccount(user = null) {
  const wrap = document.querySelector('.topbar-right');
  if (!wrap) return;
  const roleText = user?.role === 'admin' ? '管理员' : user?.role === 'operator' ? '运营人员' : user ? '会员账号' : '未登录';
  const phone = user?.phone ? ` · ${user.phone}` : '';
  wrap.innerHTML = `
    <button class="topbar-reconnect-btn" id="dhTemuReconnectTopBtn" type="button">重新连接店铺</button>
    <div class="topbar-account">
      <div class="topbar-avatar">${escapeHtml(getTopbarAccountInitials(user))}</div>
      <div class="topbar-account-main">
        <div class="topbar-account-name">${escapeHtml(getTopbarAccountLabel(user))}</div>
        <div class="topbar-account-meta">${escapeHtml(roleText)}${escapeHtml(phone)}</div>
      </div>
      ${user ? '<button class="topbar-account-action" id="topbarLogoutBtn" type="button">退出</button>' : ''}
    </div>
  `;
  const reconnectBtn = document.getElementById('dhTemuReconnectTopBtn');
  if (reconnectBtn && reconnectBtn.dataset.bound !== '1') {
    reconnectBtn.dataset.bound = '1';
    reconnectBtn.addEventListener('click', async () => {
      await reconnectCurrentShop();
    });
  }
  const logoutBtn = document.getElementById('topbarLogoutBtn');
  if (logoutBtn && logoutBtn.dataset.bound !== '1') {
    logoutBtn.dataset.bound = '1';
    logoutBtn.addEventListener('click', () => logoutDashboardAuth());
  }
}


const AUTH_PLATFORM_KEY = 'xqwaSelectedPlatform';
const SCRIPT_PLATFORM_LOCK = 'temu';

function normalizeAuthPlatform(value) {
  return String(value || '').toLowerCase() === 'ozon' ? 'ozon' : 'temu';
}

function getScriptPlatformLock() {
  return SCRIPT_PLATFORM_LOCK ? normalizeAuthPlatform(SCRIPT_PLATFORM_LOCK) : '';
}

function isWebsiteWorkspaceUrl() {
  return window.location.protocol === 'http:' || window.location.protocol === 'https:';
}

function getPlatformFromPath() {
  if (!isWebsiteWorkspaceUrl()) return '';
  const first = String(window.location.pathname || '').split('/').filter(Boolean)[0] || '';
  return first.toLowerCase() === 'ozon' ? 'ozon' : first.toLowerCase() === 'temu' ? 'temu' : '';
}

function isWorkspaceEntryPath() {
  if (!isWebsiteWorkspaceUrl()) return false;
  const parts = String(window.location.pathname || '').split('/').filter(Boolean);
  return parts.length === 1 && parts[0].toLowerCase() === 'app';
}

function buildPlatformUrl(platform) {
  const selected = getScriptPlatformLock() || normalizeAuthPlatform(platform);
  const next = new URL(window.location.href);
  if (isWebsiteWorkspaceUrl()) {
    next.pathname = `/${selected}`;
    next.searchParams.delete('platform');
  } else {
    next.searchParams.set('platform', selected);
  }
  return next.toString();
}

function getSelectedAuthPlatform() {
  return normalizeAuthPlatform(getScriptPlatformLock() || window.__XQW_SELECTED_PLATFORM__ || getPlatformFromPath() || localStorage.getItem(AUTH_PLATFORM_KEY) || 'temu');
}

function applyAuthPlatform(platform, options = {}) {
  const selected = getScriptPlatformLock() || normalizeAuthPlatform(platform);
  window.__XQW_SELECTED_PLATFORM__ = selected;
  localStorage.setItem(AUTH_PLATFORM_KEY, selected);
  document.documentElement?.setAttribute?.('data-platform', selected);
  document.body?.setAttribute?.('data-platform', selected);
  updateAuthEntryPlatformText(selected);
  document.querySelectorAll('.auth-platform-btn[data-platform]').forEach((button) => {
    button.classList.toggle('active', normalizeAuthPlatform(button.dataset.platform) === selected);
  });
  if (options.updateUrl && typeof history?.replaceState === 'function') {
    history.replaceState(null, '', buildPlatformUrl(selected));
  }
  if (options.switchWorkspace) {
    switchWorkspaceForPlatform(selected);
  }
  return selected;
}

function updateAuthEntryPlatformText(platform = getSelectedAuthPlatform()) {
  const selected = normalizeAuthPlatform(platform);
  const textEl = document.getElementById('authEntryPlatformText');
  if (textEl) textEl.textContent = `当前入口：${selected === 'ozon' ? 'OZON' : 'TEMU'} 工作台`;
}

function syncAuthPlatformVisibility(mode = 'login') {
  void mode;
  const chooser = document.querySelector('.auth-platform-choice');
  if (!chooser) return;
  chooser.style.display = 'none';
}

function initAuthPlatformChoice() {
  const params = new URLSearchParams(window.location.search || '');
  const pathPlatform = getPlatformFromPath();
  const initial = getScriptPlatformLock() || normalizeAuthPlatform(pathPlatform || params.get('platform') || localStorage.getItem(AUTH_PLATFORM_KEY) || 'temu');
  document.querySelectorAll('.auth-platform-btn[data-platform]').forEach((button) => {
    if (button.dataset.platformBound === '1') return;
    button.dataset.platformBound = '1';
    button.addEventListener('click', (event) => {
      event.preventDefault();
      if (getScriptPlatformLock() && normalizeAuthPlatform(button.dataset.platform) !== getScriptPlatformLock()) return;
      applyAuthPlatform(button.dataset.platform, { updateUrl: true, switchWorkspace: Boolean(localStorage.getItem('temuServerToken')) });
    });
  });
  applyAuthPlatform(initial, { updateUrl: params.has('platform') || Boolean(pathPlatform) || isWorkspaceEntryPath() });
  syncAuthPlatformVisibility('login');
}


const TEMU_TAB_IDS = new Set(['dashboard', 'salesdata', 'temuAiDiagnosis', 'compliance', 'livephotos', 'pricing', 'customized', 'temuUploadTest', 'activity', 'qualification', 'collection', 'finance']);
const OZON_TAB_IDS = new Set(['ozonHome', 'ozonCollection', 'ozonProducts', 'ozonOrders', 'ozonAccounts', 'ozonFinance', 'ozonMessages', 'ozonTools']);
const WORKSPACE_TAB_LABELS = {
  dashboard: { icon: '🏠', label: '首页' },
  salesdata: { icon: '📊', label: '销量查询' },
  temuAiDiagnosis: { icon: '🤖', label: 'AI商品诊断' },
  compliance: { icon: '📋', label: '合规信息' },
  livephotos: { icon: '📷', label: '实拍图' },
  pricing: { icon: '💰', label: '核价' },
  customized: { icon: '🎨', label: '商品管理' },
  temuUploadTest: { icon: '🧪', label: '批量上架（测试）' },
  activity: { icon: '🎉', label: '活动报名' },
  qualification: { icon: '📄', label: '资质上传' },
  collection: { icon: '🛒', label: '店铺搬家' },
  finance: { icon: '💳', label: '财务数据' },
  ozonHome: { icon: '🏠', label: '首页' },
  ozonOrders: { icon: '📦', label: '销量分析' },
  ozonProducts: { icon: '🤖', label: 'AI诊断' },
  ozonCollection: { icon: '🔗', label: '链接池' },
  ozonFinance: { icon: '💳', label: '财务中心' },
  ozonMessages: { icon: '💬', label: '消息中心' },
  ozonAccounts: { icon: '🔐', label: '店铺授权' },
  ozonTools: { icon: '🧮', label: '运营工具' }
};

function normalizeWorkspaceTabLabels(root = document) {
  root.querySelectorAll?.('.tab.nav-item[data-tab]')?.forEach((tab) => {
    const meta = WORKSPACE_TAB_LABELS[tab.dataset.tab || ''];
    if (!meta) return;
    tab.dataset.navLabel = meta.label;
    tab.dataset.navIcon = meta.icon;
    tab.replaceChildren(document.createTextNode(meta.icon));
    const label = document.createElement('span');
    label.textContent = meta.label;
    label.className = 'nav-primary';
    tab.appendChild(label);
  });
}
const OZON_COLLECTION_STATUS_LABELS = {
  collected: '已采集',
  selected: '选品池',
  ready: '准备上架',
  listed: '已上架',
  archived: '已归档'
};
const ozonState = {
  accounts: [],
  products: [],
  orders: [],
  overview: null,
  collectionLinks: [],
  selectedAccountId: '',
  productKeyword: '',
  productVisibility: '',
  collectionStatus: '',
  collectionKeyword: '',
  productAccountId: '',
  productAccountTouched: false,
  productName: '',
  productSku: '',
  productSelectedIds: new Set(),
  productStatusCounts: {},
  productTotal: 0,
  productPage: 1,
  productLimit: 50,
  orderAnalysis: null,
  orderAnalysisPage: 1,
  orderAnalysisLimit: 50,
  orderAnalysisTotal: 0,
  productDiagnosis: null,
  productDiagnosisAccountId: '',
  productDiagnosisSku: '',
  productDiagnosisRisk: '',
  productDiagnosisKeyword: '',
  orderAccountId: '',
  orderAnalysisAccountId: '',
  orderAccountTouched: false,
  orderAnalysisSku: '',
  orderKeyword: '',
  orderStatus: '',
  orderDateFrom: '',
  orderDateTo: '',
  orderSelectedIds: new Set(),
  orderStatusCounts: {},
  orderTotal: 0,
  orderPage: 1,
  orderLimit: 50,
  accountKeyword: '',
  selectedCollectionIds: new Set(),
  aiProviders: [],
  aiConfigs: [],
  selectedAiConfigId: '',
  aiDefaultConfig: null,
  finance: null,
  financeAccountId: '',
  messages: null,
  messageAccountId: '',
  messageType: '',
  selectedMessageId: '',
  overviewRequestSeq: 0,
  orderAnalysisRequestSeq: 0,
  productDiagnosisRequestSeq: 0
};

function isOzonWorkspace() {
  return getSelectedAuthPlatform() === 'ozon';
}

function switchWorkspaceForPlatform(platform = getSelectedAuthPlatform()) {
  const selected = normalizeAuthPlatform(platform);
  normalizeWorkspaceTabLabels();
  document.body?.setAttribute('data-platform', selected);
  document.documentElement?.setAttribute('data-platform', selected);
  updateAuthEntryPlatformText(selected);
  getServerAuthState().then(data => {
    renderTopbarAccount(data?.temuServerToken ? data?.temuServerUser : null);
  }).catch(() => renderTopbarAccount(null));
  const logoText = document.querySelector('.sidebar-logo-text');
  if (logoText) logoText.textContent = selected === 'ozon' ? 'OZON 工作台' : 'TEMU 工作台';
  const topTitle = document.querySelector('.topbar h1');
  if (topTitle) topTitle.innerHTML = selected === 'ozon'
    ? '<span class="logo-icon">OZ</span> OZON 工作台'
    : '<span class="logo-icon">TM</span> TEMU 工作台';
  document.querySelectorAll('.tab.nav-item').forEach((tab) => {
    const tabId = tab.dataset.tab || '';
    const isOzonTab = OZON_TAB_IDS.has(tabId);
    const isLegacyOzonAccountsTab = tabId === 'ozonAccounts';
    if (selected === 'ozon') {
      tab.style.display = isOzonTab && !isLegacyOzonAccountsTab ? 'flex' : 'none';
      tab.disabled = !isOzonTab || isLegacyOzonAccountsTab;
    } else if (isOzonTab) {
      tab.style.display = 'none';
      tab.disabled = true;
    } else {
      tab.style.display = '';
    }
  });
  const banner = document.getElementById('bridgeBanner');
  if (banner && selected === 'ozon') banner.style.display = 'none';
  if (selected === 'ozon') {
    const activeOzonSection = Array.from(document.querySelectorAll('.section.active')).find(section => OZON_TAB_IDS.has(section.id));
    const targetTabId = activeOzonSection?.id === 'ozonAccounts' ? 'ozonHome' : (activeOzonSection?.id || 'ozonHome');
    document.querySelectorAll('.section').forEach(section => section.classList.remove('active'));
    document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
    document.getElementById(targetTabId)?.classList.add('active');
    document.querySelector(`.tab[data-tab="${targetTabId}"]`)?.classList.add('active');
    loadOzonTab(targetTabId).catch(err => console.warn('[xiaoqingwa][OZON] workspace load failed:', err));
  } else if (Array.from(document.querySelectorAll('.section.active')).some(section => OZON_TAB_IDS.has(section.id))) {
    document.getElementById('dashboard')?.classList.add('active');
    document.querySelector('.tab[data-tab="dashboard"]')?.classList.add('active');
  }
}

function setOzonResult(message, tone = 'info') {
  const el = document.getElementById('ozonBindResult');
  if (!el) return;
  el.style.display = 'block';
  el.textContent = message;
  el.style.color = tone === 'success' ? '#16a34a' : tone === 'error' ? '#dc2626' : '#475569';
}

function setOzonAuthModalResult(message = '', tone = 'info') {
  const el = document.getElementById('ozonAccountAuthModalResult');
  if (!el) return;
  el.textContent = message || '';
  el.style.color = tone === 'success' ? '#16a34a' : tone === 'error' ? '#dc2626' : '#475569';
}

function setOzonOrderSyncStatus(message, tone = 'info') {
  const el = document.getElementById('ozonOrderSyncStatus');
  if (!el) return;
  el.textContent = message;
  el.style.background = tone === 'error' ? '#fef2f2' : tone === 'success' ? '#f0fdf4' : '#eff6ff';
  el.style.color = tone === 'error' ? '#991b1b' : tone === 'success' ? '#166534' : '#1e3a8a';
}

function setOzonSyncProgress(kind = 'product', percent = 0, message = '', tone = 'info') {
  const ids = kind === 'order'
    ? ['ozonOrderSyncProgress', 'ozonOrderSyncProgressText', 'ozonOrderSyncProgressPercent', 'ozonOrderSyncProgressFill']
    : ['ozonProductSyncProgress', 'ozonProductSyncProgressText', 'ozonProductSyncProgressPercent', 'ozonProductSyncProgressFill'];
  const [cardId, textId, percentId, fillId] = ids;
  const card = document.getElementById(cardId);
  const text = document.getElementById(textId);
  const percentEl = document.getElementById(percentId);
  const fill = document.getElementById(fillId);
  const value = Math.max(0, Math.min(100, Math.round(Number(percent) || 0)));
  if (card) {
    card.classList.add('is-visible');
    card.classList.toggle('is-success', tone === 'success');
    card.classList.toggle('is-error', tone === 'error');
  }
  if (text) text.textContent = message || '正在同步...';
  if (percentEl) percentEl.textContent = `${value}%`;
  if (fill) fill.style.width = `${value}%`;
  if (kind === 'order') setOzonOrderSyncStatus(message, tone);
  else setOzonResult(message, tone);
}

function setOzonSyncButtonsDisabled(disabled = false, kind = 'all') {
  const ids = kind === 'order'
    ? ['ozonSyncOrdersBtn2', 'ozonPullNewOrdersBtn']
    : kind === 'product'
      ? ['ozonSyncProductsBtn', 'ozonSyncProductsBtn2', 'ozonSyncSelectedProductsBtn', 'ozonRunProductDiagnosisBtn']
      : ['ozonSyncOrdersBtn2', 'ozonPullNewOrdersBtn', 'ozonSyncProductsBtn', 'ozonSyncProductsBtn2', 'ozonSyncSelectedProductsBtn', 'ozonRunProductDiagnosisBtn', 'ozonSyncWarehousesBtn', 'ozonSyncLimitsBtn'];
  ids.forEach(id => {
    const el = document.getElementById(id);
    if (el) el.disabled = disabled;
  });
}

function toggleOzonAccountAuthPanel(force) {
  const panel = document.getElementById('ozonAccountAuthModal') || document.getElementById('ozonAccountAuthPanel');
  const buttons = ['ozonGoAccountsBtn', 'ozonToggleAuthPanelBtn']
    .map(id => document.getElementById(id))
    .filter(Boolean);
  if (!panel) return;
  const willOpen = typeof force === 'boolean' ? force : !panel.classList.contains('is-open');
  panel.classList.toggle('is-open', willOpen);
  panel.setAttribute('aria-hidden', willOpen ? 'false' : 'true');
  buttons.forEach((button) => {
    button.textContent = '添加店铺授权';
    button.setAttribute('aria-expanded', willOpen ? 'true' : 'false');
  });
  if (willOpen) {
    setOzonAuthModalResult('');
    window.setTimeout(() => document.getElementById('ozonShopTitleInput')?.focus(), 120);
  }
}

async function ozonRequest(path, options = {}) {
  const token = await getServerAuthToken();
  if (!token) throw new Error('\u8bf7\u5148\u767b\u5f55\u8d26\u53f7');
  const headers = {
    'Authorization': `Bearer ${token}`,
    ...(options.body ? { 'Content-Type': 'application/json' } : {}),
    ...(options.headers || {})
  };
  const resp = await fetch(`${SERVER_URL}/ozon${path}`, { ...options, headers });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || result.ok === false) {
    if (resp.status === 504) {
      throw new Error('同步请求超时：已改为分段同步，请稍后重试或缩小日期范围');
    }
    const detail = [result.error || result.message || `HTTP ${resp.status}`, result.code, result.status ? `OZON ${result.status}` : '']
      .filter(Boolean)
      .join(' / ');
    throw new Error(detail);
  }
  return result;
}

async function aiRequest(path, options = {}) {
  const token = await getServerAuthToken();
  if (!token) throw new Error('请先登录账号');
  const headers = {
    'Authorization': `Bearer ${token}`,
    ...(options.body ? { 'Content-Type': 'application/json' } : {}),
    ...(options.headers || {})
  };
  const resp = await fetch(`${SERVER_URL}/ai${path}`, { ...options, headers });
  const result = await resp.json().catch(() => ({}));
  if (!resp.ok || result.ok === false) {
    throw new Error(result.error || result.message || `HTTP ${resp.status}`);
  }
  return result;
}

function syncOzonAccountsToExtension() {
  try {
    localStorage.setItem('ozonAccounts', JSON.stringify(ozonState.accounts || []));
  } catch (_) {}
  try {
    globalThis.chrome?.storage?.local?.set?.({
      ozonAccounts: ozonState.accounts || []
    });
  } catch (_) {}
}

const OZON_AI_TITLE_BATCH_SIZE = 8;
const OZON_AI_TITLE_MAX_ITEMS = 60;

function setOzonAiStatus(message, tone = 'info') {
  const status = document.getElementById('ozonAiConfigStatus');
  if (status) {
    status.textContent = message;
    status.style.color = tone === 'success' ? '#16a34a' : tone === 'error' ? '#dc2626' : '#475569';
  }
}

function setOzonAiResult(message, tone = 'info') {
  const el = document.getElementById('ozonAiAnalysisResult');
  if (!el) return;
  el.textContent = message;
  el.style.borderColor = tone === 'error' ? '#fecaca' : tone === 'success' ? '#bbf7d0' : '#c7d2fe';
  el.style.background = tone === 'error' ? '#fef2f2' : tone === 'success' ? '#f0fdf4' : '#eef2ff';
  el.style.color = tone === 'error' ? '#991b1b' : tone === 'success' ? '#166534' : '#312e81';
}

function renderOzonAiConfig() {
  const config = ozonState.aiDefaultConfig || ozonState.aiConfigs[0] || null;
  if (config) {
    ozonState.selectedAiConfigId = String(config.id || '');
    setOzonAiStatus('AI分析已启用', 'success');
  } else {
    ozonState.selectedAiConfigId = '';
    setOzonAiStatus('AI分析暂不可用，请稍后再试。', 'error');
  }
}

async function loadOzonAiConfig() {
  const configsResult = await aiRequest('/configs?platform=ozon');
  ozonState.aiProviders = [];
  ozonState.aiConfigs = Array.isArray(configsResult.configs) ? configsResult.configs : [];
  ozonState.aiDefaultConfig = ozonState.aiConfigs.find(config => config.isDefault) || ozonState.aiConfigs[0] || null;
  renderOzonAiConfig();
}

async function ensureOzonAiSourceData(type = 'overall') {
  const tasks = [];
  if (!ozonState.orderAnalysis) tasks.push(loadOzonOrderAnalysis());
  if (!ozonState.productDiagnosis) tasks.push(loadOzonProductDiagnosis({ enrichAi: false }));
  if (type === 'links' && !ozonState.collectionLinks.length) tasks.push(loadOzonCollectionLinks());
  if (tasks.length) await Promise.allSettled(tasks);
}

function compactOzonAiOrderAnalysis(source = {}) {
  const items = Array.isArray(source.items) ? source.items : [];
  return {
    summary: source.summary || {},
    status: (Array.isArray(source.status) ? source.status : []).slice(0, 12),
    daily: (Array.isArray(source.daily) ? source.daily : []).slice(-28).map(row => ({
      date: row.date,
      orders: row.orders,
      qty: row.qty,
      amount: row.amount
    })),
    items: items.slice(0, 200).map(item => ({
      accountId: item.accountId,
      accountTitle: item.accountTitle,
      currencyCode: item.currencyCode || 'RUB',
      todayQty: item.todayQty || 0,
      monthAvg: item.monthAvg || 0,
      qty7: item.qty7 || 0,
      prevQty7: item.prevQty7 || 0,
      trend7Percent: item.trend7Percent || 0,
      qty28: item.qty28 || 0,
      prevQty28: item.prevQty28 || 0,
      trend28Percent: item.trend28Percent || 0,
      share28Percent: item.share28Percent || 0,
      tag: item.tag || '',
      alert: Boolean(item.alert)
    }))
  };
}

function compactOzonAiProductDiagnosis(source = {}) {
  const items = Array.isArray(source.items) ? source.items : [];
  const priorityItems = items
    .filter(item => item.diagnosis?.riskCode === 'high' || item.sales?.alert || Number(item.sales?.trend7Percent || 0) <= -20)
    .concat(items.filter(item => item.diagnosis?.riskCode === 'medium'))
    .concat(items.filter(item => Number(item.diagnosis?.score || 0) >= 85));
  const seen = new Set();
  const rows = priorityItems.filter(item => {
    const ui = normalizeOzonProductForUi(item.product || {});
    const key = ui.productId || ui.offerId || ui.sku || ui.title;
    if (!key || seen.has(key)) return false;
    seen.add(key);
    return true;
  }).slice(0, 100);
  return {
    summary: source.summary || {},
    items: rows.map(item => {
      const ui = normalizeOzonProductForUi(item.product || {});
      const diag = item.diagnosis || {};
      const sales = item.sales || {};
      return {
        product: {
          productId: ui.productId,
          offerId: ui.offerId,
          sku: ui.sku,
          name: ui.title,
          accountTitle: ui.accountTitle,
          statusLabel: ui.statusLabel,
          priceIndex: ui.priceIndex?.label,
          stock: ui.stock?.available
        },
        sales: {
          qty7: sales.qty7 || 0,
          qty28: sales.qty28 || 0,
          todayQty: sales.todayQty || 0,
          trend7Percent: sales.trend7Percent || 0,
          trend28Percent: sales.trend28Percent || 0,
          alert: Boolean(sales.alert)
        },
        diagnosis: {
          score: diag.score,
          riskCode: diag.riskCode,
          riskLevel: diag.riskLevel,
          imageCount: diag.imageCount,
          stock: diag.stock,
          priceIndex: diag.priceIndex,
          problems: (diag.problems || []).slice(0, 4),
          suggestions: (diag.suggestions || []).slice(0, 4),
          optimizedTitle: diag.optimizedTitle,
          optimizedKeywords: diag.optimizedKeywords
        }
      };
    })
  };
}

function compactOzonAiLinks(links = []) {
  return (Array.isArray(links) ? links : []).slice(0, 50).map(link => ({
    title: link.title,
    url: link.url,
    priceText: link.priceText,
    status: link.status,
    productId: link.productId,
    accountTitle: link.accountTitle
  }));
}

async function runOzonAiAnalysis(type = 'overall') {
  setOzonAiResult(type === 'links' ? '正在分析产品链接...' : '正在分析订单与商品数据...');
  try {
    if (!ozonState.aiDefaultConfig) {
      await loadOzonAiConfig().catch(() => null);
    }
    await ensureOzonAiSourceData(type);
    const result = await aiRequest('/ozon/analyze', {
      method: 'POST',
      body: JSON.stringify({
        type,
        prompt: '',
        ordersAnalysis: compactOzonAiOrderAnalysis(ozonState.orderAnalysis || {}),
        productsAnalysis: compactOzonAiProductDiagnosis(ozonState.productDiagnosis || {}),
        links: compactOzonAiLinks(ozonState.collectionLinks || [])
      })
    });
    setOzonAiResult(result.text || '模型已返回分析结果。', 'success');
  } catch (err) {
    const message = String(err.message || 'AI不可用：请稍后再试');
    setOzonAiResult(message.startsWith('AI不可用') ? message : `AI不可用：${message}`, 'error');
  }
}

function getOzonStatusLabel(status = '') {
  return OZON_COLLECTION_STATUS_LABELS[status] || status || '已采集';
}

function setOzonCollectionCounts() {
  const all = ozonState.collectionLinks || [];
  const active = all.filter(link => link.status !== 'archived');
  const selectedCount = active.filter(link => link.status === 'selected').length;
  const readyCount = active.filter(link => link.status === 'ready').length;
  const listedCount = active.filter(link => link.status === 'listed').length;
  const collectionEl = document.getElementById('ozonCollectionCount');
  const selectedEl = document.getElementById('ozonSelectedCount');
  const readyEl = document.getElementById('ozonReadyCount');
  const listedEl = document.getElementById('ozonListedCount');
  if (collectionEl) collectionEl.textContent = String(active.length || 0);
  if (selectedEl) selectedEl.textContent = String(selectedCount || 0);
  if (readyEl) readyEl.textContent = String(readyCount || 0);
  if (listedEl) listedEl.textContent = String(listedCount || 0);
}

function formatOzonAmount(value, currency = 'RUB') {
  const n = Number(value || 0);
  const code = String(currency || 'RUB').toUpperCase();
  const rate = code === 'RUB' ? 0.0938 : code === 'CNY' || code === 'RMB' ? 1 : 1;
  const converted = Number.isFinite(n) ? n * rate : 0;
  const formatted = converted.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  return `¥${formatted}`;
}

function getOzonBusinessDateKey(value = new Date(), offsetMinutes = 8 * 60) {
  const date = value instanceof Date ? value : new Date(value);
  if (Number.isNaN(date.getTime())) return '';
  return new Date(date.getTime() + offsetMinutes * 60 * 1000).toISOString().slice(0, 10);
}

function getOzonTodayDateRange() {
  const today = getOzonBusinessDateKey();
  return { dateFrom: today, dateTo: today };
}

function getOzonRecentOrderSyncDateRange(days = 2) {
  const now = new Date();
  const today = getOzonBusinessDateKey(now);
  const startDate = new Date(now.getTime() - Math.max(1, Number(days || 2) - 1) * 24 * 60 * 60 * 1000);
  return { dateFrom: getOzonBusinessDateKey(startDate), dateTo: today };
}

function normalizeOzonDateKey(value) {
  const text = normalizeOzonText(value);
  return /^\d{4}-\d{2}-\d{2}$/.test(text) ? text : '';
}

function ozonDateKeyToUtcDate(value) {
  const key = normalizeOzonDateKey(value);
  if (!key) return null;
  const [year, month, day] = key.split('-').map(Number);
  return new Date(Date.UTC(year, month - 1, day));
}

function formatOzonUtcDateKey(date) {
  if (!(date instanceof Date) || Number.isNaN(date.getTime())) return '';
  return date.toISOString().slice(0, 10);
}

function addOzonDateKeyDays(value, days) {
  const date = ozonDateKeyToUtcDate(value);
  if (!date) return '';
  date.setUTCDate(date.getUTCDate() + Number(days || 0));
  return formatOzonUtcDateKey(date);
}

function compareOzonDateKeys(left, right) {
  const a = ozonDateKeyToUtcDate(left);
  const b = ozonDateKeyToUtcDate(right);
  if (!a || !b) return 0;
  return a.getTime() - b.getTime();
}

function buildOzonOrderSyncDateChunks(dateFrom = '', dateTo = '', chunkDays = 30) {
  const today = getOzonBusinessDateKey();
  let end = normalizeOzonDateKey(dateTo) || today;
  let start = normalizeOzonDateKey(dateFrom) || addOzonDateKeyDays(end, -1094);
  if (compareOzonDateKeys(start, end) > 0) {
    const tmp = start;
    start = end;
    end = tmp;
  }
  const size = Math.max(1, Math.min(30, Number(chunkDays || 30)));
  const chunks = [];
  let cursorEnd = end;
  while (cursorEnd && compareOzonDateKeys(start, cursorEnd) <= 0 && chunks.length < 120) {
    const candidateStart = addOzonDateKeyDays(cursorEnd, -(size - 1));
    const chunkStart = compareOzonDateKeys(candidateStart, start) < 0 ? start : candidateStart;
    chunks.push({ dateFrom: chunkStart, dateTo: cursorEnd });
    cursorEnd = addOzonDateKeyDays(chunkStart, -1);
  }
  return chunks.length ? chunks : [{ dateFrom: start, dateTo: end }];
}

function normalizeOzonText(value) {
  if (value === null || value === undefined) return '';
  return String(value).trim();
}

function getOzonAccountTitle(accountId) {
  const account = (ozonState.accounts || []).find(item => String(item.id) === String(accountId));
  return account?.title || '';
}

function getSelectedOzonScopeId(type = 'product') {
  const scoped = type === 'order'
    ? (ozonState.orderAccountId || ozonState.orderAnalysisAccountId)
    : (ozonState.productAccountId || ozonState.productDiagnosisAccountId);
  return scoped || getDefaultOzonAccountId();
}

function getDefaultOzonAccountId() {
  const account = (ozonState.accounts || []).find(item => item && item.status !== 'deleted' && item.status !== 'disabled' && item.id);
  return account?.id ? String(account.id) : '';
}

function getOzonFullSyncAccountIds(type = 'product') {
  const productAccountId = document.getElementById('ozonProductAccountFilter')?.value || ozonState.productAccountId || '';
  const orderAccountId = document.getElementById('ozonOrderAccountFilter')?.value || ozonState.orderAccountId || '';
  const scoped = type === 'order'
    ? orderAccountId
    : productAccountId;
  if (scoped) return [String(scoped)];
  const activeAccounts = (ozonState.accounts || [])
    .filter(account => account && account.status !== 'deleted' && account.status !== 'disabled' && account.id)
    .map(account => String(account.id));
  return activeAccounts.length ? activeAccounts : (getDefaultOzonAccountId() ? [getDefaultOzonAccountId()] : []);
}

function syncOzonAccountSelects() {
  const optionHtml = ['<option value="">全部店铺</option>']
    .concat((ozonState.accounts || []).map(account => `<option value="${escapeHtml(account.id)}">${escapeHtml(account.title || account.clientId || 'OZON 店铺')}</option>`))
    .join('');
  [
    ['ozonProductAccountFilter', ozonState.productAccountId],
    ['ozonProductDiagnosisAccountFilter', ozonState.productDiagnosisAccountId || ozonState.productAccountId],
    ['ozonOrderAccountFilter', ozonState.orderAccountId],
    ['ozonOrderAnalysisAccountFilter', ozonState.orderAnalysisAccountId],
    ['ozonFinanceAccountFilter', ozonState.financeAccountId],
    ['ozonMessageAccountFilter', ozonState.messageAccountId]
  ].forEach(([id, value]) => {
    const select = document.getElementById(id);
    if (!select) return;
    const current = value || '';
    if (select.dataset.ozonOptions !== optionHtml) {
      select.innerHTML = optionHtml;
      select.dataset.ozonOptions = optionHtml;
    }
    select.value = current;
  });
}

function getOzonProductStatusLabel(status = '') {
  const text = normalizeOzonText(status).toUpperCase();
  const labels = {
    VISIBLE: '销售中',
    READY_TO_SUPPLY: '准备出售',
    TO_SUPPLY: '待修改',
    INVISIBLE: '已下架',
    EMPTY_STOCK: '无库存',
    ARCHIVED: '已归档',
    ERROR: '错误',
    ALL: '所有'
  };
  return labels[text] || status || '未知';
}

function setOzonProductAccountFilterValue(accountId = '', { userTouched = false } = {}) {
  const normalized = normalizeOzonText(accountId);
  ozonState.productAccountId = normalized;
  if (userTouched) ozonState.productAccountTouched = true;
  const select = document.getElementById('ozonProductAccountFilter');
  if (select) select.value = normalized;
}

function setOzonOrderAccountFilterValue(accountId = '', { userTouched = false } = {}) {
  const normalized = normalizeOzonText(accountId);
  ozonState.orderAccountId = normalized;
  if (userTouched) ozonState.orderAccountTouched = true;
  const select = document.getElementById('ozonOrderAccountFilter');
  if (select) select.value = normalized;
}

function getOzonOrderStatusLabel(status = '') {
  const text = normalizeOzonText(status);
  const labels = {
    awaiting_packaging: '等待备货',
    awaiting_deliver: '等待发运',
    delivering: '运输中',
    arbitration: '有争议的',
    delivered: '已签收',
    cancelled: '已取消'
  };
  return labels[text] || status || '未知';
}

function formatOzonDateRange(start, end) {
  const a = formatOzonDateTime(start);
  const b = formatOzonDateTime(end);
  if (a && b) return `${a} 至 ${b}`;
  return a || b || '暂无订单';
}

function getOzonProductMetric(product = {}, names = []) {
  const raw = product.rawData || product.raw || {};
  const sources = [
    product,
    raw,
    raw.list_data,
    raw.price_indexes,
    raw.price_indexes?.external_index_data,
    raw.marketing_price,
    raw.price,
    raw.stocks,
    raw.stocks?.stocks?.[0],
    Array.isArray(raw.stocks) ? raw.stocks[0] : null,
    Array.isArray(raw.sources) ? raw.sources[0] : null,
    raw.sources?.[0],
    raw.model_info,
    raw.dimensions,
    raw.package,
    raw.category,
    raw.commissions?.find?.(item => item.sale_schema === 'FBS') || raw.commissions?.[0]
  ].filter(Boolean);
  for (const source of sources) {
    for (const name of names) {
      const value = source?.[name];
      if (value !== undefined && value !== null && value !== '') return value;
    }
  }
  return '';
}

function parseOzonNumber(value) {
  if (value === undefined || value === null || value === '') return null;
  if (typeof value === 'number') return Number.isFinite(value) ? value : null;
  if (typeof value === 'object') {
    const nested = value.value ?? value.price ?? value.marketing_price ?? value.amount ?? value.total ?? value.sum;
    return parseOzonNumber(nested);
  }
  const normalized = String(value).replace(/[^\d.,-]/g, '').replace(',', '.');
  const n = Number(normalized);
  return Number.isFinite(n) ? n : null;
}

function formatOzonMoney(value, currency = '¥') {
  if (value === undefined || value === null || value === '') return '--';
  const n = parseOzonNumber(value);
  if (!Number.isFinite(n)) return String(value);
  return `${currency}${n.toFixed(n % 1 === 0 ? 0 : 2)}`;
}

function getOzonStatusClass(status = '') {
  const text = normalizeOzonText(status).toUpperCase();
  if (text === 'VISIBLE') return '';
  if (text === 'ERROR') return 'is-error';
  if (['READY_TO_SUPPLY', 'TO_SUPPLY', 'EMPTY_STOCK'].includes(text)) return 'is-warn';
  return 'is-muted';
}

function formatOzonNumber(value, suffix = '') {
  const n = Number(value);
  if (!Number.isFinite(n)) return value === 0 ? `0${suffix}` : '';
  return `${n}${suffix}`;
}

function pickFirstOzonValue(sources = [], names = []) {
  for (const source of sources.filter(Boolean)) {
    for (const name of names) {
      const value = source?.[name];
      if (value !== undefined && value !== null && value !== '') return value;
    }
  }
  return '';
}

function joinOzonParts(parts = [], separator = ' / ') {
  return parts.map(value => normalizeOzonText(value)).filter(Boolean).join(separator);
}

function formatOzonDateTime(value) {
  if (!value) return '--';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return String(value);
  return date.toLocaleString('zh-CN', { hour12: false });
}

function getOzonCategoryText(product = {}) {
  const raw = product.rawData || product.raw || {};
  const sources = [product, raw, raw.category, raw.description_category, raw.type, raw.list_data].filter(Boolean);
  const pathValue = pickFirstOzonValue(sources, ['category_name', 'categoryName', 'description_category_name', 'type_name', 'typeName', 'category_full_name', 'categoryFullName']);
  if (pathValue) return pathValue;
  if (Array.isArray(raw.category_path) && raw.category_path.length) {
    return raw.category_path.map(item => item?.name || item).filter(Boolean).join(' > ');
  }
  if (Array.isArray(raw.description_category?.path) && raw.description_category.path.length) {
    return raw.description_category.path.map(item => item?.name || item).filter(Boolean).join(' > ');
  }
  const id = getOzonProductMetric(product, ['description_category_id', 'category_id', 'categoryId', 'type_id', 'typeId']);
  return id ? `类目ID ${id}` : '--';
}

function getOzonCommissionInfo(product = {}) {
  const raw = product.rawData || product.raw || {};
  const commissions = Array.isArray(raw.commissions) ? raw.commissions : [];
  const target = commissions.find(item => /rfbs/i.test(item.sale_schema || item.delivery_schema || item.schema || ''))
    || commissions.find(item => /fbs/i.test(item.sale_schema || item.delivery_schema || item.schema || ''))
    || commissions[0]
    || {};
  const percent = pickFirstOzonValue([target, raw, product], ['percent', 'commission_percent', 'commissionPercent', 'sales_percent', 'salesPercent', 'fbs_commission_percent', 'fbo_commission_percent', 'rate', 'commission']);
  const amount = pickFirstOzonValue([target, raw, product], ['value', 'amount', 'commission_amount', 'commissionAmount']);
  return { percent, amount };
}

function getOzonPriceInfo(product = {}) {
  const raw = product.rawData || product.raw || {};
  const priceObj = typeof raw.price === 'object' ? raw.price : {};
  const marketingObj = typeof raw.marketing_price === 'object' ? raw.marketing_price : {};
  return {
    price: pickFirstOzonValue([product, raw, priceObj, marketingObj], ['price', 'marketing_price', 'marketingPrice']),
    oldPrice: pickFirstOzonValue([product, raw, priceObj, marketingObj], ['old_price', 'oldPrice']),
    minPrice: pickFirstOzonValue([product, raw, priceObj, marketingObj], ['min_price', 'minPrice']),
    promoPrice: pickFirstOzonValue([raw, priceObj, marketingObj], ['marketing_seller_price', 'marketingSellerPrice', 'auto_action_enabled', 'action_price', 'actionPrice']),
    currency: pickFirstOzonValue([raw, priceObj, marketingObj], ['currency_code', 'currencyCode', 'currency']) || 'RUB'
  };
}

function getOzonStockInfo(product = {}) {
  const raw = product.rawData || product.raw || {};
  const stockList = Array.isArray(raw.stocks?.stocks) ? raw.stocks.stocks : (Array.isArray(raw.stocks) ? raw.stocks : []);
  const stockSource = stockList[0] || raw.stocks || raw;
  const available = pickFirstOzonValue([stockSource, raw.stocks, raw], ['present', 'stock', 'available_stock', 'availableStock', 'quantity', 'sellable_stock']);
  const reserved = pickFirstOzonValue([stockSource, raw.stocks, raw], ['reserved', 'reserved_stock', 'reservedStock']);
  const total = stockList.length
    ? stockList.reduce((sum, item) => sum + (parseOzonNumber(item.present ?? item.stock ?? item.available_stock ?? item.quantity) || 0), 0)
    : parseOzonNumber(available);
  return {
    available: Number.isFinite(total) && total !== null ? total : available,
    reserved,
    warehouses: stockList.length
  };
}

function getOzonWeightText(product = {}) {
  const raw = product.rawData || product.raw || {};
  const weight = getOzonProductMetric(product, ['weight', 'weight_g', 'weightG', 'package_weight', 'packageWeight', 'volume_weight']);
  const dimensions = raw.dimensions || raw.package || {};
  const size = joinOzonParts([
    dimensions.width || raw.width,
    dimensions.height || raw.height,
    dimensions.depth || dimensions.length || raw.depth || raw.length
  ], '×');
  const weightText = weight ? formatOzonNumber(weight, 'g') : '--';
  return size ? `${weightText}<br><span class="ozon-product-muted">${escapeHtml(size)}</span>` : escapeHtml(weightText);
}

function getOzonProductErrorText(product = {}) {
  const raw = product.rawData || product.raw || {};
  const errors = Array.isArray(raw.errors) ? raw.errors : [];
  const text = errors.map(item => item?.description || item?.message || item?.error || item?.attribute_name || item).filter(Boolean).join('；');
  return text || raw.statuses?.status_description || raw.statuses?.statusDescription || '';
}

function getOzonPriceIndexInfoForUi(product = {}) {
  const raw = product.rawData || product.raw || {};
  const source = raw.price_indexes || raw.priceIndexes || raw.price_index || raw.priceIndex || {};
  const external = source.external_index_data || source.externalIndexData || {};
  const rawColor = normalizeOzonText(
    source.color_index ||
    source.colorIndex ||
    source.price_index ||
    source.priceIndex ||
    external.color_index ||
    external.colorIndex ||
    raw.price_index ||
    raw.priceIndex
  ).toUpperCase();
  if (/RED|UNPROFIT|BAD|HIGH/.test(rawColor)) return { label: '不利', tone: 'is-error', raw: rawColor };
  if (/YELLOW|MODERATE|MIDDLE|MEDIUM/.test(rawColor)) return { label: '中等', tone: 'is-warn', raw: rawColor };
  if (/GREEN|PROFIT|GOOD|LOW/.test(rawColor)) return { label: '有利', tone: '', raw: rawColor };
  if (/WITHOUT|NONE|NO_INDEX|EMPTY/.test(rawColor) || !rawColor) return { label: '无指数', tone: 'is-muted', raw: rawColor };
  return { label: rawColor, tone: 'is-muted', raw: rawColor };
}

function getOzonSourceInfo(product = {}) {
  const raw = product.rawData || product.raw || {};
  const source = Array.isArray(raw.sources) ? (raw.sources[0] || {}) : (raw.source || {});
  return {
    purchasePrice: raw.purchase_price || raw.purchasePrice || source.purchase_price || source.price || '',
    url: raw.source_url || raw.sourceUrl || raw.purchase_url || raw.purchaseUrl || source.url || '',
    remark: raw.source_remark || raw.sourceRemark || raw.remark || raw.note || ''
  };
}

function normalizeOzonProductForUi(product = {}) {
  const raw = product.rawData || product.raw || {};
  const price = getOzonPriceInfo(product);
  const stock = getOzonStockInfo(product);
  const commission = getOzonCommissionInfo(product);
  const productId = product.productId || getOzonProductMetric(product, ['product_id', 'productId', 'id']) || '';
  const offerId = product.offerId || getOzonProductMetric(product, ['offer_id', 'offerId']) || '';
  const sku = product.sku || getOzonProductMetric(product, ['sku']) || '';
  const title = product.name || getOzonProductMetric(product, ['name', 'product_name', 'productName']) || 'OZON 商品';
  const visibility = product.visibility || raw.visibility || raw.status || '';
  const priceIndex = getOzonPriceIndexInfoForUi(product);
  return {
    productId,
    offerId,
    sku,
    title,
    image: pickOzonImage(product),
    category: getOzonCategoryText(product),
    commission,
    price,
    stock,
    source: getOzonSourceInfo(product),
    visibility,
    statusLabel: getOzonProductStatusLabel(visibility),
    statusClass: getOzonStatusClass(visibility),
    priceIndex,
    errorText: getOzonProductErrorText(product),
    accountTitle: product.accountTitle || ozonState.accounts.find(account => String(account.id) === String(product.ozonAccountId))?.title || '',
    createdAt: raw.created_at || raw.createdAt || product.createdAt || product.updatedAt || '',
    updatedAt: product.updatedAt || raw.updated_at || raw.updatedAt || ''
  };
}

function getOzonOrderProducts(order = {}) {
  const raw = order.rawData || order.raw || {};
  const products = Array.isArray(raw.products) ? raw.products : [];
  const financialProducts = Array.isArray(raw.financial_data?.products)
    ? raw.financial_data.products
    : (Array.isArray(raw.financialData?.products) ? raw.financialData.products : []);
  if (!products.length && financialProducts.length) return financialProducts.map(item => ({ ...item }));
  return products.map((product, index) => ({ ...product, financial: financialProducts[index] || {} }));
}

function getOzonOrderMoney(order = {}) {
  const raw = order.rawData || order.raw || {};
  const fin = raw.financial_data || raw.financialData || {};
  const products = getOzonOrderProducts(order);
  const productTotal = products.reduce((sum, product) => sum + (parseOzonNumber(product.price ?? product.financial?.price ?? product.financial?.old_price) || 0) * (parseOzonNumber(product.quantity) || 1), 0);
  const customerPrice = parseOzonNumber(order.customerPrice ?? raw.customer_price ?? raw.customerPrice) ?? productTotal;
  const services = Array.isArray(fin.products)
    ? fin.products.flatMap(item => Array.isArray(item.actions) ? item.actions : [])
    : [];
  const serviceTotal = services.reduce((sum, item) => sum + (parseOzonNumber(item?.amount ?? item?.price ?? item?.value) || 0), 0);
  const feeCandidates = [
    fin.commission_amount,
    fin.commissionAmount,
    fin.services_amount,
    fin.servicesAmount,
    fin.accruals_for_sale,
    fin.processing_and_delivery,
    Math.abs(serviceTotal)
  ];
  const fee = feeCandidates.map(parseOzonNumber).find(value => Number.isFinite(value) && value !== 0) || 0;
  const payout = parseOzonNumber(fin.payout || fin.payout_amount || fin.payoutAmount || fin.sale_commission);
  const profit = Number.isFinite(payout) ? payout : (customerPrice - Math.abs(fee));
  return {
    amount: customerPrice || 0,
    fee: Math.abs(fee || 0),
    profit,
    currency: order.currencyCode || raw.currency_code || raw.currencyCode || fin.currency_code || 'RUB'
  };
}

function getOzonOrderDelivery(order = {}) {
  const raw = order.rawData || order.raw || {};
  const delivery = raw.delivery_method || raw.deliveryMethod || {};
  const analytics = raw.analytics_data || raw.analyticsData || {};
  return {
    warehouse: analytics.warehouse_name || delivery.warehouse || delivery.warehouse_name || raw.warehouse_name || raw.warehouse_id || delivery.warehouse_id || '--',
    method: delivery.tpl_provider || delivery.name || raw.tpl_provider || raw.tpl_integration_type || '--',
    tracking: raw.tracking_number || raw.trackingNumber || raw.barcode || raw.tpl_integration_type || ''
  };
}

function getOzonOrderCancelText(order = {}) {
  const raw = order.rawData || order.raw || {};
  const cancel = raw.cancellation || raw.cancelation || {};
  return joinOzonParts([
    cancel.cancelled_by || cancel.canceled_by || raw.cancelled_by,
    cancel.cancel_reason || cancel.cancelReason || raw.cancel_reason || raw.cancelReason
  ], ' / ') || '--';
}

function getOzonProductKey(product = {}) {
  return normalizeOzonText(product.id || product.productId || product.offerId || product.sku);
}

function getOzonOrderKey(order = {}) {
  return normalizeOzonText(order.id || order.postingNumber || order.orderId);
}

function getOzonCachedProductMap() {
  const map = new Map();
  const scoreProduct = (product) => {
    const ui = normalizeOzonProductForUi(product);
    const raw = product.rawData || product.raw || {};
    let score = 0;
    if (ui.image) score += 12;
    if (ui.title && ui.title !== 'OZON 商品') score += 6;
    if (ui.sku || raw.sku || raw.sku_id || raw.skuId) score += 4;
    if (ui.productId) score += 2;
    if (ui.offerId) score += 2;
    if (Object.keys(raw || {}).length > 8) score += 1;
    return score;
  };
  (ozonState.products || []).forEach(product => {
    const ui = normalizeOzonProductForUi(product);
    const raw = product.rawData || product.raw || {};
    [
      ui.productId,
      ui.offerId,
      ui.sku,
      raw.product_id,
      raw.productId,
      raw.id,
      raw.offer_id,
      raw.offerId,
      raw.sku,
      raw.sku_id,
      raw.skuId,
      raw.barcode
    ].map(normalizeOzonText).filter(Boolean).forEach(key => {
      const existing = map.get(key);
      if (!existing || scoreProduct(product) >= scoreProduct(existing.product)) {
        map.set(key, { product, ui });
      }
    });
  });
  return map;
}

function getSelectedOzonProductRows() {
  const selected = new Set(Array.from(document.querySelectorAll('[data-ozon-product-check]:checked')).map(input => String(input.dataset.ozonProductCheck || '')));
  ozonState.productSelectedIds = selected;
  return (ozonState.products || []).filter(product => selected.has(getOzonProductKey(product)));
}

function getOzonProductRowsForTargetedSync() {
  const selected = getSelectedOzonProductRows();
  if (selected.length) return selected;
  const diagnosisRows = getFilteredOzonDiagnosisItems()
    .map(item => item.product || null)
    .filter(Boolean);
  if (diagnosisRows.length) return diagnosisRows;
  return ozonState.products || [];
}

function getSelectedOzonOrderRows() {
  const selected = new Set(Array.from(document.querySelectorAll('[data-ozon-order-check]:checked')).map(input => String(input.dataset.ozonOrderCheck || '')));
  ozonState.orderSelectedIds = selected;
  return (ozonState.orders || []).filter(order => selected.has(getOzonOrderKey(order)));
}

function updateOzonProductSelectionInfo() {
  const selected = getSelectedOzonProductRows();
  const info = document.getElementById('ozonProductSelectionInfo');
  if (info) info.textContent = `已选择 ${selected.length} 个商品`;
}

function updateOzonOrderSelectionInfo() {
  const selected = getSelectedOzonOrderRows();
  const info = document.getElementById('ozonOrderSelectionInfo');
  if (info) info.textContent = `已选择 ${selected.length} 个订单`;
}

function updateOzonPagination(type) {
  const isOrder = type === 'order';
  const total = Number(isOrder ? ozonState.orderTotal : ozonState.productTotal) || 0;
  const page = Math.max(1, Number(isOrder ? ozonState.orderPage : ozonState.productPage) || 1);
  const limit = Math.max(1, Number(isOrder ? ozonState.orderLimit : ozonState.productLimit) || 50);
  const maxPage = Math.max(1, Math.ceil(total / limit));
  const prev = document.getElementById(isOrder ? 'ozonOrderPrevPageBtn' : 'ozonProductPrevPageBtn');
  const next = document.getElementById(isOrder ? 'ozonOrderNextPageBtn' : 'ozonProductNextPageBtn');
  const info = document.getElementById(isOrder ? 'ozonOrderPageInfo' : 'ozonProductPageInfo');
  if (prev) prev.disabled = page <= 1;
  if (next) next.disabled = page >= maxPage || total <= page * limit;
  if (info) info.textContent = `第 ${page} 页 / 共 ${total} 条`;
}

function updateOzonProductStatusTabs() {
  const counts = ozonState.productStatusCounts || {};
  const countedTotal = Object.values(counts).reduce((sum, value) => sum + Number(value || 0), 0);
  const total = Number(countedTotal || ozonState.productTotal || ozonState.products.length || 0);
  const labels = {
    '': '所有',
    VISIBLE: '销售中',
    READY_TO_SUPPLY: '准备出售',
    ERROR: '错误',
    TO_SUPPLY: '待修改',
    INVISIBLE: '已下架',
    ARCHIVED: '已归档'
  };
  document.querySelectorAll('[data-ozon-product-tab]').forEach(button => {
    const status = button.dataset.ozonProductTab || '';
    const count = status ? Number(counts[status] || counts[status.toLowerCase()] || 0) : total;
    button.innerHTML = `${escapeHtml(labels[status] || status)} <span class="ozon-tab-count">${escapeHtml(count)}</span>`;
    button.classList.toggle('active', String(ozonState.productVisibility || '') === status);
  });
}

function updateOzonOrderStatusTabs() {
  const counts = ozonState.orderStatusCounts || {};
  const countedTotal = Object.values(counts).reduce((sum, value) => sum + Number(value || 0), 0);
  const total = Number(countedTotal || ozonState.orderTotal || ozonState.orders.length || 0);
  const labels = {
    '': '所有订单',
    awaiting_packaging: '等待备货',
    awaiting_deliver: '等待发运',
    delivering: '运输中',
    arbitration: '有争议的',
    delivered: '已签收',
    cancelled: '已取消'
  };
  document.querySelectorAll('[data-ozon-order-tab]').forEach(button => {
    const status = button.dataset.ozonOrderTab || '';
    const count = status ? Number(counts[status] || 0) : total;
    button.textContent = `${labels[status] || status} ${count}`;
    button.classList.toggle('active', String(ozonState.orderStatus || '') === status);
  });
}

function pickOzonImage(value) {
  if (!value || typeof value !== 'object') return '';
  const raw = value.rawData || value.raw || value;
  const asImageValue = (item) => {
    if (!item) return '';
    if (typeof item === 'string') return item;
    if (Array.isArray(item)) return item.map(asImageValue).find(Boolean) || '';
    if (typeof item !== 'object') return '';
    return item.url || item.file_url || item.fileUrl || item.image || item.image_url || item.imageUrl || item.src || '';
  };
  const firstFromArray = (...arrays) => arrays
    .flatMap(item => Array.isArray(item) ? item : [])
    .map(asImageValue)
    .find(Boolean) || '';
  const candidates = [
    value.imageUrl,
    value.primaryImage,
    value.mainImage,
    value.pictureUrl,
    value.pictures?.[0],
    value.images?.[0],
    value.images360?.[0],
    raw.primary_image,
    raw.primaryImage,
    raw.image,
    raw.image_url,
    raw.imageUrl,
    raw.main_image,
    raw.mainImage,
    raw.picture_url,
    raw.pictureUrl,
    raw.photo,
    raw.photo_url,
    raw.photoUrl,
    asImageValue(raw.marketing_price?.image),
    asImageValue(raw.marketingPrice?.image),
    asImageValue(raw.media?.main_image),
    asImageValue(raw.media?.mainImage),
    firstFromArray(
      raw.primary_image,
      raw.primaryImage,
      raw.images,
      raw.images360,
      raw.pictures,
      raw.picture,
      raw.color_image,
      raw.colorImage,
      raw.image_group,
      raw.imageGroup,
      raw.media?.images,
      raw.media?.pictures
    )
  ];
  return normalizeOzonImageUrl(candidates.find(Boolean) || '');
}

function normalizeOzonImageUrl(url = '') {
  const text = normalizeOzonText(url);
  if (!text) return '';
  if (text.startsWith('//')) return `https:${text}`;
  if (/^http:\/\//i.test(text)) return text.replace(/^http:\/\//i, 'https://');
  return text;
}

function renderOzonImageThumb(url, className = 'ozon-product-thumb') {
  const safeUrl = normalizeOzonImageUrl(url);
  return safeUrl
    ? `<img class="${escapeHtml(className)}" src="${escapeHtml(safeUrl)}" alt="">`
    : `<div class="${escapeHtml(className)} ozon-thumb-placeholder">无图</div>`;
}

function getOzonProductStatusSummary() {
  return (ozonState.products || []).reduce((acc, product) => {
    const status = product.visibility || product.status || product.rawData?.visibility || 'unknown';
    acc[status] = (acc[status] || 0) + 1;
    return acc;
  }, {});
}

function updateOzonProductSummary() {
  const el = document.getElementById('ozonProductStatusSummary');
  if (!el) return;
  const summary = getOzonProductStatusSummary();
  const rows = Object.entries(summary);
  el.innerHTML = rows.length
    ? `<div class="ozon-list-title">商品状态</div><div class="ozon-list-meta">${rows.map(([status, count]) => `${escapeHtml(status)}：${escapeHtml(count)}`).join('<br>')}</div>`
    : '<div class="ozon-list-title">商品状态</div><div class="ozon-list-meta">暂无商品状态数据</div>';
}

function getOzonProductUrl(product = {}) {
  const id = product.sku || product.productId || product.rawData?.sku || product.rawData?.product_id || '';
  return id ? `https://www.ozon.ru/search/?text=${encodeURIComponent(id)}` : 'https://www.ozon.ru/';
}

function buildCsv(rows = [], headers = []) {
  const escapeCell = (value) => `"${String(value ?? '').replace(/"/g, '""')}"`;
  return [
    headers.map(header => escapeCell(header.label)).join(','),
    ...rows.map(row => headers.map(header => escapeCell(typeof header.value === 'function' ? header.value(row) : row[header.value])).join(','))
  ].join('\r\n');
}

function downloadOzonCsv(filename, rows, headers) {
  const csv = buildCsv(rows, headers);
  const blob = new Blob([`\uFEFF${csv}`], { type: 'text/csv;charset=utf-8' });
  triggerBrowserDownload(filename, blob);
}

function getSelectedOzonCollectionIds() {
  return Array.from(document.querySelectorAll('[data-ozon-link-check]:checked'))
    .map(input => Number(input.dataset.ozonLinkCheck || 0))
    .filter(id => Number.isInteger(id) && id > 0);
}

function rememberOzonCheckedState() {
  ozonState.selectedCollectionIds = new Set(getSelectedOzonCollectionIds().map(String));
}

function renderOzonAccounts() {
  const list = document.getElementById('ozonAccountList');
  const count = document.getElementById('ozonAccountCount');
  const listCount = document.getElementById('ozonAccountListCount');
  if (count) count.textContent = String(ozonState.accounts.length || 0);
  if (listCount) listCount.textContent = `共 ${ozonState.accounts.length || 0} 个`;
  if (!list) return;
  const keyword = normalizeOzonText(document.getElementById('ozonAccountKeywordInput')?.value || ozonState.accountKeyword).toLowerCase();
  ozonState.accountKeyword = keyword;
  syncOzonAccountSelects();
  const rows = (ozonState.accounts || []).filter(account => {
    if (!keyword) return true;
    return [account.title, account.clientId, account.status, account.apiKeyMasked].some(value => String(value || '').toLowerCase().includes(keyword));
  });
  if (listCount) listCount.textContent = keyword ? `共 ${rows.length} / ${ozonState.accounts.length || 0} 个` : `共 ${rows.length} 个`;
  if (!rows.length) {
    list.innerHTML = '<div class="account-shop-table-wrap"><table class="account-shop-table"><tbody><tr><td style="padding:24px 14px;text-align:center;color:#64748b;">还没有绑定 OZON 店铺</td></tr></tbody></table></div>';
    return;
  }
  list.innerHTML = `<div class="account-shop-table-wrap"><table class="account-shop-table"><thead><tr>
    <th style="width:48px;text-align:center;">序号</th><th>店铺</th><th>授权状态</th><th>操作</th>
  </tr></thead><tbody>${rows.map((account, index) => {
    const active = account.status !== 'disabled';
    const statusLabel = active ? '已授权' : '已停用';
    const statusBg = active ? '#dcfce7' : '#fee2e2';
    const statusColor = active ? '#166534' : '#b91c1c';
    return `<tr>
      <td class="account-shop-index">${index + 1}</td>
      <td colspan="3">
        <div class="account-shop-mainline">
          <div class="account-shop-name">
            <span>${escapeHtml(account.title || 'OZON 店铺')}</span>
          </div>
          <div class="account-shop-actions">
            <button class="btn btn-default btn-sm" type="button" data-ozon-delete-account-id="${escapeHtml(account.id)}">删除</button>
          </div>
        </div>
        <div class="account-shop-statusline">
          <span style="display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:${statusBg};color:${statusColor};font-size:12px;font-weight:800;">${escapeHtml(statusLabel)}</span>
          <span class="account-shop-meta">授权尾号 ${escapeHtml(account.apiKeyMasked || '--')}</span>
        </div>
      </td>
    </tr>`;
  }).join('')}</tbody></table></div>`;
  list.querySelectorAll('[data-ozon-delete-account-id]').forEach(button => {
    button.addEventListener('click', async () => {
      const id = button.dataset.ozonDeleteAccountId;
      if (!id || !window.confirm('确定删除这个 OZON 店铺授权吗？')) return;
      try {
        await ozonRequest(`/accounts/${encodeURIComponent(id)}`, { method: 'DELETE' });
        if (String(ozonState.selectedAccountId) === String(id)) ozonState.selectedAccountId = '';
        setOzonResult('OZON 店铺授权已删除', 'success');
        await loadOzonDashboard();
      } catch (err) {
        setOzonResult(err.message || '删除失败', 'error');
      }
    });
  });
}

function renderOzonOverview() {
  const overview = ozonState.overview || {};
  const stats = overview.stats || {};
  const quality = overview.quality || {};
  const fulfillment = quality.fulfillment || {};
  const account = overview.selectedAccount || null;
  const user = getCurrentServerUser();
  const setText = (id, value) => {
    const el = document.getElementById(id);
    if (el) el.textContent = value;
  };
  const orderDailyRows = Array.isArray(overview.orderDaily) ? overview.orderDaily : [];
  const todayOrders = Number(stats.todayOrders || 0) || 0;
  const todayAmount = Number(stats.todayAmount || 0) || 0;
  const monthOrders = Number(stats.monthOrders || 0) || 0;
  const monthAmount = Number(stats.monthAmount || 0) || 0;
  const accountCount = Number(stats.accountCount || ozonState.accounts.length || 0) || 0;
  const orderSyncAt = stats.lastOrderCacheAt || stats.lastSyncAt || account?.lastSyncAt;
  setText('ozonTodayOrders', String(todayOrders));
  setText('ozonSevenDayOrders', String(stats.sevenDayOrders || 0));
  setText('ozonTotalOrders', String(stats.totalOrders || 0));
  setText('ozonTodayAmount', formatOzonAmount(todayAmount, 'RUB'));
  setText('ozonMonthQty', String(monthOrders || 0));
  setText('ozonMonthAmount', formatOzonAmount(monthAmount || 0, 'RUB'));
  setText('ozonSevenDayAmount', formatOzonAmount(stats.sevenDayAmount || 0));
  setText('ozonProductCount', String(stats.productCount || ozonState.products.length || 0));
  setText('ozonCollectionCount', String(stats.collectionCount || 0));
  setText('ozonSelectedCount', String(stats.selectedCount || 0));
  setText('ozonReadyCount', String(stats.readyCount || 0));
  setText('ozonListedCount', String(stats.listedCount || 0));
  setText('ozonAccountCount', String(accountCount));
  setText('ozonAccountSummaryTitle', user?.nickname || user?.realName || user?.name || user?.phone || 'OZON 工作台');
  setText('ozonAccountApiStatus', accountCount ? `API授权：${accountCount}个店铺` : 'API授权：未绑定');
  setText('ozonLoginPhone', user?.phone || '—');
  setText('ozonLoginNickname', user?.nickname || user?.realName || user?.name || '—');
  setText('ozonQualityVisibleRate', `${quality.visibleRate || 0}%`);
  setText('ozonQualityVisibleSub', `销售中 ${quality.visibleProducts || 0} / 商品 ${quality.totalProducts || stats.productCount || 0}`);
  setText('ozonQualityNotSelling', String(quality.notSellingProducts || 0));
  setText('ozonQualityPriceRed', String(quality.priceRiskProducts || quality.priceIndexCounts?.red || 0));
  setText('ozonQualityLowImage', String((quality.lowImageProducts || 0) + (quality.noImageProducts || 0)));
  setText('ozonFulfillmentPackaging', String(fulfillment.awaitingPackaging || 0));
  setText('ozonFulfillmentDeliver', String(fulfillment.awaitingDeliver || 0));
  setText('ozonFulfillmentDelivering', String(fulfillment.delivering || 0));
  setText('ozonFulfillmentCancelled', String(fulfillment.cancelled || 0));
  renderOzonHomeSalesChart(orderDailyRows);
  renderOzonHomeShopSalesDistribution(Array.isArray(overview.accountSales) ? overview.accountSales : Array.isArray(overview.salesByAccount) ? overview.salesByAccount : [], ozonState.accounts);
  renderOzonHomeAuthorizedAccounts(ozonState.accounts);
  const statusEl = document.getElementById('ozonAccountBindStatus');
  if (statusEl) {
    const connected = Boolean(accountCount);
    statusEl.textContent = connected ? '已绑定' : '未绑定';
    statusEl.className = connected ? 'status-badge status-connected' : 'status-badge status-disconnected';
  }
  const statusList = document.getElementById('ozonOrderStatusList');
  if (statusList) {
    const rows = Array.isArray(overview.orderStatus) ? overview.orderStatus : [];
    statusList.innerHTML = rows.length
      ? rows.map(row => `<div class="ozon-list-item"><div class="ozon-list-title">${escapeHtml(row.label || getOzonOrderStatusLabel(row.status) || '未知状态')}</div><div class="ozon-list-meta">缓存订单数：${escapeHtml(row.count || 0)}</div></div>`).join('')
      : '<div class="ozon-empty">暂无订单状态数据</div>';
  }
  const riskList = document.getElementById('ozonQualityRiskList');
  if (riskList) {
    const riskItems = Array.isArray(quality.riskItems) ? quality.riskItems : [];
    riskList.innerHTML = riskItems.length
      ? riskItems.slice(0, 6).map(item => `<div class="ozon-list-item"><div class="ozon-list-title">${escapeHtml(item.name || 'OZON 商品')}</div><div class="ozon-list-meta">Offer: ${escapeHtml(item.offerId || '--')} · SKU: ${escapeHtml(item.sku || '--')} · ${escapeHtml(item.visibility || 'UNKNOWN')}<br>${escapeHtml((item.reasons || []).join(' / '))}</div></div>`).join('')
      : '<div class="ozon-empty">暂无商品质量风险</div>';
  }
}

function renderOzonProducts() {
  const wrap = document.getElementById('ozonProductList');
  const count = document.getElementById('ozonProductCount');
  if (count) count.textContent = String(ozonState.products.length || 0);
  syncOzonAccountSelects();
  updateOzonProductStatusTabs();
  updateOzonProductSummary();
  if (!wrap) return;
  if (!ozonState.products.length) {
    ozonState.productSelectedIds.clear();
    updateOzonProductSelectionInfo();
    updateOzonPagination('product');
    wrap.innerHTML = '<div class="ozon-empty">\u6682\u65e0\u5546\u54c1\u7f13\u5b58\uff0c\u8bf7\u5148\u540c\u6b65 OZON \u5546\u54c1</div>';
    return;
  }
  const visibleKeys = new Set(ozonState.products.map(getOzonProductKey).filter(Boolean));
  ozonState.productSelectedIds = new Set(Array.from(ozonState.productSelectedIds).filter(id => visibleKeys.has(id)));
  wrap.innerHTML = `<table class="ozon-table ozon-product-table"><thead><tr>
    <th><input type="checkbox" id="ozonProductSelectAllCurrentPage" aria-label="全选当前页商品"></th>
    <th>商品信息</th><th>类目佣金</th><th>店铺</th><th>状态</th><th>价格</th><th>库存</th><th>重量</th><th>货源信息</th><th>创建日期</th><th>操作</th>
  </tr></thead><tbody>${ozonState.products.map(product => {
    const key = getOzonProductKey(product);
    const checked = ozonState.productSelectedIds.has(key) ? 'checked' : '';
    const ui = normalizeOzonProductForUi(product);
    const priceValue = parseOzonNumber(ui.price.price);
    const commissionPercent = parseOzonNumber(ui.commission.percent);
    const commissionAmount = parseOzonNumber(ui.commission.amount)
      ?? (Number.isFinite(priceValue) && Number.isFinite(commissionPercent) ? priceValue * commissionPercent / 100 : null);
    const commissionText = Number.isFinite(commissionPercent) ? `${commissionPercent.toFixed(commissionPercent % 1 === 0 ? 0 : 2)}%` : '--';
    const sourceUrl = ui.source.url ? `<a href="${escapeHtml(ui.source.url)}" target="_blank" rel="noopener">打开货源</a>` : '--';
    const errorHtml = ui.errorText ? `<div class="ozon-product-error">${escapeHtml(ui.errorText)}</div>` : '';
    return `<tr>
      <td><input type="checkbox" data-ozon-product-check="${escapeHtml(key)}" ${checked}></td>
      <td><div class="ozon-product-cell">${ui.image ? `<img class="ozon-product-thumb" src="${escapeHtml(ui.image)}" alt="">` : '<div class="ozon-product-thumb"></div>'}<div><div class="ozon-product-title">${escapeHtml(ui.title)}</div><div class="ozon-product-meta">货号: <span class="ozon-mono">${escapeHtml(ui.offerId || '--')}</span><br>SKU: <span class="ozon-mono">${escapeHtml(ui.sku || '--')}</span><br>Product ID: <span class="ozon-mono">${escapeHtml(ui.productId || '--')}</span></div></div></div></td>
      <td><div class="ozon-product-meta">类目：${escapeHtml(ui.category)}<br>rFBS: <strong>${escapeHtml(commissionText)}</strong><br>佣金: <strong>${escapeHtml(formatOzonAmount(commissionAmount, ui.price.currency || 'RUB'))}</strong></div></td>
      <td><span style="color:#4f46e5;font-weight:800;">[${escapeHtml(ui.accountTitle || 'OZON')}]</span></td>
      <td><span class="ozon-product-status ${escapeHtml(ui.statusClass)}">${escapeHtml(ui.statusLabel)}</span>${errorHtml}</td>
      <td><div class="ozon-product-price">销售价：<strong>${escapeHtml(formatOzonAmount(ui.price.price, ui.price.currency || 'RUB'))}</strong><br><span class="ozon-product-muted">划线价：${escapeHtml(ui.price.oldPrice ? formatOzonAmount(ui.price.oldPrice, ui.price.currency || 'RUB') : '--')}</span><br><span class="ozon-product-muted">最低价：${escapeHtml(ui.price.minPrice ? formatOzonAmount(ui.price.minPrice, ui.price.currency || 'RUB') : '未设置')}</span><br><span class="promo">促销后：${escapeHtml(ui.price.promoPrice ? formatOzonAmount(ui.price.promoPrice, ui.price.currency || 'RUB') : formatOzonAmount(ui.price.price, ui.price.currency || 'RUB'))}</span><br><span class="ozon-product-status ${escapeHtml(ui.priceIndex.tone)}" style="margin-top:4px;">价格指数：${escapeHtml(ui.priceIndex.label)}</span></div></td>
      <td><div class="ozon-product-stock"><strong>${escapeHtml(formatOzonNumber(ui.stock.available) || '--')}</strong><br><span class="ozon-product-muted">预留 ${escapeHtml(formatOzonNumber(ui.stock.reserved) || '0')}</span><br><span class="ozon-product-muted">仓库 ${escapeHtml(ui.stock.warehouses || 0)}</span></div></td>
      <td><div class="ozon-product-weight"><strong>${getOzonWeightText(product)}</strong></div></td>
      <td><div class="ozon-list-meta">采购价格：${escapeHtml(ui.source.purchasePrice ? formatOzonMoney(ui.source.purchasePrice, '￥') : '--')}<br>货源地址：${sourceUrl}<br>货源备注：${escapeHtml(ui.source.remark || '--')}</div></td>
      <td><div class="ozon-list-meta">${escapeHtml(formatOzonDateTime(ui.createdAt))}</div></td>
      <td><div class="ozon-product-actions"><button class="btn btn-sm primary-action" type="button" data-ozon-product-seller="${escapeHtml(ui.productId || ui.offerId || ui.sku || '')}">编辑</button><button class="btn btn-default btn-sm" type="button" data-ozon-product-copy="${escapeHtml(ui.offerId || ui.sku || ui.productId || '')}">复制</button><button class="btn btn-default btn-sm" type="button" data-ozon-product-open="${escapeHtml(getOzonProductUrl(product))}">更多</button></div></td>
    </tr>`;
  }).join('')}</tbody></table>`;
  const selectAll = wrap.querySelector('#ozonProductSelectAllCurrentPage');
  const checks = Array.from(wrap.querySelectorAll('[data-ozon-product-check]'));
  if (selectAll) {
    selectAll.checked = checks.length > 0 && checks.every(input => input.checked);
    selectAll.indeterminate = checks.some(input => input.checked) && !selectAll.checked;
    selectAll.addEventListener('change', () => {
      checks.forEach(input => {
        input.checked = selectAll.checked;
        if (input.checked) ozonState.productSelectedIds.add(String(input.dataset.ozonProductCheck || ''));
        else ozonState.productSelectedIds.delete(String(input.dataset.ozonProductCheck || ''));
      });
      updateOzonProductSelectionInfo();
    });
  }
  checks.forEach(input => {
    input.addEventListener('change', () => {
      if (input.checked) ozonState.productSelectedIds.add(String(input.dataset.ozonProductCheck || ''));
      else ozonState.productSelectedIds.delete(String(input.dataset.ozonProductCheck || ''));
      updateOzonProductSelectionInfo();
      if (selectAll) {
        selectAll.checked = checks.length > 0 && checks.every(item => item.checked);
        selectAll.indeterminate = checks.some(item => item.checked) && !selectAll.checked;
      }
    });
  });
  wrap.querySelectorAll('[data-ozon-product-copy]').forEach(button => {
    button.addEventListener('click', () => copyOzonText(button.dataset.ozonProductCopy || ''));
  });
  wrap.querySelectorAll('[data-ozon-product-open]').forEach(button => {
    button.addEventListener('click', () => window.open(button.dataset.ozonProductOpen || 'https://www.ozon.ru/', '_blank'));
  });
  wrap.querySelectorAll('[data-ozon-product-seller]').forEach(button => {
    button.addEventListener('click', () => window.open('https://seller.ozon.ru/app/products', '_blank'));
  });
  updateOzonProductSelectionInfo();
  updateOzonPagination('product');
}

function renderOzonHomeSalesChart(dailyRows = []) {
  const wraps = [
    document.getElementById('ozonHomeDailySalesBarChart'),
    document.getElementById('ozonSalesBarChart')
  ].filter(Boolean);
  if (!wraps.length) return;
  const rows = (Array.isArray(dailyRows) ? dailyRows : []).slice(-28);
  if (!rows.length) {
    wraps.forEach(wrap => {
      wrap.innerHTML = '<div class="ozon-empty">暂无销量趋势数据，请先同步订单。</div>';
    });
    return;
  }
  const maxQty = Math.max(1, ...rows.map(row => Number(row.orders ?? row.qty ?? 0)));
  const html = rows.map(row => {
    const qty = Number(row.orders ?? row.qty ?? 0);
    const amount = Number(row.amount || 0);
    const height = Math.max(qty ? 8 : 2, Math.round((qty / maxQty) * 180));
    const dateText = String(row.date || '').slice(5).replace('-', '/');
    return `<div class="ozon-sales-bar ${qty ? '' : 'is-zero'}" title="${escapeHtml(row.date || '')} 订单 ${escapeHtml(qty)} / 销售额 ${escapeHtml(formatOzonAmount(amount, 'RUB'))}">
      <div class="ozon-sales-bar-stack">
        <div class="ozon-sales-bar-value">${escapeHtml(qty)}</div>
        <div class="ozon-sales-bar-fill" style="height:${height}px"></div>
      </div>
      <div class="ozon-sales-bar-date">${escapeHtml(dateText || '--')}</div>
    </div>`;
  }).join('');
  wraps.forEach(wrap => {
    wrap.innerHTML = `<div class="ozon-sales-bars">${html}</div>`;
  });
}

function renderOzonHomeAuthorizedAccounts(accounts = []) {
  const listEl = document.getElementById('ozonHomeAuthorizedAccountList');
  const summaryEl = document.getElementById('ozonHomeAuthorizedAccountSummary');
  if (!listEl && !summaryEl) return;

  const rows = Array.isArray(accounts) ? accounts : [];
  const visibleRows = rows.filter(account => account && account.status !== 'deleted');

  if (summaryEl) {
    summaryEl.textContent = `共 ${visibleRows.length} 个`;
  }

  if (!listEl) return;
  if (!visibleRows.length) {
    listEl.innerHTML = '<div class="ozon-home-authorized-empty">还没有绑定 OZON 店铺</div>';
    return;
  }

  const sortedRows = [...visibleRows].sort((a, b) => {
    const ta = String(a.title || a.clientId || '').toLowerCase();
    const tb = String(b.title || b.clientId || '').toLowerCase();
    return ta.localeCompare(tb, 'zh-Hans-CN');
  });

  listEl.innerHTML = sortedRows.map((account, index) => {
    const active = account.status !== 'disabled';
    const statusLabel = active ? '已授权' : '已停用';
    const statusBg = active ? '#dcfce7' : '#fee2e2';
    const statusColor = active ? '#166534' : '#b91c1c';
    const syncText = account.lastSyncAt ? `同步 ${formatOzonDateTime(account.lastSyncAt)}` : '暂无同步记录';
    const apiTail = account.apiKeyMasked || '--';
    const currency = account.currencyCode || 'RUB';
    return `
      <div class="ozon-home-authorized-row" title="${escapeHtml(account.title || 'OZON 店铺')}">
        <div class="ozon-home-authorized-main">
          <div class="ozon-home-authorized-name">${escapeHtml(account.title || 'OZON 店铺')}</div>
          <div class="ozon-home-authorized-meta">授权尾号 ${escapeHtml(apiTail)} · ${escapeHtml(currency)}</div>
        </div>
        <div class="ozon-home-authorized-right">
          <span style="display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:${statusBg};color:${statusColor};font-size:12px;font-weight:800;">${escapeHtml(statusLabel)}</span>
          <div class="ozon-home-authorized-time">#${index + 1} · ${escapeHtml(syncText)}</div>
        </div>
      </div>`;
  }).join('');
}

function renderOzonHomeShopSalesDistribution(accountSalesRows = [], fallbackAccounts = []) {
  const summaryEl = document.getElementById('ozonHomeShopSalesSummary');
  const listEl = document.getElementById('ozonHomeShopSalesList');
  const donutEl = document.getElementById('ozonHomeShopSalesDonut');
  if (!summaryEl && !listEl && !donutEl) return;

  const rowsFromApi = Array.isArray(accountSalesRows) ? accountSalesRows : [];
  const accounts = Array.isArray(fallbackAccounts) ? fallbackAccounts : [];
  const byAccountId = new Map(rowsFromApi.map(row => [String(row.accountId ?? row.ozonAccountId ?? row.id ?? ''), row]));
  const mergedRows = accounts.length
    ? accounts.map(account => {
        const key = String(account.id ?? '');
        const row = byAccountId.get(key) || {};
        return {
          accountId: account.id,
          accountTitle: account.title || row.accountTitle || `店铺 ${key || '--'}`,
          todayOrders: Number(row.todayOrders || 0),
          todayAmount: Number(row.todayAmount || 0),
          monthOrders: Number(row.monthOrders || 0),
          monthAmount: Number(row.monthAmount || 0),
          currencyCode: row.currencyCode || account.currencyCode || 'RUB'
        };
      })
    : rowsFromApi.map(row => ({
        accountId: row.accountId ?? row.ozonAccountId ?? row.id ?? '',
        accountTitle: row.accountTitle || row.title || 'OZON 店铺',
        todayOrders: Number(row.todayOrders || 0),
        todayAmount: Number(row.todayAmount || 0),
        monthOrders: Number(row.monthOrders || 0),
        monthAmount: Number(row.monthAmount || 0),
        currencyCode: row.currencyCode || 'RUB'
      }));

  const mergedKeySet = new Set(mergedRows.map(row => String(row.accountId ?? row.accountTitle ?? '')));
  rowsFromApi.forEach(row => {
    const key = String(row.accountId ?? row.ozonAccountId ?? row.id ?? '');
    if (mergedKeySet.has(key)) return;
    mergedRows.push({
      accountId: key,
      accountTitle: row.accountTitle || row.title || 'OZON 店铺',
      todayOrders: Number(row.todayOrders || 0),
      todayAmount: Number(row.todayAmount || 0),
      monthOrders: Number(row.monthOrders || 0),
      monthAmount: Number(row.monthAmount || 0),
      currencyCode: row.currencyCode || 'RUB'
    });
  });

  mergedRows.sort((a, b) => Number(b.monthOrders || 0) - Number(a.monthOrders || 0) || String(a.accountTitle || '').localeCompare(String(b.accountTitle || ''), 'zh-Hans-CN'));
  const activeRows = mergedRows.filter(row => Number(row.monthOrders || 0) > 0);
  const totalMonthOrders = mergedRows.reduce((sum, row) => sum + Number(row.monthOrders || 0), 0);

  if (summaryEl) {
    summaryEl.textContent = mergedRows.length
      ? `共 ${mergedRows.length} 个店铺 · 本月 ${totalMonthOrders.toLocaleString()} 单`
      : '暂无店铺数据';
  }

  if (donutEl) {
    if (!activeRows.length || totalMonthOrders <= 0) {
      donutEl.style.background = 'conic-gradient(#cbd5e1 0 100%)';
    } else {
      const palette = ['#3b82f6', '#10b981', '#8b5cf6', '#f59e0b', '#ef4444', '#06b6d4', '#f97316', '#6366f1'];
      const slices = activeRows.slice(0, 6).map((row, index) => ({
        label: row.accountTitle || `店铺 ${index + 1}`,
        value: Number(row.monthOrders || 0),
        color: palette[index % palette.length]
      }));
      if (activeRows.length > 6) {
        const otherValue = activeRows.slice(6).reduce((sum, row) => sum + Number(row.monthOrders || 0), 0);
        if (otherValue > 0) slices.push({ label: '其他', value: otherValue, color: '#94a3b8' });
      }
      let cursor = 0;
      const gradient = slices
        .filter(slice => Number(slice.value || 0) > 0)
        .map((slice) => {
          const start = cursor;
          cursor += (Number(slice.value || 0) / totalMonthOrders) * 100;
          return `${slice.color} ${start.toFixed(2)}% ${cursor.toFixed(2)}%`;
        })
        .join(', ');
      donutEl.style.background = gradient ? `conic-gradient(${gradient})` : 'conic-gradient(#cbd5e1 0 100%)';
    }
  }

  if (!listEl) return;
  if (!mergedRows.length) {
    listEl.innerHTML = '<div class="dh-shop-sales-row"><div><div class="dh-shop-sales-name">暂无店铺数据</div><div class="dh-shop-sales-meta">绑定店铺后会显示销量分布</div></div><div class="dh-shop-sales-value">0</div></div>';
    return;
  }
  listEl.innerHTML = mergedRows.map((row) => {
    const monthOrders = Number(row.monthOrders || 0);
    const todayOrders = Number(row.todayOrders || 0);
    const monthAmount = Number(row.monthAmount || 0);
    const currency = row.currencyCode || 'RUB';
    return `
      <div class="dh-shop-sales-row" title="${escapeHtml(row.accountTitle || 'OZON 店铺')}">
        <div>
          <div class="dh-shop-sales-name">${escapeHtml(row.accountTitle || 'OZON 店铺')}</div>
          <div class="dh-shop-sales-meta">本月 ${escapeHtml(monthOrders.toLocaleString())} 单${todayOrders ? ` · 今日 ${escapeHtml(todayOrders.toLocaleString())} 单` : ''}${monthAmount ? ` · 销售额 ${escapeHtml(formatOzonAmount(monthAmount, currency))}` : ''}</div>
        </div>
        <div class="dh-shop-sales-value">${escapeHtml(monthOrders.toLocaleString())}</div>
      </div>`;
  }).join('');
}

function setOzonText(id, value) {
  const el = document.getElementById(id);
  if (el) el.textContent = value;
}

function setOzonHtml(id, html) {
  const el = document.getElementById(id);
  if (el) el.innerHTML = html;
}

function getOzonAccountLabel(accountId = '') {
  const id = normalizeOzonText(accountId);
  if (!id) return '全部店铺';
  return ozonState.accounts.find(account => String(account.id) === id)?.title || `店铺 ${id}`;
}

function buildOzonHydrationText(hydration = {}) {
  const attempted = Number(hydration.attempted || 0);
  const saved = Number(hydration.saved || 0);
  const errors = Array.isArray(hydration.errors) ? hydration.errors.length : 0;
  if (!attempted && !saved && !errors) return '商品资料缓存正常';
  return `已自动回查商品详情 ${attempted} 条，补充/更新 ${saved} 条${errors ? `，失败 ${errors} 批` : ''}`;
}

function renderOzonMetaItems(items = []) {
  return items
    .filter(Boolean)
    .map(item => `<span>${item}</span>`)
    .join('');
}

function getOzonTrendLabel(status = '') {
  const labels = { up: '上涨', down: '下滑', flat: '持平' };
  return labels[status] || '持平';
}

function getOzonTrendClass(status = '') {
  if (status === 'up') return 'ozon-trend-up';
  if (status === 'down') return 'ozon-trend-down';
  return 'ozon-trend-flat';
}

function renderOzonAnalysisMetric(label, value) {
  return `<div class="ozon-analysis-metric"><span>${escapeHtml(label)}</span><strong>${escapeHtml(value ?? '--')}</strong></div>`;
}

function renderOzonProblemLines(items = [], fallback = '暂无明显问题') {
  const lines = (Array.isArray(items) ? items : [])
    .filter(Boolean)
    .slice(0, 3);
  if (!lines.length) return `<span>${escapeHtml(fallback)}</span>`;
  return lines.map(text => `<span>• ${escapeHtml(text)}</span>`).join('');
}

function buildOzonTitleBasisText(diag = {}, sales = {}, ui = {}) {
  const basis = Array.isArray(diag.basis) ? diag.basis.filter(Boolean) : [];
  if (basis.length) return basis.slice(0, 3).join('；');
  const trend = Number(sales.trend28Percent || sales.trend7Percent || 0);
  const trendText = trend < -5 ? '当前销量趋势偏弱，需要提升标题核心词覆盖与卡片点击率' : '当前销量趋势可观察，标题应继续强化高相关核心词';
  const statusText = ui.statusLabel ? `商品状态为${ui.statusLabel}` : '结合商品当前状态';
  return `${statusText}，参考 OZON 标题规范使用“核心类目词 + 规格/材质 + 适用场景/卖点”的结构；${trendText}，让搜索词、类目词和买家需求更集中。`;
}

function getOzonDiagnosisKey(item = {}, index = 0) {
  const ui = normalizeOzonProductForUi(item.product || {});
  return String(ui.productId || ui.offerId || ui.sku || ui.title || `item-${index + 1}`);
}

function getOzonAiTitleState(diag = {}) {
  if (diag.aiTitleStatus === 'loading') {
    return {
      title: 'AI 正在生成建议标题...',
      basis: '正在根据 OZON 标题规范、销量趋势和关键词热度代理生成标题。',
      canCopy: false,
      unavailable: false
    };
  }
  if (diag.aiTitleStatus === 'success' && diag.aiOptimizedTitle) {
    return {
      title: diag.aiOptimizedTitle,
      basis: diag.aiTitleBasis || diag.aiKeywordBasis || 'AI 已根据 OZON 官方标题规范、商品数据和销量趋势生成。',
      canCopy: true,
      unavailable: false
    };
  }
  const reason = diag.aiTitleError || 'AI不可用：请稍后再试';
  return {
    title: reason,
    basis: reason,
    canCopy: false,
    unavailable: true
  };
}

function buildOzonTitleSuggestionPayload(items = []) {
  return items.slice(0, 60).map((item, index) => {
    const ui = normalizeOzonProductForUi(item.product || {});
    const raw = item.product?.rawData || item.product?.raw || {};
    const diag = item.diagnosis || {};
    const sales = item.sales || {};
    const attributes = [];
    const rawAttributes = raw.attributes || raw.attribute_values || raw.attributes_values || raw.characteristics || [];
    if (Array.isArray(rawAttributes)) {
      rawAttributes.slice(0, 12).forEach(attr => {
        if (typeof attr === 'string') attributes.push(attr);
        else {
          const name = attr?.name || attr?.attribute_name || attr?.attributeName || attr?.id || '';
          const value = attr?.value || attr?.values?.[0]?.value || attr?.values?.[0] || attr?.text || '';
          const text = [name, value].filter(Boolean).join(': ');
          if (text) attributes.push(text);
        }
      });
    }
    return {
      key: getOzonDiagnosisKey(item, index),
      title: ui.title,
      category: ui.category,
      productId: ui.productId,
      offerId: ui.offerId,
      sku: ui.sku,
      status: ui.statusLabel,
      attributes,
      sales: {
        todayQty: sales.todayQty || 0,
        qty7: sales.qty7 || 0,
        qty28: sales.qty28 || 0,
        trend7Percent: sales.trend7Percent || 0,
        trend28Percent: sales.trend28Percent || 0,
        share28Percent: sales.share28Percent || 0
      },
      problems: (diag.problems || []).slice(0, 5),
      suggestions: (diag.suggestions || []).slice(0, 5)
    };
  });
}

function renderOzonOrderAnalysis() {
  const wrap = document.getElementById('ozonOrderAnalysisList');
  const analysis = ozonState.orderAnalysis || {};
  const summary = analysis.accountSalesAnalysis?.summary || analysis.summary || {};
  const todayQty = Number(summary.todayQty || 0);
  const monthAvg = Number(summary.monthAvg || 0);
  setOzonText('ozonAnalysisTodayQty', String(todayQty || 0));
  setOzonText('ozonAnalysisValidOrders', String(summary.validOrderCount || 0));
  setOzonText('ozonAnalysisQty28', String(summary.qty28 || summary.totalQty28 || 0));
  setOzonText('ozonAnalysisTrend28', `${Number(summary.trend28Percent || 0).toFixed(1)}%`);
  setOzonText('ozonAnalysisAmount28', formatOzonAmount(summary.totalAmount28 || 0, 'RUB'));
  setOzonText('ozonAnalysisWarningCount', String(summary.warningCount || 0));
  setOzonText('ozonAnalysisMultiOrders', String(summary.activeAccountCount || summary.accountCount || 0));
  if (!wrap) return;
  const accountRows = Array.isArray(analysis.accountSalesAnalysis?.items)
    ? analysis.accountSalesAnalysis.items
    : Array.isArray(analysis.accountSales)
      ? analysis.accountSales
      : Array.isArray(analysis.salesByAccount)
        ? analysis.salesByAccount
        : Array.isArray(analysis.items)
          ? analysis.items
          : [];
  const items = accountRows;
  const total = Number(summary.activeAccountCount || summary.accountCount || items.length || ozonState.orderAnalysisTotal || 0);
  const page = Math.max(1, Number(ozonState.orderAnalysisPage || 1) || 1);
  const limit = Math.max(1, Number(ozonState.orderAnalysisLimit || 50) || 50);
  const start = (page - 1) * limit;
  const visibleItems = items.slice(start, start + limit);
  const accountId = document.getElementById('ozonOrderAnalysisAccountFilter')?.value || ozonState.orderAnalysisAccountId || '';
  const skuFilter = document.getElementById('ozonOrderAnalysisSkuInput')?.value?.trim() || ozonState.orderAnalysisSku || '';
  setOzonHtml('ozonOrderAnalysisMeta', renderOzonMetaItems([
    `统计范围：<strong>${escapeHtml(getOzonAccountLabel(accountId))}</strong>`,
    skuFilter ? `筛选：<strong>${escapeHtml(skuFilter)}</strong>` : '筛选：全部店铺',
    `今日销量：<strong>${escapeHtml(todayQty || 0)}</strong>`,
    `当月日均销量：<strong>${escapeHtml(monthAvg || 0)}</strong>`,
    `显示：<strong>${escapeHtml(Math.min(start + visibleItems.length, total || items.length))}</strong> / ${escapeHtml(total || items.length)} 条`,
    escapeHtml(buildOzonHydrationText(analysis.hydration || {}))
  ].map(item => item.replace(/^([^：]+)：/, '<b>$1：</b>'))));
  if (!items.length) {
    wrap.innerHTML = '<div class="ozon-empty">暂无销量分析数据。请先同步订单。</div>';
    return;
  }
  const rowsHtml = visibleItems.map(item => {
    const trend7 = Number(item.trend7Percent || 0);
    const trend28 = Number(item.trend28Percent || 0);
    const trend7Class = trend7 > 0 ? 'ozon-trend-up' : trend7 < 0 ? 'ozon-trend-down' : 'ozon-trend-flat';
    const trend28Class = trend28 > 0 ? 'ozon-trend-up' : trend28 < 0 ? 'ozon-trend-down' : 'ozon-trend-flat';
    const trend7Text = `${trend7 > 0 ? '+' : ''}${trend7.toFixed(1)}%`;
    const trend28Text = `${trend28 > 0 ? '+' : ''}${trend28.toFixed(1)}%`;
    return `<tr class="${item.alert ? 'is-warning' : ''}">
      <td><div class="ozon-list-title">${escapeHtml(item.accountTitle || item.name || item.title || 'OZON 店铺')}</div><div class="ozon-list-meta">${escapeHtml([item.accountId, item.currencyCode].filter(Boolean).join(' · ') || '--')}</div></td>
      <td>${escapeHtml(item.todayQty || 0)}</td>
      <td>${escapeHtml(item.monthAvg || 0)}</td>
      <td>${escapeHtml(item.qty7 || 0)}</td>
      <td>${escapeHtml(item.prevQty7 || 0)}</td>
      <td><span class="${trend7Class}">${escapeHtml(trend7Text)}</span></td>
      <td>${escapeHtml(item.qty28 || 0)}</td>
      <td>${escapeHtml(item.prevQty28 || 0)}</td>
      <td><span class="${trend28Class}">${escapeHtml(trend28Text)}</span></td>
      <td>${escapeHtml(item.share28Percent || 0)}%</td>
    </tr>`;
  }).join('');
  const totalRows = total || items.length;
  const maxPage = Math.max(1, Math.ceil(totalRows / limit));
  wrap.innerHTML = `<table class="ozon-table ozon-analysis-table">
    <thead>
      <tr>
        <th>连接/店铺</th>
        <th>今日销量</th>
        <th>当月日均销量</th>
        <th>近7日销量</th>
        <th>上个7日销量</th>
        <th>7日环比</th>
        <th>近28日销量</th>
        <th>上个28日销量</th>
        <th>28日环比</th>
        <th>销量占比</th>
      </tr>
    </thead>
    <tbody>${rowsHtml || '<tr><td colspan="10">暂无数据</td></tr>'}</tbody>
  </table>
  <div class="ozon-pagination" id="ozonOrderAnalysisPagination">
    <button type="button" id="ozonOrderAnalysisPrevPageBtn"${page <= 1 ? ' disabled' : ''}>上一页</button>
    <span id="ozonOrderAnalysisPageInfo">第 ${page} 页 / 共 ${maxPage} 页 · ${totalRows} 条</span>
    <button type="button" id="ozonOrderAnalysisNextPageBtn"${page >= maxPage ? ' disabled' : ''}>下一页</button>
  </div>`;
  wrap.querySelector('#ozonOrderAnalysisPrevPageBtn')?.addEventListener('click', () => {
    ozonState.orderAnalysisPage = Math.max(1, (ozonState.orderAnalysisPage || 1) - 1);
    loadOzonOrderAnalysis();
  });
  wrap.querySelector('#ozonOrderAnalysisNextPageBtn')?.addEventListener('click', () => {
    ozonState.orderAnalysisPage = Math.min(maxPage, (ozonState.orderAnalysisPage || 1) + 1);
    loadOzonOrderAnalysis();
  });
}

function getFilteredOzonDiagnosisItems() {
  const data = ozonState.productDiagnosis || {};
  let items = Array.isArray(data.items) ? data.items : [];
  const visibility = document.getElementById('ozonProductVisibilityFilter')?.value || ozonState.productVisibility || '';
  const risk = document.getElementById('ozonProductRiskFilter')?.value || ozonState.productDiagnosisRisk || '';
  const keyword = normalizeOzonText(document.getElementById('ozonProductDiagnosisKeywordInput')?.value || ozonState.productDiagnosisKeyword).toLowerCase();
  ozonState.productDiagnosisRisk = risk;
  ozonState.productDiagnosisKeyword = keyword;
  if (visibility) {
    items = items.filter(item => normalizeOzonProductForUi(item.product || {}).visibility === visibility);
  }
  if (risk === 'sales-warning') {
    items = items.filter(item => item.sales?.alert || Number(item.sales?.trend7Percent || 0) <= -20);
  } else if (risk === 'good') {
    items = items.filter(item => Number(item.diagnosis?.score || 0) >= 85);
  } else if (risk) {
    const riskCodeMap = { '高': 'high', '中': 'medium', '低': 'low' };
    items = items.filter(item => item.diagnosis?.riskCode === riskCodeMap[risk] || item.diagnosis?.riskLevel === risk);
  }
  if (keyword) {
    items = items.filter(item => {
      const ui = normalizeOzonProductForUi(item.product || {});
      const diag = item.diagnosis || {};
      return [
        ui.title, ui.productId, ui.offerId, ui.sku, ui.accountTitle,
        ...(diag.problems || []), ...(diag.suggestions || []), diag.optimizedTitle, diag.optimizedKeywords
      ].some(value => String(value || '').toLowerCase().includes(keyword));
    });
  }
  return items;
}

function renderOzonProductDiagnosis() {
  const wrap = document.getElementById('ozonProductDiagnosisList');
  const data = ozonState.productDiagnosis || {};
  const summary = data.summary || {};
  setOzonText('ozonProductAiAvgScore', String(summary.avgScore || 0));
  setOzonText('ozonProductAiHighRisk', String(summary.highRisk || 0));
  setOzonText('ozonProductAiMediumRisk', String(summary.mediumRisk || 0));
  setOzonText('ozonProductAiSalesWarnings', String(summary.salesWarnings || 0));
  setOzonText('ozonProductAiGood', String(summary.good || 0));
  if (!wrap) return;
  const items = getFilteredOzonDiagnosisItems();
  const rawItems = Array.isArray(data.items) ? data.items : [];
  const totalItems = Number(data.totalItems || data.summary?.totalProducts || rawItems.length || 0);
  const filteredTotal = Number(data.filteredItems || rawItems.length || 0);
  const renderLimit = 180;
  const visibleItems = items.slice(0, renderLimit);
  const missingImageCount = items.filter(item => !normalizeOzonProductForUi(item.product || {}).image).length;
  const accountId = document.getElementById('ozonProductDiagnosisAccountFilter')?.value || ozonState.productDiagnosisAccountId || ozonState.productAccountId || '';
  const skuFilter = document.getElementById('ozonProductDiagnosisSkuInput')?.value?.trim() || ozonState.productDiagnosisSku || '';
  const risk = document.getElementById('ozonProductRiskFilter')?.value || ozonState.productDiagnosisRisk || '';
  setOzonHtml('ozonProductDiagnosisMeta', renderOzonMetaItems([
    `统计范围：<strong>${escapeHtml(getOzonAccountLabel(accountId))}</strong>`,
    skuFilter ? `筛选：<strong>${escapeHtml(skuFilter)}</strong>` : '筛选：全部 SKU / Offer',
    risk ? `风险：<strong>${escapeHtml(risk)}</strong>` : '风险：全部',
    `显示：<strong>${escapeHtml(Math.min(visibleItems.length, items.length))}</strong> / ${escapeHtml(filteredTotal || items.length)} 个诊断`,
    filteredTotal && totalItems && filteredTotal !== totalItems ? `全量：${escapeHtml(totalItems)} 个` : '',
    rawItems.length !== items.length ? `当前结果：${escapeHtml(rawItems.length)} 个` : '',
    `缺主图：<strong>${escapeHtml(missingImageCount)}</strong> 个`,
    escapeHtml(buildOzonHydrationText(data.hydration || {}))
  ].filter(Boolean).map(item => item.replace(/^([^：]+)：/, '<b>$1：</b>'))));
  if (!items.length) {
    wrap.innerHTML = '<div class="ozon-empty">暂无匹配的商品诊断结果。请先同步商品，或调整筛选条件。</div>';
    return;
  }
  wrap.innerHTML = `<div class="ozon-diagnosis-list">${visibleItems.map((item, index) => {
    const product = item.product || {};
    const ui = normalizeOzonProductForUi(product);
    const diag = item.diagnosis || {};
    const sales = item.sales || {};
    const scoreTone = diag.tone === 'good' ? 'ozon-score-good' : diag.tone === 'warn' ? 'ozon-score-warn' : 'ozon-score-bad';
    const trendClass = getOzonTrendClass(sales.trendStatus);
    const image = renderOzonImageThumb(ui.image);
    const trend28 = Number(sales.trend28Percent || 0);
    const titleState = getOzonAiTitleState(diag);
    return `<div class="ozon-diagnosis-item">
      <div class="ozon-diagnosis-product">
        <div class="ozon-product-cell">${image}<div>
          <div class="ozon-product-title">${escapeHtml(ui.title)}</div>
          <div class="ozon-product-meta">Product ID: <span class="ozon-mono">${escapeHtml(ui.productId || '--')}</span><br>Offer: <span class="ozon-mono">${escapeHtml(ui.offerId || '--')}</span><br>SKU: <span class="ozon-mono">${escapeHtml(ui.sku || '--')}</span><br>${escapeHtml(ui.accountTitle || 'OZON')} · ${escapeHtml(ui.statusLabel || '未知状态')}</div>
        </div></div>
        <div class="ozon-diagnosis-summary">
          <span class="ozon-score-badge ${scoreTone}">${escapeHtml(diag.score ?? 0)}分</span>
          <span class="ozon-risk-badge ${scoreTone}">风险 ${escapeHtml(diag.riskLevel || '低')}</span>
          <span class="ozon-trend-pill ${trendClass}">${escapeHtml(getOzonTrendLabel(sales.trendStatus))}</span>
          <span class="ozon-diagnosis-kpi">图 <strong>${escapeHtml(diag.imageCount || 0)}</strong></span>
          <span class="ozon-diagnosis-kpi">属性 <strong>${escapeHtml(diag.attributeCount || 0)}</strong></span>
        </div>
      </div>
      <div class="ozon-diagnosis-content">
        <div class="ozon-diagnosis-two-col">
          <div class="ozon-diagnosis-column">
            <b>销量趋势</b>
            <span>今日 ${escapeHtml(sales.todayQty || 0)} / 7日 ${escapeHtml(sales.qty7 || 0)} / 28日 ${escapeHtml(sales.qty28 || 0)}</span>
            <span>7日环比 ${escapeHtml(Number(sales.trend7Percent || 0).toFixed(1))}% · 28日环比 ${escapeHtml(trend28.toFixed(1))}%</span>
          </div>
          <div class="ozon-diagnosis-column">
            <b>现存问题</b>
            ${renderOzonProblemLines(diag.problems || [])}
          </div>
        </div>
        <div class="ozon-diagnosis-column">
          <b>整改建议</b>
          <span>${escapeHtml((diag.suggestions || [])[0] || '保持观察')}</span>
          ${(diag.suggestions || []).slice(1, 3).map(text => `<span>• ${escapeHtml(text)}</span>`).join('')}
        </div>
        <div class="ozon-diagnosis-copy is-title-card">
          <strong>建议标题</strong>
          <span class="${titleState.unavailable ? 'ozon-ai-unavailable' : ''}">${escapeHtml(titleState.title)}</span>
          <strong>为什么更好</strong>
          <span class="ozon-title-basis">${escapeHtml(titleState.basis)}</span>
        </div>
      </div>
      <div class="ozon-diagnosis-actions">
        <button class="btn btn-primary btn-sm" type="button" data-ozon-diagnosis-detail="${index}">完整方案</button>
        <button class="btn btn-default btn-sm" type="button" data-ozon-copy-title="${escapeHtml(titleState.canCopy ? titleState.title : '')}" ${titleState.canCopy ? '' : 'disabled'}>复制标题</button>
      </div>
    </div>`;
  }).join('')}</div>`;
  wrap.querySelectorAll('[data-ozon-diagnosis-detail]').forEach(button => {
    button.addEventListener('click', () => showOzonDiagnosisDetail(items[Number(button.dataset.ozonDiagnosisDetail || 0)]));
  });
  wrap.querySelectorAll('[data-ozon-copy-title]').forEach(button => {
    button.addEventListener('click', () => copyOzonText(button.dataset.ozonCopyTitle || ''));
  });
}

function ensureOzonDiagnosisModal() {
  let modal = document.getElementById('ozonDiagnosisModal');
  if (modal) return modal;
  modal = document.createElement('div');
  modal.id = 'ozonDiagnosisModal';
  modal.style.cssText = 'position:fixed;inset:0;z-index:10000;display:none;align-items:center;justify-content:center;background:rgba(15,23,42,.45);padding:20px;';
  modal.setAttribute('aria-hidden', 'true');
  modal.innerHTML = `<div style="width:min(980px,96vw);max-height:86vh;overflow:auto;background:#fff;border-radius:8px;box-shadow:0 24px 70px rgba(15,23,42,.28);">
    <div style="display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--border);gap:12px;">
      <h3 style="margin:0;font-size:16px;color:var(--text-primary);">OZON 商品整改方案</h3>
      <button class="btn btn-default btn-sm" type="button" data-ozon-diagnosis-close>关闭</button>
    </div>
    <div id="ozonDiagnosisModalBody" style="padding:16px;"></div>
  </div>`;
  modal.querySelector('[data-ozon-diagnosis-close]')?.addEventListener('click', () => setOzonOrderDetailModalVisible(modal, false));
  modal.addEventListener('click', event => {
    if (event.target === modal) setOzonOrderDetailModalVisible(modal, false);
  });
  document.body.appendChild(modal);
  return modal;
}

function showOzonDiagnosisDetail(item = {}) {
  const modal = ensureOzonDiagnosisModal();
  const body = document.getElementById('ozonDiagnosisModalBody');
  const ui = normalizeOzonProductForUi(item.product || {});
  const diag = item.diagnosis || {};
  const sales = item.sales || {};
  const titleState = getOzonAiTitleState(diag);
  if (!body) return;
  body.innerHTML = `<div class="ozon-card-grid" style="grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin-bottom:14px;">
    <div class="ozon-mini-card"><div class="ozon-mini-label">标题得分</div><div class="ozon-mini-value">${escapeHtml(diag.score ?? 0)}分</div></div>
    <div class="ozon-mini-card"><div class="ozon-mini-label">风险等级</div><div class="ozon-mini-value">${escapeHtml(diag.riskLevel || '低')}</div></div>
    <div class="ozon-mini-card"><div class="ozon-mini-label">近7日销量</div><div class="ozon-mini-value">${escapeHtml(sales.qty7 || 0)}</div></div>
    <div class="ozon-mini-card"><div class="ozon-mini-label">近28日销量</div><div class="ozon-mini-value">${escapeHtml(sales.qty28 || 0)}</div></div>
  </div>
  <div class="ozon-table-wrap" style="margin-bottom:14px;"><table class="ozon-table"><tbody>
    <tr><th>原始标题</th><td>${escapeHtml(ui.title)}</td></tr>
    <tr><th>AI建议标题</th><td><div class="ozon-copy-block">${escapeHtml(titleState.title)}</div></td></tr>
    <tr><th>AI标题依据</th><td><div class="ozon-copy-block">${escapeHtml(titleState.basis)}</div></td></tr>
    <tr><th>规则关键词</th><td><div class="ozon-copy-block">${escapeHtml(diag.optimizedKeywords || '--')}</div></td></tr>
  </tbody></table></div>
  <div class="ozon-card-grid" style="grid-template-columns:repeat(auto-fit,minmax(260px,1fr));">
    <div class="ozon-tool-card"><h4>现存问题</h4><div class="ozon-problem-list">${(diag.problems || []).map(text => `<span>• ${escapeHtml(text)}</span>`).join('')}</div></div>
    <div class="ozon-tool-card"><h4>优化方案</h4><div class="ozon-problem-list">${(diag.suggestions || []).map(text => `<span>• ${escapeHtml(text)}</span>`).join('')}</div></div>
    <div class="ozon-tool-card"><h4>优化依据</h4><div class="ozon-problem-list">${(diag.basis || []).map(text => `<span>• ${escapeHtml(text)}</span>`).join('')}</div></div>
  </div>
  <div class="ozon-actions" style="margin-top:14px;">
    <button class="btn btn-primary btn-sm" type="button" id="ozonCopyDiagnosisTitleBtn" ${titleState.canCopy ? '' : 'disabled'}>复制新标题</button>
    <button class="btn btn-default btn-sm" type="button" id="ozonCopyDiagnosisKeywordsBtn">复制新关键词</button>
  </div>`;
  document.getElementById('ozonCopyDiagnosisTitleBtn')?.addEventListener('click', () => copyOzonText(titleState.canCopy ? titleState.title : ''));
  document.getElementById('ozonCopyDiagnosisKeywordsBtn')?.addEventListener('click', () => copyOzonText(diag.optimizedKeywords || ''));
  setOzonOrderDetailModalVisible(modal, true);
}

function renderOzonFinance() {
  syncOzonAccountSelects();
  const data = ozonState.finance || {};
  const summaryWrap = document.getElementById('ozonFinanceSummary');
  const payoutWrap = document.getElementById('ozonFinancePayoutList');
  const accounts = Array.isArray(data.accounts) ? data.accounts : [];
  const summaries = Array.isArray(data.summaries) ? data.summaries : [];
  const rows = summaries.length ? summaries : accounts.map(account => ({
    accountId: account.id,
    accountTitle: account.title || account.clientId || 'OZON 店铺',
    balance: null,
    awaitingPayout: null,
    currency: account.currencyCode || 'RUB',
    statusText: data.sourceStatusText || '等待财务数据'
  }));
  if (summaryWrap) {
    summaryWrap.innerHTML = rows.length
      ? rows.map(row => `<div class="ozon-finance-card">
          <span>${escapeHtml(row.accountTitle || 'OZON 店铺')}</span>
          <strong>${row.balance === null || row.balance === undefined ? '--' : escapeHtml(formatOzonAmount(row.balance, row.currency || 'RUB'))}</strong>
          <div class="ozon-list-meta">
            期初余额：${row.startingBalance === null || row.startingBalance === undefined ? '--' : escapeHtml(formatOzonAmount(row.startingBalance, row.currency || 'RUB'))}<br>
            待打款/流水合计：${row.awaitingPayout === null || row.awaitingPayout === undefined ? '--' : escapeHtml(formatOzonAmount(row.awaitingPayout, row.currency || 'RUB'))}<br>
            销售入账：${row.revenue === null || row.revenue === undefined ? '--' : escapeHtml(formatOzonAmount(row.revenue, row.currency || 'RUB'))}<br>
            服务费：${row.serviceFee === null || row.serviceFee === undefined ? '--' : escapeHtml(formatOzonAmount(row.serviceFee, row.currency || 'RUB'))}<br>
            现金流 ${escapeHtml(row.cashFlowCount || 0)} 条 · 流水 ${escapeHtml(row.transactionCount || 0)} 条<br>
            ${escapeHtml(row.statusText || data.sourceStatusText || '等待财务数据')}
          </div>
        </div>`).join('')
      : '<div class="ozon-empty">暂无店铺财务数据，请先绑定 OZON 店铺。</div>';
  }
  const errorRows = Array.isArray(data.errors) ? data.errors : [];
  const payouts = Array.isArray(data.payouts) ? data.payouts : [];
  if (payoutWrap) {
    payoutWrap.innerHTML = payouts.length
      ? `<table class="ozon-table"><thead><tr><th>店铺</th><th>打款单号</th><th>金额</th><th>状态</th><th>时间</th></tr></thead><tbody>${payouts.map(row => `<tr><td>${escapeHtml(row.accountTitle || 'OZON')}</td><td>${escapeHtml(row.payoutId || row.id || '--')}</td><td>${escapeHtml(formatOzonAmount(row.amount || 0, row.currency || 'RUB'))}</td><td>${escapeHtml(row.status || '--')}</td><td>${escapeHtml(formatOzonDateTime(row.createdAt || row.date))}</td></tr>`).join('')}</tbody></table>`
      : `<div class="ozon-empty">${escapeHtml(data.sourceStatusText || '暂无打款明细')}${errorRows.length ? `<br>${escapeHtml(errorRows.map(item => `${item.accountTitle || item.accountId}: ${item.message}`).join('；'))}` : ''}</div>`;
  }
}

function renderOzonMessages() {
  syncOzonAccountSelects();
  const data = ozonState.messages || {};
  const list = document.getElementById('ozonMessageList');
  const detail = document.getElementById('ozonMessageDetail');
  const messages = Array.isArray(data.messages) ? data.messages : [];
  const filtered = ozonState.messageType ? messages.filter(item => String(item.type || '') === ozonState.messageType) : messages;
  const counts = messages.reduce((acc, item) => {
    const type = item.type || 'system';
    acc[type] = (acc[type] || 0) + 1;
    acc.all += 1;
    return acc;
  }, { all: 0, buyer: 0, system: 0, service: 0 });
  setOzonText('ozonMessageAllCount', String(counts.all || 0));
  setOzonText('ozonMessageBuyerCount', String(counts.buyer || 0));
  setOzonText('ozonMessageSystemCount', String(counts.system || 0));
  setOzonText('ozonMessageServiceCount', String(counts.service || 0));
  document.querySelectorAll('[data-ozon-message-tab]').forEach(button => {
    button.classList.toggle('active', (button.dataset.ozonMessageTab || '') === (ozonState.messageType || ''));
  });
  if (list) {
    const errorRows = Array.isArray(data.errors) ? data.errors : [];
    list.innerHTML = filtered.length
      ? filtered.map(item => `<div class="ozon-message-item ${String(item.id || '') === String(ozonState.selectedMessageId || '') ? 'is-active' : ''}" data-ozon-message-id="${escapeHtml(item.id || '')}">
          <div class="ozon-list-title">${escapeHtml(item.title || 'OZON 消息')}${Number(item.unreadCount || 0) ? ` <span class="ozon-tab-count">${escapeHtml(item.unreadCount)}</span>` : ''}</div>
          <div class="ozon-list-meta">${escapeHtml(item.accountTitle || '全部店铺')} · ${escapeHtml(item.typeLabel || item.type || '消息')}<br>${escapeHtml(formatOzonDateTime(item.createdAt || item.updatedAt))}<br>${escapeHtml(item.preview || '')}</div>
        </div>`).join('')
      : `<div class="ozon-empty">${escapeHtml(data.sourceStatusText || '暂无客服、买家或系统消息')}${errorRows.length ? `<br>${escapeHtml(errorRows.map(item => `${item.accountTitle || item.accountId}: ${item.message}`).join('；'))}` : ''}</div>`;
    list.querySelectorAll('[data-ozon-message-id]').forEach(item => {
      item.addEventListener('click', () => {
        ozonState.selectedMessageId = item.dataset.ozonMessageId || '';
        renderOzonMessages();
      });
    });
  }
  const current = filtered.find(item => String(item.id || '') === String(ozonState.selectedMessageId || '')) || filtered[0] || null;
  if (detail) {
    if (!current) {
      detail.innerHTML = `<div class="ozon-empty">${escapeHtml(data.sourceStatusText || '选择一条消息开始查看')}</div>`;
    } else {
      detail.innerHTML = `<div class="ozon-tool-card">
        <h4>${escapeHtml(current.title || 'OZON 消息')}</h4>
        <div class="ozon-list-meta">店铺：${escapeHtml(current.accountTitle || '全部店铺')} · 类型：${escapeHtml(current.typeLabel || current.type || '消息')} · 时间：${escapeHtml(formatOzonDateTime(current.createdAt || current.updatedAt))}</div>
        <div class="ozon-tool-result" style="margin-top:10px;background:#f8fafc;color:#0f172a;border-color:#e2e8f0;">${escapeHtml(current.content || current.preview || '暂无消息正文')}</div>
      </div>`;
    }
  }
}

async function loadOzonFinance() {
  const accountId = document.getElementById('ozonFinanceAccountFilter')?.value || ozonState.financeAccountId || '';
  ozonState.financeAccountId = accountId;
  const query = accountId ? `?ozonAccountId=${encodeURIComponent(accountId)}` : '';
  const result = await ozonRequest(`/finance${query}`);
  ozonState.finance = result;
  renderOzonFinance();
}

async function loadOzonMessages() {
  const accountId = document.getElementById('ozonMessageAccountFilter')?.value || ozonState.messageAccountId || '';
  ozonState.messageAccountId = accountId;
  const query = accountId ? `?ozonAccountId=${encodeURIComponent(accountId)}` : '';
  const result = await ozonRequest(`/messages${query}`);
  ozonState.messages = result;
  if (!ozonState.selectedMessageId && Array.isArray(result.messages) && result.messages[0]?.id) {
    ozonState.selectedMessageId = String(result.messages[0].id);
  }
  renderOzonMessages();
}

function analyzeOzonCollectionLink(link = {}) {
  const title = normalizeOzonText(link.title || link.payload?.title || '');
  const url = normalizeOzonText(link.url || '');
  const image = normalizeOzonText(link.imageUrl || link.payload?.image || link.payload?.imageUrl || '');
  const price = normalizeOzonText(link.priceText || link.payload?.price || link.payload?.priceText || '');
  const problems = [];
  if (!/^https?:\/\/([^/]+\.)?ozon\./i.test(url)) problems.push('链接不是标准 OZON 地址');
  if (!title || title.length < 15) problems.push('标题过短或未采集到标题');
  if (!image) problems.push('未采集到主图');
  if (!price) problems.push('未采集到价格');
  if (link.status === 'archived') problems.push('当前记录已归档');
  return {
    score: Math.max(0, 100 - problems.length * 20),
    problems: problems.length ? problems : ['链接基础信息完整'],
    suggestion: problems.length ? '重新打开商品详情页采集，补齐标题、图片和价格后再进入选品池。' : '可进入选品池或准备上架，后续结合商品诊断继续优化标题和关键词。'
  };
}

function renderOzonLinkDiagnosis() {
  const list = document.getElementById('ozonLinkDiagnosisList');
  const summaryEl = document.getElementById('ozonLinkDiagnosisSummary');
  const rows = (ozonState.collectionLinks || []).map(link => ({ link, diagnosis: analyzeOzonCollectionLink(link) }));
  const risky = rows.filter(row => row.diagnosis.score < 80);
  if (summaryEl) {
    summaryEl.innerHTML = `采集记录：<strong>${rows.length}</strong><br>需要整改：<strong>${risky.length}</strong><br>主要问题：${escapeHtml(risky[0]?.diagnosis.problems?.[0] || '暂无明显问题')}`;
  }
  if (!list) return;
  if (!rows.length) {
    list.innerHTML = '<div class="ozon-empty">暂无采集链接。</div>';
    return;
  }
  list.innerHTML = `<table class="ozon-table"><thead><tr><th>链接商品</th><th>质量分</th><th>问题</th><th>建议</th></tr></thead><tbody>${rows.slice(0, 80).map(row => {
    const link = row.link || {};
    const diag = row.diagnosis || {};
    const tone = diag.score >= 85 ? 'ozon-score-good' : diag.score >= 65 ? 'ozon-score-warn' : 'ozon-score-bad';
    return `<tr>
      <td><div class="ozon-list-title">${escapeHtml(link.title || 'OZON 商品链接')}</div><div class="ozon-list-meta"><a href="${escapeHtml(link.url || '#')}" target="_blank" rel="noopener">${escapeHtml(link.url || '')}</a></div></td>
      <td><span class="ozon-score-badge ${tone}">${escapeHtml(diag.score)}分</span></td>
      <td><div class="ozon-problem-list">${diag.problems.map(text => `<span>• ${escapeHtml(text)}</span>`).join('')}</div></td>
      <td><div class="ozon-suggestion-text">${escapeHtml(diag.suggestion)}</div></td>
    </tr>`;
  }).join('')}</tbody></table>`;
}

function renderOzonOrders() {
  const wrap = document.getElementById('ozonOrderList');
  syncOzonAccountSelects();
  updateOzonOrderStatusTabs();
  if (!wrap) return;
  if (!ozonState.orders.length) {
    ozonState.orderSelectedIds.clear();
    updateOzonOrderSelectionInfo();
    updateOzonPagination('order');
    wrap.innerHTML = '<div class="ozon-empty">暂无订单缓存，请先同步 OZON 订单</div>';
    return;
  }
  const visibleKeys = new Set(ozonState.orders.map(getOzonOrderKey).filter(Boolean));
  ozonState.orderSelectedIds = new Set(Array.from(ozonState.orderSelectedIds).filter(id => visibleKeys.has(id)));
  wrap.classList.add('ozon-order-table-wrap');
  const productMap = getOzonCachedProductMap();
  wrap.innerHTML = `<table class="ozon-table ozon-order-table"><thead><tr>
    <th><input type="checkbox" id="ozonOrderSelectAllCurrentPage" aria-label="全选当前页订单"></th>
    <th>标记</th><th>货件编号</th><th>所属店铺</th><th>状态</th><th>商品信息</th><th>订单金额/费用/利润</th><th>仓库与配送方式</th><th>追踪号码</th><th>取消方,取消原因</th><th>下单/更新时间</th><th>操作</th>
  </tr></thead><tbody>${ozonState.orders.map(order => {
    const key = getOzonOrderKey(order);
    const checked = ozonState.orderSelectedIds.has(key) ? 'checked' : '';
    const products = getOzonOrderProducts(order);
    const money = getOzonOrderMoney(order);
    const delivery = getOzonOrderDelivery(order);
    const cancelText = getOzonOrderCancelText(order);
    const profitClass = Number(money.profit || 0) >= 0 ? 'ozon-profit-good' : 'ozon-profit-bad';
    const productHtml = products.length ? products.slice(0, 4).map(product => {
      const keys = [
        product.product_id,
        product.productId,
        product.id,
        product.offer_id,
        product.offerId,
        product.sku,
        product.financial?.product_id,
        product.financial?.offer_id,
        product.financial?.sku
      ].map(normalizeOzonText).filter(Boolean);
      const cached = keys.map(key => productMap.get(key)).find(Boolean);
      const image = pickOzonImage(product) || cached?.ui?.image || '';
      const offer = product.offer_id || product.offerId || product.sku || product.financial?.offer_id || '--';
      const sku = product.sku || product.financial?.sku || '';
      const qty = product.quantity || product.financial?.quantity || 1;
      const price = product.price || product.financial?.price || '';
      return `<div class="ozon-order-product-item">${renderOzonImageThumb(image, 'ozon-order-product-thumb')}<div><div class="ozon-list-title">${escapeHtml(product.name || cached?.ui?.title || product.financial?.name || 'OZON 商品')}</div><div class="ozon-list-meta">Offer: <span class="ozon-mono">${escapeHtml(offer)}</span>${sku ? `<br>SKU: <span class="ozon-mono">${escapeHtml(sku)}</span>` : ''}<br>数量: ${escapeHtml(qty)}${price ? ` · ${escapeHtml(formatOzonAmount(price, money.currency))}` : ''}</div></div></div>`;
    }).join('') : `<div class="ozon-list-title">${escapeHtml(order.productsCount || 0)} 个商品</div>`;
    const moreProducts = products.length > 4 ? `<div class="ozon-list-meta">还有 ${escapeHtml(products.length - 4)} 个商品，点详情查看</div>` : '';
    return `<tr>
      <td><input type="checkbox" data-ozon-order-check="${escapeHtml(key)}" ${checked}></td>
      <td><span class="ozon-status-pill">${escapeHtml(products.length > 1 ? '多品' : '单品')}</span></td>
      <td><div class="ozon-list-title ozon-mono">${escapeHtml(order.postingNumber || '')}</div><div class="ozon-list-meta">Order: ${escapeHtml(order.orderId || '--')}</div></td>
      <td><span style="color:#4f46e5;font-weight:800;">[${escapeHtml(order.accountTitle || 'OZON')}]</span></td>
      <td><span class="ozon-status-pill">${escapeHtml(getOzonOrderStatusLabel(order.status || ''))}</span></td>
      <td><div class="ozon-order-product-list">${productHtml}${moreProducts}</div></td>
      <td><div class="ozon-money-stack"><span>订单金额：<strong>${escapeHtml(formatOzonAmount(money.amount || 0, money.currency))}</strong></span><span>费用：${escapeHtml(formatOzonAmount(money.fee || 0, money.currency))}</span><span>利润：<span class="${profitClass}">${escapeHtml(formatOzonAmount(money.profit || 0, money.currency))}</span></span></div></td>
      <td><div class="ozon-list-meta">仓库：${escapeHtml(delivery.warehouse)}<br>配送：${escapeHtml(delivery.method)}</div></td>
      <td><span class="ozon-mono">${escapeHtml(delivery.tracking || '--')}</span></td>
      <td>${escapeHtml(cancelText)}</td>
      <td><div class="ozon-list-meta">下单：${escapeHtml(formatOzonDateTime(order.inProcessAt))}<br>发货：${escapeHtml(formatOzonDateTime(order.shipmentDate))}<br>更新：${escapeHtml(formatOzonDateTime(order.updatedAt))}</div></td>
      <td><div class="ozon-table-actions"><button class="btn btn-default btn-sm" type="button" data-ozon-order-detail="${escapeHtml(order.postingNumber || '')}" data-ozon-order-account-id="${escapeHtml(order.ozonAccountId || '')}">查看详情</button><button class="btn btn-default btn-sm" type="button" data-ozon-order-copy="${escapeHtml(order.postingNumber || '')}">复制</button></div></td>
    </tr>`;
  }).join('')}</tbody></table>`;
  const selectAll = wrap.querySelector('#ozonOrderSelectAllCurrentPage');
  const checks = Array.from(wrap.querySelectorAll('[data-ozon-order-check]'));
  if (selectAll) {
    selectAll.checked = checks.length > 0 && checks.every(input => input.checked);
    selectAll.indeterminate = checks.some(input => input.checked) && !selectAll.checked;
    selectAll.addEventListener('change', () => {
      checks.forEach(input => {
        input.checked = selectAll.checked;
        if (input.checked) ozonState.orderSelectedIds.add(String(input.dataset.ozonOrderCheck || ''));
        else ozonState.orderSelectedIds.delete(String(input.dataset.ozonOrderCheck || ''));
      });
      updateOzonOrderSelectionInfo();
    });
  }
  checks.forEach(input => {
    input.addEventListener('change', () => {
      if (input.checked) ozonState.orderSelectedIds.add(String(input.dataset.ozonOrderCheck || ''));
      else ozonState.orderSelectedIds.delete(String(input.dataset.ozonOrderCheck || ''));
      updateOzonOrderSelectionInfo();
      if (selectAll) {
        selectAll.checked = checks.length > 0 && checks.every(item => item.checked);
        selectAll.indeterminate = checks.some(item => item.checked) && !selectAll.checked;
      }
    });
  });
  wrap.querySelectorAll('[data-ozon-order-detail]').forEach(button => {
    button.addEventListener('click', () => loadOzonOrderDetail(button.dataset.ozonOrderDetail || '', button.dataset.ozonOrderAccountId || ''));
  });
  wrap.querySelectorAll('[data-ozon-order-copy]').forEach(button => {
    button.addEventListener('click', () => copyOzonText(button.dataset.ozonOrderCopy || ''));
  });
  updateOzonOrderSelectionInfo();
  updateOzonPagination('order');
}

function ensureOzonOrderDetailModal() {
  let modal = document.getElementById('ozonOrderDetailModal');
  if (modal) return modal;
  modal = document.createElement('div');
  modal.id = 'ozonOrderDetailModal';
  modal.style.cssText = 'position:fixed;inset:0;z-index:10000;display:none;align-items:center;justify-content:center;background:rgba(15,23,42,.45);padding:20px;';
  modal.setAttribute('aria-hidden', 'true');
  modal.innerHTML = `<div style="width:min(920px,96vw);max-height:86vh;overflow:auto;background:#fff;border-radius:8px;box-shadow:0 24px 70px rgba(15,23,42,.28);">
    <div style="display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--border);gap:12px;">
      <h3 style="margin:0;font-size:16px;color:var(--text-primary);">OZON 订单明细</h3>
      <button class="btn btn-default btn-sm" type="button" data-ozon-detail-close>关闭</button>
    </div>
    <div id="ozonOrderDetailBody" style="padding:16px;"></div>
  </div>`;
  modal.querySelector('[data-ozon-detail-close]')?.addEventListener('click', () => setOzonOrderDetailModalVisible(modal, false));
  modal.addEventListener('click', (event) => {
    if (event.target === modal) setOzonOrderDetailModalVisible(modal, false);
  });
  document.body.appendChild(modal);
  return modal;
}

function setOzonOrderDetailModalVisible(modal, visible) {
  if (!modal) return;
  modal.style.setProperty('display', visible ? 'flex' : 'none', 'important');
  modal.style.visibility = visible ? 'visible' : 'hidden';
  modal.style.opacity = visible ? '1' : '0';
  modal.setAttribute('aria-hidden', visible ? 'false' : 'true');
}

function renderOzonOrderDetail(order = {}) {
  const products = getOzonOrderProducts({ rawData: order });
  const customer = order.customer || {};
  const addressee = order.addressee || {};
  const delivery = order.delivery_method || {};
  const analytics = order.analytics_data || {};
  const money = getOzonOrderMoney({ rawData: order, customerPrice: order.customer_price, currencyCode: order.currency_code });
  const deliveryUi = getOzonOrderDelivery({ rawData: order });
  const cancelText = getOzonOrderCancelText({ rawData: order });
  const modal = ensureOzonOrderDetailModal();
  const body = document.getElementById('ozonOrderDetailBody');
  if (!body) return;
  const productRows = products.map((product, index) => {
    const fin = product.financial || {};
    const image = pickOzonImage(product);
    return `<tr>
      <td>${image ? `<img class="ozon-order-product-thumb" src="${escapeHtml(image)}" alt="">` : '<div class="ozon-order-product-thumb"></div>'}</td>
      <td><span class="ozon-mono">${escapeHtml(product.offer_id || product.offerId || product.sku || fin.offer_id || '')}</span></td>
      <td>${escapeHtml(product.name || fin.name || '')}</td>
      <td>${escapeHtml(product.quantity || fin.quantity || 0)}</td>
      <td>${escapeHtml(formatOzonAmount(product.price || fin.price || 0, money.currency))}</td>
    </tr>`;
  }).join('');
  body.innerHTML = `<div class="ozon-card-grid" style="grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin-bottom:14px;">
    <div class="ozon-mini-card"><div class="ozon-mini-label">Posting</div><div class="ozon-list-title">${escapeHtml(order.posting_number || '')}</div></div>
    <div class="ozon-mini-card"><div class="ozon-mini-label">Order</div><div class="ozon-list-title">${escapeHtml(order.order_number || order.order_id || '')}</div></div>
    <div class="ozon-mini-card"><div class="ozon-mini-label">状态</div><div class="ozon-list-title">${escapeHtml(getOzonOrderStatusLabel(order.status || ''))}</div></div>
    <div class="ozon-mini-card"><div class="ozon-mini-label">订单金额</div><div class="ozon-list-title">${escapeHtml(formatOzonAmount(money.amount || 0, money.currency))}</div></div>
  </div>
  <div class="ozon-table-wrap" style="margin-bottom:14px;"><table class="ozon-table"><tbody>
    <tr><th>物流方式</th><td>${escapeHtml(delivery.name || deliveryUi.method || '')}</td><th>物流单号</th><td><span class="ozon-mono">${escapeHtml(deliveryUi.tracking || '')}</span></td></tr>
    <tr><th>收件人</th><td>${escapeHtml(addressee.name || customer.name || '')}</td><th>电话</th><td>${escapeHtml(addressee.phone || customer.phone || '')}</td></tr>
    <tr><th>城市</th><td>${escapeHtml(analytics.city || '')}</td><th>仓库</th><td>${escapeHtml(deliveryUi.warehouse || '')}</td></tr>
    <tr><th>费用</th><td>${escapeHtml(formatOzonAmount(money.fee || 0, money.currency))}</td><th>利润</th><td>${escapeHtml(formatOzonAmount(money.profit || 0, money.currency))}</td></tr>
    <tr><th>取消信息</th><td colspan="3">${escapeHtml(cancelText)}</td></tr>
  </tbody></table></div>
  <div class="ozon-table-wrap"><table class="ozon-table"><thead><tr><th>图片</th><th>Offer / SKU</th><th>商品</th><th>数量</th><th>价格</th></tr></thead><tbody>${productRows || '<tr><td colspan="5">暂无商品明细</td></tr>'}</tbody></table></div>`;
  setOzonOrderDetailModalVisible(modal, true);
}

async function loadOzonOrderDetail(postingNumber, accountId = '') {
  if (!postingNumber) return;
  const resolvedAccountId = accountId || getSelectedOzonScopeId('order');
  if (!resolvedAccountId) {
    setOzonResult('未找到该订单所属店铺，请先同步订单', 'error');
    return;
  }
  setOzonResult('正在读取 OZON 订单明细...');
  try {
    const result = await ozonRequest(`/orders/${encodeURIComponent(postingNumber)}?ozonAccountId=${encodeURIComponent(resolvedAccountId)}`);
    renderOzonOrderDetail(result.order || {});
    setOzonResult('订单明细已加载', 'success');
  } catch (err) {
    setOzonResult(err.message || '读取订单明细失败', 'error');
  }
}

function renderOzonCollectionLinks() {
  const list = document.getElementById('ozonCollectionList');
  setOzonCollectionCounts();
  if (!list) return;
  if (!ozonState.collectionLinks.length) {
    list.innerHTML = '<div class="ozon-empty">暂无采集记录。打开 OZON 商品页后点击“采集到小青蛙”。</div>';
    return;
  }
  list.innerHTML = ozonState.collectionLinks.slice(0, 200).map(link => {
    const id = String(link.id || '');
    const url = link.url || '#';
    const status = link.status || 'collected';
    const checked = ozonState.selectedCollectionIds.has(id) ? 'checked' : '';
    const image = link.imageUrl ? `<img class="ozon-thumb" src="${escapeHtml(link.imageUrl)}" alt="">` : '';
    return `<div class="ozon-list-item" data-ozon-link-id="${escapeHtml(id)}">
      <div class="ozon-list-head">
        <input class="ozon-check" type="checkbox" data-ozon-link-check="${escapeHtml(id)}" ${checked}>
        <div>
          <div class="ozon-list-title">${escapeHtml(link.title || link.url || 'OZON 商品')}</div>
          <div class="ozon-list-meta">
            ${escapeHtml(link.priceText || '')}${link.productId ? '<br>商品ID: ' + escapeHtml(link.productId) : ''}${link.accountTitle ? '<br>店铺: ' + escapeHtml(link.accountTitle) : ''}<br>
            <a href="${escapeHtml(url)}" target="_blank" rel="noopener">${escapeHtml(url)}</a>
          </div>
        </div>
        <div style="display:grid;gap:8px;justify-items:end;">${image}<span class="ozon-status-pill ozon-status-${escapeHtml(status)}">${escapeHtml(getOzonStatusLabel(status))}</span></div>
      </div>
      <div class="ozon-actions">
        <button class="btn btn-default btn-sm" type="button" data-ozon-open-link="${escapeHtml(url)}">打开</button>
        <button class="btn btn-default btn-sm" type="button" data-ozon-copy-link="${escapeHtml(url)}">复制链接</button>
        <button class="btn btn-default btn-sm" type="button" data-ozon-status="selected" data-ozon-link-id="${escapeHtml(id)}">加入选品池</button>
        <button class="btn btn-primary btn-sm" type="button" data-ozon-status="ready" data-ozon-link-id="${escapeHtml(id)}">准备上架</button>
        <button class="btn btn-default btn-sm" type="button" data-ozon-status="listed" data-ozon-link-id="${escapeHtml(id)}">已上架</button>
        <button class="btn btn-default btn-sm" type="button" data-ozon-status="archived" data-ozon-link-id="${escapeHtml(id)}">归档</button>
      </div>
    </div>`;
  }).join('');
  list.querySelectorAll('[data-ozon-link-check]').forEach(input => {
    input.addEventListener('change', rememberOzonCheckedState);
  });
  list.querySelectorAll('[data-ozon-status]').forEach(button => {
    button.addEventListener('click', () => updateOzonCollectionStatus([Number(button.dataset.ozonLinkId)], button.dataset.ozonStatus));
  });
  list.querySelectorAll('[data-ozon-open-link]').forEach(button => {
    button.addEventListener('click', () => window.open(button.dataset.ozonOpenLink || 'https://www.ozon.ru/', '_blank'));
  });
  list.querySelectorAll('[data-ozon-copy-link]').forEach(button => {
    button.addEventListener('click', () => copyOzonText(button.dataset.ozonCopyLink || ''));
  });
}

async function loadOzonAccounts() {
  const result = await ozonRequest('/accounts');
  ozonState.accounts = Array.isArray(result.accounts) ? result.accounts : [];
  if (ozonState.selectedAccountId && !ozonState.accounts.some(account => String(account.id) === String(ozonState.selectedAccountId))) {
    ozonState.selectedAccountId = '';
  }
  if (!ozonState.productAccountTouched && ozonState.productAccountId) {
    ozonState.productAccountId = '';
  }
  if (!ozonState.orderAccountTouched && ozonState.orderAccountId) {
    ozonState.orderAccountId = '';
  }
  syncOzonAccountsToExtension();
  renderOzonAccounts();
}

async function loadOzonProducts() {
  const query = buildOzonProductQuery();
  const result = await ozonRequest(`/products${query}`);
  ozonState.products = Array.isArray(result.products) ? result.products : [];
  ozonState.productTotal = Number(result.total || ozonState.products.length || 0);
  ozonState.productStatusCounts = result.statusCounts || {};
  renderOzonProducts();
}

function buildOzonAnalyticsQuery(type = 'product') {
  const params = new URLSearchParams();
  const accountId = type === 'order'
    ? (document.getElementById('ozonOrderAnalysisAccountFilter')?.value || ozonState.orderAnalysisAccountId || '')
    : (document.getElementById('ozonProductDiagnosisAccountFilter')?.value || ozonState.productDiagnosisAccountId || '');
  if (accountId) params.set('ozonAccountId', accountId);
  if (type === 'order') {
    const sku = document.getElementById('ozonOrderAnalysisSkuInput')?.value?.trim() || ozonState.orderAnalysisSku || '';
    ozonState.orderAnalysisAccountId = accountId;
    ozonState.orderAnalysisSku = sku;
    params.set('days', '70');
    params.set('limit', '12000');
    if (sku) params.set('sku', sku);
  } else {
    const sku = document.getElementById('ozonProductDiagnosisSkuInput')?.value?.trim() || ozonState.productDiagnosisSku || '';
    const risk = document.getElementById('ozonProductRiskFilter')?.value || ozonState.productDiagnosisRisk || '';
    const keyword = document.getElementById('ozonProductDiagnosisKeywordInput')?.value?.trim() || ozonState.productDiagnosisKeyword || '';
    ozonState.productDiagnosisAccountId = accountId;
    ozonState.productDiagnosisSku = sku;
    ozonState.productDiagnosisRisk = risk;
    ozonState.productDiagnosisKeyword = keyword;
    params.set('limit', '12000');
    params.set('orderLimit', '12000');
    params.set('hydrate', '0');
    params.set('responseLimit', '180');
    if (sku) params.set('sku', sku);
    if (risk) params.set('risk', risk);
    if (keyword) params.set('keyword', keyword);
  }
  const text = params.toString();
  return text ? `?${text}` : '';
}

async function loadOzonOrderAnalysis() {
  const seq = (ozonState.orderAnalysisRequestSeq || 0) + 1;
  ozonState.orderAnalysisRequestSeq = seq;
  const page = Math.max(1, Number(ozonState.orderAnalysisPage || 1) || 1);
  const limit = Math.max(1, Number(ozonState.orderAnalysisLimit || 50) || 50);
  const query = buildOzonAnalyticsQuery('order');
  const result = await ozonRequest(`/analytics/orders${query}&page=${page}&limit=${limit}`);
  if (seq !== ozonState.orderAnalysisRequestSeq) return;
  ozonState.orderAnalysis = result;
  ozonState.orderAnalysisTotal = Number(result.summary?.activeAccountCount || result.summary?.activeProductCount || result.items?.length || 0);
  renderOzonOrderAnalysis();
}

async function loadOzonProductDiagnosis(options = {}) {
  const seq = (ozonState.productDiagnosisRequestSeq || 0) + 1;
  ozonState.productDiagnosisRequestSeq = seq;
  const query = buildOzonAnalyticsQuery('product');
  const result = await ozonRequest(`/analytics/products${query}`);
  if (seq !== ozonState.productDiagnosisRequestSeq) return;
  ozonState.productDiagnosis = result;
  renderOzonProductDiagnosis();
  if (options.enrichAi !== false) {
    const aiPromise = enrichOzonDiagnosisTitlesWithAi().catch(err => console.warn('[小青蛙电商助手][OZON] AI标题生成失败:', err));
    if (options.waitForAi) await aiPromise;
  }
}

async function enrichOzonDiagnosisTitlesWithAi() {
  const data = ozonState.productDiagnosis || {};
  const items = Array.isArray(data.items) ? data.items : [];
  if (!items.length) return;
  const targetItems = getFilteredOzonDiagnosisItems().slice(0, OZON_AI_TITLE_MAX_ITEMS);
  if (!targetItems.length) return;
  targetItems.forEach((item) => {
    item.diagnosis = item.diagnosis || {};
    item.diagnosis.aiTitleStatus = 'loading';
    item.diagnosis.aiTitleError = '';
  });
  renderOzonProductDiagnosis();
  try {
    if (!ozonState.selectedAiConfigId && !ozonState.aiDefaultConfig) {
      await loadOzonAiConfig().catch(() => null);
    }
    if (!ozonState.aiDefaultConfig) throw new Error('AI不可用：请稍后再试');
    const payload = buildOzonTitleSuggestionPayload(targetItems);
    if (!payload.length) throw new Error('AI不可用：没有可生成标题的商品数据');
    const storeScope = getOzonAccountLabel(ozonState.productDiagnosisAccountId || ozonState.productAccountId || '');
    let successCount = 0;
    let unavailableCount = 0;
    for (let start = 0; start < payload.length; start += OZON_AI_TITLE_BATCH_SIZE) {
      const batchPayload = payload.slice(start, start + OZON_AI_TITLE_BATCH_SIZE);
      const batchItems = targetItems.slice(start, start + OZON_AI_TITLE_BATCH_SIZE);
      const batchNo = Math.floor(start / OZON_AI_TITLE_BATCH_SIZE) + 1;
      const batchTotal = Math.ceil(payload.length / OZON_AI_TITLE_BATCH_SIZE);
      setOzonSyncProgress('product', Math.min(96, 58 + Math.round((start / payload.length) * 36)), `AI建议标题生成中：第 ${batchNo}/${batchTotal} 批...`);
      try {
        const result = await aiRequest('/ozon/title-suggestions', {
          method: 'POST',
          body: JSON.stringify({
            context: { storeScope },
            products: batchPayload
          })
        });
        const suggestions = new Map((result.suggestions || []).map(row => [String(row.key || ''), row]));
        batchItems.forEach((item, batchIndex) => {
          const diag = item.diagnosis || {};
          const key = getOzonDiagnosisKey(item, start + batchIndex);
          const suggestion = suggestions.get(key);
          if (suggestion?.optimizedTitle) {
            diag.aiTitleStatus = 'success';
            diag.aiOptimizedTitle = suggestion.optimizedTitle;
            diag.aiTitleBasis = suggestion.titleBasis || suggestion.keywordBasis || '';
            diag.aiKeywordBasis = suggestion.keywordBasis || '';
            diag.aiTitleError = '';
            successCount += 1;
          } else {
            diag.aiTitleStatus = 'unavailable';
            diag.aiTitleError = 'AI不可用：模型未返回该商品的建议标题';
            unavailableCount += 1;
          }
        });
      } catch (batchErr) {
        const message = String(batchErr.message || 'AI不可用：请稍后再试');
        batchItems.forEach((item) => {
          item.diagnosis = item.diagnosis || {};
          item.diagnosis.aiTitleStatus = 'unavailable';
          item.diagnosis.aiTitleError = message.startsWith('AI不可用') ? message : `AI不可用：${message}`;
          item.diagnosis.aiOptimizedTitle = '';
          item.diagnosis.aiTitleBasis = '';
          item.diagnosis.aiKeywordBasis = '';
          unavailableCount += 1;
        });
      }
      renderOzonProductDiagnosis();
    }
    const tone = successCount ? 'success' : 'error';
    setOzonSyncProgress('product', 100, `AI建议标题完成：成功 ${successCount} 个，未返回 ${unavailableCount} 个`, tone);
  } catch (err) {
    const message = String(err.message || 'AI不可用：请稍后再试');
    targetItems.forEach((item) => {
      item.diagnosis = item.diagnosis || {};
      item.diagnosis.aiTitleStatus = 'unavailable';
      item.diagnosis.aiTitleError = message.startsWith('AI不可用') ? message : `AI不可用：${message}`;
      item.diagnosis.aiOptimizedTitle = '';
      item.diagnosis.aiTitleBasis = '';
      item.diagnosis.aiKeywordBasis = '';
    });
  } finally {
    renderOzonProductDiagnosis();
  }
}

async function runOzonProductDiagnosisWithProgress() {
  setOzonSyncButtonsDisabled(true, 'product');
  setOzonSyncProgress('product', 12, '正在重新诊断 OZON 商品...');
  try {
    await loadOzonProductDiagnosis({ enrichAi: false });
    setOzonSyncProgress('product', 58, '规则诊断完成，正在生成 AI 建议标题...');
    await enrichOzonDiagnosisTitlesWithAi();
    setOzonSyncProgress('product', 100, '商品诊断已刷新，AI标题状态已更新', 'success');
  } catch (err) {
    setOzonSyncProgress('product', 100, err.message || '商品诊断失败', 'error');
  } finally {
    setOzonSyncButtonsDisabled(false, 'product');
  }
}

async function loadOzonOverview() {
  const seq = (ozonState.overviewRequestSeq || 0) + 1;
  ozonState.overviewRequestSeq = seq;
  const result = await ozonRequest('/overview');
  if (seq !== ozonState.overviewRequestSeq) return;
  ozonState.overview = result;
  renderOzonOverview();
}

function buildOzonOrderQuery() {
  const params = new URLSearchParams();
  const accountEl = document.getElementById('ozonOrderAccountFilter');
  const accountId = accountEl ? accountEl.value : (ozonState.orderAccountId || '');
  const statusEl = document.getElementById('ozonOrderStatusFilter');
  const dateFromEl = document.getElementById('ozonOrderDateFromInput');
  const dateToEl = document.getElementById('ozonOrderDateToInput');
  const keywordEl = document.getElementById('ozonOrderKeywordInput');
  const skuEl = document.getElementById('ozonOrderSkuInput');
  const status = statusEl ? statusEl.value : (ozonState.orderStatus || '');
  const dateFrom = dateFromEl ? dateFromEl.value : (ozonState.orderDateFrom || '');
  const dateTo = dateToEl ? dateToEl.value : (ozonState.orderDateTo || '');
  const keyword = keywordEl ? keywordEl.value.trim() : (ozonState.orderKeyword || '');
  const sku = skuEl ? skuEl.value.trim() : (ozonState.orderSku || '');
  ozonState.orderAccountId = accountId;
  ozonState.orderStatus = status;
  ozonState.orderDateFrom = dateFrom;
  ozonState.orderDateTo = dateTo;
  ozonState.orderKeyword = keyword;
  ozonState.orderSku = sku;
  if (accountId) params.set('ozonAccountId', accountId);
  if (status) params.set('status', status);
  if (dateFrom) params.set('dateFrom', dateFrom);
  if (dateTo) params.set('dateTo', dateTo);
  if (keyword) params.set('keyword', keyword);
  if (sku) params.set('sku', sku);
  params.set('limit', String(ozonState.orderLimit || 50));
  params.set('offset', String(Math.max(0, ((ozonState.orderPage || 1) - 1) * (ozonState.orderLimit || 50))));
  const text = params.toString();
  return text ? `?${text}` : '';
}

function buildOzonProductQuery() {
  const params = new URLSearchParams();
  const accountEl = document.getElementById('ozonProductAccountFilter');
  const visibilityEl = document.getElementById('ozonProductVisibilityFilter');
  const keywordEl = document.getElementById('ozonProductKeywordInput');
  const nameEl = document.getElementById('ozonProductNameInput');
  const skuEl = document.getElementById('ozonProductSkuInput');
  const accountId = accountEl ? accountEl.value : (ozonState.productAccountId || '');
  const visibility = visibilityEl ? visibilityEl.value : (ozonState.productVisibility || '');
  const name = nameEl ? nameEl.value.trim() : (ozonState.productName || '');
  const sku = skuEl ? skuEl.value.trim() : (ozonState.productSku || '');
  const keyword = keywordEl ? keywordEl.value.trim() : (ozonState.productKeyword || '');
  ozonState.productAccountId = accountId;
  ozonState.productVisibility = visibility;
  ozonState.productName = name;
  ozonState.productSku = sku;
  ozonState.productKeyword = keyword;
  if (accountId) params.set('ozonAccountId', accountId);
  if (visibility) params.set('visibility', visibility);
  if (name) params.set('name', name);
  if (sku) params.set('sku', sku);
  if (keyword) params.set('keyword', keyword);
  params.set('limit', String(ozonState.productLimit || 50));
  params.set('offset', String(Math.max(0, ((ozonState.productPage || 1) - 1) * (ozonState.productLimit || 50))));
  const text = params.toString();
  return text ? `?${text}` : '';
}

async function loadOzonOrders() {
  const result = await ozonRequest(`/orders${buildOzonOrderQuery()}`);
  ozonState.orders = Array.isArray(result.orders) ? result.orders : [];
  ozonState.orderTotal = Number(result.total || ozonState.orders.length || 0);
  ozonState.orderStatusCounts = result.statusCounts || {};
  renderOzonOrders();
}

function buildOzonCollectionQuery() {
  const params = new URLSearchParams();
  const statusEl = document.getElementById('ozonCollectionStatusFilter');
  const keywordEl = document.getElementById('ozonCollectionKeywordInput');
  const status = statusEl ? statusEl.value : (ozonState.collectionStatus || '');
  const keyword = keywordEl ? keywordEl.value.trim() : (ozonState.collectionKeyword || '');
  ozonState.collectionStatus = status;
  ozonState.collectionKeyword = keyword;
  if (status) params.set('status', status);
  if (keyword) params.set('keyword', keyword);
  const text = params.toString();
  return text ? `?${text}` : '';
}

async function loadOzonCollectionLinks() {
  rememberOzonCheckedState();
  const result = await ozonRequest(`/collection-links${buildOzonCollectionQuery()}`);
  ozonState.collectionLinks = Array.isArray(result.links) ? result.links : [];
  const visibleIds = new Set(ozonState.collectionLinks.map(link => String(link.id || '')));
  ozonState.selectedCollectionIds = new Set(Array.from(ozonState.selectedCollectionIds).filter(id => visibleIds.has(id)));
  renderOzonCollectionLinks();
  renderOzonLinkDiagnosis();
}

async function loadOzonDashboard() {
  if (!isOzonWorkspace()) return;
  try {
    await loadOzonAccounts();
    const activeTabId = document.querySelector('.section.active')?.id || 'ozonHome';
    if (activeTabId === 'ozonProducts') {
      await Promise.allSettled([loadOzonOverview(), loadOzonProducts(), loadOzonProductDiagnosis({ enrichAi: false })]);
    } else if (activeTabId === 'ozonOrders') {
      await Promise.allSettled([loadOzonOverview(), loadOzonOrders(), loadOzonOrderAnalysis()]);
    } else if (activeTabId === 'ozonCollection') {
      await Promise.allSettled([loadOzonOverview(), loadOzonCollectionLinks()]);
    } else if (activeTabId === 'ozonFinance') {
      await Promise.allSettled([loadOzonOverview(), loadOzonFinance()]);
    } else if (activeTabId === 'ozonMessages') {
      await Promise.allSettled([loadOzonOverview(), loadOzonMessages()]);
    } else {
      await Promise.allSettled([loadOzonOverview(), loadOzonOrderAnalysis(), loadOzonAiConfig()]);
    }
  } catch (err) {
    console.warn('[小青蛙电商助手][OZON] 加载失败:', err);
    setOzonResult(err.message || '\u52a0\u8f7d\u5931\u8d25', 'error');
  }
}

async function loadOzonTab(tabId) {
  if (!isOzonWorkspace()) return;
  if (tabId === 'ozonHome') {
    await Promise.allSettled([loadOzonOverview(), loadOzonAccounts(), loadOzonOrderAnalysis(), loadOzonAiConfig()]);
  } else if (tabId === 'ozonCollection') {
    await loadOzonCollectionLinks();
  } else if (tabId === 'ozonProducts') {
    await Promise.allSettled([loadOzonProducts(), loadOzonProductDiagnosis({ enrichAi: false })]);
  } else if (tabId === 'ozonOrders') {
    ozonState.orderAnalysis = null;
    await Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
  } else if (tabId === 'ozonAccounts') {
    await loadOzonAccounts();
  } else if (tabId === 'ozonFinance') {
    await Promise.allSettled([loadOzonAccounts(), loadOzonFinance()]);
  } else if (tabId === 'ozonMessages') {
    await Promise.allSettled([loadOzonAccounts(), loadOzonMessages()]);
  }
}

async function saveOzonAccountFromForm(checkOnly = false) {
  const title = document.getElementById('ozonShopTitleInput')?.value?.trim() || 'OZON \u5e97\u94fa';
  const clientId = document.getElementById('ozonClientIdInput')?.value?.trim();
  const apiKey = document.getElementById('ozonApiKeyInput')?.value?.trim();
  if (!clientId || !apiKey) {
    setOzonAuthModalResult('Client-Id 和 Api-Key 不能为空', 'error');
    return;
  }
  setOzonAuthModalResult(checkOnly ? '正在校验授权...' : '正在保存店铺...');
  try {
    const path = checkOnly ? '/accounts/check' : '/accounts';
    const result = await ozonRequest(path, {
      method: 'POST',
      body: JSON.stringify({ title, clientId, apiKey })
    });
    if (!checkOnly && result.account?.id) {
      document.getElementById('ozonApiKeyInput').value = '';
      await loadOzonAccounts();
    }
    if (checkOnly) {
      setOzonAuthModalResult('授权校验通过', 'success');
    } else {
      setOzonAuthModalResult('添加店铺成功', 'success');
      setOzonResult('添加店铺成功', 'success');
      window.setTimeout(() => toggleOzonAccountAuthPanel(false), 500);
      await Promise.allSettled([loadOzonOverview(), loadOzonProducts(), loadOzonProductDiagnosis({ enrichAi: false }), loadOzonOrderAnalysis()]);
    }
  } catch (err) {
    setOzonAuthModalResult(err.message || '操作失败', 'error');
  }
}

async function syncOzonProductsFromSelectedAccount() {
  const accountIds = getOzonFullSyncAccountIds('product');
  if (!accountIds.length) {
    setOzonResult('\u8bf7\u5148\u9009\u62e9 OZON \u5e97\u94fa', 'error');
    return;
  }
  const isAll = accountIds.length > 1;
  setOzonSyncButtonsDisabled(true, 'product');
  setOzonSyncProgress('product', 3, `正在同步 OZON ${isAll ? `全部 ${accountIds.length} 个店铺` : '所选店铺'}商品...`);
  try {
    let totalFetched = 0;
    let totalSaved = 0;
    for (let index = 0; index < accountIds.length; index += 1) {
      const accountId = accountIds[index];
      const accountTitle = getOzonAccountTitle(accountId) || `店铺 ${index + 1}`;
      const basePct = Math.round((index / accountIds.length) * 82) + 5;
      setOzonSyncProgress('product', basePct, `正在同步商品：${accountTitle}（${index + 1}/${accountIds.length}）...`);
      const result = await ozonRequest('/products/sync', {
        method: 'POST',
        body: JSON.stringify({ ozonAccountId: accountId, limit: 1000, maxPages: 200 })
      });
      totalFetched += Number(result.totalFetched || 0);
      totalSaved += Number(result.totalSaved || 0);
      setOzonSyncProgress('product', Math.round(((index + 1) / accountIds.length) * 82) + 8, `商品同步中：${accountTitle} 已保存 ${result.totalSaved || 0} 个`);
    }
    setOzonSyncProgress('product', 92, '商品已同步，正在刷新诊断数据...');
    ozonState.productPage = 1;
    await Promise.allSettled([loadOzonAccounts(), loadOzonProducts(), loadOzonProductDiagnosis({ enrichAi: false })]);
    setOzonSyncProgress('product', 100, `商品同步完成：拉取 ${totalFetched} 个，保存 ${totalSaved} 个`, 'success');
  } catch (err) {
    setOzonSyncProgress('product', 100, err.message || '\u540c\u6b65\u5931\u8d25', 'error');
  } finally {
    setOzonSyncButtonsDisabled(false, 'product');
  }
}

async function syncSelectedOzonProducts() {
  const selectedRows = getSelectedOzonProductRows();
  const rowsForSync = selectedRows.length ? selectedRows : getOzonProductRowsForTargetedSync();
  if (!rowsForSync.length) {
    setOzonResult('当前没有可同步的 OZON 商品，请先同步商品或调整筛选条件', 'error');
    return;
  }
  const fallbackAccountId = getSelectedOzonScopeId('product');
  const groups = rowsForSync.reduce((acc, row) => {
    const accountId = normalizeOzonText(row.ozonAccountId || fallbackAccountId);
    if (!accountId) return acc;
    if (!acc.has(accountId)) acc.set(accountId, []);
    acc.get(accountId).push(row);
    return acc;
  }, new Map());
  if (!groups.size) {
    setOzonResult('请先选择 OZON 店铺', 'error');
    return;
  }
  const limitedGroups = new Map(Array.from(groups.entries()).map(([accountId, rows]) => [accountId, rows.slice(0, 200)]));
  const totalTarget = Array.from(limitedGroups.values()).reduce((sum, rows) => sum + rows.length, 0);
  setOzonSyncButtonsDisabled(true, 'product');
  setOzonSyncProgress('product', 5, `正在同步${selectedRows.length ? '所选' : '当前筛选'} ${totalTarget} 个 OZON 商品...`);
  try {
    let totalSaved = 0;
    const entries = Array.from(limitedGroups.entries());
    for (let index = 0; index < entries.length; index += 1) {
      const [accountId, rows] = entries[index];
      setOzonSyncProgress('product', Math.round((index / entries.length) * 80) + 8, `正在同步当前筛选商品：${getOzonAccountTitle(accountId) || `店铺 ${index + 1}`}（${index + 1}/${entries.length}）`);
      const result = await ozonRequest('/products/sync', {
        method: 'POST',
        body: JSON.stringify({
          ozonAccountId: accountId,
          includeList: false,
          productIds: rows.map(row => Number(row.productId || 0)).filter(n => Number.isFinite(n) && n > 0),
          offerIds: rows.map(row => row.offerId).filter(Boolean),
          skus: rows.map(row => Number(row.sku || 0)).filter(n => Number.isFinite(n) && n > 0)
        })
      });
      totalSaved += Number(result.totalSaved || 0);
    }
    setOzonSyncProgress('product', 92, '商品已同步，正在刷新诊断数据...');
    await Promise.allSettled([loadOzonProducts(), loadOzonProductDiagnosis({ enrichAi: false })]);
    setOzonSyncProgress('product', 100, `${selectedRows.length ? '所选' : '当前筛选'}商品同步完成：保存 ${totalSaved} 个`, 'success');
  } catch (err) {
    setOzonSyncProgress('product', 100, err.message || '同步当前筛选商品失败', 'error');
  } finally {
    setOzonSyncButtonsDisabled(false, 'product');
  }
}

async function syncOzonOrdersFromSelectedAccount() {
  const accountIds = getOzonFullSyncAccountIds('order');
  if (!accountIds.length) {
    setOzonOrderSyncStatus('订单同步状态：请先选择 OZON 店铺', 'error');
    return;
  }
  const isAll = accountIds.length > 1;
  setOzonSyncButtonsDisabled(true, 'order');
  setOzonSyncProgress('order', 3, `正在同步 OZON ${isAll ? `全部 ${accountIds.length} 个店铺` : '所选店铺'}订单...`);
  try {
    const onOrdersTab = document.querySelector('.section.active')?.id === 'ozonOrders';
    const dateFrom = onOrdersTab ? (document.getElementById('ozonOrderDateFromInput')?.value || '') : '';
    const dateTo = onOrdersTab ? (document.getElementById('ozonOrderDateToInput')?.value || '') : '';
    const status = onOrdersTab ? (document.getElementById('ozonOrderStatusFilter')?.value || '') : '';
    const chunks = buildOzonOrderSyncDateChunks(dateFrom, dateTo, onOrdersTab ? 30 : 21);
    const totalJobs = Math.max(1, accountIds.length * Math.min(3, chunks.length));
    let totalFetched = 0;
    let totalSaved = 0;
    let completedJobs = 0;
    for (let index = 0; index < accountIds.length; index += 1) {
      const accountId = accountIds[index];
      const accountTitle = getOzonAccountTitle(accountId) || `店铺 ${index + 1}`;
      const selectedChunks = chunks.slice(0, onOrdersTab ? chunks.length : Math.min(3, chunks.length));
      for (let chunkIndex = 0; chunkIndex < selectedChunks.length; chunkIndex += 1) {
        const chunk = selectedChunks[chunkIndex];
        const pct = 5 + Math.round((completedJobs / totalJobs) * 84);
        setOzonSyncProgress('order', pct, `正在同步订单：${accountTitle} ${chunk.dateFrom} 至 ${chunk.dateTo}（${completedJobs + 1}/${totalJobs}）...`);
        const result = await ozonRequest('/orders/sync', {
          method: 'POST',
          body: JSON.stringify({
            ozonAccountId: accountId,
            limit: 200,
            maxPages: onOrdersTab ? 6 : 4,
            dateFrom: chunk.dateFrom,
            dateTo: chunk.dateTo,
            rangeDays: onOrdersTab ? 30 : 21,
            syncTypes: ['fbs', 'fbo'],
            filter: status ? { status } : {}
          })
        });
        totalFetched += Number(result.totalFetched || 0);
        totalSaved += Number(result.totalSaved || 0);
        completedJobs += 1;
        setOzonSyncProgress('order', 5 + Math.round((completedJobs / totalJobs) * 84), `订单同步中：${accountTitle} ${chunk.dateFrom} 至 ${chunk.dateTo}，本段保存 ${result.totalSaved || 0} 个，累计 ${totalSaved} 个`);
      }
    }
    setOzonSyncProgress('order', 92, '订单已同步，正在刷新销量分析...');
    ozonState.orderPage = 1;
    await Promise.allSettled([loadOzonOverview(), loadOzonOrders(), loadOzonOrderAnalysis(), loadOzonProductDiagnosis({ enrichAi: false })]);
    setOzonSyncProgress('order', 100, `订单同步完成：拉取 ${totalFetched} 个，保存 ${totalSaved} 个`, 'success');
  } catch (err) {
    setOzonSyncProgress('order', 100, err.message || '订单同步失败', 'error');
  } finally {
    setOzonSyncButtonsDisabled(false, 'order');
  }
}

async function pullNewOzonOrders() {
  const accountIds = getOzonFullSyncAccountIds('order');
  if (!accountIds.length) {
    setOzonOrderSyncStatus('订单同步状态：请先选择 OZON 店铺', 'error');
    return;
  }
  setOzonSyncButtonsDisabled(true, 'order');
  const todayRange = getOzonTodayDateRange();
  const recentRange = getOzonRecentOrderSyncDateRange(2);
  setOzonSyncProgress('order', 10, `正在按北京时间补拉今日订单（${todayRange.dateFrom}）和近期待处理订单...`);
  try {
    let totalFetched = 0;
    let totalSaved = 0;
    const totalsByType = {};
    for (let index = 0; index < accountIds.length; index += 1) {
      const accountId = accountIds[index];
      const accountTitle = getOzonAccountTitle(accountId) || `店铺 ${index + 1}`;
      const basePct = Math.round((index / accountIds.length) * 72) + 12;
      setOzonSyncProgress('order', basePct, `正在读取今日订单：${accountTitle}（FBS + FBO）...`);
      const todayResult = await ozonRequest('/orders/sync', {
        method: 'POST',
        body: JSON.stringify({ ozonAccountId: accountId, limit: 200, maxPages: 5, dateFrom: todayRange.dateFrom, dateTo: todayRange.dateTo, rangeDays: 1, syncTypes: ['fbs', 'fbo'] })
      });
      totalFetched += Number(todayResult.totalFetched || 0);
      totalSaved += Number(todayResult.totalSaved || 0);
      Object.entries(todayResult.totalsByType || {}).forEach(([type, count]) => {
        totalsByType[type] = (totalsByType[type] || 0) + Number(count || 0);
      });
      setOzonSyncProgress('order', basePct + Math.round(36 / Math.max(1, accountIds.length)), `正在补拉近期待处理订单：${accountTitle}（等待备货/发运）...`);
      const recentResult = await ozonRequest('/orders/sync', {
        method: 'POST',
        body: JSON.stringify({
          ozonAccountId: accountId,
          limit: 200,
          maxPages: 5,
          dateFrom: recentRange.dateFrom,
          dateTo: recentRange.dateTo,
          rangeDays: 2,
          syncTypes: ['fbs', 'fbo'],
          filter: { status: 'awaiting_packaging' }
        })
      });
      totalFetched += Number(recentResult.totalFetched || 0);
      totalSaved += Number(recentResult.totalSaved || 0);
      Object.entries(recentResult.totalsByType || {}).forEach(([type, count]) => {
        totalsByType[type] = (totalsByType[type] || 0) + Number(count || 0);
      });
    }
    setOzonSyncProgress('order', 88, '新订单已拉取，正在刷新销量分析...');
    ozonState.orderPage = 1;
    await Promise.allSettled([loadOzonOverview(), loadOzonOrders(), loadOzonOrderAnalysis(), loadOzonProductDiagnosis({ enrichAi: false })]);
    const typeText = Object.entries(totalsByType).map(([type, count]) => `${type.toUpperCase()} ${count}`).join(' / ');
    setOzonSyncProgress('order', 100, `订单补拉完成：拉取 ${totalFetched} 个，保存 ${totalSaved} 个${typeText ? `（${typeText}）` : ''}`, 'success');
  } catch (err) {
    setOzonSyncProgress('order', 100, err.message || '拉取新订单失败', 'error');
  } finally {
    setOzonSyncButtonsDisabled(false, 'order');
  }
}

async function updateOzonCollectionStatus(ids, status) {
  const cleanIds = (ids || []).map(id => Number(id)).filter(id => Number.isInteger(id) && id > 0);
  if (!cleanIds.length) {
    setOzonResult('请选择要处理的采集记录', 'error');
    return;
  }
  setOzonResult(`正在更新为${getOzonStatusLabel(status)}...`);
  try {
    await ozonRequest('/collection-links/status', {
      method: 'POST',
      body: JSON.stringify({ ids: cleanIds, status })
    });
    cleanIds.forEach(id => ozonState.selectedCollectionIds.delete(String(id)));
    setOzonResult(`已更新 ${cleanIds.length} 条为${getOzonStatusLabel(status)}`, 'success');
    await loadOzonCollectionLinks();
  } catch (err) {
    setOzonResult(err.message || '更新失败', 'error');
  }
}

async function copyOzonText(text) {
  try {
    await navigator.clipboard.writeText(text || '');
    setOzonResult('已复制链接', 'success');
  } catch (_) {
    const input = document.createElement('textarea');
    input.value = text || '';
    input.style.position = 'fixed';
    input.style.left = '-9999px';
    document.body.appendChild(input);
    input.select();
    document.execCommand('copy');
    input.remove();
    setOzonResult('已复制链接', 'success');
  }
}

function exportOzonProducts() {
  const selected = getSelectedOzonProductRows();
  const rows = selected.length ? selected : ozonState.products;
  if (!rows.length) {
    setOzonResult('暂无商品可导出', 'error');
    return;
  }
  downloadOzonCsv(`xiaoqingwa-ozon-products-${Date.now()}.csv`, rows, [
    { label: 'Product ID', value: row => normalizeOzonProductForUi(row).productId },
    { label: 'Offer ID', value: row => normalizeOzonProductForUi(row).offerId },
    { label: 'SKU', value: row => normalizeOzonProductForUi(row).sku },
    { label: '商品名称', value: row => normalizeOzonProductForUi(row).title },
    { label: '商品图片', value: row => normalizeOzonProductForUi(row).image },
    { label: '类目', value: row => normalizeOzonProductForUi(row).category },
    { label: '店铺', value: row => normalizeOzonProductForUi(row).accountTitle },
    { label: '状态', value: row => normalizeOzonProductForUi(row).statusLabel },
    { label: '销售价', value: row => normalizeOzonProductForUi(row).price.price },
    { label: '划线价', value: row => normalizeOzonProductForUi(row).price.oldPrice },
    { label: '最低价', value: row => normalizeOzonProductForUi(row).price.minPrice },
    { label: '促销价', value: row => normalizeOzonProductForUi(row).price.promoPrice },
    { label: '佣金比例', value: row => normalizeOzonProductForUi(row).commission.percent },
    { label: '库存', value: row => normalizeOzonProductForUi(row).stock.available },
    { label: '预留库存', value: row => normalizeOzonProductForUi(row).stock.reserved },
    { label: '重量', value: row => getOzonProductMetric(row, ['weight', 'weight_g', 'weightG', 'package_weight']) },
    { label: '采购价', value: row => normalizeOzonProductForUi(row).source.purchasePrice },
    { label: '货源地址', value: row => normalizeOzonProductForUi(row).source.url },
    { label: '错误原因', value: row => normalizeOzonProductForUi(row).errorText },
    { label: '创建日期', value: row => formatOzonDateTime(normalizeOzonProductForUi(row).createdAt) },
    { label: '更新时间', value: row => formatOzonDateTime(normalizeOzonProductForUi(row).updatedAt) }
  ]);
  setOzonResult(`已导出 ${rows.length} 个商品`, 'success');
}

function exportOzonOrders() {
  const selected = getSelectedOzonOrderRows();
  const rows = selected.length ? selected : ozonState.orders;
  if (!rows.length) {
    setOzonResult('暂无订单可导出', 'error');
    return;
  }
  const detailRows = rows.flatMap(order => {
    const products = getOzonOrderProducts(order);
    const money = getOzonOrderMoney(order);
    const delivery = getOzonOrderDelivery(order);
    const cancelText = getOzonOrderCancelText(order);
    const base = { order, money, delivery, cancelText };
    if (!products.length) return [{ ...base, product: {} }];
    return products.map(product => ({ ...base, product }));
  });
  downloadOzonCsv(`xiaoqingwa-ozon-orders-${Date.now()}.csv`, detailRows, [
    { label: 'Posting', value: row => row.order.postingNumber },
    { label: 'Order ID', value: row => row.order.orderId },
    { label: '店铺', value: row => row.order.accountTitle },
    { label: '状态', value: row => getOzonOrderStatusLabel(row.order.status) },
    { label: '商品名称', value: row => row.product.name || row.product.financial?.name || '' },
    { label: 'Offer ID', value: row => row.product.offer_id || row.product.offerId || row.product.financial?.offer_id || '' },
    { label: 'SKU', value: row => row.product.sku || row.product.financial?.sku || '' },
    { label: '数量', value: row => row.product.quantity || row.product.financial?.quantity || 0 },
    { label: '商品价格', value: row => row.product.price || row.product.financial?.price || '' },
    { label: '订单金额', value: row => formatOzonAmount(row.money.amount || 0, row.money.currency) },
    { label: '费用', value: row => formatOzonAmount(row.money.fee || 0, row.money.currency) },
    { label: '利润', value: row => formatOzonAmount(row.money.profit || 0, row.money.currency) },
    { label: '仓库', value: row => row.delivery.warehouse },
    { label: '配送方式', value: row => row.delivery.method },
    { label: '追踪号码', value: row => row.delivery.tracking },
    { label: '取消信息', value: row => row.cancelText },
    { label: '下单时间', value: row => formatOzonDateTime(row.order.inProcessAt) },
    { label: '发货时间', value: row => formatOzonDateTime(row.order.shipmentDate) },
    { label: '更新时间', value: row => formatOzonDateTime(row.order.updatedAt) }
  ]);
  setOzonResult(`已导出 ${rows.length} 个订单，${detailRows.length} 行明细`, 'success');
}

function exportOzonOrderAnalysis() {
  const rows = ozonState.orderAnalysis?.items || [];
  if (!rows.length) {
    setOzonResult('暂无销量分析可导出', 'error');
    return;
  }
  downloadOzonCsv(`xiaoqingwa-ozon-order-analysis-${Date.now()}.csv`, rows, [
    { label: 'Product ID', value: row => row.productId },
    { label: 'Offer ID', value: row => row.offerId },
    { label: 'SKU', value: row => row.sku },
    { label: '商品名称', value: row => row.name },
    { label: '类目', value: row => row.category },
    { label: '店铺', value: row => row.accountTitle },
    { label: '今日销量', value: row => row.todayQty },
    { label: '近7日销量', value: row => row.qty7 },
    { label: '上个7日销量', value: row => row.prevQty7 },
    { label: '7日环比', value: row => `${row.trend7Percent}%` },
    { label: '近28日销量', value: row => row.qty28 },
    { label: '近28日日均销量', value: row => row.avg28 },
    { label: '销量占比', value: row => `${row.share28Percent}%` },
    { label: '经营标签', value: row => row.tag },
    { label: '预警', value: row => row.alert ? '是' : '否' }
  ]);
  setOzonResult(`已导出 ${rows.length} 条销量分析`, 'success');
}

function exportOzonProductDiagnosis() {
  const rows = getFilteredOzonDiagnosisItems();
  if (!rows.length) {
    setOzonResult('暂无商品诊断可导出', 'error');
    return;
  }
  downloadOzonCsv(`xiaoqingwa-ozon-product-diagnosis-${Date.now()}.csv`, rows, [
    { label: 'Product ID', value: row => normalizeOzonProductForUi(row.product || {}).productId },
    { label: 'Offer ID', value: row => normalizeOzonProductForUi(row.product || {}).offerId },
    { label: 'SKU', value: row => normalizeOzonProductForUi(row.product || {}).sku },
    { label: '商品名称', value: row => normalizeOzonProductForUi(row.product || {}).title },
    { label: '店铺', value: row => normalizeOzonProductForUi(row.product || {}).accountTitle },
    { label: '标题得分', value: row => row.diagnosis?.score },
    { label: '风险等级', value: row => row.diagnosis?.riskLevel },
    { label: '商品状态', value: row => normalizeOzonProductForUi(row.product || {}).statusLabel },
    { label: '价格指数', value: row => row.diagnosis?.priceIndex?.label || normalizeOzonProductForUi(row.product || {}).priceIndex?.label || '' },
    { label: '库存', value: row => row.diagnosis?.stock ?? normalizeOzonProductForUi(row.product || {}).stock?.available ?? '' },
    { label: '图片数', value: row => row.diagnosis?.imageCount ?? '' },
    { label: '近7日销量', value: row => row.sales?.qty7 || 0 },
    { label: '近28日销量', value: row => row.sales?.qty28 || 0 },
    { label: '7日环比', value: row => `${Number(row.sales?.trend7Percent || 0).toFixed(1)}%` },
    { label: '现存问题', value: row => (row.diagnosis?.problems || []).join('；') },
    { label: '优化建议', value: row => (row.diagnosis?.suggestions || []).join('；') },
    { label: '优化依据', value: row => (row.diagnosis?.basis || []).join('；') },
    { label: 'AI建议标题', value: row => getOzonAiTitleState(row.diagnosis || {}).title },
    { label: 'AI标题依据', value: row => getOzonAiTitleState(row.diagnosis || {}).basis },
    { label: '规则关键词', value: row => row.diagnosis?.optimizedKeywords || '' }
  ]);
  setOzonResult(`已导出 ${rows.length} 条商品诊断`, 'success');
}

function exportOzonLinkDiagnosis() {
  const rows = (ozonState.collectionLinks || []).map(link => ({ link, diagnosis: analyzeOzonCollectionLink(link) }));
  if (!rows.length) {
    setOzonResult('暂无链接诊断可导出', 'error');
    return;
  }
  downloadOzonCsv(`xiaoqingwa-ozon-link-diagnosis-${Date.now()}.csv`, rows, [
    { label: '标题', value: row => row.link.title },
    { label: '链接', value: row => row.link.url },
    { label: '状态', value: row => getOzonStatusLabel(row.link.status) },
    { label: '质量分', value: row => row.diagnosis.score },
    { label: '问题', value: row => row.diagnosis.problems.join('；') },
    { label: '建议', value: row => row.diagnosis.suggestion }
  ]);
  setOzonResult(`已导出 ${rows.length} 条链接诊断`, 'success');
}

function copySelectedOzonProductIds() {
  const selected = getSelectedOzonProductRows();
  if (!selected.length) {
    setOzonResult('请先勾选商品', 'error');
    return;
  }
  copyOzonText(selected.map(row => row.offerId || row.sku || row.productId || '').filter(Boolean).join('\n'));
}

function toggleOzonBulkProductMenu(force) {
  const menu = document.getElementById('ozonBulkProductMenu');
  const button = document.getElementById('ozonBulkProductMenuBtn');
  if (!menu || !button) return;
  const willOpen = typeof force === 'boolean' ? force : !menu.classList.contains('is-open');
  menu.classList.toggle('is-open', willOpen);
  menu.setAttribute('aria-hidden', willOpen ? 'false' : 'true');
  button.setAttribute('aria-expanded', willOpen ? 'true' : 'false');
}

function copySelectedOzonPostingNumbers() {
  const selected = getSelectedOzonOrderRows();
  if (!selected.length) {
    setOzonResult('请先勾选订单', 'error');
    return;
  }
  copyOzonText(selected.map(row => row.postingNumber || row.orderId || '').filter(Boolean).join('\n'));
}

async function syncOzonWarehouses() {
  const accountId = ozonState.orderAccountId || ozonState.productAccountId || getDefaultOzonAccountId();
  if (!accountId) {
    setOzonSyncProgress('product', 100, '请先选择 OZON 店铺', 'error');
    return;
  }
  setOzonSyncButtonsDisabled(true, 'all');
  setOzonSyncProgress('product', 15, '正在同步 OZON 仓库...');
  try {
    const result = await ozonRequest('/accounts/sync-warehouses', {
      method: 'POST',
      body: JSON.stringify({ ozonAccountId: accountId })
    });
    await loadOzonAccounts();
    setOzonSyncProgress('product', 100, `仓库同步完成：${result.warehouseCount ?? 0} 个`, 'success');
  } catch (err) {
    setOzonSyncProgress('product', 100, err.message || '同步仓库失败', 'error');
  } finally {
    setOzonSyncButtonsDisabled(false, 'all');
  }
}

async function syncOzonProductLimits() {
  const accountId = ozonState.orderAccountId || ozonState.productAccountId || getDefaultOzonAccountId();
  if (!accountId) {
    setOzonSyncProgress('product', 100, '请先选择 OZON 店铺', 'error');
    return;
  }
  setOzonSyncButtonsDisabled(true, 'all');
  setOzonSyncProgress('product', 15, '正在同步 OZON 商品限额...');
  try {
    const result = await ozonRequest('/accounts/sync-limits', {
      method: 'POST',
      body: JSON.stringify({ ozonAccountId: accountId })
    });
    await loadOzonAccounts();
    setOzonSyncProgress('product', 100, `商品限额同步完成：${result.productLimit || '已更新'}`, 'success');
  } catch (err) {
    setOzonSyncProgress('product', 100, err.message || '同步商品限额失败', 'error');
  } finally {
    setOzonSyncButtonsDisabled(false, 'all');
  }
}

function copyOzonProductSummary() {
  const summary = getOzonProductStatusSummary();
  const text = [
    `店铺范围：${ozonState.productAccountId ? (getOzonAccountTitle(ozonState.productAccountId) || '所选店铺') : '全部店铺'}`,
    `商品数：${ozonState.products.length}`,
    ...Object.entries(summary).map(([status, count]) => `${status}: ${count}`)
  ].join('\n');
  copyOzonText(text);
}

function calcOzonPrice() {
  const cost = Number(document.getElementById('ozonToolCostInput')?.value || 0);
  const weight = Number(document.getElementById('ozonToolWeightInput')?.value || 0);
  const length = Number(document.getElementById('ozonToolLengthInput')?.value || 0);
  const width = Number(document.getElementById('ozonToolWidthInput')?.value || 0);
  const height = Number(document.getElementById('ozonToolHeightInput')?.value || 0);
  const domestic = Number(document.getElementById('ozonToolDomesticInput')?.value || 0);
  const commission = Number(document.getElementById('ozonToolCommissionInput')?.value || 0) / 100;
  const profit = Number(document.getElementById('ozonToolProfitInput')?.value || 0) / 100;
  const discount = Number(document.getElementById('ozonToolDiscountInput')?.value || 0) / 100;
  const ads = Number(document.getElementById('ozonToolAdsInput')?.value || 0) / 100;
  const otherRate = Number(document.getElementById('ozonToolOtherRateInput')?.value || 0) / 100;
  const resultEl = document.getElementById('ozonPriceCalcResult');
  const volumetricWeight = length && width && height ? (length * width * height) / 5000 : 0;
  const billableKg = Math.max(weight / 1000, volumetricWeight, 0.1);
  const crossBorderLogistics = billableKg * 28;
  const fixedCost = cost + domestic + crossBorderLogistics;
  const denominator = 1 - commission - profit - ads - otherRate;
  if (!resultEl) return;
  if (!Number.isFinite(fixedCost) || denominator <= 0) {
    resultEl.textContent = '佣金、利润、广告和其他费率合计不能大于或等于 100%。';
    return;
  }
  const suggested = fixedCost / denominator;
  const strike = discount < 1 ? suggested / Math.max(1 - discount, 0.01) : suggested;
  const commissionCost = suggested * commission;
  const profitValue = suggested * profit;
  const adsCost = suggested * ads;
  const otherCost = suggested * otherRate;
  resultEl.innerHTML = `售价 <strong>¥ ${suggested.toFixed(2)}</strong><br>
    划线价 ¥ ${strike.toFixed(2)}<br>
    利润 ¥ ${profitValue.toFixed(2)}；采购成本 ¥ ${cost.toFixed(2)}；平台佣金 ¥ ${commissionCost.toFixed(2)}<br>
    跨境物流费 ¥ ${crossBorderLogistics.toFixed(2)}；国内运费+代贴单 ¥ ${domestic.toFixed(2)}；广告费 ¥ ${adsCost.toFixed(2)}；其他费用 ¥ ${otherCost.toFixed(2)}`;
}

function calcOzonDuty() {
  const declared = Number(document.getElementById('ozonToolDeclareInput')?.value || 0);
  const rate = Number(document.getElementById('ozonToolDutyRateInput')?.value || 0) / 100;
  const actualPrice = Number(document.getElementById('ozonToolActualPriceInput')?.value || 0);
  const actualCost = Number(document.getElementById('ozonToolActualCostInput')?.value || 0);
  const resultEl = document.getElementById('ozonDutyCalcResult');
  if (!resultEl) return;
  if (!Number.isFinite(declared) || !Number.isFinite(rate) || declared < 0 || rate < 0) {
    resultEl.textContent = '请输入有效的申报金额和税率。';
    return;
  }
  const duty = declared * rate;
  const actualProfit = actualPrice > 0 ? actualPrice - actualCost - duty : 0;
  resultEl.innerHTML = `预估关税：<strong>${duty.toFixed(2)}</strong><br>含税成本：${(declared + duty).toFixed(2)}<br>实际利润：${actualPrice > 0 ? actualProfit.toFixed(2) : '--'}`;
}

function bindOzonDashboardEvents() {
  const bindOnce = (id, handler) => {
    const el = document.getElementById(id);
    if (!el || el.dataset.ozonBound === '1') return;
    el.dataset.ozonBound = '1';
    el.addEventListener('click', handler);
  };
  bindOnce('ozonRunAiOrderAnalysisBtn', () => runOzonAiAnalysis('orders'));
  bindOnce('ozonRunAiProductAnalysisBtn', () => runOzonAiAnalysis('links'));
  bindOnce('ozonOpenSellerProductsBtn', () => window.open('https://seller.ozon.ru/app/products', '_blank'));
  bindOnce('ozonOpenSellerOrdersBtn', () => window.open('https://seller.ozon.ru/app/postings', '_blank'));
  bindOnce('ozonOpenSellerHomeBtn', () => window.open('https://seller.ozon.ru/', '_blank'));
  bindOnce('ozonOpenOzonMallBtn', () => window.open('https://www.ozon.ru/', '_blank'));
  bindOnce('ozonGoAccountsBtn', () => {
    toggleOzonAccountAuthPanel();
  });
  bindOnce('ozonToggleAuthPanelBtn', () => toggleOzonAccountAuthPanel());
  bindOnce('ozonAccountAuthCloseBtn', () => toggleOzonAccountAuthPanel(false));
  bindOnce('ozonBackHomeFromAccountsBtn', () => {
    const tab = document.getElementById('ozonHomeNav');
    if (tab) handleTabClick(tab);
  });
  bindOnce('ozonGoCollectionBtn', () => {
    const tab = document.getElementById('ozonCollectionNav');
    if (tab) handleTabClick(tab);
  });
  bindOnce('ozonAuthLogoutBtn', () => logoutDashboardAuth());
  bindOnce('ozonSaveAccountBtn', () => saveOzonAccountFromForm(false));
  bindOnce('ozonCheckAccountBtn', () => saveOzonAccountFromForm(true));
  bindOnce('ozonRefreshAccountsBtn', () => loadOzonAccounts());
  bindOnce('ozonSyncProductsBtn', syncOzonProductsFromSelectedAccount);
  bindOnce('ozonSyncProductsBtn2', syncOzonProductsFromSelectedAccount);
  bindOnce('ozonSyncSelectedProductsBtn', syncSelectedOzonProducts);
  bindOnce('ozonSyncOrdersBtn2', syncOzonOrdersFromSelectedAccount);
  bindOnce('ozonPullNewOrdersBtn', pullNewOzonOrders);
  bindOnce('ozonExportProductsBtn', exportOzonProducts);
  bindOnce('ozonExportOrdersBtn', exportOzonOrders);
  bindOnce('ozonExportOrderAnalysisBtn', exportOzonOrderAnalysis);
  bindOnce('ozonExportProductDiagnosisBtn', exportOzonProductDiagnosis);
  bindOnce('ozonExportLinkDiagnosisBtn', exportOzonLinkDiagnosis);
  bindOnce('ozonRunProductDiagnosisBtn', runOzonProductDiagnosisWithProgress);
  bindOnce('ozonBulkExportOrdersBtn', exportOzonOrders);
  bindOnce('ozonBulkCopyProductIdsBtn', copySelectedOzonProductIds);
  bindOnce('ozonBulkCopyPostingBtn', copySelectedOzonPostingNumbers);
  bindOnce('ozonBulkOpenSellerProductsBtn', () => window.open('https://seller.ozon.ru/app/products', '_blank'));
  bindOnce('ozonBulkProductMenuBtn', (event) => {
    event.stopPropagation();
    toggleOzonBulkProductMenu();
  });
  const bulkProductMenu = document.getElementById('ozonBulkProductMenu');
  if (bulkProductMenu && bulkProductMenu.dataset.ozonBound !== '1') {
    bulkProductMenu.dataset.ozonBound = '1';
    bulkProductMenu.addEventListener('click', (event) => {
      if (event.target?.closest?.('button')) toggleOzonBulkProductMenu(false);
    });
  }
  if (!window.__OZON_BULK_PRODUCT_MENU_CLOSE_BOUND__) {
    window.__OZON_BULK_PRODUCT_MENU_CLOSE_BOUND__ = true;
    document.addEventListener('click', (event) => {
      const wrap = event.target?.closest?.('.ozon-bulk-menu-wrap');
      if (!wrap) toggleOzonBulkProductMenu(false);
    });
  }
  bindOnce('ozonRefreshProductsBtn', () => loadOzonProducts());
  bindOnce('ozonRefreshOrdersBtn', () => Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]));
  bindOnce('ozonRefreshFinanceBtn', () => loadOzonFinance().catch(err => setOzonResult(err.message || '刷新财务失败', 'error')));
  bindOnce('ozonRefreshMessagesBtn', () => loadOzonMessages().catch(err => setOzonResult(err.message || '刷新消息失败', 'error')));
  bindOnce('ozonProductPrevPageBtn', () => {
    ozonState.productPage = Math.max(1, (ozonState.productPage || 1) - 1);
    loadOzonProducts();
  });
  bindOnce('ozonProductNextPageBtn', () => {
    ozonState.productPage = (ozonState.productPage || 1) + 1;
    loadOzonProducts();
  });
  bindOnce('ozonOrderPrevPageBtn', () => {
    ozonState.orderPage = Math.max(1, (ozonState.orderPage || 1) - 1);
    Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
  });
  bindOnce('ozonOrderNextPageBtn', () => {
    ozonState.orderPage = (ozonState.orderPage || 1) + 1;
    Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
  });
  bindOnce('ozonSyncWarehousesBtn', syncOzonWarehouses);
  bindOnce('ozonSyncLimitsBtn', syncOzonProductLimits);
  bindOnce('ozonCopyProductSummaryBtn', copyOzonProductSummary);
  bindOnce('ozonCalcPriceBtn', calcOzonPrice);
  bindOnce('ozonCalcDutyBtn', calcOzonDuty);
  bindOnce('ozonSearchProductsBtn', () => {
    ozonState.productPage = 1;
    loadOzonProducts();
  });
  bindOnce('ozonSearchDiagnosisBtn', runOzonProductDiagnosisWithProgress);
  bindOnce('ozonSearchOrderAnalysisBtn', () => loadOzonOrderAnalysis());
  bindOnce('ozonClearOrderAnalysisBtn', () => {
    const account = document.getElementById('ozonOrderAnalysisAccountFilter');
    const sku = document.getElementById('ozonOrderAnalysisSkuInput');
    if (account) account.value = '';
    if (sku) sku.value = '';
    ozonState.orderAnalysisAccountId = '';
    ozonState.orderAnalysisSku = '';
    loadOzonOrderAnalysis();
  });
  bindOnce('ozonClearDiagnosisBtn', () => {
    const account = document.getElementById('ozonProductDiagnosisAccountFilter');
    const sku = document.getElementById('ozonProductDiagnosisSkuInput');
    const risk = document.getElementById('ozonProductRiskFilter');
    const keyword = document.getElementById('ozonProductDiagnosisKeywordInput');
    if (account) account.value = '';
    if (sku) sku.value = '';
    if (risk) risk.value = '';
    if (keyword) keyword.value = '';
    const visibility = document.getElementById('ozonProductVisibilityFilter');
    if (visibility) visibility.value = '';
    ozonState.productVisibility = '';
    ozonState.productDiagnosisAccountId = '';
    ozonState.productDiagnosisSku = '';
    ozonState.productDiagnosisRisk = '';
    ozonState.productDiagnosisKeyword = '';
    runOzonProductDiagnosisWithProgress();
  });
  bindOnce('ozonClearProductsBtn', () => {
    const account = document.getElementById('ozonProductAccountFilter');
    const visibility = document.getElementById('ozonProductVisibilityFilter');
    const keyword = document.getElementById('ozonProductKeywordInput');
    const name = document.getElementById('ozonProductNameInput');
    const sku = document.getElementById('ozonProductSkuInput');
    if (account) account.value = '';
    if (visibility) visibility.value = '';
    if (keyword) keyword.value = '';
    if (name) name.value = '';
    if (sku) sku.value = '';
    ozonState.productAccountId = '';
    ozonState.productAccountTouched = true;
    ozonState.productVisibility = '';
    ozonState.productKeyword = '';
    ozonState.productName = '';
    ozonState.productSku = '';
    ozonState.productSelectedIds.clear();
    ozonState.productPage = 1;
    loadOzonProducts();
  });
  bindOnce('ozonSearchOrdersBtn', () => {
    ozonState.orderPage = 1;
    Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
  });
  bindOnce('ozonClearOrdersBtn', () => {
    const account = document.getElementById('ozonOrderAccountFilter');
    const status = document.getElementById('ozonOrderStatusFilter');
    const dateFrom = document.getElementById('ozonOrderDateFromInput');
    const dateTo = document.getElementById('ozonOrderDateToInput');
    const keyword = document.getElementById('ozonOrderKeywordInput');
    const sku = document.getElementById('ozonOrderSkuInput');
    if (account) account.value = '';
    if (status) status.value = '';
    if (dateFrom) dateFrom.value = '';
    if (dateTo) dateTo.value = '';
    if (keyword) keyword.value = '';
    if (sku) sku.value = '';
    ozonState.orderAccountId = '';
    ozonState.orderAccountTouched = true;
    ozonState.orderStatus = '';
    ozonState.orderDateFrom = '';
    ozonState.orderDateTo = '';
    ozonState.orderKeyword = '';
    ozonState.orderSku = '';
    ozonState.orderSelectedIds.clear();
    ozonState.orderPage = 1;
    Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
  });
  bindOnce('ozonSearchAccountsBtn', () => renderOzonAccounts());
  bindOnce('ozonClearAccountsBtn', () => {
    const keyword = document.getElementById('ozonAccountKeywordInput');
    if (keyword) keyword.value = '';
    ozonState.accountKeyword = '';
    renderOzonAccounts();
  });
  bindOnce('ozonSearchCollectionBtn', () => loadOzonCollectionLinks());
  bindOnce('ozonClearCollectionFilterBtn', () => {
    const status = document.getElementById('ozonCollectionStatusFilter');
    const keyword = document.getElementById('ozonCollectionKeywordInput');
    if (status) status.value = '';
    if (keyword) keyword.value = '';
    ozonState.collectionStatus = '';
    ozonState.collectionKeyword = '';
    ozonState.selectedCollectionIds.clear();
    loadOzonCollectionLinks();
  });
  bindOnce('ozonBulkSelectedBtn', () => updateOzonCollectionStatus(getSelectedOzonCollectionIds(), 'selected'));
  bindOnce('ozonBulkReadyBtn', () => updateOzonCollectionStatus(getSelectedOzonCollectionIds(), 'ready'));
  bindOnce('ozonBulkListedBtn', () => updateOzonCollectionStatus(getSelectedOzonCollectionIds(), 'listed'));
  bindOnce('ozonBulkArchiveBtn', () => updateOzonCollectionStatus(getSelectedOzonCollectionIds(), 'archived'));
  const statusFilter = document.getElementById('ozonCollectionStatusFilter');
  if (statusFilter && statusFilter.dataset.ozonBound !== '1') {
    statusFilter.dataset.ozonBound = '1';
    statusFilter.addEventListener('change', () => loadOzonCollectionLinks());
  }
  const productVisibilityFilter = document.getElementById('ozonProductVisibilityFilter');
  if (productVisibilityFilter && productVisibilityFilter.dataset.ozonBound !== '1') {
    productVisibilityFilter.dataset.ozonBound = '1';
    productVisibilityFilter.addEventListener('change', () => {
      ozonState.productSelectedIds.clear();
      ozonState.productPage = 1;
      loadOzonProducts();
    });
  }
  const productAccountFilter = document.getElementById('ozonProductAccountFilter');
  if (productAccountFilter && productAccountFilter.dataset.ozonBound !== '1') {
    productAccountFilter.dataset.ozonBound = '1';
    productAccountFilter.addEventListener('change', () => {
      ozonState.productAccountTouched = true;
      ozonState.productSelectedIds.clear();
      ozonState.productPage = 1;
      Promise.allSettled([loadOzonProducts(), loadOzonProductDiagnosis({ enrichAi: false })]);
    });
  }
  const productDiagnosisAccountFilter = document.getElementById('ozonProductDiagnosisAccountFilter');
  if (productDiagnosisAccountFilter && productDiagnosisAccountFilter.dataset.ozonBound !== '1') {
    productDiagnosisAccountFilter.dataset.ozonBound = '1';
    productDiagnosisAccountFilter.addEventListener('change', () => {
      ozonState.productDiagnosisAccountId = productDiagnosisAccountFilter.value || '';
      loadOzonProductDiagnosis({ enrichAi: false });
    });
  }
  document.querySelectorAll('[data-ozon-product-tab]').forEach(button => {
    if (button.dataset.ozonBound === '1') return;
    button.dataset.ozonBound = '1';
    button.addEventListener('click', () => {
      const visibility = button.dataset.ozonProductTab || '';
      ozonState.productVisibility = visibility;
      ozonState.productSelectedIds.clear();
      ozonState.productPage = 1;
      const hidden = document.getElementById('ozonProductVisibilityFilter');
      if (hidden) hidden.value = visibility;
      Promise.allSettled([loadOzonProducts(), loadOzonProductDiagnosis({ enrichAi: false })]);
    });
  });
  const financeAccountFilter = document.getElementById('ozonFinanceAccountFilter');
  if (financeAccountFilter && financeAccountFilter.dataset.ozonBound !== '1') {
    financeAccountFilter.dataset.ozonBound = '1';
    financeAccountFilter.addEventListener('change', () => {
      ozonState.financeAccountId = financeAccountFilter.value || '';
      loadOzonFinance();
    });
  }
  const messageAccountFilter = document.getElementById('ozonMessageAccountFilter');
  if (messageAccountFilter && messageAccountFilter.dataset.ozonBound !== '1') {
    messageAccountFilter.dataset.ozonBound = '1';
    messageAccountFilter.addEventListener('change', () => {
      ozonState.messageAccountId = messageAccountFilter.value || '';
      ozonState.selectedMessageId = '';
      loadOzonMessages();
    });
  }
  document.querySelectorAll('[data-ozon-message-tab]').forEach(button => {
    if (button.dataset.ozonBound === '1') return;
    button.dataset.ozonBound = '1';
    button.addEventListener('click', () => {
      ozonState.messageType = button.dataset.ozonMessageTab || '';
      renderOzonMessages();
    });
  });
  const productKeywordInput = document.getElementById('ozonProductKeywordInput');
  if (productKeywordInput && productKeywordInput.dataset.ozonBound !== '1') {
    productKeywordInput.dataset.ozonBound = '1';
    productKeywordInput.addEventListener('keydown', (event) => {
      if (event.key === 'Enter') {
        ozonState.productPage = 1;
        loadOzonProducts();
      }
    });
  }
  ['ozonProductNameInput', 'ozonProductSkuInput'].forEach((id) => {
    const input = document.getElementById(id);
    if (input && input.dataset.ozonBound !== '1') {
      input.dataset.ozonBound = '1';
      input.addEventListener('keydown', (event) => {
        if (event.key === 'Enter') {
          ozonState.productPage = 1;
          loadOzonProducts();
        }
      });
    }
  });
  const orderStatusFilter = document.getElementById('ozonOrderStatusFilter');
  if (orderStatusFilter && orderStatusFilter.dataset.ozonBound !== '1') {
    orderStatusFilter.dataset.ozonBound = '1';
    orderStatusFilter.addEventListener('change', () => {
      ozonState.orderPage = 1;
      Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
    });
  }
  const orderAccountFilter = document.getElementById('ozonOrderAccountFilter');
  if (orderAccountFilter && orderAccountFilter.dataset.ozonBound !== '1') {
    orderAccountFilter.dataset.ozonBound = '1';
    orderAccountFilter.addEventListener('change', () => {
      ozonState.orderAccountTouched = true;
      ozonState.orderSelectedIds.clear();
      ozonState.orderPage = 1;
      Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
    });
  }
  const orderAnalysisAccountFilter = document.getElementById('ozonOrderAnalysisAccountFilter');
  if (orderAnalysisAccountFilter && orderAnalysisAccountFilter.dataset.ozonBound !== '1') {
    orderAnalysisAccountFilter.dataset.ozonBound = '1';
    orderAnalysisAccountFilter.addEventListener('change', () => {
      ozonState.orderAnalysisAccountId = orderAnalysisAccountFilter.value || '';
      loadOzonOrderAnalysis();
    });
  }
  document.querySelectorAll('[data-ozon-order-tab]').forEach(button => {
    if (button.dataset.ozonBound === '1') return;
    button.dataset.ozonBound = '1';
    button.addEventListener('click', () => {
      const status = button.dataset.ozonOrderTab || '';
      ozonState.orderStatus = status;
      ozonState.orderPage = 1;
      const hidden = document.getElementById('ozonOrderStatusFilter');
      if (hidden) hidden.value = status;
      Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
    });
  });
  ['ozonOrderDateFromInput', 'ozonOrderDateToInput'].forEach((id) => {
    const input = document.getElementById(id);
    if (input && input.dataset.ozonBound !== '1') {
      input.dataset.ozonBound = '1';
      input.addEventListener('change', () => {
        ozonState.orderPage = 1;
        Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
      });
    }
  });
  const keywordInput = document.getElementById('ozonCollectionKeywordInput');
  if (keywordInput && keywordInput.dataset.ozonBound !== '1') {
    keywordInput.dataset.ozonBound = '1';
    keywordInput.addEventListener('keydown', (event) => {
      if (event.key === 'Enter') loadOzonCollectionLinks();
    });
  }
  const orderKeywordInput = document.getElementById('ozonOrderKeywordInput');
  if (orderKeywordInput && orderKeywordInput.dataset.ozonBound !== '1') {
    orderKeywordInput.dataset.ozonBound = '1';
    orderKeywordInput.addEventListener('keydown', (event) => {
      if (event.key === 'Enter') {
        ozonState.orderPage = 1;
        Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
      }
    });
  }
  ['ozonOrderSkuInput', 'ozonOrderAnalysisSkuInput', 'ozonProductDiagnosisSkuInput', 'ozonProductDiagnosisKeywordInput'].forEach((id) => {
    const input = document.getElementById(id);
    if (input && input.dataset.ozonBound !== '1') {
      input.dataset.ozonBound = '1';
      input.addEventListener('keydown', (event) => {
        if (event.key !== 'Enter') return;
        if (id === 'ozonOrderSkuInput') {
          ozonState.orderPage = 1;
          Promise.allSettled([loadOzonOrders(), loadOzonOrderAnalysis()]);
        } else if (id === 'ozonOrderAnalysisSkuInput') {
          loadOzonOrderAnalysis();
        } else {
          loadOzonProductDiagnosis({ enrichAi: false });
        }
      });
    }
  });
  const accountKeywordInput = document.getElementById('ozonAccountKeywordInput');
  if (accountKeywordInput && accountKeywordInput.dataset.ozonBound !== '1') {
    accountKeywordInput.dataset.ozonBound = '1';
    accountKeywordInput.addEventListener('keydown', (event) => {
      if (event.key === 'Enter') renderOzonAccounts();
    });
  }
  [
    'ozonToolCostInput', 'ozonToolWeightInput', 'ozonToolLengthInput', 'ozonToolWidthInput',
    'ozonToolHeightInput', 'ozonToolCommissionInput', 'ozonToolProfitInput', 'ozonToolDiscountInput',
    'ozonToolDomesticInput', 'ozonToolAdsInput', 'ozonToolOtherRateInput'
  ].forEach((id) => {
    const input = document.getElementById(id);
    if (input && input.dataset.ozonBound !== '1') {
      input.dataset.ozonBound = '1';
      input.addEventListener('input', calcOzonPrice);
    }
  });
  ['ozonToolDeclareInput', 'ozonToolDutyRateInput', 'ozonToolActualPriceInput', 'ozonToolActualCostInput'].forEach((id) => {
    const input = document.getElementById(id);
    if (input && input.dataset.ozonBound !== '1') {
      input.dataset.ozonBound = '1';
      input.addEventListener('input', calcOzonDuty);
    }
  });
  bindOnce('ozonProfitToolTabBtn', () => document.getElementById('ozonToolActualPriceInput')?.focus());
  bindOnce('ozonWatermarkToolTabBtn', () => setOzonResult('水印管理需要接入图片处理流程，已保留入口。', 'info'));
  bindOnce('ozonDeepseekToolTabBtn', () => setOzonResult('DeepSeek 工具入口已保留，后续接入标题/属性优化。', 'info'));
  bindOnce('ozonTrackToolTabBtn', () => window.open('https://www.17track.net/', '_blank'));
  calcOzonPrice();
  calcOzonDuty();
}

function setAuthMode(mode = 'login') {
  const normalizedMode = mode === 'register' || mode === 'reset' ? mode : 'login';
  const loginPanel = document.getElementById('authLoginPanel');
  const registerPanel = document.getElementById('authRegisterPanel');
  const resetPanel = document.getElementById('authResetPanel');
  const loginBtn = document.getElementById('authModeLoginBtn');
  const registerBtn = document.getElementById('authModeRegisterBtn');
  const title = document.getElementById('authPanelTitle');
  if (loginPanel) loginPanel.style.display = normalizedMode === 'login' ? 'grid' : 'none';
  if (registerPanel) registerPanel.style.display = normalizedMode === 'register' ? 'grid' : 'none';
  if (resetPanel) resetPanel.style.display = normalizedMode === 'reset' ? 'grid' : 'none';
  syncAuthPlatformVisibility(normalizedMode);
  if (loginBtn) loginBtn.classList.toggle('active', normalizedMode === 'login' || normalizedMode === 'reset');
  if (registerBtn) registerBtn.classList.toggle('active', normalizedMode === 'register');
  if (title) {
    title.textContent = normalizedMode === 'register' ? '账号注册' : normalizedMode === 'reset' ? '找回密码' : '账号登录';
  }
  if (normalizedMode === 'register') refreshAuthCaptcha();
}


function refreshAuthCaptcha() {
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  authCaptchaValue = Array.from({ length: 4 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
  const btn = document.getElementById('authCaptchaRefreshBtn');
  if (btn) btn.textContent = authCaptchaValue;
}

function renderDashAuthStatus(token, user) {
  renderTopbarAccount(token ? user : null);
  const statusEls = [
    document.getElementById('loginAuthStatus'),
    document.getElementById('dashAuthStatus')
  ].filter(Boolean);
  if (!statusEls.length) return;
  if (!token) {
    statusEls.forEach(el => { el.textContent = '未登录'; });
    syncDingHuiTemuHome();
    return;
  }
  const phone = user?.phone ? `：${user.phone}` : '';
  statusEls.forEach(el => { el.textContent = `已登录${phone}`; });
  syncDingHuiTemuHome();
}

function startDashAuthCooldown(buttonId = 'authSendResetCodeBtn', seconds = 60) {
  const btn = document.getElementById(buttonId);
  if (!btn) return;
  let remaining = seconds;
  btn.disabled = true;
  btn.textContent = `${remaining}s后重试`;
  if (dashAuthCooldownTimer) clearInterval(dashAuthCooldownTimer);
  dashAuthCooldownTimer = setInterval(() => {
    remaining -= 1;
    if (remaining <= 0) {
      clearInterval(dashAuthCooldownTimer);
      dashAuthCooldownTimer = null;
      btn.disabled = false;
      btn.textContent = '发送验证码';
      return;
    }
    btn.textContent = `${remaining}s后重试`;
  }, 1000);
}

async function submitPasswordLogin() {
  const phone = document.getElementById('authLoginPhone')?.value?.trim();
  const password = document.getElementById('authLoginPassword')?.value || '';
  if (!(/^(?:admin|1\d{10})$/.test(phone || ''))) {
    setDashAuthResult('请输入正确的手机号或管理员账号', 'error');
    return;
  }
  if (!password) {
    setDashAuthResult('请输入密码', 'error');
    return;
  }
  const resultEl = document.getElementById('loginAuthResult') || document.getElementById('dashAuthResult');
  if (resultEl) {
    resultEl.style.display = 'block';
    resultEl.textContent = '登录中...';
    resultEl.style.color = '#666';
  }
  const loginBtn = document.getElementById('dashPasswordLoginBtn');
  if (loginBtn) {
    loginBtn.disabled = true;
    loginBtn.textContent = '登录中...';
  }
  const pendingUser = { phone, nickname: '同步中...' };
  renderDashAuthStatus('pending', pendingUser);
  setDashboardAuthPanelVisible(false);
  setDashboardLockedState(false);
  renderPendingDashboardLogin(pendingUser);
  setFeatureAccessState(true, '正在登录并同步店铺授权');
  setDashAuthResult('正在登录，页面已先为你打开...', 'info');
  const paintPromise = waitForDashboardPaint();
  try {
    clearServerAuthState({ localOnly: true }).catch(err => {
      console.warn('[小青蛙电商助手] 登录前本地清理失败:', err);
    });
    const loginPromise = postJsonWithFallback('/user/login', { phone, password }, true);
    await paintPromise;
    const loginResult = await loginPromise;
    if (!loginResult.ok || !loginResult.result?.token) {
      setDashboardLockedState(true);
      setDashboardAuthPanelVisible(true);
      setDashAuthResult(loginResult.error || loginResult.result?.error || '登录失败', 'error');
      return;
    }
    SERVER_URL = loginResult.base;
    localStorage.setItem('temuApiBase', SERVER_URL);
    localStorage.setItem(AUTH_PLATFORM_KEY, getSelectedAuthPlatform());
    await setServerAuthState({
      token: loginResult.result.token,
      user: loginResult.result.user || { phone }
    });
    enterDashboardOptimistically(loginResult.result.token, loginResult.result.user || { phone });
  } catch (err) {
    console.error('[小青蛙电商助手] 密码登录失败:', err);
    setDashboardLockedState(true);
    setDashboardAuthPanelVisible(true);
    setDashAuthResult('登录失败，请检查后端服务', 'error');
  } finally {
    const latestBtn = document.getElementById('dashPasswordLoginBtn');
    if (latestBtn) {
      latestBtn.disabled = false;
      latestBtn.textContent = '登录';
    }
  }
}

async function sendResetPasswordCode() {
  await sendSmsCode('reset');
}

async function submitResetPassword() {
  const phone = document.getElementById('authResetPhone')?.value?.trim();
  const code = document.getElementById('authResetCode')?.value?.trim();
  const password = document.getElementById('authResetPassword')?.value || '';
  if (!/^1\d{10}$/.test(phone || '')) return setDashAuthResult('请输入正确的 11 位手机号', 'error');
  if (!code) return setDashAuthResult('请输入短信验证码', 'error');
  if (password.length < 6) return setDashAuthResult('新密码至少 6 位', 'error');
  const result = await postJsonWithFallback('/auth/reset-password', { phone, code, password }, true);
  if (!result.ok) return setDashAuthResult(result.error || result.result?.error || '重置密码失败', 'error');
  setDashAuthResult(result.result?.message || '密码已重置，请使用新密码登录', 'success');
  setAuthMode('login');
}

async function sendSmsCode(purpose = 'login') {
  const phone = purpose === 'reset'
    ? document.getElementById('authResetPhone')?.value?.trim()
    : purpose === 'register'
      ? document.getElementById('authRegisterPhone')?.value?.trim()
      : document.getElementById('authCodePhone')?.value?.trim();
  if (!/^1\d{10}$/.test(phone || '')) return setDashAuthResult('请输入正确的 11 位手机号', 'error');
  if (purpose === 'register') {
    const captcha = document.getElementById('authCaptchaInput')?.value?.trim().toUpperCase();
    if (!captcha || captcha !== authCaptchaValue) {
      refreshAuthCaptcha();
      return setDashAuthResult('图形验证码不正确，请重新输入', 'error');
    }
  }
  const buttonId = purpose === 'reset'
    ? 'authSendResetCodeBtn'
    : purpose === 'register'
      ? 'authSendRegisterCodeBtn'
      : 'authSendLoginCodeBtn';
  const result = await postJsonWithFallback('/auth/send-code', { phone, purpose }, true);
  if (!result.ok) return setDashAuthResult(result.error || result.result?.error || '验证码发送失败', 'error');
  setDashAuthResult(result.result?.message || '验证码已发送', 'success');
  startDashAuthCooldown(buttonId, 60);
}

async function submitCodeLogin() {
  const phone = document.getElementById('authCodePhone')?.value?.trim();
  const code = document.getElementById('authLoginCode')?.value?.trim();
  if (!/^1\d{10}$/.test(phone || '')) return setDashAuthResult('请输入正确的 11 位手机号', 'error');
  if (!code) return setDashAuthResult('请输入短信验证码', 'error');
  const loginResult = await postJsonWithFallback('/auth/login', { phone, code }, true);
  if (!loginResult.ok || !loginResult.result?.token) {
    setDashboardLockedState(true);
    setDashboardAuthPanelVisible(true);
    return setDashAuthResult(loginResult.error || loginResult.result?.error || '验证码登录失败', 'error');
  }
  SERVER_URL = loginResult.base;
  localStorage.setItem('temuApiBase', SERVER_URL);
  await setServerAuthState({
    token: loginResult.result.token,
    user: loginResult.result.user || { phone }
  });
  enterDashboardOptimistically(loginResult.result.token, loginResult.result.user || { phone });
}

async function submitRegister() {
  const phone = document.getElementById('authRegisterPhone')?.value?.trim();
  const operatorName = document.getElementById('authRegisterOperator')?.value?.trim();
  const captcha = document.getElementById('authCaptchaInput')?.value?.trim().toUpperCase();
  const code = document.getElementById('authRegisterCode')?.value?.trim();
  const password = document.getElementById('authRegisterPassword')?.value || '';
  const passwordConfirm = document.getElementById('authRegisterPasswordConfirm')?.value || '';
  if (!/^1\d{10}$/.test(phone || '')) return setDashAuthResult('请输入正确的 11 位手机号', 'error');
  if (!/^(?=.*[\u4e00-\u9fa5])[\u4e00-\u9fa5\d]{2,12}$/.test(operatorName || '')) {
    return setDashAuthResult('运营人员必填，只能填写中文或中文加数字', 'error');
  }
  if (!captcha || captcha !== authCaptchaValue) {
    refreshAuthCaptcha();
    return setDashAuthResult('图形验证码不正确，请重新输入', 'error');
  }
  if (!code) return setDashAuthResult('请输入短信验证码', 'error');
  if (password.length < 6) return setDashAuthResult('密码至少 6 位', 'error');
  if (password !== passwordConfirm) return setDashAuthResult('两次输入的密码不一致', 'error');

  const registerBtn = document.getElementById('dashRegisterBtn');
  if (registerBtn) {
    registerBtn.disabled = true;
    registerBtn.textContent = '注册中...';
  }
  try {
    const registerResult = await postJsonWithFallback('/auth/register', { phone, code, password, operatorName }, true);
    if (!registerResult.ok || !registerResult.result?.token) {
      return setDashAuthResult(registerResult.error || registerResult.result?.error || '注册失败', 'error');
    }
    SERVER_URL = registerResult.base;
    localStorage.setItem('temuApiBase', SERVER_URL);
    await setServerAuthState({
      token: registerResult.result.token,
      user: registerResult.result.user || { phone }
    });
    enterDashboardOptimistically(registerResult.result.token, registerResult.result.user || { phone });
  } catch (err) {
    console.error('[小青蛙电商助手] 注册失败:', err);
    setDashAuthResult('注册失败，请稍后重试', 'error');
  } finally {
    const latestBtn = document.getElementById('dashRegisterBtn');
    if (latestBtn) {
      latestBtn.disabled = false;
      latestBtn.textContent = '注册并登录';
    }
  }
}

async function loadDashboardMembership() {
  try {
    const data = await getServerAuthState();
    const token = data.temuServerToken;
    const user = data.temuServerUser;
    renderDashAuthStatus(token, user);
    if (!token) {
      setDashboardAuthPanelVisible(true);
      state.boundShops = [];
      renderDashboardAccessPanels(null, null, [], null);
      clearDashboardSalesVisuals();
      setDashboardLockedState(true);
      setFeatureAccessState(true, '请先登录并完成店铺授权');
      setAuthMode('login');
      setDashAuthResult('请输入手机号和密码登录，或切换到注册创建新账号', 'info');
      return;
    }
    if (isOzonWorkspace()) {
      const resp = await fetch(`${SERVER_URL}/user/me?platform=ozon`, { headers: { 'Authorization': `Bearer ${token}` } });
      const result = await resp.json().catch(() => ({}));
      if (!resp.ok || !result.ok) {
        const authExpired = isDashboardAuthExpiredResponse(resp.status, result);
        if (authExpired) {
          await handleDashboardAuthExpired(result.error || result.message || '登录已失效，请重新登录');
        } else {
          keepDashboardVisibleAfterMembershipFailure(result.error || result.message || `会员状态加载失败（HTTP ${resp.status}），请稍后重试`);
        }
        return;
      }
      const currentUser = result.user || user;
      persistCurrentServerUser(currentUser);
      applyRoleFeatureVisibility(currentUser);
      renderDashAuthStatus(token, currentUser);
      setDashboardAuthPanelVisible(false);
      setDashboardLockedState(false);
      setFeatureAccessState(false, '');
      setDashAuthResult('已进入 OZON 工作台', 'success');
      switchWorkspaceForPlatform('ozon');
      return;
    }
    const resp = await fetch(`${SERVER_URL}/user/me?platform=temu`, { headers: { 'Authorization': `Bearer ${token}` } });
    const result = await resp.json().catch(() => ({}));
    if (result.ok) {
      setDashboardAuthPanelVisible(false);
      let membership = result.membership;
      let shops = (Array.isArray(result.shops) ? result.shops : []).filter(shop => !String(shop?.mallId || '').toLowerCase().startsWith('ozon:'));
      const currentUser = result.user || user;
      persistCurrentServerUser(currentUser);
      applyRoleFeatureVisibility(currentUser);
      state.boundShops = shops;
      let currentShopStatus = null;
      if (state.mallId) {
        try {
          const shopResp = await fetch(`${SERVER_URL}/membership/shop-status/${encodeURIComponent(state.mallId)}`, {
            headers: { 'Authorization': `Bearer ${token}` }
          });
          const shopResult = await shopResp.json();
          if (shopResp.ok && shopResult.ok) currentShopStatus = shopResult;
        } catch (shopErr) {
          console.warn('[小青蛙电商助手] 查询当前店铺授权状态失败:', shopErr);
        }
      }
      setDashboardLockedState(false);
      renderDashboardAccessPanels(currentUser, membership, shops, currentShopStatus);
      restoreConnectedShopFromStorage({ updateUi: true, detectIfMissing: true }).catch(err => {
        console.warn('[小青蛙电商助手] 后台恢复当前 TEMU 店铺失败:', err);
      });
      scheduleDashboardDataRefresh();
      const renewCard = document.getElementById('dashRenewSection');
      if (renewCard) renewCard.style.display = 'none';
      const admin = isAdminUser(currentUser);
      const currentShopAllowed = isCurrentShopAllowed(currentShopStatus, shops, state.mallId);
      setFeatureAccessState(!currentShopAllowed, !state.mallId
        ? (admin ? '' : '当前账号暂无可用授权店铺，请先绑定并完成卡密授权')
        : currentShopAllowed
          ? ''
          : currentShopStatus?.shopBound
            ? '当前店铺已绑定但未授权，请输入卡密购买年卡/月卡'
            : '当前店铺未绑定，请先绑定店铺并完成授权');
      if (!state.mallId) {
        setDashAuthResult(admin
          ? '管理员账号已登录；执行 TEMU 操作前请连接当前店铺'
          : (currentShopAllowed
            ? '账号已登录，服务器绑定店铺已加载；执行 TEMU 操作前请连接当前店铺'
            : '账号已登录，但当前账号暂无可用授权店铺，请先绑定并授权'),
          currentShopAllowed ? 'success' : 'info');
      } else if (currentShopAllowed) {
        setDashAuthResult(admin ? '管理员账号已登录，当前店铺可直接使用' : '当前店铺已授权，可正常使用插件', 'success');
      } else if (currentShopStatus && currentShopStatus.shopBound && !currentShopStatus.isMember) {
        setDashAuthResult('当前店铺已绑定但未授权，请输入卡密购买年卡/月卡', 'error');
      } else {
        setDashAuthResult('当前店铺未绑定，请先绑定店铺并完成授权', 'error');
      }
    } else {
      const statusCode = resp.status;
      const authExpired = isDashboardAuthExpiredResponse(statusCode, result);
      if (authExpired) {
        await handleDashboardAuthExpired(result.error || result.message || '登录已失效，请重新登录');
      } else {
        keepDashboardVisibleAfterMembershipFailure(result.error || result.message || `会员状态加载失败（HTTP ${statusCode}），请稍后重试`);
      }
    }
  } catch (err) {
    console.error('[小青蛙电商助手] 加载会员状态失败:', err);
    keepDashboardVisibleAfterMembershipFailure('加载会员状态失败，请稍后重试');
  }
}

function isDashboardAuthExpiredResponse(statusCode, result = {}) {
  const status = Number(statusCode || 0);
  if (status === 401 || status === 403) return true;
  const text = String(result.error || result.message || result.code || '');
  return /token|登录已失效|用户不存在|expired|unauthor|USER_NOT_FOUND|INVALID_TOKEN|TOKEN_EXPIRED|USER_DISABLED/i.test(text);
}

async function handleDashboardAuthExpired(message = '登录已失效，请重新登录') {
  state.boundShops = [];
  renderDashboardAccessPanels(null, null, [], null);
  clearDashboardSalesVisuals();
  await clearServerAuthState();
  renderDashAuthStatus(null, null);
  setDashboardLockedState(true);
  setAuthMode('login');
  setDashAuthResult(message, 'error');
}

function keepDashboardVisibleAfterMembershipFailure(message = '会员状态加载失败，请稍后重试') {
  setDashboardAuthPanelVisible(false);
  setDashboardLockedState(false);
  setDashAuthResult(message, 'error');
}

async function logoutDashboardAuth() {
  state.boundShops = [];
  renderDashboardAccessPanels(null, null, [], null);
  clearDashboardSalesVisuals();
  await clearServerAuthState();
  renderDashAuthStatus(null, null);
  setDashboardLockedState(true);
  setFeatureAccessState(true, '请先登录并完成店铺授权');
  setAuthMode('login');
  setDashAuthResult('已退出登录', 'info');
}

function cleanupLaunchAuthUi() {
  const authTitle = document.querySelector('#authGate .auth-card h2');
  if (authTitle) authTitle.textContent = '账号登录';

  const loginPhone = document.getElementById('authLoginPhone');
  if (loginPhone) {
    loginPhone.setAttribute('placeholder', '手机号 / 管理员账号');
    loginPhone.removeAttribute('inputmode');
    loginPhone.removeAttribute('maxlength');
  }

  const loginPassword = document.getElementById('authLoginPassword');
  if (loginPassword) loginPassword.setAttribute('placeholder', '密码');

  const passwordLoginBtn = document.getElementById('dashPasswordLoginBtn');
  if (passwordLoginBtn) passwordLoginBtn.textContent = '登录';
}

function bindTemuReconnectDelegates() {
  if (!document.body || document.body.dataset.temuReconnectDelegated === '1') return;
  document.body.dataset.temuReconnectDelegated = '1';
  document.body.addEventListener('click', async (event) => {
    const button = event.target.closest?.('#dhTemuReconnectBtn, #dhTemuReconnectQuickBtn');
    if (!button) return;
    event.preventDefault();
    await reconnectCurrentShop();
  });
}

// 初始化首页事件
function initDashboard() {
  cleanupLaunchAuthUi();
  bindExtensionDownloadButtons();
  bindTemuReconnectDelegates();
  normalizeWorkspaceTabLabels();
  mergeDashboardTrendCardIntoSalesCard();
  initAuthPlatformChoice();
  bindOzonDashboardEvents();
  switchWorkspaceForPlatform(getSelectedAuthPlatform());
  setAuthMode('login');
  document.getElementById('authModeLoginBtn')?.addEventListener('click', () => setAuthMode('login'));
  document.getElementById('authModeRegisterBtn')?.addEventListener('click', () => setAuthMode('register'));
  document.getElementById('authModeCodeBtn')?.addEventListener('click', () => setAuthMode('code'));
  document.getElementById('authModeResetBtn')?.addEventListener('click', () => setAuthMode('reset'));
  document.getElementById('authBackLoginBtn')?.addEventListener('click', () => setAuthMode('login'));
  document.getElementById('authCaptchaRefreshBtn')?.addEventListener('click', refreshAuthCaptcha);
  document.getElementById('dashPasswordLoginBtn')?.addEventListener('click', submitPasswordLogin);
  document.getElementById('authSendLoginCodeBtn')?.addEventListener('click', () => sendSmsCode('login'));
  document.getElementById('dashCodeLoginBtn')?.addEventListener('click', submitCodeLogin);
  document.getElementById('authSendRegisterCodeBtn')?.addEventListener('click', () => sendSmsCode('register'));
  document.getElementById('dashRegisterBtn')?.addEventListener('click', submitRegister);
  document.getElementById('authSendResetCodeBtn')?.addEventListener('click', sendResetPasswordCode);
  document.getElementById('dashResetPasswordBtn')?.addEventListener('click', submitResetPassword);
  document.getElementById('dhTemuVisitStoreBtn')?.addEventListener('click', () => {
    visitCurrentStore();
  });
  document.getElementById('dhTemuBindCurrentShopBtn')?.addEventListener('click', handleCurrentDashboardShopBindClick);
  document.getElementById('dashThirtyCard')?.addEventListener('click', refreshDashboardMonthSalesFromCards);
  document.getElementById('dashThirtyRow')?.addEventListener('click', refreshDashboardMonthSalesFromCards);
  document.getElementById('dashRedeemBtn')?.addEventListener('click', async () => {
    const codeInput = document.getElementById('dashCardInput');
    const code = codeInput?.value?.trim();
    if (!code) { showToast('请输入卡密', 'error'); return; }
    const token = await getServerAuthToken();
    if (!token) { showToast('请先登录会员账号', 'error'); return; }
    const resultEl = document.getElementById('dashRedeemResult');
    resultEl.style.display = 'block'; resultEl.textContent = '兑换中...'; resultEl.style.color = '#666';
    try {
      const resp = await fetch(`${SERVER_URL}/membership/redeem`, {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ code })
      });
      const result = await resp.json();
      if (result.ok) {
        resultEl.textContent = '🎉 ' + result.message; resultEl.style.color = '#52c41a'; codeInput.value = '';
        loadDashboardMembership();
      } else { resultEl.textContent = '❌ ' + (result.error || '兑换失败'); resultEl.style.color = '#ff4d4f'; }
    } catch (err) { resultEl.textContent = '❌ 网络错误'; resultEl.style.color = '#ff4d4f'; }
  });
  syncDingHuiTemuHome();
}

function buildSalesRequestParams() {
  if (!state.mallId || !state.antiContent) return null;
  return { mallId: state.mallId, antiContent: state.antiContent, pageNum: 1, pageSize: salesDataState.pageSize };
}

async function refreshDashboardMonthSalesFromCards() {
  if (!state.mallId || !state.antiContent) {
    showToast('请先连接店铺', 'error');
    return;
  }
  if (salesDataState.loading) {
    showToast('销量数据正在同步中，请稍等', 'info');
    return;
  }
  const amount = document.getElementById('dashThirtyDayAmount');
  const count = document.getElementById('dashThirtyDayCount');
  const stand = document.getElementById('dashThirtyDayAmountStandalone');
  if (amount) amount.textContent = '同步中...';
  if (count) count.textContent = '...';
  if (stand) stand.textContent = '同步中...';
  showToast('正在同步销量查询数据...', 'info');
  await refreshSalesData({ fromDashboardMonthCard: true });
}
// 渲染每日销量柱状图（使用真实每日销量数据）
function renderSalesBarChart(products) {
  const containers = [
    document.getElementById('salesBarChart'),
    document.getElementById('dashDailySalesBarChart'),
    document.getElementById('dashBarChart')
  ].filter(Boolean);
  if (containers.length === 0) return;

  if (!products || products.length === 0) {
    containers.forEach(container => { container.innerHTML = '<div class="bar-chart-empty">暂无销量数据，请先查询</div>'; });
    return;
  }

  const bars = buildSevenDayTrendBars(products);
  if (bars.length === 0) {
    containers.forEach(container => { container.innerHTML = '<div class="bar-chart-empty">暂无真实日趋势数据</div>'; });
    return;
  }

  const html = `<div class="bar-chart-row">${renderTrendBarChartMarkup(bars)}</div>`;
  containers.forEach(container => { container.innerHTML = html; });
}

function initSalesDataTab() {
  const salesHeadRow = document.querySelector('#salesTable thead tr');
  if (salesHeadRow && salesHeadRow.children.length >= 3) {
    salesHeadRow.children[0].textContent = '商品信息';
    salesHeadRow.children[1].textContent = 'SKU明细';
    salesHeadRow.removeChild(salesHeadRow.children[2]);
  }
  const sortEl = document.getElementById('salesSortSelect');
  if (sortEl) sortEl.value = 'todayDesc';
  document.getElementById('salesRefreshBtn')?.addEventListener('click', () => refreshSalesData());
  document.getElementById('salesRefreshTodayBtn')?.addEventListener('click', () => {
    refreshSalesData();
  });
  document.getElementById('salesSearchBtn')?.addEventListener('click', () => runSalesSearch());
  const salesExportBtn = document.getElementById('salesExportBtn');
  if (salesExportBtn && salesExportBtn.dataset.temuSalesExportBound !== '1') {
    salesExportBtn.dataset.temuSalesExportBound = '1';
    salesExportBtn.addEventListener('click', () => exportSalesData());
  }
  document.getElementById('salesPageSizeSelect')?.addEventListener('change', (e) => {
    salesDataState.pageSize = parseInt(e.target.value);
    salesDataState.pageNum = 1;
    applySalesSearchFilter();
  });
  document.getElementById('salesTableSearchInput')?.addEventListener('keydown', (e) => {
    if (e.key === 'Enter') runSalesSearch();
  });
  // 排序选择
  document.getElementById('salesSortSelect')?.addEventListener('change', () => {
    salesDataState.sortBy = document.getElementById('salesSortSelect')?.value || 'todayDesc';
    sortSalesData();
  });

  // 初次载入后补刷一次会员状态，避免认证信息尚未恢复完成就被锁死
  setTimeout(() => {
    if (document.body.contains(document.getElementById('dashAuthStatus'))) {
      loadDashboardMembership().catch(err => console.warn('[小青蛙电商助手] 延迟刷新会员状态失败:', err));
    }
  }, 1800);
}

function sortSalesData() {
  salesDataState.pageNum = 1;
  sortSalesProducts();
  applySalesSearchFilter();
}

function runSalesSearch() {
  salesDataState.pageNum = 1;
  const query = getSalesSearchQuery();
  if (!query) salesDataState.viewProducts = null;
  applySalesSearchFilter();
}

async function refreshSalesData(params = {}) {
  if (salesDataState.loading) return;
  salesDataState.loading = true;
  updateSalesSyncDisplay();

  const container = document.getElementById('salesTableBody');
  if (!container && !params.fromDashboardMonthCard) {
    salesDataState.loading = false;
    console.warn('[小青蛙电商助手] 销量表格未初始化，跳过刷新');
    showToast('销量查询界面未初始化完成，请刷新插件页后重试', 'error');
    return;
  }
  if (container) container.innerHTML = '<tr><td colspan="2" class="loading">加载中...</td></tr>';

  const pageNum = params.pageNum || salesDataState.pageNum;
  const pageSize = salesDataState.pageSize;
  const requestParams = { pageSize: 100 };

  try {
    const result = await fetchSalesDataAllDirect(requestParams);

    if (result?.success) {
      const products = result.products || [];
      salesDataState.products = products;
      salesDataState.total = result.total || products.length || 0;
      salesDataState.pageNum = pageNum;
      salesDataState.totalPages = Math.max(1, Math.ceil(products.length / pageSize));
      salesDataState.allProducts = products;
      salesDataState.viewProducts = null;
      salesDataState.lastSyncAt = Date.now();
      sortSalesProducts();
      await saveSalesDataCache();
      updateSalesSyncDisplay();
      if (container) applySalesSearchFilter();
      updateSalesDashboard();
      if (params.fromDashboardMonthCard) showToast('当月销量已同步', 'success');
    } else {
      if (container) container.innerHTML = `<tr><td colspan="2" class="empty"><div class="empty-icon">❌</div><p>${escapeHtml(result?.error || '获取销售数据失败')}</p></td></tr>`;
      if (params.fromDashboardMonthCard) showToast(result?.error || '获取销售数据失败', 'error');
    }
  } catch (err) {
    const message = err?.message || '获取销售数据失败';
    if (container) container.innerHTML = `<tr><td colspan="2" class="empty"><div class="empty-icon">❌</div><p>${escapeHtml(message)}</p></td></tr>`;
    showToast(message, 'error');
  } finally {
    salesDataState.loading = false;
  }
}

function renderSalesDataTable() {
  const container = document.getElementById('salesTableBody');
  if (!container) {
    console.warn('[小青蛙电商助手] 销量表格不存在，跳过渲染');
    return;
  }
  const products = getSalesDisplayProducts();

  if (products.length === 0) {
    const emptyText = salesDataState.lastSyncAt ? '暂无可展示的数据' : '点击同步数据获取销售数据';
    container.innerHTML = `<tr><td colspan="2" class="empty"><div class="empty-icon">📦</div><p>${escapeHtml(emptyText)}</p></td></tr>`;
    return;
  }

  container.innerHTML = products.map(p => {
    const skuRows = getSalesVisibleSkuRows(p);
    const imgHtml = p.mainImage
      ? `<img src="${escapeHtml(p.mainImage)}" class="img-fallback" data-fallback-size="56" style="width:56px;height:56px;object-fit:cover;border-radius:8px;border:1px solid #e5e7eb;flex-shrink:0;">`
      : '<div style="width:56px;height:56px;background:#f5f5f5;border-radius:8px;display:flex;align-items:center;justify-content:center;color:#cbd5e1;font-size:18px;flex-shrink:0;">📷</div>';
    const scoreText = p.score !== null && p.score !== undefined && p.score !== '' ? p.score : '—';
    const durationText = formatDuration(getSalesDurationSeconds(p));
    const nameText = escapeHtml(p.productCNName || p.productName || '—');

    return `<tr>
      <td style="padding:14px 12px;border-bottom:12px solid #f5f7fb;background:#fff;vertical-align:middle;width:320px;">
        <div style="border:1px solid #dbeafe;border-radius:14px;padding:14px;background:linear-gradient(135deg,#eff6ff 0%,#f8fbff 40%,#ffffff 100%);box-shadow:0 4px 14px rgba(59,130,246,0.08);min-height:176px;display:flex;align-items:center;">
          <div style="display:flex;gap:12px;align-items:center;width:100%;">
            ${imgHtml}
            <div style="min-width:0;flex:1;font-size:12px;color:#475569;line-height:1.55;">
              <div style="margin-bottom:8px;font-size:13px;font-weight:800;color:#0f172a;line-height:1.45;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;" title="${nameText}">${nameText}</div>
              <div><span style="display:inline-block;min-width:82px;color:#3b82f6;font-weight:700;">SPU</span><span style="font-weight:700;color:#0f172a;">${escapeHtml(p.productId || '—')}</span></div>
              <div><span style="display:inline-block;min-width:82px;color:#8b5cf6;font-weight:700;">SKC</span><span style="font-weight:700;color:#0f172a;">${escapeHtml(p.skcId || '—')}</span></div>
              <div><span style="display:inline-block;min-width:82px;color:#14b8a6;font-weight:700;">加入站点时长</span><span style="font-weight:700;color:#0f172a;">${escapeHtml(durationText)}</span></div>
              <div><span style="display:inline-block;min-width:82px;color:#f59e0b;font-weight:700;">评分</span><span style="font-weight:700;color:#0f172a;">${escapeHtml(scoreText)}</span></div>
            </div>
          </div>
        </div>
      </td>
      <td style="padding:14px 12px;border-bottom:12px solid #f5f7fb;background:#fff;vertical-align:top;">
        ${renderSalesSkuList(p, skuRows)}
      </td>
    </tr>`;
  }).join('');
}

function renderSalesSkuList(product, visibleRows = null) {
  const rows = Array.isArray(visibleRows) ? visibleRows : getSalesVisibleSkuRows(product);
  const totalRows = rows.length;
  const totalToday = rows.reduce((sum, row) => sum + (row.todaySaleVolume || 0), 0);
  const totalSeven = rows.reduce((sum, row) => sum + (row.sevenDaysSaleVolume || 0), 0);
  const totalThirty = rows.reduce((sum, row) => sum + (row.thirtyDaysSaleVolume || 0), 0);
  const totalAvailable = rows.reduce((sum, row) => sum + (row.availableInventoryNum || 0), 0);
  const totalReserved = rows.reduce((sum, row) => sum + (row.reservedInventoryNum || 0), 0);
  const totalUnavailable = rows.reduce((sum, row) => sum + (row.unavailableInventoryNum || 0), 0);
  const totalShipped = rows.reduce((sum, row) => sum + (row.shippedInventoryNum || 0), 0);
  const columns = 'minmax(0,1.42fr) 92px 52px 52px 52px 70px minmax(112px,0.8fr) minmax(150px,1.1fr)';
  const headerBaseStyle = 'padding:9px 10px;border-bottom:1px solid #eef2f7;background:linear-gradient(180deg,#f8fbff 0%,#f4f8ff 100%);font-size:12px;color:#5b6b7f;font-weight:800;letter-spacing:0.01em;line-height:1.2;';
  const rowBaseStyle = `display:grid;grid-template-columns:${columns};gap:8px;padding:9px 10px;border-bottom:1px solid #eef2f7;align-items:center;`;

  if (totalRows === 0) {
    return `
      <div style="border:1px solid #dbeafe;border-radius:16px;overflow:hidden;background:linear-gradient(180deg,#ffffff 0%,#fbfdff 100%);box-shadow:0 10px 28px rgba(59,130,246,0.08);">
        <div style="${headerBaseStyle};display:grid;grid-template-columns:${columns};gap:10px;">
          <div>SKU信息</div>
          <div style="text-align:right;color:#c2410c;">申报价格(CNY)</div>
          <div style="text-align:right;color:#2563eb;">今日</div>
          <div style="text-align:right;color:#16a34a;">7日</div>
          <div style="text-align:right;color:#7c3aed;">30日</div>
          <div style="text-align:right;color:#d97706;">可售天数</div>
          <div style="color:#0f766e;">备货逻辑</div>
          <div style="color:#334155;">库存数据</div>
        </div>
        <div style="padding:16px 14px;color:#94a3b8;font-size:12px;text-align:center;">暂无可展示的 SKU</div>
      </div>
    `;
  }

  const rowsHtml = rows.map(row => {
    const amountText = row.amountValue !== null && row.amountValue !== undefined ? `¥${formatAmount(row.amountValue)}` : '-';
    const skuId = escapeHtml(row.skuId || '—');
    const skuName = escapeHtml(row.skuName || '—');
    const priceStyle = row.amountValue ? 'color:#c62828;font-weight:700;' : 'color:#94a3b8;';
    const todayText = row.todaySaleVolume || row.todaySaleVolume === 0 ? row.todaySaleVolume.toLocaleString() : '-';
    const sevenText = row.sevenDaysSaleVolume || row.sevenDaysSaleVolume === 0 ? row.sevenDaysSaleVolume.toLocaleString() : '-';
    const thirtyText = row.thirtyDaysSaleVolume || row.thirtyDaysSaleVolume === 0 ? row.thirtyDaysSaleVolume.toLocaleString() : '-';
    const saleDaysText = row.availableSaleDays || row.availableSaleDays === 0 ? `${row.availableSaleDays}` : (row.stockDays || row.stockDays === 0 ? `${row.stockDays}` : '-');
    const purchaseLogicText = row.purchaseConfig || (row.adviceQuantity || row.adviceQuantity === 0 ? `建议备货 ${row.adviceQuantity}` : '-');

    return `
      <div style="${rowBaseStyle}">
        <div style="display:flex;align-items:center;gap:12px;min-width:0;padding:9px 11px;border-radius:12px;background:linear-gradient(135deg,#f8fafc 0%,#eff6ff 100%);border:1px solid #dbeafe;">
          <div style="flex:0 0 auto;font-size:12px;font-weight:800;color:#0f172a;white-space:nowrap;" title="${skuId}">${skuId}</div>
          <div title="${skuName}" style="flex:1 1 auto;min-width:0;font-size:13px;line-height:1.3;color:#1f2937;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${skuName}</div>
        </div>
        <div style="text-align:right;${priceStyle};font-size:12px;padding:2px 0;">${amountText}</div>
        <div style="text-align:right;color:#2563eb;font-size:12px;font-weight:700;padding:2px 0;">${todayText}</div>
        <div style="text-align:right;color:#16a34a;font-size:12px;font-weight:700;padding:2px 0;">${sevenText}</div>
        <div style="text-align:right;color:#7c3aed;font-size:12px;font-weight:700;padding:2px 0;">${thirtyText}</div>
        <div style="text-align:right;color:#d97706;font-size:12px;font-weight:700;padding:2px 0;padding-right:10px;border-right:1px solid #e2e8f0;">${saleDaysText}</div>
        <div style="color:#0f766e;font-size:12px;line-height:1.35;font-weight:700;padding-left:10px;min-width:0;">${escapeHtml(purchaseLogicText)}</div>
        <div>${renderSalesInventoryCell(row)}</div>
      </div>
    `;
  }).join('');

  return `
    <div style="border:1px solid #dbeafe;border-radius:16px;overflow:hidden;background:linear-gradient(180deg,#ffffff 0%,#fbfdff 100%);box-shadow:0 10px 28px rgba(59,130,246,0.08);">
      <div style="${headerBaseStyle};display:grid;grid-template-columns:${columns};gap:10px;">
        <div>SKU信息（${totalRows}）</div>
        <div style="text-align:right;color:#c2410c;">申报价格(CNY)</div>
        <div style="text-align:right;color:#2563eb;">今日</div>
        <div style="text-align:right;color:#16a34a;">7日</div>
        <div style="text-align:right;color:#7c3aed;">30日</div>
        <div style="text-align:right;color:#d97706;">可售天数</div>
        <div style="color:#0f766e;">备货逻辑</div>
        <div style="color:#334155;">库存数据</div>
      </div>
      <div>${rowsHtml}</div>
      <div style="display:grid;grid-template-columns:${columns};gap:10px;padding:10px 12px;background:#f8fafc;border-top:1px solid #e5e7eb;font-weight:800;align-items:center;">
        <div style="font-size:12px;">销量合计</div>
        <div style="text-align:right;color:#94a3b8;font-size:12px;">-</div>
        <div style="text-align:right;color:#0f172a;font-size:12px;">${totalToday.toLocaleString()}</div>
        <div style="text-align:right;color:#0f172a;font-size:12px;">${totalSeven.toLocaleString()}</div>
        <div style="text-align:right;color:#0f172a;font-size:12px;">${totalThirty.toLocaleString()}</div>
        <div style="text-align:right;color:#94a3b8;font-size:12px;">-</div>
        <div style="color:#94a3b8;font-size:12px;padding-left:12px;">-</div>
        <div>${renderSalesInventoryCell({
          availableInventoryNum: totalAvailable,
          reservedInventoryNum: totalReserved,
          unavailableInventoryNum: totalUnavailable,
          shippedInventoryNum: totalShipped,
          warehouseInfoList: []
        })}</div>
      </div>
    </div>
  `;
}

function formatDuration(seconds) {
  if (!seconds) return '-';
  const days = Math.floor(seconds / 86400);
  if (days > 365) return Math.floor(days / 365) + '年' + (days % 365) + '天';
  if (days > 30) return Math.floor(days / 30) + '月' + (days % 30) + '天';
  return days + '天';
}

function renderSalesDataPagination() {
  const info = document.getElementById('salesPaginationInfo');
  const btns = document.getElementById('salesPaginationButtons');
  const container = document.getElementById('salesPaginationContainer');
  if (!info || !btns) return;

  const query = getSalesSearchQuery();

  if (query) {
    if (container) container.style.display = 'none';
    info.innerHTML = `已筛选 <strong>${getSalesFilteredProducts().length}</strong> 条`;
    btns.innerHTML = '';
    return;
  }

  const visibleProducts = getSalesFilteredProducts();
  const totalVisible = visibleProducts.length;
  const totalPages = Math.max(1, Math.ceil(totalVisible / Math.max(1, salesDataState.pageSize || 20)));
  if (salesDataState.total > 0 && container) container.style.display = 'flex';

  if (totalVisible > 0) {
    info.innerHTML = `共 <strong>${totalVisible}</strong> 条，第 <strong>${salesDataState.pageNum}</strong>/<strong>${totalPages}</strong> 页`;
  } else {
    info.innerHTML = '共 0 条';
  }

  btns.innerHTML = '';
  if (totalPages <= 1) return;

  if (salesDataState.pageNum > 1) {
    const btn = document.createElement('button');
    btn.className = 'btn btn-default btn-sm'; btn.textContent = '上一页';
    btn.addEventListener('click', () => goToSalesPage(salesDataState.pageNum - 1));
    btns.appendChild(btn);
  }

  const maxBtns = 5;
  let s = Math.max(1, salesDataState.pageNum - Math.floor(maxBtns / 2));
  let e = Math.min(totalPages, s + maxBtns - 1);
  if (e - s < maxBtns - 1) s = Math.max(1, e - maxBtns + 1);

  for (let i = s; i <= e; i++) {
    const btn = document.createElement('button');
    btn.className = i === salesDataState.pageNum ? 'btn btn-primary btn-sm' : 'btn btn-default btn-sm';
    btn.textContent = i;
    if (i !== salesDataState.pageNum) btn.addEventListener('click', () => goToSalesPage(i));
    btns.appendChild(btn);
  }

  if (salesDataState.pageNum < totalPages) {
    const btn = document.createElement('button');
    btn.className = 'btn btn-default btn-sm'; btn.textContent = '下一页';
    btn.addEventListener('click', () => goToSalesPage(salesDataState.pageNum + 1));
    btns.appendChild(btn);
  }

  if (totalPages > 5) {
    const jumpWrap = document.createElement('span');
    jumpWrap.style.cssText = 'display:flex;align-items:center;gap:4px;margin-left:8px;font-size:12px;';
    jumpWrap.innerHTML = '跳至<input type="number" min="1" max="' + totalPages + '" style="width:50px;padding:2px 4px;border:1px solid #d9d9d9;border-radius:4px;font-size:12px;text-align:center;">页';
    const jumpInput = jumpWrap.querySelector('input');
    jumpInput.value = salesDataState.pageNum;
    jumpInput.addEventListener('keydown', (ev) => {
      if (ev.key === 'Enter') {
        const p = parseInt(jumpInput.value);
        if (p >= 1 && p <= totalPages) goToSalesPage(p);
      }
    });
    btns.appendChild(jumpWrap);
  }
}

async function exportSalesData() {
  const products = getSalesDisplayProducts();
  if (products.length === 0) {
    showToast('没有数据可导出', 'warning');
    return;
  }

  try {
    const XLSX = await ensureXlsxLoaded();
    const rows = [['SPU ID', '商品名称', '今日销量', '7天销量', '30天销量', '库存', '售价', '上架时长(天)']];
    products.forEach(p => {
      rows.push([
        p.productId, p.productCNName, p.todaySaleVolume, p.sevenDaysSaleVolume, p.thirtyDaysSaleVolume,
        p.stockQuantity, p.price || '', Math.floor(getSalesDurationSeconds(p) / 86400)
      ]);
    });

    const ws = XLSX.utils.aoa_to_sheet(rows);
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, '销售数据');
    XLSX.writeFile(wb, `销售数据_${new Date().toISOString().slice(0, 10)}.xlsx`);
    showToast('导出成功！', 'success');
  } catch (err) {
    showToast('导出失败: ' + err.message, 'error');
  }
}

// ========== 优惠活动报名 Tab（大白鹅风格：表格+一键报名）==========
const activityState = {
  activities: [],
  isRunning: false,
  logHidden: false
};

function updateActivityLogVisibility() {
  const progressSection = document.getElementById('activityProgressSection');
  const fill = document.getElementById('activityProgressFill');
  const text = document.getElementById('activityProgressText');
  const list = document.getElementById('activityResultList');
  const toggleBtn = document.getElementById('activityLogToggleBtn');
  if (!progressSection) return;
  const hasLog = Boolean(text?.textContent?.trim() || list?.innerHTML?.trim());
  progressSection.style.display = hasLog || activityState.isRunning ? 'block' : 'none';
  if (fill) fill.style.display = '';
  if (text) text.style.display = '';
  if (list) list.style.display = activityState.logHidden ? 'none' : '';
  if (toggleBtn) toggleBtn.textContent = activityState.logHidden ? '显示日志' : '隐藏日志';
}

function bindActivityLogToggle() {
  const toggleBtn = document.getElementById('activityLogToggleBtn');
  if (!toggleBtn || toggleBtn.dataset.bound === '1') return;
  toggleBtn.dataset.bound = '1';
  toggleBtn.addEventListener('click', () => {
    activityState.logHidden = !activityState.logHidden;
    updateActivityLogVisibility();
  });
}

function initActivityTab() {
  document.getElementById('activityLoadBtn')?.addEventListener('click', loadActivityList);
  document.getElementById('activityRefreshBtn')?.addEventListener('click', loadActivityList);
  bindActivityLogToggle();
  updateActivityLogVisibility();
}

function getActivityDisplayName(act) {
  return act?.activityName || act?.activityThematicName || act?.activityThemeName || act?.name || act?.title || '未知活动';
}

function formatActivityTime(act) {
  const start = act?.sessionStartTime || act?.startTime || act?.start_time || act?.enrollStartAt || act?.enrollStartTime || act?.enroll_start_time || '';
  const end = act?.sessionEndTime || act?.endTime || act?.end_time || act?.enrollDeadLine || act?.enrollEndTime || act?.enroll_end_time || '';
  const rawText = [
    act?.activityTime,
    act?.timeText,
    act?.timeRange,
    act?.dateRange,
    act?.sessionTime
  ].map(value => String(value || '')).find(Boolean) || '';
  if (rawText.includes('长期活动')) return '长期活动';
  if (start && end) {
    return `${formatTimestamp(start)} ~ ${formatTimestamp(end)}`;
  }
  if (start) return formatTimestamp(start);
  if (end) return formatTimestamp(end);
  return '长期活动';
}

function formatActivityRate(act) {
  const raw = act?.discountRate;
  if (raw === undefined || raw === null || raw === '') return '-';
  const text = String(raw).trim();
  if (!text) return '-';
  if (/^[0-9]+(\.[0-9]+)?$/.test(text)) return `${text}%`;
  return text.endsWith('%') || text.includes('折') ? text : `${text}%`;
}

function formatActivityEnrollCount(act) {
  const raw = act?.enrolledCount ?? act?.enrollCount ?? act?.participantCount ?? act?.registeredCount ?? act?.canEnrollCnt;
  if (raw === undefined || raw === null || raw === '') return '-';
  return String(raw);
}

function extractActivityMatchProducts(matchResult) {
  const result = matchResult?.result ?? matchResult?.data ?? matchResult?.detail ?? matchResult;
  const candidates = [
    result?.matchList,
    result?.products,
    result?.list,
    result?.items,
    result?.productList,
    Array.isArray(result) ? result : []
  ];

  for (const candidate of candidates) {
    if (Array.isArray(candidate) && candidate.length > 0) return candidate;
  }
  return [];
}

function isActivityEnrollSuccess(result) {
  return result?.success === true
    || result?.code === 0
    || result?.code === '0'
    || result?.result?.success === true
    || result?.result?.code === 0
    || result?.result?.code === '0';
}

function getActivityEnrollError(result) {
  return result?.error_msg
    || result?.msg
    || result?.message
    || result?.error
    || result?.result?.error_msg
    || result?.result?.msg
    || result?.result?.message
    || result?.result?.error
    || '未知错误';
}

function getActivityEnrollErrorDetail(result) {
  const root = result?.result ?? result?.data ?? result?.detail ?? result ?? {};
  const code = result?.code ?? result?.errorCode ?? result?.error_code ?? root?.code ?? root?.errorCode ?? root?.error_code ?? '';
  const message = getActivityEnrollError(result);
  const extras = [
    root?.failReason,
    root?.reason,
    root?.tips,
    root?.toast,
    root?.errorTips,
    root?.errorDetail
  ].filter(Boolean).map(String);
  const detail = [message, ...extras].filter(Boolean).join('；');
  return code !== '' && code !== 0 && code !== '0' ? `code=${code}；${detail}` : detail;
}

function getActivitySubmitDiagnostics(payload = {}) {
  const product = Array.isArray(payload.productList) ? payload.productList[0] || {} : {};
  const skcList = Array.isArray(product.skcList) ? product.skcList : [];
  const skuCount = skcList.reduce((sum, skc) => sum + (Array.isArray(skc.skuList) ? skc.skuList.length : 0), 0);
  const missing = [];
  if (!payload.activityType) missing.push('activityType');
  if (!payload.activityThematicId) missing.push('activityThematicId');
  if (!product.productId) missing.push('productId');
  if (!Number(product.activityStock || 0)) missing.push('activityStock');
  if (!skcList.length) missing.push('skcList');
  if (!skuCount) missing.push('skuList');
  return {
    skcCount: skcList.length,
    skuCount,
    sessionCount: Array.isArray(product.sessionIds) ? product.sessionIds.length : 0,
    stock: product.activityStock,
    missing
  };
}

function renderActivitySubmitDiagnostics(diag = {}) {
  const parts = [
    `SKC ${diag.skcCount || 0}`,
    `SKU ${diag.skuCount || 0}`,
    `场次 ${diag.sessionCount || 0}`,
    `库存 ${diag.stock ?? ''}`
  ];
  if (diag.missing?.length) parts.push(`缺少：${diag.missing.join('、')}`);
  return parts.join(' / ');
}

const ACTIVITY_ENROLL_NORMAL_DELAY_MS = 1800;
const ACTIVITY_ENROLL_MAX_ATTEMPTS = 4;
const ACTIVITY_ENROLL_RATE_LIMIT_DELAYS_MS = [3000, 8000, 15000];

function sleepActivityEnroll(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function getActivityEnrollErrorCode(result) {
  const root = result?.result ?? result?.data ?? result?.detail ?? {};
  return result?.code
    ?? result?.errorCode
    ?? result?.error_code
    ?? root?.code
    ?? root?.errorCode
    ?? root?.error_code
    ?? '';
}

function isActivityEnrollRateLimited(result) {
  const code = String(getActivityEnrollErrorCode(result) ?? '').trim();
  if (code === '2000000') return true;
  const root = result?.result ?? result?.data ?? result?.detail ?? {};
  const detail = [
    getActivityEnrollErrorDetail(result),
    result?.error_msg,
    result?.message,
    result?.msg,
    result?.error,
    root?.error_msg,
    root?.message,
    root?.msg,
    root?.error
  ].filter(Boolean).join(' ').toLowerCase();
  return detail.includes('operating too quickly')
    || detail.includes('submit again later')
    || detail.includes('too quickly')
    || detail.includes('操作太快')
    || detail.includes('提交太快')
    || detail.includes('稍后');
}

function isActivityEnrollTransientError(result) {
  const code = String(getActivityEnrollErrorCode(result) ?? '').trim();
  const root = result?.result ?? result?.data ?? result?.detail ?? {};
  const detail = [
    getActivityEnrollErrorDetail(result),
    result?.error_msg,
    result?.message,
    result?.msg,
    result?.error,
    root?.error_msg,
    root?.message,
    root?.msg,
    root?.error
  ].filter(Boolean).join(' ').toLowerCase();
  return code === '408'
    || code === '429'
    || code === '500'
    || code === '502'
    || code === '503'
    || code === '504'
    || detail.includes('network timeout')
    || detail.includes('timeout')
    || detail.includes('timed out')
    || detail.includes('please try again later')
    || detail.includes('try again later')
    || detail.includes('temporarily unavailable')
    || detail.includes('请求超时')
    || detail.includes('网络超时')
    || detail.includes('稍后再试');
}

function getActivityEnrollRetryDelayMs(attempt) {
  return ACTIVITY_ENROLL_RATE_LIMIT_DELAYS_MS[Math.min(
    Math.max(attempt - 1, 0),
    ACTIVITY_ENROLL_RATE_LIMIT_DELAYS_MS.length - 1
  )] || 2000;
}

function renderActivityEnrollAttemptLabel(attempt) {
  return attempt <= 1 ? '报名中' : `重试中 ${attempt}/${ACTIVITY_ENROLL_MAX_ATTEMPTS}`;
}

async function submitActivityEnrollWithRetry({
  enrollParams,
  submitDiagnostics,
  statusCell,
  productId,
  text,
  currentIndex,
  totalCount
}) {
  let lastResult = null;
  for (let attempt = 1; attempt <= ACTIVITY_ENROLL_MAX_ATTEMPTS; attempt++) {
    const attemptLabel = renderActivityEnrollAttemptLabel(attempt);
    if (statusCell) statusCell.innerHTML = `<span style="color:#faad14;">⏳ ${attemptLabel}</span>`;
    if (text) text.textContent = `报名中 ${currentIndex}/${totalCount}（${escapeHtml(productId || '未知商品')}）${attempt > 1 ? `，第 ${attempt} 次提交` : ''}...`;

    const result = await safeRuntimeSendMessage({
      action: 'enrollActivity',
      params: enrollParams
    }, { success: false, error: '活动报名请求失败' }, 75000);
    lastResult = result;

    if (isActivityEnrollSuccess(result)) {
      return { success: true, result, attempts: attempt };
    }

    const shouldRetry = (isActivityEnrollRateLimited(result) || isActivityEnrollTransientError(result))
      && attempt < ACTIVITY_ENROLL_MAX_ATTEMPTS;
    if (shouldRetry) {
      const delayMs = getActivityEnrollRetryDelayMs(attempt);
      const secondsText = (delayMs / 1000).toFixed(delayMs % 1000 === 0 ? 0 : 1);
      if (statusCell) {
        const retryReason = isActivityEnrollRateLimited(result) ? 'TEMU限流' : 'TEMU临时超时';
        statusCell.innerHTML = `<div style="color:#fa8c16;line-height:1.45;"><div>⏳ ${retryReason}，${secondsText}秒后重试</div><small style="display:block;max-width:260px;white-space:normal;color:#b45309;">${escapeHtml(getActivityEnrollErrorDetail(result))}</small><small style="display:block;max-width:260px;white-space:normal;color:#64748b;">${escapeHtml(renderActivitySubmitDiagnostics(submitDiagnostics))}</small></div>`;
      }
      if (text) text.textContent = `TEMU临时失败，等待 ${secondsText} 秒后重试 ${currentIndex}/${totalCount}（${escapeHtml(productId || '未知商品')}）...`;
      await sleepActivityEnroll(delayMs);
      continue;
    }

    return {
      success: false,
      result,
      errorDetail: getActivityEnrollErrorDetail(result),
      attempts: attempt
    };
  }

  return {
    success: false,
    result: lastResult,
    errorDetail: getActivityEnrollErrorDetail(lastResult),
    attempts: ACTIVITY_ENROLL_MAX_ATTEMPTS
  };
}

function extractActivityIdFromUrl(url) {
  if (!url || typeof url !== 'string') return '';
  try {
    const parsed = new URL(url, 'https://agentseller.temu.com');
    return (
      parsed.searchParams.get('activityId')
      || parsed.searchParams.get('activity_id')
      || parsed.searchParams.get('activityid')
      || parsed.searchParams.get('id')
      || ''
    ).trim();
  } catch (err) {
    return '';
  }
}

function getActivityThematicId(act) {
  return String(
    act?.activityThematicId
    || act?.activityThematic
    || act?.activityThemeId
    || act?.thematicId
    || ''
  ).trim();
}

function buildActivityMatchRequest(act, searchScrollContext = null) {
  const activityThematicId = getActivityThematicId(act);
  const activityType = String(act?.activityType || '').trim();
  return {
    activityType: activityType || undefined,
    rowCount: 50,
    productSkcExtCodes: [],
    productSkuExtCodes: [],
    addSite: true,
    searchScrollContext,
    activityThematicId: activityThematicId || undefined,
    filterUnsalableWarning: false
  };
}

function buildActivitySubmitProduct(product) {
  const skcSource = Array.isArray(product?.skcList) && product.skcList.length > 0
    ? product.skcList
    : (Array.isArray(product?.skcArr) ? product.skcArr : []);

  return {
    productId: String(product?.productId || product?.spuId || product?.goodsId || product?.productSpuId || '').trim(),
    activityStock: product?.suggestActivityStock ?? product?.activityStock ?? product?.stock ?? product?.currentStock ?? 0,
    skcList: skcSource.map((skc) => {
      const skuSource = Array.isArray(skc?.skuList) && skc.skuList.length > 0
        ? skc.skuList
        : (Array.isArray(skc?.skuArr) ? skc.skuArr : []);
      return {
        skcId: String(skc?.skcId || skc?.skc_id || skc?.productSkcId || '').trim(),
        activityPrice: skc?.activityPrice ?? skc?.suggestActivityPrice ?? skc?.privilegeSuggestActivityPrice ?? undefined,
        activityDiscount: skc?.activityDiscount ?? skc?.suggestActivityDiscount ?? undefined,
        skuList: skuSource.map((sku) => ({
          skuId: String(sku?.skuId || sku?.productSkuId || sku?.id || '').trim(),
          activityPrice: sku?.activityPrice ?? sku?.suggestActivityPrice ?? sku?.privilegeSuggestActivityPrice ?? undefined,
          activityDiscount: sku?.activityDiscount ?? sku?.suggestActivityDiscount ?? undefined
        })).filter((sku) => sku.skuId)
      };
    }).filter((skc) => skc.skcId || skc.skuList.length > 0),
    sessionIds: product?.suggestEnrollSessionIdList || product?.sessionIds || product?.sessionIdList || [],
    activityEnrollPrivilegeType: product?.activityEnrollPrivilegeType ?? 0
  };
}

async function matchActivityProductsWithFallback(act) {
  const searchContexts = [null];
  const tried = [];
  const allMatched = [];
  let lastResult = null;
  const baseRequest = buildActivityMatchRequest(act);

  for (let round = 0; round < 20; round++) {
    const searchScrollContext = searchContexts.length > 0 ? searchContexts.shift() : null;
    const requestParams = { ...baseRequest, searchScrollContext };
    const result = await safeRuntimeSendMessage({
      action: 'scrollMatchActivity',
      params: requestParams
    }, { success: false, error: '活动匹配请求失败' }, 30000);

    console.log('[小青蛙电商助手] scrollMatchActivity 返回:', JSON.stringify({ requestParams, result })?.substring(0, 500));
    lastResult = result;
    tried.push(searchScrollContext ? 'scroll' : 'initial');

    const matchedProducts = extractActivityMatchProducts(result);
    if (matchedProducts.length > 0) {
      allMatched.push(...matchedProducts);
    }

    const resultData = result?.result ?? result?.data ?? result?.detail ?? result;
    const stillCount = Number(resultData?.stillCount ?? result?.stillCount ?? 0);
    const nextScrollContext = resultData?.searchScrollContext ?? result?.searchScrollContext ?? null;

    if (stillCount > 0 && nextScrollContext) {
      searchContexts.push(nextScrollContext);
      continue;
    }

    if (matchedProducts.length === 0 && round === 0 && !getActivityThematicId(act)) {
      break;
    }

    if (stillCount > 0 && !nextScrollContext) {
      break;
    }

    break;
  }

  if (allMatched.length === 0) {
    const legacyCandidates = [];
    const legacyId = [
      act?.activityId,
      act?.activityThematicId,
      act?.activityThemeId,
      act?.parentActivityId,
      act?.id,
      extractActivityIdFromUrl(act?.enrollUrl)
    ].filter(Boolean);
    for (const id of legacyId) {
      legacyCandidates.push({ activityId: String(id).trim(), activityType: act?.activityType || undefined, pageNo: 1, pageSize: 200 });
    }

    for (const requestParams of legacyCandidates) {
      const result = await safeRuntimeSendMessage({
        action: 'scrollMatchActivity',
        params: requestParams
      }, { success: false, error: '活动匹配请求失败' }, 30000);
      console.log('[小青蛙电商助手] scrollMatchActivity 旧参数返回:', JSON.stringify({ requestParams, result })?.substring(0, 500));
      lastResult = result;
      tried.push(`legacy:${requestParams.activityId}`);
      const matchedProducts = extractActivityMatchProducts(result);
      if (matchedProducts.length > 0) {
        allMatched.push(...matchedProducts);
        break;
      }
    }
  }

  if (allMatched.length > 0) {
    return { result: lastResult, matchedProducts: allMatched, tried };
  }

  return { result: lastResult, matchedProducts: [], tried };
}

async function loadActivityList() {
  const tbody = document.getElementById('activityTableBody');
  const loadBtn = document.getElementById('activityLoadBtn');
  const refreshBtn = document.getElementById('activityRefreshBtn');
  const countInfo = document.getElementById('activityCountInfo');

  if (!tbody || !loadBtn || !refreshBtn || !countInfo) {
    showToast('活动报名界面未初始化完成', 'error');
    return;
  }

  loadBtn.disabled = true;
  refreshBtn.disabled = true;
  loadBtn.textContent = '⏳ 加载中...';
  tbody.innerHTML = '<tr><td colspan="7" class="loading">加载活动列表中...</td></tr>';

  try {
    const result = await safeRuntimeSendMessage({ action: 'fetchActivityList', params: {} }, { success: false, error: '获取活动列表失败' }, 30000);

    if (result?.success) {
      activityState.activities = result.activities || [];

      if (activityState.activities.length === 0) {
        tbody.innerHTML = '<tr><td colspan="7" class="empty">📦 暂无可报名活动</td></tr>';
        countInfo.textContent = '暂无可报名活动';
      } else {
        countInfo.textContent = `共 ${activityState.activities.length} 个活动`;
        renderActivityTable();
      }
    } else {
      activityState.activities = [];
      tbody.innerHTML = `<tr><td colspan="7" class="empty"><div class="empty-icon">❌</div><p>${escapeHtml(result?.error || '获取活动列表失败')}</p></td></tr>`;
      countInfo.textContent = '加载失败，请重试';
      showToast(result?.error || '获取活动列表失败', 'error');
    }
  } catch (error) {
    activityState.activities = [];
    tbody.innerHTML = `<tr><td colspan="7" class="empty"><div class="empty-icon">❌</div><p>${escapeHtml(error?.message || '获取活动列表失败')}</p></td></tr>`;
    countInfo.textContent = '加载失败，请重试';
    showToast(error?.message || '获取活动列表失败', 'error');
  } finally {
    loadBtn.disabled = false;
    loadBtn.textContent = '🔄 加载活动列表';
    refreshBtn.disabled = activityState.activities.length === 0;
  }
}

function renderActivityTable() {
  const tbody = document.getElementById('activityTableBody');
  if (activityState.activities.length === 0) {
    tbody.innerHTML = '<tr><td colspan="7" class="empty">📦 暂无可报名活动</td></tr>';
    return;
  }

  tbody.innerHTML = activityState.activities.map((act, idx) => {
    const activityName = getActivityDisplayName(act);
    const activityTime = formatActivityTime(act);
    const enrollTime = (act.enrollStartTime && act.enrollEndTime)
      ? `${formatTimestamp(act.enrollStartTime)} ~ ${formatTimestamp(act.enrollEndTime)}`
      : (act.enrollStartTime ? formatTimestamp(act.enrollStartTime) : '-');
    const discount = formatActivityRate(act);
    const enrolledCount = formatActivityEnrollCount(act);

    return `<tr>
      <td style="text-align:center;color:#999;">${idx + 1}</td>
      <td>
        <div style="font-weight:600;font-size:13px;color:var(--text-primary);">${escapeHtml(activityName)}</div>
        ${act.activityContent ? `<div style="margin-top:3px;font-size:11px;color:var(--text-secondary);line-height:1.4;">${escapeHtml(String(act.activityContent))}</div>` : ''}
      </td>
      <td style="font-size:11px;color:var(--text-secondary);white-space:nowrap;">${escapeHtml(activityTime)}</td>
      <td style="text-align:center;font-weight:600;color:#c62828;font-size:13px;">${escapeHtml(String(discount))}</td>
      <td style="font-size:11px;color:var(--text-secondary);white-space:nowrap;">${escapeHtml(enrollTime)}</td>
      <td style="text-align:center;font-weight:500;color:var(--text-primary);">${enrolledCount}</td>
      <td style="text-align:center;">
        <button class="btn btn-primary btn-sm enroll-btn" data-idx="${idx}">一键报名</button>
      </td>
    </tr>`;
  }).join('');

  // 绑定一键报名按钮事件
  tbody.querySelectorAll('.enroll-btn').forEach(btn => {
    btn.addEventListener('click', function() {
      const idx = parseInt(this.dataset.idx);
      if (!isNaN(idx) && typeof oneClickEnroll === 'function') oneClickEnroll(idx);
    });
  });
}

function formatTimestamp(ts) {
  if (!ts) return '-';
  const d = new Date(typeof ts === 'number' ? ts : parseInt(ts));
  if (isNaN(d.getTime())) return '-';
  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
}

// 一键报名：自动匹配商品 + 批量提交
window.oneClickEnroll = async function(idx) {
  const act = activityState.activities[idx];
  if (!act) { showToast('活动不存在', 'error'); return; }
  if (activityState.isRunning) { showToast('有任务正在执行，请稍候', 'info'); return; }

  activityState.isRunning = true;

  // 显示进度区域
  const progressSection = document.getElementById('activityProgressSection');
  if (!progressSection) {
    activityState.isRunning = false;
    showToast('活动报名进度区域未找到', 'error');
    return;
  }
  activityState.logHidden = false;
  progressSection.style.display = 'block';
  const fill = document.getElementById('activityProgressFill');
  const text = document.getElementById('activityProgressText');
  const list = document.getElementById('activityResultList');
  if (!fill || !text || !list) {
    activityState.isRunning = false;
    showToast('活动报名界面未初始化完成', 'error');
    return;
  }
  fill.style.width = '0%';
  fill.className = 'progress-fill blue';
  text.textContent = `开始处理活动: ${getActivityDisplayName(act)}...`;
  list.innerHTML = '';
  updateActivityLogVisibility();

  try {
    // 第一步：匹配可报名商品
    text.textContent = '🔍 正在匹配可报名商品...';
    fill.style.width = '5%';

    const matchPack = await matchActivityProductsWithFallback(act);
    const matchResult = matchPack.result;
    const matchedProducts = matchPack.matchedProducts;

    console.log('[小青蛙电商助手] scrollMatchActivity 返回:', JSON.stringify({ tried: matchPack.tried, matchResult })?.substring(0, 500));

    if (matchedProducts.length === 0 && !matchResult?.success) {
      console.warn('[小青蛙电商助手] 活动匹配失败:', matchResult?.error || '未知错误');
    }

    if (matchedProducts.length === 0) {
      fill.style.width = '100%';
      fill.className = 'progress-fill blue';
      text.textContent = '⚠️ 该活动没有匹配到可报名商品';
      const item = document.createElement('div');
      item.className = 'result-item';
      const triedText = (matchPack.tried || []).length > 0 ? `已尝试：${escapeHtml(matchPack.tried.join('，'))}` : '';
      item.innerHTML = `<span class="icon">ℹ️</span><span class="reason">活动「${escapeHtml(getActivityDisplayName(act))}」没有匹配到可报名商品${triedText ? `，${triedText}` : ''}</span>`;
      list.appendChild(item);
      updateActivityLogVisibility();
      activityState.isRunning = false;
      return;
    }

    text.textContent = `匹配到 ${matchedProducts.length} 个可报名商品`;
    fill.style.width = '15%';

    // 显示匹配到的商品列表（带报名链接）
    const enrollBaseUrl = `https://agentseller.temu.com/marketing-center/activity-manage/detail?activityId=${act.activityId}`;
    let html = `<div style="margin-bottom:10px;display:flex;justify-content:space-between;align-items:center;">
      <span style="font-size:13px;color:#666;">共 ${matchedProducts.length} 个商品可报名</span>
      <div>
        <a href="${enrollBaseUrl}" target="_blank" style="font-size:12px;color:#1890ff;text-decoration:none;margin-right:12px;">🔗 在TEMU查看活动</a>
        <button class="btn btn-primary btn-sm" id="enrollAllBtn">🚀 一键全部报名</button>
      </div>
    </div>`;
    html += `<table class="product-table" style="font-size:12px;">
      <thead><tr><th style="width:40px;">#</th><th>商品ID</th><th>商品名称</th><th>操作</th></tr></thead><tbody>`;
    matchedProducts.forEach((p, i) => {
      const pId = p.productId || p.spuId || p.goodsId || '';
      const pName = p.productCNName || p.productName || '';
      const pUrl = `https://agentseller.temu.com/goods/detail?spuId=${pId}`;
      html += `<tr>
        <td style="text-align:center;color:#999;">${i+1}</td>
        <td><a href="${pUrl}" target="_blank" style="color:#1890ff;text-decoration:none;">${escapeHtml(pId)}</a></td>
        <td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${escapeHtml(pName || '-')}</td>
        <td style="text-align:center;"><a href="${pUrl}" target="_blank" class="btn btn-default btn-sm" style="font-size:11px;">查看</a></td>
      </tr>`;
    });
    html += '</tbody></table>';
    list.innerHTML = html;
    fill.style.width = '20%';
    updateActivityLogVisibility();

    // 绑定"一键全部报名"按钮
    document.getElementById('enrollAllBtn')?.addEventListener('click', async () => {
      const activityName = act.activityName || act.name || act.title || getActivityThematicId(act) || '当前活动';
      const confirmed = await showCenterConfirm({
        title: '确认活动报名',
        message: `即将把 ${matchedProducts.length} 个匹配商品报名到「${activityName}」。这是会提交到 TEMU 店铺的真实操作，请确认后继续。`,
        confirmText: '确认报名',
        cancelText: '取消'
      });
      if (!confirmed) return;

      const enrollAllBtn = document.getElementById('enrollAllBtn');
      if (enrollAllBtn) {
        enrollAllBtn.disabled = true;
        enrollAllBtn.textContent = '报名中...';
      }
      let successCount = 0, failCount = 0;
      for (let i = 0; i < matchedProducts.length; i++) {
        const product = matchedProducts[i] || {};
        const productId = String(product.productId || product.spuId || product.goodsId || product.id || product.productSkuId || '').trim();
        const pct = 20 + Math.round(((i + 1) / matchedProducts.length) * 75);
        fill.style.width = pct + '%';
        fill.className = 'progress-fill blue';
        text.textContent = `报名中 ${i + 1}/${matchedProducts.length}（${escapeHtml(productId || '未知商品')}）...`;

        // 更新表格行状态
        const rows = list.querySelectorAll('table tbody tr');
        const statusCell = rows[i]?.querySelector('td:last-child');
        if (statusCell) statusCell.innerHTML = '<span style="color:#faad14;">⏳ 报名中</span>';

        if (!productId) {
          if (statusCell) statusCell.innerHTML = '<span style="color:#ff4d4f;">❌ 无商品ID</span>';
          failCount++;
          continue;
        }

        try {
          const enrollParams = {
            activityType: act.activityType || undefined,
            activityThematicId: getActivityThematicId(act) || undefined,
            productList: [buildActivitySubmitProduct(product)]
          };
          const submitDiagnostics = getActivitySubmitDiagnostics(enrollParams);

          const result = await submitActivityEnrollWithRetry({
            enrollParams,
            submitDiagnostics,
            statusCell,
            productId,
            text,
            currentIndex: i + 1,
            totalCount: matchedProducts.length
          });

          if (statusCell) {
            if (result.success) {
              statusCell.innerHTML = '<span style="color:#52c41a;font-weight:500;">✅ 成功</span>';
              successCount++;
            } else {
              const errorDetail = result.errorDetail || getActivityEnrollErrorDetail(result.result);
              statusCell.innerHTML = `<div style="color:#ff4d4f;line-height:1.45;" title="${escapeHtml(errorDetail)}"><div>❌ 失败</div><small style="display:block;max-width:260px;white-space:normal;color:#b42318;">${escapeHtml(errorDetail)}</small><small style="display:block;max-width:260px;white-space:normal;color:#64748b;">${escapeHtml(renderActivitySubmitDiagnostics(submitDiagnostics))}</small></div>`;
              failCount++;
            }
          }
        } catch (err) {
          if (statusCell) statusCell.innerHTML = `<div style="color:#ff4d4f;line-height:1.45;"><div>❌ 异常</div><small style="display:block;max-width:260px;white-space:normal;color:#b42318;">${escapeHtml(err?.message || err || '请求异常')}</small></div>`;
          failCount++;
        }

        await sleepActivityEnroll(ACTIVITY_ENROLL_NORMAL_DELAY_MS);
      }

    // 最终状态
    fill.style.width = '100%';
    if (failCount === 0) fill.className = 'progress-fill green';
    else if (successCount === 0) fill.className = 'progress-fill red';
    else fill.className = 'progress-fill blue';
    const btn = document.getElementById('enrollAllBtn');
    if (btn) { btn.disabled = false; btn.textContent = `✅ 报名完成：${successCount}成功 ${failCount}失败`; }
    text.textContent = `报名完成！✅ ${successCount} 成功, ❌ ${failCount} 失败（共 ${matchedProducts.length} 个商品）`;
    showToast(`活动报名完成！成功: ${successCount}, 失败: ${failCount}`, successCount > 0 ? 'success' : 'error');
    activityState.isRunning = false;
    updateActivityLogVisibility();
    }); // end enrollAllBtn click handler

  } catch (error) {
    fill.style.width = '100%';
    fill.className = 'progress-fill red';
    text.textContent = `处理失败: ${error.message}`;
    showToast(`一键报名失败: ${error.message}`, 'error');
  } finally {
    activityState.isRunning = false;
    updateActivityLogVisibility();
  }
};

// ========== 资质批量上传 Tab（大白鹅风格：创建任务面板）==========
// ===== 资质上传 Tab =====
const qualState = {
  products: [],
  selectedSpus: [],
  files: [],
  livePhotoFiles: [],
  templates: [],
  selectedMainTemplateId: '',
  templateName: '',
  pageNum: 1,
  pageSize: 10,
  totalPages: 0,
  total: 0,
  searchProductIds: [],
  searchText: '',
  manualUploadSpuIds: []
};

function normalizeQualTemplateRecord(record = {}) {
  const source = record.data && typeof record.data === 'object' ? { ...record.data, ...record } : record;
  const livePhotoFiles = Array.isArray(source.livePhotoFiles) ? source.livePhotoFiles : [];
  const files = Array.isArray(source.files) ? source.files : [];
  const certDetail = source.detail || source.certDetail || {};
  return {
    id: String(record.id || record.serverId || `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`),
    serverId: record.serverId || record.id || '',
    name: String(record.name || source.name || '未命名模板'),
    sourceSpuId: String(source.sourceSpuId || source.spuId || ''),
    certType: String(source.certType || certDetail.certType || ''),
    certName: String(source.certName || certDetail.certName || certDetail.showName || ''),
    detail: certDetail,
    files: files.map(file => ({
      name: String(file?.name || file?.fileName || '文件'),
      size: Number(file?.size || 0) || 0,
      type: String(file?.type || ''),
      dataUrl: String(file?.dataUrl || ''),
      fileUrl: String(file?.fileUrl || file?.file_url || file?.url || ''),
      originalFileUrl: String(file?.originalFileUrl || file?.original_file_url || file?.raw?.original_file_url || file?.raw?.originalFileUrl || ''),
      signedFileUrl: String(file?.signedFileUrl || file?.signed_file_url || file?.raw?.file_url || file?.raw?.fileUrl || ''),
      raw: file?.raw && typeof file.raw === 'object' ? file.raw : null,
      kind: String(file?.kind || '')
    })).filter(file => file.name || file.dataUrl || file.fileUrl),
    livePhotoFiles: livePhotoFiles.map(file => ({
      name: String(file?.name || file?.fileName || '实拍图'),
      size: Number(file?.size || 0) || 0,
      type: String(file?.type || 'image/png'),
      dataUrl: String(file?.dataUrl || ''),
      imageUrl: String(file?.imageUrl || file?.url || file?.fileUrl || '')
    })).filter(file => file.name || file.dataUrl || file.imageUrl),
    createdAt: record.createdAt || new Date().toISOString(),
    updatedAt: record.updatedAt || record.createdAt || new Date().toISOString()
  };
}

async function loadQualTemplates() {
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) {
    qualState.templates = [];
    renderQualTemplates();
    return [];
  }
  const result = await fetchUserTemplates('qualification', mallId);
  if (!result.success) {
    showToast(result.error || '加载资质模板失败', 'error');
    qualState.templates = [];
  } else {
    qualState.templates = (Array.isArray(result.templates) ? result.templates : []).map(row => {
      const data = row.data && typeof row.data === 'object' ? row.data : {};
      return normalizeQualTemplateRecord({
        ...data,
        id: data.id || `server_qualification_${row.id}`,
        serverId: row.id,
        name: row.name || data.name,
        createdAt: row.createdAt || data.createdAt,
        updatedAt: row.updatedAt || data.updatedAt
      });
    });
  }
  renderQualTemplates();
  syncQualUploadTypeSelect();
  return qualState.templates;
}

function getQualSelectedMainTemplate() {
  return qualState.templates.find(t => String(t.id) === String(qualState.selectedMainTemplateId)) || null;
}

function renderQualTemplateSelectOptions(selectEl, currentId, placeholder) {
  if (!selectEl) return;
  selectEl.innerHTML = `<option value="">${escapeHtml(placeholder)}</option>` +
    qualState.templates.map(t => `<option value="${escapeHtml(String(t.id))}">${escapeHtml(t.name || '未命名模板')}</option>`).join('');
  if (currentId) selectEl.value = String(currentId);
}

function getQualificationTemplateCertTypeOptions() {
  const options = new Map();
  (Array.isArray(qualState.templates) ? qualState.templates : []).forEach(template => {
    const value = String(template?.certType || template?.detail?.certType || '').trim();
    if (!value) return;
    const label = String(template?.certName || template?.detail?.certName || template?.detail?.showName || value).trim();
    options.set(value, label || value);
  });
  return options;
}

function syncQualUploadTypeSelect(options = null) {
  const uploadTypeSelect = document.getElementById('qualUploadTypeSelect');
  if (!uploadTypeSelect) return;
  const currentVal = uploadTypeSelect.value;
  const merged = new Map(options instanceof Map ? Array.from(options.entries()) : []);
  getQualificationTemplateCertTypeOptions().forEach((label, value) => {
    if (!merged.has(value)) merged.set(value, label);
  });
  const mainTemplate = getQualSelectedMainTemplate();
  const selectedTemplateType = String(mainTemplate?.certType || mainTemplate?.detail?.certType || '').trim();
  uploadTypeSelect.innerHTML = '<option value="">自动使用模板或 TEMU 返回的资质类型</option>' +
    Array.from(merged.entries()).map(([value, label]) => `<option value="${escapeHtml(String(value))}">${escapeHtml(String(label || value))}</option>`).join('');
  if (currentVal && merged.has(currentVal)) uploadTypeSelect.value = currentVal;
  else if (selectedTemplateType && merged.has(selectedTemplateType)) uploadTypeSelect.value = selectedTemplateType;
}

function renderQualTemplates() {
  const mainSelect = document.getElementById('qualMainTemplateSelect');
  const mainList = document.getElementById('qualMainTemplateList');
  renderQualTemplateSelectOptions(mainSelect, qualState.selectedMainTemplateId, '-- 请选择资质模板 --');
  syncQualUploadTypeSelect();
  if (mainList) {
    mainList.innerHTML = qualState.templates.map(t => `
      <button type="button" class="template-inline-item ${String(t.id) === String(qualState.selectedMainTemplateId) ? 'active' : ''}" data-qual-template-id="${escapeHtml(String(t.id))}" data-role="main">
        <span class="template-inline-name">${escapeHtml(t.name || '未命名模板')}</span>
        <span class="template-inline-meta">${escapeHtml(t.sourceSpuId ? `SPU ${t.sourceSpuId}` : '')}</span>
      </button>
    `).join('');
    mainList.querySelectorAll('[data-qual-template-id]').forEach(btn => {
      btn.addEventListener('click', () => {
        const template = qualState.templates.find(item => String(item.id) === String(btn.dataset.qualTemplateId));
        if (!template) return;
        qualState.selectedMainTemplateId = String(template.id);
        applyQualTemplate(template);
        renderQualTemplates();
      });
    });
  }
}

function applyQualTemplate(template = {}) {
  if (!template || typeof template !== 'object') return;
  const files = Array.isArray(template.files) ? template.files : [];
  const livePhotoFiles = Array.isArray(template.livePhotoFiles) ? template.livePhotoFiles : [];
  qualState.templateName = String(template.name || '');
  qualState.files = files.map(file => ({
    name: String(file.name || file.fileName || '文件'),
    size: Number(file.size || 0) || 0,
    type: String(file.type || ''),
    dataUrl: String(file.dataUrl || ''),
    fileUrl: String(file.fileUrl || file.url || ''),
    originalFileUrl: String(file.originalFileUrl || file.original_file_url || file.raw?.original_file_url || file.raw?.originalFileUrl || ''),
    signedFileUrl: String(file.signedFileUrl || file.signed_file_url || file.raw?.file_url || file.raw?.fileUrl || ''),
    raw: file.raw && typeof file.raw === 'object' ? file.raw : null,
    kind: String(file.kind || '')
  }));
  qualState.livePhotoFiles = livePhotoFiles.map(file => ({
    name: String(file.name || file.fileName || '实拍图'),
    size: Number(file.size || 0) || 0,
    type: String(file.type || 'image/png'),
    dataUrl: String(file.dataUrl || ''),
    imageUrl: String(file.imageUrl || file.url || file.fileUrl || '')
  }));
  const templateNameInput = document.getElementById('qualTemplateNameInput');
  if (templateNameInput) templateNameInput.value = qualState.templateName;
  const sourceSpuInput = document.getElementById('qualTemplateSourceSpuInput');
  if (sourceSpuInput) sourceSpuInput.value = template.sourceSpuId || '';
  syncQualUploadTypeSelect();
  updateQualTemplateSummary(template);
}

function updateQualManualSpuUi() {
  const scope = document.getElementById('qualUploadScope')?.value || 'selected';
  const section = document.getElementById('qualManualSpuSection');
  const input = document.getElementById('qualManualSpuInput');
  const countEl = document.getElementById('qualManualSpuCount');
  const spuIds = uniqueStringList(parseSpuIdList(input?.value || ''));
  qualState.manualUploadSpuIds = spuIds;
  if (section) section.classList.toggle('active', scope === 'manual');
  if (countEl) {
    countEl.style.display = scope === 'manual' && spuIds.length ? 'block' : 'none';
    countEl.innerHTML = `共 <strong>${spuIds.length}</strong> 个 SPU`;
  }
}

function getQualTemplatePayloadFromState() {
  return {
    id: '',
    name: String(document.getElementById('qualTemplateNameInput')?.value?.trim() || qualState.templateName || '').trim(),
    sourceSpuId: String(document.getElementById('qualTemplateSourceSpuInput')?.value?.trim() || '').trim(),
    files: qualState.files.map(file => ({
      name: file.name,
      size: file.size,
      type: file.type,
      dataUrl: file.dataUrl || '',
      fileUrl: file.fileUrl || '',
      originalFileUrl: file.originalFileUrl || '',
      signedFileUrl: file.signedFileUrl || '',
      raw: file.raw || null,
      kind: file.kind || ''
    })),
    livePhotoFiles: qualState.livePhotoFiles.map(file => ({
      name: file.name,
      size: file.size,
      type: file.type,
      dataUrl: file.dataUrl || '',
      imageUrl: file.imageUrl || ''
    }))
  };
}

function initQualificationTab() {
  // 刷新按钮
  document.getElementById('qualRefreshBtn')?.addEventListener('click', () => {
    qualState.pageNum = 1;
    qualState.searchProductIds = [];
    qualState.searchText = '';
    const searchInput = document.getElementById('qualSearchInput');
    if (searchInput) searchInput.value = '';
    loadQualProductList();
  });

  document.getElementById('qualTemplateRefreshBtn')?.addEventListener('click', async (event) => {
    event.preventDefault();
    await loadQualTemplates();
    showToast('资质模板已刷新', 'success');
  });

  // 搜索
  document.getElementById('qualSearchBtn')?.addEventListener('click', () => searchQualProducts());
  document.getElementById('qualSearchInput')?.addEventListener('keydown', (event) => {
    if (event.key !== 'Enter') return;
    event.preventDefault();
    searchQualProducts();
  });

  // 状态筛选
  document.getElementById('qualStatusFilter')?.addEventListener('change', () => {
    qualState.pageNum = 1;
    loadQualProductList();
  });

  // 每页条数
  document.getElementById('qualPageSizeSelect')?.addEventListener('change', (e) => {
    qualState.pageSize = parseInt(e.target.value);
    qualState.pageNum = 1;
    loadQualProductList();
  });

  document.getElementById('qualMainTemplateSelect')?.addEventListener('change', (event) => {
    qualState.selectedMainTemplateId = event.target.value || '';
    const template = getQualSelectedMainTemplate();
    if (template) applyQualTemplate(template);
    else updateQualTemplateSummary(null);
    renderQualTemplates();
  });
  document.getElementById('qualUploadScope')?.addEventListener('change', updateQualManualSpuUi);
  document.getElementById('qualManualSpuInput')?.addEventListener('input', updateQualManualSpuUi);
  document.getElementById('qualSaveTemplateBtn')?.addEventListener('click', async (event) => {
    event.preventDefault();
    await saveCurrentQualTemplate();
  });
  // 上传按钮
  document.getElementById('qualUploadBtn')?.addEventListener('click', startQualUpload);

  // 自动加载商品列表
  loadQualTemplates().catch(err => console.warn('[小青蛙电商助手] 加载资质模板失败:', err));
  updateQualManualSpuUi();
  loadQualProductList();
}

function updateQualTemplateSummary(template = getQualSelectedMainTemplate()) {
  const container = document.getElementById('qualTemplateSummary');
  if (!container) return;
  if (!template) {
    container.innerHTML = '<div class="qual-summary-empty">尚未选择模板。保存或选择模板后，这里会显示资质文件、实拍图和来源 SPU。</div>';
    return;
  }
  const files = Array.isArray(template.files) ? template.files : [];
  const livePhotoFiles = Array.isArray(template.livePhotoFiles) ? template.livePhotoFiles : [];
  const updatedText = template.updatedAt ? formatTemuSnapshotTime(template.updatedAt) : '--';
  container.innerHTML = `
    <div><strong>${escapeHtml(template.name || '未命名模板')}</strong></div>
    <div class="qual-summary-grid">
      <div class="qual-summary-cell"><span>来源 SPU</span><strong>${escapeHtml(template.sourceSpuId || '--')}</strong></div>
      <div class="qual-summary-cell"><span>资质文件</span><strong>${files.length} 份</strong></div>
      <div class="qual-summary-cell"><span>实拍图</span><strong>${livePhotoFiles.length} 张</strong></div>
    </div>
    <div style="margin-top:6px;color:var(--text-muted);">资质类型：${escapeHtml(template.certName || template.certType || '自动识别')} · 更新时间：${escapeHtml(updatedText)}</div>
  `;
}

async function saveCurrentQualTemplate() {
  const mallId = await resolveCurrentMallIdForStorage();
  if (!mallId) {
    showToast('未检测到当前店铺，无法保存模板', 'error');
    return;
  }
  const payload = getQualTemplatePayloadFromState();
  const sourceSpuId = String(payload.sourceSpuId || '').trim();
  if (!sourceSpuId) {
    showToast('请填写已完成资质和实拍图的模板 SPU 后再保存模板', 'warning');
    return;
  }
  const templateNameInput = document.getElementById('qualTemplateNameInput');
  const defaultName = sourceSpuId ? `资质模板-${sourceSpuId}` : (qualState.products[0]?.categoryName || qualState.products[0]?.category || qualState.products[0]?.catName || '资质模板');
  showToast(`正在读取模板 SPU ${sourceSpuId} 的资质与实拍图...`, 'info');
  const result = await safeRuntimeSendMessage({
    action: 'buildQualificationTemplateFromSpu',
    params: {
      productId: sourceSpuId,
      certType: document.getElementById('qualTypeSelect')?.value || ''
    }
  }, { success: false, error: '读取模板 SPU 失败' }, 60000);
  if (!result?.success || !result.template) {
    showToast(result?.error || '读取模板 SPU 失败', 'error');
    return;
  }
  const templateData = {
    ...payload,
    ...result.template,
    files: result.template.files || [],
    livePhotoFiles: result.template.livePhotoFiles || []
  };

  const template = normalizeQualTemplateRecord({
    ...templateData,
    id: qualState.selectedMainTemplateId || '',
    name: payload.name || defaultName,
    mallId,
    updatedAt: new Date().toISOString()
  });
  const saveResult = await saveUserTemplate('qualification', template);
  if (!saveResult.success) {
    showToast(saveResult.error || '保存资质模板失败', 'error');
    return;
  }
  if (saveResult.id) template.serverId = saveResult.id;
  await loadQualTemplates();
  const saved = qualState.templates.find(item =>
    String(item.serverId || item.id) === String(saveResult.id || '') ||
    String(item.name) === String(template.name)
  ) || template;
  qualState.selectedMainTemplateId = String(saved.id);
  applyQualTemplate(saved);
  if (templateNameInput && !templateNameInput.value.trim()) templateNameInput.value = template.name;
  renderQualTemplates();
  showToast('资质模板已保存到当前店铺云端', 'success');
}

async function loadQualProductList() {
  const container = document.getElementById('qualProductListContainer');
  const refreshBtn = document.getElementById('qualRefreshBtn');
  const searchBtn = document.getElementById('qualSearchBtn');
  if (!container) return;
  container.innerHTML = '<div class="loading">加载中...</div>';
  if (refreshBtn) refreshBtn.disabled = true;
  if (searchBtn) searchBtn.disabled = true;

  try {
    const searchProductIds = uniqueStringList(qualState.searchProductIds || []);
    const isSpuSearch = searchProductIds.length > 0;
    const result = await safeRuntimeSendMessage({
      action: 'fetchQualificationPool',
      params: {
        pageNum: isSpuSearch ? 1 : qualState.pageNum,
        pageSize: isSpuSearch ? Math.max(50, searchProductIds.length) : qualState.pageSize,
        fetchAll: false,
        includeAllStatus: true,
        productIds: isSpuSearch ? searchProductIds : undefined
      }
    }, { success: false, error: '加载资质商品失败' }, 30000);

    if (result?.success) {
      const products = Array.isArray(result.products) ? result.products : [];
      qualState.products = isSpuSearch
        ? products.filter(product => searchProductIds.includes(String(product?.spuId || '').trim()))
        : products;
      qualState.total = Number(result.total || qualState.products.length || 0);
      if (isSpuSearch) {
        qualState.pageNum = 1;
        qualState.total = qualState.products.length;
        qualState.totalPages = 1;
      } else {
        qualState.totalPages = Number(result.totalPages || Math.ceil(qualState.total / Math.max(1, qualState.pageSize)) || 1);
      }
      renderQualProductList();
      renderQualPagination();
      await saveTemuSnapshot('qualification', {
        products: qualState.products,
        total: qualState.total,
        totalPages: qualState.totalPages,
        pageNum: qualState.pageNum,
        pageSize: qualState.pageSize
      }, { itemCount: qualState.products.length });
    } else {
      qualState.products = [];
      qualState.total = 0;
      qualState.totalPages = 0;
      container.innerHTML = `<div class="empty"><div class="empty-icon">❌</div><p>${escapeHtml(result?.error || '加载失败')}</p></div>`;
      renderQualPagination();
    }
  } catch (err) {
    console.error('[小青蛙电商助手] loadQualProductList 异常:', err);
    qualState.products = [];
    qualState.total = 0;
    qualState.totalPages = 0;
    container.innerHTML = `<div class="empty"><div class="empty-icon">❌</div><p>${escapeHtml(err?.message || String(err))}</p></div>`;
    renderQualPagination();
  } finally {
    if (refreshBtn) refreshBtn.disabled = false;
    if (searchBtn) searchBtn.disabled = false;
  }
}

function searchQualProducts() {
  const input = document.getElementById('qualSearchInput');
  const raw = String(input?.value || '').trim();
  qualState.pageNum = 1;
  qualState.searchProductIds = uniqueStringList(parseSpuIdList(raw));
  qualState.searchText = qualState.searchProductIds.length ? '' : raw.toLowerCase();
  if (!raw) {
    loadQualProductList();
    return;
  }
  if (qualState.searchProductIds.length) {
    loadQualProductList();
    return;
  }
  renderQualProductList();
}

function renderQualProductList() {
  const container = document.getElementById('qualProductListContainer');
  if (!container) return;

  if (qualState.products.length === 0) {
    const searchedIds = uniqueStringList(qualState.searchProductIds || []);
    const emptyText = searchedIds.length
      ? `未找到这些 SPU 的资质商品：${searchedIds.join('、')}`
      : '暂无商品数据';
    container.innerHTML = `<div class="empty"><div class="empty-icon">📦</div><p>${escapeHtml(emptyText)}</p></div>`;
    updateQualSelectedCount();
    return;
  }

  const statusFilter = document.getElementById('qualStatusFilter')?.value || '';
  const searchQuery = String(qualState.searchText || '').trim().toLowerCase();

  let filtered = qualState.products;
  if (statusFilter === 'pending') filtered = filtered.filter(p => p.status === 'pending');
  if (statusFilter === 'reviewing') filtered = filtered.filter(p => p.status === 'reviewing');
  if (statusFilter === 'completed') filtered = filtered.filter(p => p.status === 'completed');
  if (statusFilter === 'unknown') filtered = filtered.filter(p => p.status === 'unknown');
  if (searchQuery) filtered = filtered.filter(p =>
    String(p.spuId || '').toLowerCase().includes(searchQuery) ||
    String(p.spuName || '').toLowerCase().includes(searchQuery) ||
    String(p.categoryName || p.category || p.catName || '').toLowerCase().includes(searchQuery)
  );

  // 收集所有资质类型，提交值必须使用 TEMU 返回的 cert_type。
  const allTaskTypes = new Map();
  qualState.products.forEach(p => {
    (Array.isArray(p.waitTaskList) ? p.waitTaskList : []).filter(t => t && typeof t === 'object').forEach(t => {
      const certValue = getQualificationTaskCertType(t);
      if (!certValue) return;
      const displayName = translateQualificationName(t);
      const rawName = getQualificationRawName(t);
      const label = rawName && rawName !== displayName
        ? `${displayName} / ${rawName}`
        : displayName;
      allTaskTypes.set(certValue, label || certValue);
    });
  });
  // 更新资质类型下拉
  const typeSelect = document.getElementById('qualTypeSelect');
  if (typeSelect && allTaskTypes.size > 0) {
    const currentVal = typeSelect.value;
    typeSelect.innerHTML = '<option value="">-- 请选择资质类型 --</option>' +
      Array.from(allTaskTypes.entries()).map(([value, label]) => `<option value="${escapeHtml(String(value))}">${escapeHtml(String(label))}</option>`).join('');
    if (currentVal) typeSelect.value = currentVal;
  }
  syncQualUploadTypeSelect(allTaskTypes);

  const html = `
    <table class="product-table">
      <thead>
        <tr>
          <th style="width:34px;"><input type="checkbox" class="checkbox" id="qualSelectAll"></th>
          <th style="width:290px;">SPU信息</th>
          <th style="min-width:180px;">资质名称</th>
          <th style="width:130px;">资质上传状态</th>
          <th style="min-width:220px;">中文资质名称</th>
        </tr>
      </thead>
      <tbody>
        ${filtered.map(p => {
          const certTasks = Array.isArray(p.waitTaskList) ? p.waitTaskList : [];
          const renderedTasks = renderQualificationTaskRows(certTasks);
          const rows = renderedTasks.length ? renderedTasks : [{ rawName: '—', chineseLabel: '—', statusText: '—' }];
          const rowCount = rows.length;
          const categoryText = p.categoryName || p.category || p.catName || '—';
          const spuInfoCell = `
            <div style="display:flex;gap:10px;align-items:center;height:100%;">
              <div style="width:52px;height:52px;flex:0 0 auto;border-radius:6px;overflow:hidden;border:1px solid #e5e7eb;background:#f8fafc;display:flex;align-items:center;justify-content:center;">
                ${p.mainImage
                  ? `<img src="${escapeHtml(p.mainImage)}" class="img-fallback" data-fallback-size="52" style="width:52px;height:52px;object-fit:cover;">`
                  : '<span style="font-size:18px;color:#cbd5e1;">📷</span>'}
              </div>
              <div style="min-width:0;display:flex;flex-direction:column;gap:4px;justify-content:center;">
                <div style="font-size:12px;color:#475467;line-height:1.35;">类目：${escapeHtml(categoryText)}</div>
                <div style="font-size:12px;color:#475467;line-height:1.35;">SPU：${escapeHtml(String(p.spuId || '—'))}</div>
              </div>
            </div>`;

          return rows.map((row, index) => {
            const isFirst = index === 0;
            return `<tr>
              ${isFirst ? `<td rowspan="${rowCount}" style="vertical-align:top;padding-top:18px;"><input type="checkbox" class="checkbox qual-product-cb" data-spu="${escapeHtml(String(p.spuId))}" ${p.status === 'pending' ? '' : ''}></td>` : ''}
              ${isFirst ? `<td rowspan="${rowCount}" style="vertical-align:middle;padding:14px 10px;">${spuInfoCell}</td>` : ''}
              <td style="font-size:12px;color:#1f2937;">${escapeHtml(String(row.rawName || '—'))}</td>
              <td style="font-size:12px;color:#475467;line-height:1.35;">
                <div>${escapeHtml(String(row.statusText || '—'))}</div>
                ${row.statusDetail ? `<div style="font-size:11px;color:#94a3b8;">${escapeHtml(String(row.statusDetail))}</div>` : ''}
              </td>
              <td style="font-size:12px;color:#1f2937;">${escapeHtml(String(row.chineseLabel || '—'))}</td>
            </tr>`;
          }).join('');
        }).join('')}
      </tbody>
    </table>
  `;

  container.innerHTML = html;
  const qualChecks = () => Array.from(container.querySelectorAll('.qual-product-cb'));

  // 全选
  document.getElementById('qualSelectAll')?.addEventListener('change', (e) => {
    qualChecks().forEach(cb => cb.checked = e.target.checked);
    updateQualSelectedCount();
  });

  // 单选
  qualChecks().forEach(cb => {
    cb.addEventListener('change', updateQualSelectedCount);
  });

  updateQualSelectedCount();
}

function updateQualSelectedCount() {
  const countEl = document.getElementById('qualSelectedCountInfo');
  if (!countEl) return;
  const checked = document.querySelectorAll('#qualProductListContainer .qual-product-cb:checked').length;
  countEl.style.display = checked > 0 ? 'block' : 'none';
  countEl.innerHTML = `已选择 <strong>${checked}</strong> 个商品`;
}

function isQualificationProductPending(product = {}) {
  if (product.status === 'pending') return true;
  if (Array.isArray(product.pendingCertTypes) && product.pendingCertTypes.length > 0) return true;
  return (Array.isArray(product.waitTaskList) ? product.waitTaskList : []).some(task => {
    return isQualificationStatusUploadRequired(getQualificationStatusMeta(task));
  });
}

function getQualificationPendingTask(product = {}) {
  return (Array.isArray(product?.waitTaskList) ? product.waitTaskList : []).find(task => {
    return isQualificationStatusUploadRequired(getQualificationStatusMeta(task));
  }) || null;
}

function getQualificationFirstTask(product = {}) {
  return (Array.isArray(product?.waitTaskList) ? product.waitTaskList : []).find(task => {
    return task && typeof task === 'object' && getQualificationTaskCertType(task);
  }) || null;
}

async function fetchQualificationProductsForUploadScope(scope = 'selected') {
  if (scope === 'selected') return qualState.products || [];

  const pageSize = Math.max(50, Math.min(500, Number(qualState.total || qualState.pageSize || 50)));
  const result = await safeRuntimeSendMessage({
    action: 'fetchQualificationPool',
    params: {
      pageNum: 1,
      pageSize,
      fetchAll: true,
      includeAllStatus: true
    }
  }, { success: false, error: '加载资质商品失败' }, 60000);

  if (!result?.success || !Array.isArray(result.products)) {
    throw new Error(result?.error || '加载资质商品失败');
  }
  return result.products;
}

async function fetchQualificationProductsBySpuIds(spuIds = []) {
  const ids = uniqueStringList(spuIds);
  if (!ids.length) return [];
  const result = await safeRuntimeSendMessage({
    action: 'fetchQualificationPool',
    params: {
      pageNum: 1,
      pageSize: Math.max(50, ids.length),
      fetchAll: false,
      includeAllStatus: true,
      productIds: ids
    }
  }, { success: false, error: '查询 SPU 资质任务失败' }, 60000);
  if (!result?.success || !Array.isArray(result.products)) {
    throw new Error(result?.error || '查询 SPU 资质任务失败');
  }
  const seen = new Set();
  return result.products.filter(product => {
    const spuId = String(product?.spuId || '').trim();
    if (!spuId || !ids.includes(spuId) || seen.has(spuId)) return false;
    seen.add(spuId);
    return true;
  });
}

async function resolveQualificationUploadProducts(scope = 'selected') {
  if (scope === 'selected') {
    const selectedSpus = uniqueStringList(Array.from(document.querySelectorAll('#qualProductListContainer .qual-product-cb:checked')).map(cb => cb.dataset.spu));
    return selectedSpus
      .map(spuId => (qualState.products || []).find(p => String(p.spuId) === String(spuId)))
      .filter(Boolean);
  }
  if (scope === 'manual') {
    updateQualManualSpuUi();
    const manualSpus = uniqueStringList(qualState.manualUploadSpuIds || []);
    if (!manualSpus.length) return [];
    let officialProducts = [];
    try {
      officialProducts = await fetchQualificationProductsBySpuIds(manualSpus);
    } catch (err) {
      console.warn('[小青蛙电商助手] 手动 SPU 查询官方资质池失败，后续将按输入 SPU 直接提交官方批量任务:', err);
    }
    const officialMap = new Map(officialProducts.map(product => [String(product.spuId), product]));
    return manualSpus.map(spuId => officialMap.get(String(spuId)) || {
      spuId: String(spuId),
      productId: String(spuId),
      spuName: `SPU ${spuId}`,
      waitTaskList: [],
      pendingCertTypes: [],
      status: 'manual'
    });
  }

  const products = await fetchQualificationProductsForUploadScope(scope);
  const scopedProducts = scope === 'pending' ? products.filter(isQualificationProductPending) : products;
  const seen = new Set();
  return scopedProducts.filter(product => {
    const spuId = String(product?.spuId || '').trim();
    if (!spuId || seen.has(spuId)) return false;
    seen.add(spuId);
    return true;
  });
}

function getQualificationUploadFiles() {
  const mainTemplate = getQualSelectedMainTemplate();
  const files = [...qualState.files];
  let livePhotoFiles = [...qualState.livePhotoFiles];
  if (mainTemplate?.livePhotoFiles?.length) {
    livePhotoFiles = mainTemplate.livePhotoFiles.map(file => ({ ...file }));
  }

  return {
    files: files.length ? files : (Array.isArray(mainTemplate?.files) ? mainTemplate.files.map(file => ({ ...file })) : []),
    livePhotoFiles,
    mainTemplate,
    templateDetail: mainTemplate?.detail || null
  };
}

function renderQualPagination() {
  const info = document.getElementById('qualPaginationInfo');
  const btns = document.getElementById('qualPaginationButtons');
  const container = document.getElementById('qualPaginationContainer');
  if (!info || !btns) return;

  if (container) container.style.display = qualState.total > 0 ? 'flex' : 'none';
  if (qualState.total > 0) {
    info.innerHTML = `共 <strong>${qualState.total}</strong> 条，第 <strong>${qualState.pageNum}</strong>/<strong>${qualState.totalPages}</strong> 页`;
  } else {
    info.innerHTML = '';
  }

  btns.innerHTML = '';
  if (qualState.pageNum > 1) {
    const btn = document.createElement('button');
    btn.className = 'btn btn-default btn-sm'; btn.textContent = '上一页';
    btn.addEventListener('click', () => { qualState.pageNum--; loadQualProductList(); });
    btns.appendChild(btn);
  }
  for (let i = 1; i <= qualState.totalPages; i++) {
    if (i > 5 && i < qualState.totalPages - 1 && Math.abs(i - qualState.pageNum) > 1) {
      if (i === 6 || i === qualState.totalPages - 2) {
        const span = document.createElement('span'); span.textContent = '...'; span.style.padding = '0 4px'; btns.appendChild(span);
      }
      continue;
    }
    const btn = document.createElement('button');
    btn.className = i === qualState.pageNum ? 'btn btn-primary btn-sm' : 'btn btn-default btn-sm';
    btn.textContent = i;
    if (i !== qualState.pageNum) btn.addEventListener('click', () => { qualState.pageNum = i; loadQualProductList(); });
    btns.appendChild(btn);
  }
  if (qualState.pageNum < qualState.totalPages) {
    const btn = document.createElement('button');
    btn.className = 'btn btn-default btn-sm'; btn.textContent = '下一页';
    btn.addEventListener('click', () => { qualState.pageNum++; loadQualProductList(); });
    btns.appendChild(btn);
  }
}

async function startQualUpload() {
  const uploadScope = document.getElementById('qualUploadScope')?.value || 'selected';
  const mainTemplate = getQualSelectedMainTemplate();
  const qualType = String(
    document.getElementById('qualUploadTypeSelect')?.value
    || document.getElementById('qualTypeSelect')?.value
    || mainTemplate?.certType
    || mainTemplate?.detail?.certType
    || ''
  ).trim();
  const allowOverwrite = true;
  let selectedProducts = [];
  try {
    selectedProducts = await resolveQualificationUploadProducts(uploadScope);
  } catch (err) {
    showToast(`加载上传范围失败：${err.message}`, 'error');
    return;
  }
  if (selectedProducts.length === 0) {
    if (uploadScope === 'selected') {
      showToast('请先在左侧勾选要上传资质的商品，或切换上传范围', 'warning');
    } else if (uploadScope === 'manual') {
      showToast('请输入有效的 SPU ID', 'warning');
    } else if (uploadScope === 'pending') {
      showToast('当前范围没有待处理资质的商品；如需覆盖已成功链接，请选择“全部商品”或手动勾选', 'warning');
    } else {
      showToast('未找到可上传资质的商品数据，请先刷新列表', 'error');
    }
    return;
  }
  const inferCertType = (product = {}) => {
    const pendingTask = getQualificationPendingTask(product);
    const firstTask = getQualificationFirstTask(product);
    return qualType
      || product?.pendingCertTypes?.[0]
      || getQualificationTaskCertType(pendingTask)
      || getQualificationTaskCertType(firstTask)
      || '';
  };
  const groups = new Map();
  const unknownProducts = [];
  selectedProducts.forEach(product => {
    const certType = inferCertType(product);
    if (!certType) {
      unknownProducts.push(product);
      return;
    }
    const key = certType;
    if (!groups.has(key)) groups.set(key, []);
    groups.get(key).push(product);
  });

  const progressSection = document.getElementById('qualProgressSection');
  const fill = document.getElementById('qualProgressFill');
  const text = document.getElementById('qualProgressText');
  const list = document.getElementById('qualResultList');
  if (!progressSection || !fill || !text || !list) {
    showToast('资质上传界面未初始化完成，请刷新插件页后重试', 'error');
    return;
  }
  progressSection.style.display = 'block';
  fill.style.width = '0%';
  fill.className = 'progress-fill blue';
  text.textContent = '准备同步资质任务...';
  list.innerHTML = '';

  const appendResultItem = (className, icon, label, message) => {
    const item = document.createElement('div');
    item.className = `result-item ${className}`;
    item.innerHTML = `<span class="icon">${icon}</span><span class="reason"><strong>${escapeHtml(label)}</strong> — ${escapeHtml(message)}</span>`;
    list.appendChild(item);
  };
  if (unknownProducts.length) {
    const firstSpu = String(unknownProducts[0]?.spuId || '—');
    appendResultItem('fail', '❌', unknownProducts.length === 1 ? firstSpu : `${firstSpu} 等 ${unknownProducts.length} 个 SPU`, 'TEMU 未返回 cert_type/task_type，且当前模板没有资质类型；请选择上传资质类型后再提交');
  }
  if (!groups.size) {
    fill.className = 'progress-fill orange';
    text.textContent = '没有可提交的资质任务';
    showToast('没有可提交的资质任务：请选择资质类型后重试', 'warning');
    return;
  }

  const templateFiles = getQualificationUploadFiles();
  if (!templateFiles.mainTemplate) {
    showToast('请先选择一个资质模板，再批量上传', 'warning');
    return;
  }
  const hasCertDetail = !!(
    templateFiles.templateDetail?.multiLangProductCertFileMap
    || templateFiles.templateDetail?.docPdfContentReq
    || templateFiles.templateDetail?.inspectReportFiles?.length
    || templateFiles.templateDetail?.productCertFiles?.length
  );
  if (!hasCertDetail) {
    showToast('当前模板没有可提交的资质文件，请用已填写好的 SPU 重新保存模板', 'error');
    return;
  }
  let success = 0, fail = 0;
  const groupEntries = Array.from(groups.entries());
  for (let i = 0; i < groupEntries.length; i++) {
    const [certTypeKey, groupProducts] = groupEntries[i];
    const spuIds = groupProducts.map(p => String(p.spuId));
    const pct = Math.round(((i + 1) / groupEntries.length) * 100);
    fill.style.width = pct + '%';
    text.textContent = `同步批量资质任务（${i + 1}/${groupEntries.length}）...`;

    const displayLabel = spuIds.length === 1 ? spuIds[0] : `${spuIds[0]} 等 ${spuIds.length} 个 SPU`;

    try {
      if (!certTypeKey || certTypeKey === '__unknown__') {
        throw new Error('未能识别资质类型');
      }
      const result = await safeRuntimeSendMessage({
        action: 'uploadQualificationBatch',
        params: {
          productIds: spuIds,
          certType: certTypeKey,
          qualType: certTypeKey,
          allowOverwrite,
          files: templateFiles.files,
          livePhotoFiles: templateFiles.livePhotoFiles,
          template: {
            mainTemplateId: templateFiles.mainTemplate?.id || '',
            sourceSpuId: templateFiles.mainTemplate?.sourceSpuId || '',
            detail: templateFiles.templateDetail,
            livePhotoFiles: templateFiles.livePhotoFiles
          }
        }
      }, { success: false, error: '上传资质批量任务失败' }, 90000);

      const item = document.createElement('div');
      if (result?.success) {
        item.className = 'result-item success';
        const verifiedCount = Array.isArray(result.verifiedProductIds) ? result.verifiedProductIds.length : 0;
        const uploadedCount = verifiedCount || (Array.isArray(result.productIds) ? result.productIds.length : groupProducts.length);
        const skippedCount = Array.isArray(result.skippedProductIds) ? result.skippedProductIds.length : 0;
        const verifiedText = verifiedCount ? `，官方回读确认 ${escapeHtml(String(verifiedCount))} 个` : '';
        item.innerHTML = `<span class="icon">✅</span><span class="reason"><strong>${escapeHtml(displayLabel)}</strong> — 批量任务已提交${result.taskId ? `，Task ${escapeHtml(String(result.taskId))}` : ''}${verifiedText}${skippedCount ? `，跳过 ${escapeHtml(String(skippedCount))} 个不匹配 SPU` : ''}</span>`;
        success += uploadedCount;
        fail += skippedCount;
      } else {
        item.className = 'result-item fail';
        item.innerHTML = `<span class="icon">❌</span><span class="reason"><strong>${escapeHtml(displayLabel)}</strong> — ${escapeHtml(result?.error || '失败')}</span>`;
        fail += groupProducts.length;
      }
      list.appendChild(item);
    } catch (err) {
      const item = document.createElement('div');
      item.className = 'result-item fail';
      item.innerHTML = `<span class="icon">❌</span><span class="reason"><strong>${escapeHtml(displayLabel)}</strong> — ${escapeHtml(err.message)}</span>`;
      list.appendChild(item);
      fail += groupProducts.length;
    }

    await new Promise(r => setTimeout(r, 400));
  }

  fill.className = fail === 0 ? 'progress-fill green' : 'progress-fill orange';
  text.textContent = `资质任务完成！成功 ${success}，失败 ${fail}`;
  showToast(`资质任务完成：成功${success}，失败${fail}`, success > 0 ? 'success' : 'error');
  loadQualProductList();
}

// ========== 店铺搬家 Tab ==========
const collectionState = {
  items: [],     // 店铺搬家复制任务列表
  selected: new Set(),  // 选中的商品ID
  sourceProducts: [],
  sourceSelected: new Set(),
  templates: [],
  templateSelected: new Set(),
  successCount: 0,
  failCount: 0,
  draftCount: 0
};

function getCollectionItemKey(item) {
  return item?.id || item?.templateId || item?.goodsId || item?.url || item?.title || '';
}

function upsertCollectionItem(item) {
  const key = getCollectionItemKey(item);
  if (!key) return;
  const index = collectionState.items.findIndex(existing => getCollectionItemKey(existing) === key);
  if (index >= 0) {
    collectionState.items[index] = { ...collectionState.items[index], ...item };
  } else {
    collectionState.items.push(item);
  }
}

function formatCollectionPrice(price) {
  if (price === undefined || price === null || price === '') return '-';
  const num = Number(price);
  if (Number.isFinite(num)) return `¥${num.toFixed(2)}`;
  return String(price);
}

function pickCollectionValue(...values) {
  for (const value of values) {
    if (value === undefined || value === null || value === '') continue;
    return value;
  }
  return '';
}

function normalizeCollectionImages(product = {}) {
  const sources = [
    product.images,
    product.imageList,
    product.productImageList,
    product.skcImages,
    product.goodsImages,
    product.mainImage,
    product.image,
    product.thumbUrl
  ];
  const list = [];
  for (const source of sources) {
    if (!source) continue;
    if (Array.isArray(source)) {
      source.forEach(item => {
        if (typeof item === 'string') list.push(item);
        else if (item && typeof item === 'object') list.push(item.url || item.imageUrl || item.imgUrl || item.image || '');
      });
    } else if (typeof source === 'string') {
      list.push(source);
    } else if (typeof source === 'object') {
      list.push(source.url || source.imageUrl || source.imgUrl || source.image || '');
    }
  }
  return [...new Set(list.map(url => String(url || '').trim()).filter(Boolean))].slice(0, 30);
}

function normalizeCollectionSkus(product = {}) {
  const skuSource = [
    product.skus,
    product.skuList,
    product.productSkuSummaries,
    product.productSkuList,
    product.skuListInfo,
    product.variantList
  ].find(Array.isArray) || [];
  return skuSource.map((sku, index) => ({
    id: pickCollectionValue(sku.id, sku.skuId, sku.productSkuId, sku.goodsSkuId, sku.sku_id, `${index + 1}`),
    name: pickCollectionValue(sku.name, sku.skuName, sku.skuSpec, sku.specText, sku.specName, sku.skuSpecText, sku.goodsSkuName, `SKU ${index + 1}`),
    price: pickCollectionValue(sku.price, sku.salePrice, sku.currentPrice, sku.supplierPrice, product.price),
    stock: pickCollectionValue(sku.stock, sku.stockCnt, sku.availableStock, sku.available_stock, ''),
    image: pickCollectionValue(sku.image, sku.skuImage, sku.thumbUrl, sku.sku_image, '')
  }));
}

function normalizeCollectionProduct(product = {}, source = 'seller-product') {
  const images = normalizeCollectionImages(product);
  const skus = normalizeCollectionSkus(product);
  const goodsId = String(pickCollectionValue(product.goodsId, product.goods_id, product.productId, product.spuId, product.productSpuId, product.goodspu, product.skcId)).trim();
  const spuId = String(pickCollectionValue(product.spuId, product.spu_id, product.productId, product.productSpuId, product.goodspu, goodsId)).trim();
  const title = pickCollectionValue(product.title, product.productName, product.spuName, product.productCNName, product.name, product.goodsName, `商品 ${goodsId || spuId}`.trim());
  return {
    id: product.id || `${source}_${goodsId || spuId || Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
    goodsId,
    spuId,
    title,
    productName: title,
    price: pickCollectionValue(product.price, product.salePrice, product.supplierPrice, product.minPrice, ''),
    description: pickCollectionValue(product.description, product.goodsDesc, product.desc, ''),
    category: pickCollectionValue(product.category, product.categoryName, product.catName, product.categoryNamePath, ''),
    categoryId: pickCollectionValue(product.categoryId, product.catId, product.cat_id, ''),
    categoryPath: pickCollectionValue(product.categoryPath, product.catPath, product.category_path, ''),
    mainImage: images[0] || '',
    images,
    skus,
    productPropertyList: Array.isArray(product.productPropertyList) ? product.productPropertyList : [],
    sourceType: source,
    raw: product
  };
}

function normalizeProductMoveTemplatePayload(product, mallId = '') {
  const normalized = normalizeCollectionProduct(product, product.sourceType || 'seller-product');
  const sourceProductDetail = product.sourceProductDetail ||
    product.productDetail ||
    product.rawProductDetail ||
    product.lingfengProductDetail ||
    null;
  return {
    ...normalized,
    productId: product.productId || normalized.productId || normalized.spuId || normalized.goodsId || '',
    sourceProductId: product.sourceProductId || product.productId || normalized.spuId || normalized.goodsId || '',
    sourceProductDetail,
    sourceMallId: mallId || normalized.sourceMallId || state.mallId || '',
    sourceMallName: state.mallName || normalized.sourceMallName || '',
    savedAt: new Date().toISOString(),
    platform: 'TEMU'
  };
}

function getTemuUploadTemplateNumber(value) {
  if (value === null || value === undefined || value === '') return undefined;
  const num = Number(value);
  return Number.isFinite(num) ? num : undefined;
}

function normalizeTemuUploadTemplateVisiblePrice(value) {
  const num = getTemuUploadTemplateNumber(value);
  if (num === undefined) return value;
  return Math.abs(num) >= 100 ? num / 100 : num;
}

function normalizeTemuUploadTemplateVisibleDimensionValue(value) {
  const num = getTemuUploadTemplateNumber(value);
  if (num === undefined) return value;
  return Number.isInteger(num) ? num / 10 : num;
}

function normalizeTemuUploadTemplateVisibleWeightValue(value) {
  const num = getTemuUploadTemplateNumber(value);
  if (num === undefined) return value;
  return Math.abs(num) >= 1000 ? num / 1000 : num;
}

function normalizeTemuUploadTemplateVisibleVolume(volume = {}) {
  if (!volume || typeof volume !== 'object') return volume;
  const result = cloneTemuUploadJsonSafe(volume) || {};
  [
    ['len', 'inputLen'],
    ['width', 'inputWidth'],
    ['height', 'inputHeight']
  ].forEach(([key, inputKey]) => {
    const inputValue = getTemuUploadTemplateNumber(result[inputKey]);
    const visibleValue = inputValue !== undefined
      ? inputValue
      : normalizeTemuUploadTemplateVisibleDimensionValue(result[key]);
    if (visibleValue !== undefined && visibleValue !== null && visibleValue !== '') {
      result[key] = visibleValue;
      result[inputKey] = visibleValue;
    }
  });
  return result;
}

function normalizeTemuUploadTemplateVisibleWeight(weight = {}) {
  if (!weight || typeof weight !== 'object') return weight;
  const result = cloneTemuUploadJsonSafe(weight) || {};
  const inputValue = getTemuUploadTemplateNumber(result.inputValue);
  const visibleValue = inputValue !== undefined
    ? inputValue
    : normalizeTemuUploadTemplateVisibleWeightValue(result.value);
  if (visibleValue !== undefined && visibleValue !== null && visibleValue !== '') {
    result.inputValue = visibleValue;
    result.value = visibleValue;
  }
  return result;
}

function normalizeTemuUploadTemplateVisibleSkuValues(sku = {}) {
  if (!sku || typeof sku !== 'object') return sku;
  const result = cloneTemuUploadJsonSafe(sku) || {};
  const firstSitePrice = Array.isArray(result.siteSupplierPrices) && result.siteSupplierPrices.length
    ? result.siteSupplierPrices[0]
    : null;
  const sourcePrice = pickTemuUploadFirstDefined(
    result.supplierPrice,
    result.supplierPriceValue,
    firstSitePrice?.supplierPrice,
    firstSitePrice?.supplierPriceValue
  );
  const visiblePrice = normalizeTemuUploadTemplateVisiblePrice(sourcePrice);
  if (visiblePrice !== undefined && visiblePrice !== null && visiblePrice !== '') {
    result.supplierPrice = visiblePrice;
    result.supplierPriceValue = visiblePrice;
  }
  if (Array.isArray(result.siteSupplierPrices)) {
    result.siteSupplierPrices = result.siteSupplierPrices.map(item => {
      if (!item || typeof item !== 'object') return item;
      const priceItem = cloneTemuUploadJsonSafe(item) || {};
      const itemSourcePrice = pickTemuUploadFirstDefined(
        priceItem.supplierPrice,
        priceItem.supplierPriceValue,
        visiblePrice
      );
      const itemPrice = normalizeTemuUploadTemplateVisiblePrice(itemSourcePrice);
      if (itemPrice !== undefined && itemPrice !== null && itemPrice !== '') {
        priceItem.supplierPrice = itemPrice;
        priceItem.supplierPriceValue = itemPrice;
      }
      return priceItem;
    });
  }
  ['productSkuWhExtAttr', 'productSkuWhExtAttrReq'].forEach(whKey => {
    if (!result[whKey] || typeof result[whKey] !== 'object') return;
    result[whKey] = cloneTemuUploadJsonSafe(result[whKey]) || {};
    if (result[whKey].productSkuVolume) result[whKey].productSkuVolume = normalizeTemuUploadTemplateVisibleVolume(result[whKey].productSkuVolume);
    if (result[whKey].productSkuVolumeReq) result[whKey].productSkuVolumeReq = normalizeTemuUploadTemplateVisibleVolume(result[whKey].productSkuVolumeReq);
    if (result[whKey].productSkuWeight) result[whKey].productSkuWeight = normalizeTemuUploadTemplateVisibleWeight(result[whKey].productSkuWeight);
    if (result[whKey].productSkuWeightReq) result[whKey].productSkuWeightReq = normalizeTemuUploadTemplateVisibleWeight(result[whKey].productSkuWeightReq);
  });
  return result;
}

function normalizeTemuUploadTemplateVisibleDetail(detail = {}) {
  if (!detail || typeof detail !== 'object') return detail;
  const result = cloneTemuUploadJsonSafe(detail) || {};
  if (Array.isArray(result.productSkcList)) {
    result.productSkcList = result.productSkcList.map(skc => {
      if (!skc || typeof skc !== 'object') return skc;
      const skcResult = cloneTemuUploadJsonSafe(skc) || {};
      if (Array.isArray(skcResult.productSkuList)) {
        skcResult.productSkuList = skcResult.productSkuList.map(normalizeTemuUploadTemplateVisibleSkuValues);
      }
      return skcResult;
    });
  }
  if (Array.isArray(result.productSkuList)) {
    result.productSkuList = result.productSkuList.map(normalizeTemuUploadTemplateVisibleSkuValues);
  }
  return result;
}

function getCollectionSourceProductKey(product) {
  return String(product?.goodsId || product?.spuId || product?.productId || product?.id || product?.title || '').trim();
}

function getProductTemplateKey(template) {
  return String(template?.id || template?.goodsId || template?.data?.goodsId || template?.name || '').trim();
}

function getMoveProductSourceId(product = {}) {
  return String(
    product.sourceProductId ||
    product.productId ||
    product.spuId ||
    product.goodsId ||
    product.raw?.goodspu ||
    product.raw?.productSpuId ||
    product.raw?.productId ||
    ''
  ).trim();
}

function getMoveDetailLeafCategory(productDetail = {}) {
  const categories = productDetail.categories || {};
  if (categories && typeof categories === 'object') {
    const catKeys = Object.keys(categories)
      .filter(key => /^cat\d+$/.test(key))
      .map(key => Number(key.replace('cat', '')))
      .filter(Number.isFinite)
      .sort((a, b) => b - a);
    for (const key of catKeys) {
      const cat = categories[`cat${key}`];
      const catId = cat?.catId || cat?.categoryId || cat?.id || '';
      if (catId) {
        return {
          categoryId: String(catId),
          categoryName: cat?.catName || cat?.categoryName || cat?.name || '',
          categoryPath: catKeys
            .slice()
            .reverse()
            .map(index => categories[`cat${index}`]?.catName || categories[`cat${index}`]?.categoryName || '')
            .filter(Boolean)
            .join(' / ')
        };
      }
    }
  }
  return {
    categoryId: String(productDetail.categoryId || productDetail.catId || productDetail.leafCatId || ''),
    categoryName: productDetail.categoryName || productDetail.catName || '',
    categoryPath: ''
  };
}

function summarizeMoveProductDetail(productDetail = {}, fallback = {}) {
  const images = [];
  const pushImage = (url) => {
    const text = String(url || '').trim();
    if (text && !images.includes(text)) images.push(text);
  };
  (productDetail.carouselImageUrls || []).forEach(pushImage);
  pushImage(productDetail.materialImgUrl);
  (productDetail.productSkcList || []).forEach(skc => {
    pushImage(skc.colorImageUrl);
    (skc.previewImgUrls || []).forEach(pushImage);
    (skc.productSkcImageExtList || []).forEach(item => pushImage(item?.imageUrl || item?.url));
    (skc.productSkuList || []).forEach(sku => {
      pushImage(sku.thumbUrl);
      (sku.productSkuThumbUrlI18nVOList || []).forEach(item => pushImage(item?.imageUrl || item?.url));
    });
  });

  const skus = [];
  (productDetail.productSkcList || []).forEach(skc => {
    (skc.productSkuList || []).forEach((sku, index) => {
      const specName = (sku.productSkuSpecList || [])
        .map(spec => spec?.specName || spec?.specValue || spec?.valueName || spec?.value || spec?.displayName || spec?.name || spec?.parentSpecName || '')
        .filter(Boolean)
        .join(' / ');
      skus.push({
        id: sku.productSkuId || sku.skuId || `${skc.productSkcId || 'skc'}_${index + 1}`,
        name: specName || sku.extCode || `SKU ${index + 1}`,
        price: sku.supplierPrice || sku.siteSupplierPrices?.[0]?.supplierPrice || fallback.price || '',
        stock: sku.productSkuQuantity || '',
        image: sku.thumbUrl || skc.colorImageUrl || ''
      });
    });
  });

  const category = getMoveDetailLeafCategory(productDetail);
  return {
    productId: productDetail.productId || fallback.productId || fallback.spuId || fallback.goodsId || '',
    title: productDetail.productName || fallback.title || fallback.productName || '',
    productName: productDetail.productName || fallback.productName || fallback.title || '',
    categoryId: category.categoryId || fallback.categoryId || '',
    category: category.categoryName || fallback.category || '',
    categoryPath: category.categoryPath || fallback.categoryPath || '',
    mainImage: images[0] || fallback.mainImage || '',
    images: images.length ? images.slice(0, 60) : (fallback.images || []),
    skus: skus.length ? skus : (fallback.skus || []),
    productPropertyList: Array.isArray(productDetail.productPropertyList) ? productDetail.productPropertyList : [],
    productProperties: Array.isArray(productDetail.productProperties) ? productDetail.productProperties : []
  };
}

async function hydrateMoveSourceProduct(product, logList = null) {
  const sourceProductId = getMoveProductSourceId(product);
  if (!sourceProductId) {
    throw new Error('缺少 SPU ID，无法按凌风逻辑读取商品详情');
  }
  const detailResp = await safeRuntimeSendMessage({
    type: 'FETCH_MOVE_PRODUCT_DETAIL',
    productId: sourceProductId
  }, { success: false, error: '读取源商品详情失败' }, 40000);
  if (!detailResp?.success) {
    throw new Error(detailResp?.error || '读取源商品详情失败');
  }
  let detail = detailResp.productDetail || detailResp.data || detailResp.result || null;
  if (!detail || typeof detail !== 'object') {
    throw new Error('源商品详情为空');
  }
  detail = normalizeTemuUploadTemplateVisibleDetail(detail);
  const summary = summarizeMoveProductDetail(detail, product);
  addCollectionLog(logList, `🔎 ${product.title || product.goodsId || sourceProductId} - 已读取凌风同款商品详情`, 'success');
  return {
    ...product,
    ...summary,
    sourceProductId,
    sourceProductDetail: detail,
    lingfengReady: true
  };
}

function renderCollectionSourceProducts() {
  const container = document.getElementById('collectionSourceProductList');
  const countEl = document.getElementById('collectionSourceCount');
  if (!container) return;
  const query = (document.getElementById('collectionSourceSearchInput')?.value || '').trim().toLowerCase();
  const products = collectionState.sourceProducts.filter(product => {
    if (!query) return true;
    return [
      product.title,
      product.productName,
      product.goodsId,
      product.spuId,
      product.category
    ].some(value => String(value || '').toLowerCase().includes(query));
  });
  if (countEl) countEl.textContent = `源店商品 ${collectionState.sourceProducts.length} 个，已选 ${collectionState.sourceSelected.size} 个`;
  if (!products.length) {
    container.innerHTML = '<div class="empty" style="padding:18px;color:var(--text-muted);font-size:13px;">暂无源店商品，或筛选条件无匹配</div>';
    return;
  }
  container.innerHTML = products.map((product, idx) => {
    const key = getCollectionSourceProductKey(product);
    const image = product.mainImage || product.images?.[0] || '';
    return `
      <label style="display:grid;grid-template-columns:34px 54px minmax(0,1fr) auto;gap:10px;align-items:center;padding:10px 12px;border-bottom:1px solid var(--border);cursor:pointer;">
        <input type="checkbox" class="checkbox collection-source-check" data-key="${escapeHtml(key)}" ${collectionState.sourceSelected.has(key) ? 'checked' : ''}>
        <img src="${escapeHtml(image)}" class="img-fallback" data-fallback-size="54" style="width:54px;height:54px;object-fit:cover;border-radius:8px;border:1px solid var(--border);background:#f8fafc;">
        <span style="min-width:0;">
          <span style="display:block;font-size:13px;font-weight:700;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(product.title || '未知商品')}</span>
          <span style="display:block;margin-top:4px;font-size:12px;color:var(--text-muted);">SPU：${escapeHtml(product.spuId || product.productId || '-')} ｜ SKC：${escapeHtml(product.goodsId || product.skcId || '-')} ｜ SKU ${escapeHtml(String(product.skus?.length || 0))}</span>
        </span>
        <button type="button" class="btn btn-default btn-sm collection-source-add-one" data-key="${escapeHtml(key)}">保存模板</button>
      </label>
    `;
  }).join('');
  container.querySelectorAll('.collection-source-check').forEach(cb => {
    cb.addEventListener('change', event => {
      const key = event.currentTarget.dataset.key;
      if (event.currentTarget.checked) collectionState.sourceSelected.add(key);
      else collectionState.sourceSelected.delete(key);
      renderCollectionSourceProducts();
    });
  });
  container.querySelectorAll('.collection-source-add-one').forEach(btn => {
    btn.addEventListener('click', async event => {
      event.preventDefault();
      const product = collectionState.sourceProducts.find(item => getCollectionSourceProductKey(item) === event.currentTarget.dataset.key);
      if (product) await saveSelectedSourceProducts([product]);
    });
  });
}

async function loadCollectionSourceProducts() {
  if (!requireAdminFeature('店铺搬家')) return;
  if (!state.mallId) return showToast('请先连接源店铺', 'error');
  const btn = document.getElementById('collectionLoadSourceBtn');
  const oldText = btn?.textContent || '刷新源店商品';
  try {
    if (btn) { btn.disabled = true; btn.textContent = '加载中...'; }
    const result = await safeRuntimeSendMessage({
      type: 'FETCH_MOVE_SOURCE_PRODUCTS',
      params: { pageNo: 1, pageSize: 100 }
    }, { success: false, error: '加载源店商品失败' }, 45000);
    if (!result?.success) throw new Error(result?.error || '加载源店商品失败');
    collectionState.sourceProducts = (Array.isArray(result.products) ? result.products : []).map(item => normalizeCollectionProduct(item, 'seller-product'));
    collectionState.sourceSelected.clear();
    renderCollectionSourceProducts();
    await saveTemuSnapshot('collection', {
      sourceProducts: collectionState.sourceProducts,
      items: collectionState.items,
      total: collectionState.sourceProducts.length,
      loadedAt: Date.now()
    }, { itemCount: collectionState.sourceProducts.length });
    showToast(`已加载源店商品 ${collectionState.sourceProducts.length} 个`, 'success');
  } catch (error) {
    showToast(`加载源店商品失败：${error.message || error}`, 'error');
  } finally {
    if (btn) { btn.disabled = false; btn.textContent = oldText; }
  }
}

function resetCollectionRunCounters() {
  collectionState.successCount = 0;
  collectionState.failCount = 0;
  collectionState.draftCount = 0;
}

async function saveSelectedSourceProducts(productsOverride = null) {
  if (!requireAdminFeature('店铺搬家')) return;
  const products = Array.isArray(productsOverride)
    ? productsOverride
    : collectionState.sourceProducts.filter(item => collectionState.sourceSelected.has(getCollectionSourceProductKey(item)));
  if (!products.length) return showToast('请先选择要保存的源店商品', 'error');
  const logList = document.getElementById('collectionLogList');
  const progressSection = document.getElementById('collectionProgressSection');
  const progressText = document.getElementById('collectionProgressText');
  const progressFill = document.getElementById('collectionProgressFill');
  if (progressSection) progressSection.style.display = '';
  if (progressText) progressText.textContent = `正在保存 ${products.length} 个商品模板...`;
  if (progressFill) progressFill.style.width = '0%';
  if (logList) logList.innerHTML = '';
  let success = 0;
  let fail = 0;
  for (let i = 0; i < products.length; i += 1) {
    const product = products[i];
    if (progressFill) progressFill.style.width = `${Math.round(((i + 1) / products.length) * 100)}%`;
    setCollectionProgressStage(`读取商品详情 ${i + 1}/${products.length}`);
    try {
      const detailedProduct = await hydrateMoveSourceProduct(product, logList);
      setCollectionProgressStage(`保存模板 ${i + 1}/${products.length}`);
      const result = await saveProductMoveTemplate(detailedProduct);
      if (!result.success) throw new Error(result.error || '保存失败');
      success += 1;
      addCollectionLog(logList, `✅ ${detailedProduct.title || detailedProduct.goodsId || '商品'} - 已保存完整商品模板`, 'success');
    } catch (error) {
      fail += 1;
      addCollectionLog(logList, `❌ ${product.title || product.goodsId || '商品'} - ${error.message || error}`, 'error');
    }
  }
  if (progressText) progressText.textContent = `商品模板保存完成：成功 ${success} 个，失败 ${fail} 个`;
  setCollectionProgressStage('模板保存完成');
  showToast(`商品模板保存完成：成功 ${success}，失败 ${fail}`, success > 0 ? 'success' : 'error');
  await loadCollectionProductTemplates();
}

function renderCollectionTemplateStats() {
  const stats = document.getElementById('collectionTemplateStats');
  if (!stats) return;
  stats.innerHTML = `
    <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:#eef2ff;color:#3730a3;">模板 ${collectionState.templates.length} 个</span>
    <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:#ecfdf3;color:#027a48;">已选 ${collectionState.templateSelected.size} 个</span>
    <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:#fff7ed;color:#c2410c;">当前目标店 ${escapeHtml(state.mallId || '未连接')}</span>
  `;
}

function renderCollectionTemplates() {
  const container = document.getElementById('collectionTemplateList');
  if (!container) return;
  renderCollectionTemplateStats();
  if (!collectionState.templates.length) {
    container.innerHTML = '<div class="empty" style="padding:18px;color:var(--text-muted);font-size:13px;">暂无商品模板，请先从源店保存模板</div>';
    return;
  }
  container.innerHTML = collectionState.templates.map(template => {
    const key = getProductTemplateKey(template);
    const image = template.mainImage || template.data?.mainImage || template.data?.images?.[0] || '';
    const disabledSameShop = state.mallId && template.sourceMallId && String(template.sourceMallId) === String(state.mallId);
    const hasDetail = Boolean(template.data?.sourceProductDetail || template.data?.productDetail || template.data?.rawProductDetail);
    const sourceProductId = template.data?.sourceProductId || template.spuId || template.goodsId || '-';
    return `
      <div style="display:grid;grid-template-columns:34px 54px minmax(0,1fr) auto;gap:10px;align-items:center;padding:10px 12px;border-bottom:1px solid var(--border);${disabledSameShop ? 'background:#fff7ed;' : ''}">
        <input type="checkbox" class="checkbox collection-template-check" data-key="${escapeHtml(key)}" ${collectionState.templateSelected.has(key) ? 'checked' : ''}>
        <img src="${escapeHtml(image)}" class="img-fallback" data-fallback-size="54" style="width:54px;height:54px;object-fit:cover;border-radius:8px;border:1px solid var(--border);background:#f8fafc;">
        <div style="min-width:0;">
          <div style="font-size:13px;font-weight:700;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(template.productName || template.name || '未命名模板')}</div>
          <div style="margin-top:4px;font-size:12px;color:var(--text-muted);">源店：${escapeHtml(template.sourceMallName || template.sourceMallId || '-')} ｜ SPU：${escapeHtml(String(sourceProductId))} ｜ SKU ${escapeHtml(String(template.skuCount || template.data?.skus?.length || 0))} ｜ ${hasDetail ? '凌风详情已保存' : '缺少完整详情'}</div>
          ${disabledSameShop ? '<div style="margin-top:4px;font-size:11px;color:#c2410c;">当前连接的是源店，建议切换到目标店铺后再搬家</div>' : ''}
          ${!hasDetail ? '<div style="margin-top:4px;font-size:11px;color:#b42318;">旧模板缺少完整商品详情，请回源店重新保存后再复制</div>' : ''}
        </div>
        <div style="display:flex;gap:8px;align-items:center;">
          <button type="button" class="btn btn-primary btn-sm collection-template-add-one" data-key="${escapeHtml(key)}" ${hasDetail ? '' : 'disabled'}>加入复制</button>
          <button type="button" class="btn btn-danger btn-sm collection-template-delete" data-id="${escapeHtml(String(template.id || ''))}">删除</button>
        </div>
      </div>
    `;
  }).join('');
  container.querySelectorAll('.collection-template-check').forEach(cb => {
    cb.addEventListener('change', event => {
      const key = event.currentTarget.dataset.key;
      if (event.currentTarget.checked) collectionState.templateSelected.add(key);
      else collectionState.templateSelected.delete(key);
      renderCollectionTemplateStats();
    });
  });
  container.querySelectorAll('.collection-template-add-one').forEach(btn => {
    btn.addEventListener('click', event => {
      const template = collectionState.templates.find(item => getProductTemplateKey(item) === event.currentTarget.dataset.key);
      if (template) addTemplatesToMoveTasks([template]);
    });
  });
  container.querySelectorAll('.collection-template-delete').forEach(btn => {
    btn.addEventListener('click', async event => {
      const id = event.currentTarget.dataset.id;
      if (!id) return;
      const result = await deleteProductMoveTemplate(id);
      if (!result.success) return showToast(result.error || '删除模板失败', 'error');
      collectionState.templates = collectionState.templates.filter(item => String(item.id) !== String(id));
      collectionState.templateSelected.delete(id);
      renderCollectionTemplates();
      showToast('商品模板已删除', 'success');
    });
  });
}

async function loadCollectionProductTemplates() {
  const btn = document.getElementById('collectionTemplateRefreshBtn');
  const oldText = btn?.textContent || '刷新模板';
  try {
    if (btn) { btn.disabled = true; btn.textContent = '刷新中...'; }
    const result = await fetchProductMoveTemplates();
    if (!result.success) throw new Error(result.error || '加载商品模板失败');
    collectionState.templates = result.templates || [];
    collectionState.templateSelected.clear();
    renderCollectionTemplates();
  } catch (error) {
    showToast(`加载商品模板失败：${error.message || error}`, 'error');
  } finally {
    if (btn) { btn.disabled = false; btn.textContent = oldText; }
  }
}

function buildMoveTaskFromTemplate(template) {
  const data = template?.data || {};
  const item = normalizeCollectionProduct(data.product || data, 'product-template');
  item.id = `tpl_${template.id}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
  item.templateId = template.id;
  item.templateName = template.name;
  item.productId = data.productId || data.sourceProductId || item.productId || item.spuId || item.goodsId || '';
  item.sourceProductId = data.sourceProductId || data.productId || item.spuId || item.goodsId || '';
  item.sourceProductDetail = data.sourceProductDetail || data.productDetail || data.rawProductDetail || null;
  item.lingfengReady = Boolean(item.sourceProductDetail);
  item.sourceMallId = template.sourceMallId || data.sourceMallId || '';
  item.sourceMallName = template.sourceMallName || data.sourceMallName || '';
  item.url = data.sourceUrl || data.url || '';
  item.sourceUrl = data.sourceUrl || data.url || '';
  item.collectionProgress = {
    capture: 'done',
    category: item.category || item.categoryId || item.lingfengReady ? 'done' : 'pending',
    spec: item.skus?.length || item.lingfengReady ? 'done' : 'pending',
    draft: 'pending',
    submit: 'pending'
  };
  return item;
}

function addTemplatesToMoveTasks(templates) {
  const repeat = Math.max(1, Math.min(20, Number(document.getElementById('collectionRepeatInput')?.value || 1) || 1));
  let added = 0;
  for (const template of templates) {
    for (let i = 0; i < repeat; i += 1) {
      const item = buildMoveTaskFromTemplate(template);
      item.repeatIndex = i + 1;
      if (repeat > 1) item.title = `${item.title || template.name || '商品'} - 副本${i + 1}`;
      collectionState.items.push(item);
      collectionState.selected.add(getCollectionItemKey(item));
      added += 1;
    }
  }
  renderCollectionResults();
  showToast(`已加入 ${added} 个复制任务`, 'success');
}

const qualificationNameMap = {
  'cpc': '美国CPSC儿童产品安全认证',
  'kc safety check(children)': '韩国KC安全确认(儿童产品)',
  'kc supplier conformity check(children)': '韩国KC供应商符合性确认(儿童产品)',
  'rsl-phthalate': '欧盟RSL邻苯项目检测报告',
  'ccpsa': '加拿大消费品安全检测报告',
  'gcc': '美国一般商品合格证书',
  'uk furniture flammability': '英国家具阻燃性',
  'eu doc': '欧盟符合性声明',
  'eu doc declaration': '欧盟符合性声明',
  'doc': '符合性声明',
  'cpc': '儿童产品证书',
  'ce': 'CE 认证',
  'fsc': 'FSC 认证',
  'rohs': 'RoHS 认证',
  'reach': 'REACH 认证',
  'test report': '检测报告',
  'inspection report': '检测报告',
  'product certificate': '产品证书',
  'quality certificate': '质量证书',
  'packaging material': '包装材料信息',
  '资质': '资质'
};

function getQualificationRawName(task = {}) {
  if (!task || typeof task !== 'object') return '';
  return String(task.certName || task.showName || task.task_name || task.task_type || '').trim();
}

function pickQualificationValue(...values) {
  for (const value of values) {
    if (value === undefined || value === null) continue;
    if (typeof value === 'string') {
      const text = value.trim();
      if (text) return text;
      continue;
    }
    if (typeof value === 'number' && Number.isFinite(value)) return value;
    if (typeof value === 'boolean') return value;
  }
  return undefined;
}

function getQualificationTaskCertType(task = {}) {
  if (!task || typeof task !== 'object') return '';
  return String(pickQualificationValue(
    task.certType,
    task.cert_type,
    task.task_type,
    task.taskType,
    task.taskTypeCode,
    task.cert_type_code,
    task.certTypeCode,
    task.type,
    task.product_cert_type,
    task.productCertType,
    task.raw?.cert_type,
    task.raw?.certType,
    task.raw?.task_type,
    task.raw?.taskType,
    task.raw?.taskTypeCode,
    task.raw?.cert_type_code,
    task.raw?.certTypeCode,
    task.raw?.type,
    task.raw?.product_cert_type,
    task.raw?.productCertType
  ) || '').trim();
}

function getQualificationFailureText(task = {}) {
  if (!task || typeof task !== 'object') return '';
  return String(pickQualificationValue(
    task.raw?.failure_reason,
    task.raw?.error_message,
    task.raw?.error_msg,
    task.raw?.reject_reason,
    task.raw?.rejectReason,
    task.failure_reason,
    task.error_message,
    task.errorMsg,
    task.reject_reason,
    task.rejectReason
  ) || '').trim();
}

function getQualificationExplicitStatusText(task = {}) {
  if (!task || typeof task !== 'object') return '';
  const rawText = pickQualificationValue(
    task.statusText,
    task.statusLabel,
    task.status_name,
    task.statusName,
    task.statusDesc,
    task.status_desc,
    task.uploadStatusText,
    task.upload_status_text,
    task.uploadStatusLabel,
    task.upload_status_label,
    task.uploadStatusDesc,
    task.upload_status_desc,
    task.auditStatusText,
    task.audit_status_text,
    task.auditStatusDesc,
    task.audit_status_desc,
    task.raw?.status_text,
    task.raw?.statusText,
    task.raw?.status_name,
    task.raw?.statusName,
    task.raw?.status_desc,
    task.raw?.statusDesc,
    task.raw?.upload_status_text,
    task.raw?.uploadStatusText,
    task.raw?.upload_status_name,
    task.raw?.uploadStatusName,
    task.raw?.upload_status_desc,
    task.raw?.uploadStatusDesc,
    task.raw?.audit_status_text,
    task.raw?.auditStatusText,
    task.raw?.audit_status_name,
    task.raw?.auditStatusName,
    task.raw?.audit_status_desc,
    task.raw?.auditStatusDesc,
    task.raw?.display_status,
    task.raw?.displayStatus,
    task.raw?.process_status,
    task.raw?.processStatus
  );
  if (typeof rawText !== 'string') return '';
  const text = rawText.trim();
  if (!text || /^\d+$/.test(text) || /^状态(?:码)?\s*\d+$/i.test(text)) return '';
  return text;
}

function getQualificationRawStatusCode(task = {}) {
  if (!task || typeof task !== 'object') return undefined;
  return pickQualificationValue(
    task.statusCode,
    task.uploadStatus,
    task.status,
    task.upload_status,
    task.task_status,
    task.raw?.status_code,
    task.raw?.statusCode,
    task.raw?.upload_status_code,
    task.raw?.uploadStatusCode,
    task.raw?.task_status_code,
    task.raw?.taskStatusCode,
    task.raw?.upload_status,
    task.raw?.uploadStatus,
    task.raw?.status,
    task.raw?.task_status
  );
}

function hasQualificationReturnedFiles(task = {}) {
  if (!task || typeof task !== 'object') return false;
  const raw = task.raw || {};
  const listFields = [
    task.cert_file_list,
    task.certFileList,
    task.product_cert_files,
    task.productCertFiles,
    task.inspect_report_files,
    task.inspectReportFiles,
    raw.cert_file_list,
    raw.certFileList,
    raw.product_cert_files,
    raw.productCertFiles,
    raw.inspect_report_files,
    raw.inspectReportFiles
  ];
  if (listFields.some(list => Array.isArray(list) && list.length > 0)) return true;
  const mapFields = [
    task.multi_lang_product_cert_file_map,
    task.multiLangProductCertFileMap,
    task.doc_pdf_content_req,
    task.docPdfContentReq,
    raw.multi_lang_product_cert_file_map,
    raw.multiLangProductCertFileMap,
    raw.doc_pdf_content_req,
    raw.docPdfContentReq
  ];
  return mapFields.some(value => value && typeof value === 'object' && Object.keys(value).length > 0);
}

function resolveQualificationStateFromText(text = '') {
  const normalized = String(text || '').trim().toLowerCase();
  if (!normalized) return 'unknown';
  if (/失败|驳回|拒绝|不通过|failed|rejected|refused|error|not\s*pass|declined/.test(text) || /failed|rejected|refused|error|not\s*pass|declined/.test(normalized)) return 'failed';
  if (/上传完成|已上传|已完成|完成|通过|审核通过|成功|uploaded|passed|success|done|approved|complete/.test(text) || /uploaded|passed|success|done|approved|complete/.test(normalized)) return 'completed';
  if (/审核中|待审|待审核|审核|review|processing|in\s*review|reviewing|pending\s*review/.test(text) || /review|processing|in\s*review|reviewing|pending\s*review/.test(normalized)) return 'reviewing';
  if (/待上传|未上传|待处理|待补充|未开始|缺失|缺少|not\s*started|to\s*be\s*uploaded|waiting|to\s*upload|missing|required/.test(text) || /not\s*started|to\s*be\s*uploaded|waiting|to\s*upload|missing|required/.test(normalized)) return 'pending';
  if (/废弃|失效|作废|expired|abandoned/.test(text) || /expired|abandoned/.test(normalized)) return 'closed';
  return 'unknown';
}

function getQualificationStatusMetaFromCode(code) {
  const statusNum = Number(code);
  if (!Number.isFinite(statusNum)) return null;
  const detail = `TEMU 状态码 ${statusNum}`;
  if (statusNum === 1) return { label: '待上传', tone: 'warning', detail, state: 'pending' };
  if (statusNum === 6) return { label: '上传成功', tone: 'success', detail, state: 'completed' };
  return null;
}

function isQualificationStatusUploadRequired(meta = {}) {
  return meta.state === 'pending' || meta.state === 'failed';
}

function translateQualificationName(task = {}) {
  const raw = getQualificationRawName(task);
  if (!raw) return '资质';
  if (/[\u4e00-\u9fa5]/.test(raw)) return raw;
  const lower = raw.toLowerCase();
  for (const [key, value] of Object.entries(qualificationNameMap)) {
    if (lower === key || lower.includes(key)) {
      return value;
    }
  }
  return raw;
}

function getQualificationStatusMeta(task = {}) {
  if (!task || typeof task !== 'object') {
    return { label: '状态待确认', tone: 'muted', detail: '', state: 'unknown' };
  }
  const failureReason = getQualificationFailureText(task);
  if (failureReason) {
    return { label: '失败/驳回', tone: 'danger', detail: String(failureReason), state: 'failed' };
  }

  const rawText = getQualificationExplicitStatusText(task);
  if (rawText) {
    const state = resolveQualificationStateFromText(rawText);
    if (state === 'unknown') {
      const codedMeta = getQualificationStatusMetaFromCode(getQualificationRawStatusCode(task));
      if (codedMeta) return codedMeta;
    }
    const tone = state === 'completed' ? 'success'
      : (state === 'failed' ? 'danger'
        : (state === 'pending' || state === 'reviewing' ? 'warning' : 'muted'));
    return { label: rawText, tone, detail: '', state };
  }

  const rawStatus = getQualificationRawStatusCode(task);
  const statusNum = Number(rawStatus);
  const statusLabel = Number.isFinite(statusNum) ? `状态码 ${statusNum}` : '';
  if (hasQualificationReturnedFiles(task)) {
    return { label: '有资质文件', tone: 'success', detail: statusLabel, state: 'completed' };
  }

  const codedMeta = getQualificationStatusMetaFromCode(rawStatus);
  if (codedMeta) return codedMeta;

  if (statusLabel) {
    return { label: '状态待确认', tone: 'muted', detail: `TEMU ${statusLabel}`, state: 'unknown' };
  }

  return { label: '状态待确认', tone: 'muted', detail: '', state: 'unknown' };
}

function renderQualificationTaskRows(tasks = []) {
  return (Array.isArray(tasks) ? tasks : []).filter(t => t && typeof t === 'object').map(t => {
    const statusMeta = getQualificationStatusMeta(t);
    return {
      rawName: getQualificationRawName(t) || '—',
      chineseLabel: translateQualificationName(t) || '—',
      statusText: statusMeta.label,
      statusDetail: statusMeta.detail || ''
    };
  });
}

function getCollectionItemProgress(item) {
  return item?.collectionProgress || {
    capture: item ? 'done' : 'pending',
    category: item?.recommendedCategory?.categoryName ? 'done' : 'pending',
    spec: item?.specTemplate ? 'done' : 'pending',
    draft: item?.draftStatus || 'pending',
    submit: item?.submitStatus || 'pending'
  };
}

function validateCollectionReady(item, mode = 'draft') {
  if (item?.sourceProductDetail) {
    return { ok: true, missing: [], message: '' };
  }
  const missing = [];
  const targetCategory = (document.getElementById('collectionTargetCategoryInput')?.value || '').trim();
  if (!String(item?.title || '').trim()) missing.push('标题');
  if (!String(item?.categoryId || item?.category || item?.recommendedCategory?.categoryId || targetCategory).trim()) missing.push('类目');
  if (!Array.isArray(item?.images) || item.images.length === 0) missing.push('图片');
  if (!Array.isArray(item?.skus) || item.skus.length === 0) missing.push('SKU');
  return {
    ok: missing.length === 0,
    missing,
    message: missing.length ? `缺少${missing.join('、')}，请先补全后再${mode === 'submit' ? '提交上架' : '创建草稿'}` : ''
  };
}

function buildCollectionPayload(item, lotId = '') {
  const targetCategory = (document.getElementById('collectionTargetCategoryInput')?.value || '').trim();
  const targetSite = (document.getElementById('collectionTargetSiteInput')?.value || '').trim();
  const moveMode = 'draft';
  return {
    lotId,
    goodsId: item.goodsId,
    productId: item.productId || item.sourceProductId || item.spuId || item.goodsId || '',
    sourceProductId: item.sourceProductId || item.productId || item.spuId || item.goodsId || '',
    spuId: item.spuId || '',
    title: item.title,
    price: item.price,
    description: item.description,
    category: targetCategory || item.category,
    categoryId: item.categoryId || item.recommendedCategory?.categoryId || '',
    categoryName: targetCategory || item.recommendedCategory?.categoryName || item.category || '',
    categoryPath: item.categoryPath || item.recommendedCategory?.path || '',
    specTemplate: item.specTemplate || item.specTemplateDetail || '',
    goodsMetadata: item.goodsMetadata || null,
    images: item.images || [],
    skus: item.skus || [],
    sourceUrl: item.url || item.sourceUrl,
    sourceType: item.sourceType || 'buyer-url',
    sourceMallId: item.sourceMallId || '',
    sourceMallName: item.sourceMallName || '',
    sourceProductDetail: item.sourceProductDetail || null,
    lingfengReady: Boolean(item.sourceProductDetail || item.lingfengReady),
    templateId: item.templateId || '',
    targetMallId: state.mallId || '',
    targetMallName: state.mallName || '',
    targetSite,
    moveMode,
    platform: 'TEMU'
  };
}

function getCollectionStepLabel(status) {
  switch (status) {
    case 'done': return '已完成';
    case 'running': return '进行中';
    case 'error': return '失败';
    default: return '待处理';
  }
}

function getCollectionCardBadges(item) {
  const badges = [];
  if (item.sourceProductDetail || item.lingfengReady) badges.push('凌风复制');
  if (item.sourceProductId || item.productId) badges.push(`SPU ${item.sourceProductId || item.productId}`);
  if (item.goodsId) badges.push(`商品ID ${item.goodsId}`);
  if (item.price !== undefined && item.price !== null && item.price !== '') badges.push(`售价 ${formatCollectionPrice(item.price)}`);
  badges.push(`SKU ${item.skus?.length || 0}`);
  badges.push(`图片 ${item.images?.length || 0}`);
  if (item.category) badges.push(String(item.category));
  if (item.recommendedCategory?.categoryName) badges.push(`推荐类目 ${item.recommendedCategory.categoryName}`);
  if (item.categorySource) badges.push(`类目来源 ${item.categorySource === 'recommended' ? '推荐' : item.categorySource === 'metadata' ? '元数据' : '当前'}`);
  if (item.specTemplate) badges.push(`规格模板 ${item.specTemplate}`);
  return badges;
}

function updateCollectionStats() {
  const stats = document.getElementById('collectionStats');
  if (!stats) return;
  const total = collectionState.items.length;
  const selected = collectionState.selected.size;
  const repeat = document.getElementById('collectionRepeatInput')?.value || '1';
  stats.innerHTML = `
    <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:#eef2ff;color:#3730a3;">任务 ${total} 个</span>
    <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:#ecfdf3;color:#027a48;">已选中 ${selected} 个</span>
    <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:#fff7ed;color:#c2410c;">成功 ${collectionState.successCount} 个</span>
    <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:#fef2f2;color:#b42318;">失败 ${collectionState.failCount} 个</span>
    <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:#eff6ff;color:#1d4ed8;">草稿 ${collectionState.draftCount} 个</span>
    <span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:#f8fafc;color:#475467;">凌风草稿模式 / 重复 ${escapeHtml(repeat)}</span>
  `;
}

function setCollectionProgressStage(text) {
  const stage = document.getElementById('collectionProgressStage');
  if (stage) stage.textContent = `当前步骤：${text || '-'}`;
}

setTimeout(() => updateCollectionStats(), 0);

function addCollectionLog(container, text, type) {
  if (!container) return;
  const div = document.createElement('div');
  div.style.cssText = `font-size:12px;padding:4px 8px;margin:2px 0;border-radius:4px;${type === 'error' ? 'color:#EF4444;background:#FEF2F2;' : 'color:#10B981;background:#F0FDF4;'}`;
  div.textContent = text;
  container.appendChild(div);
  container.scrollTop = container.scrollHeight;
}

function renderCollectionResults() {
  const container = document.getElementById('collectionResultList');
  if (!container) return;
  updateCollectionStats();

  if (collectionState.items.length === 0) {
    container.innerHTML = '<div class="empty" style="padding:20px;color:var(--text-muted);font-size:13px;">暂无复制任务，请从模板库加入商品</div>';
    return;
  }

  container.innerHTML = collectionState.items.map((item, idx) => {
    const badges = getCollectionCardBadges(item);
    const image = item.images?.[0] || item.mainImage || item.image || '';
    const isSelected = collectionState.selected.has(getCollectionItemKey(item));
    const progress = getCollectionItemProgress(item);
    const progressChip = (label, status) => {
      const colors = status === 'done'
        ? 'background:#ecfdf3;color:#027a48;'
        : status === 'running'
          ? 'background:#eff6ff;color:#1d4ed8;'
          : status === 'error'
            ? 'background:#fef2f2;color:#b42318;'
            : 'background:#f4f6f8;color:#667085;';
      return `<span style="display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;${colors}">${escapeHtml(label)}：${getCollectionStepLabel(status)}</span>`;
    };
    return `
    <div style="display:flex;gap:12px;padding:12px;border:1px solid var(--border);border-radius:12px;margin-bottom:10px;align-items:flex-start;background:var(--surface);box-shadow:0 1px 2px rgba(0,0,0,.04);">
      <input type="checkbox" class="checkbox collection-item-check" data-idx="${idx}" ${isSelected ? 'checked' : ''} style="margin-top:6px;">
      <img src="${image}" class="img-fallback" style="width:72px;height:72px;object-fit:cover;border-radius:10px;border:1px solid var(--border);background:#fafafa;">
      <div style="flex:1;min-width:0;">
        <div style="display:flex;justify-content:space-between;gap:10px;align-items:flex-start;">
          <div style="min-width:0;flex:1;">
            <div style="font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(item.title || '未知商品')}</div>
            <div style="margin-top:4px;font-size:12px;color:var(--text-muted);display:flex;flex-wrap:wrap;gap:8px;">
              <span>商品ID：${escapeHtml(String(item.goodsId || '-'))}</span>
              <span>价格：${escapeHtml(formatCollectionPrice(item.price))}</span>
              <span>SKU：${escapeHtml(String(item.skus?.length || 0))}</span>
              <span>图片：${escapeHtml(String(item.images?.length || 0))}</span>
            </div>
          </div>
           <div style="display:flex;gap:8px;flex-shrink:0;">
             <button class="btn btn-default btn-sm collection-edit-btn" data-idx="${idx}">编辑</button>
             ${item.editUrl ? `<button class="btn btn-primary btn-sm collection-open-draft-btn" data-idx="${idx}">打开草稿</button>` : ''}
             <button class="btn btn-danger btn-sm collection-remove-btn" data-idx="${idx}">移除</button>
           </div>
        </div>
        <div style="margin-top:8px;display:flex;flex-wrap:wrap;gap:6px;">
          ${badges.map(tag => `<span style="display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;background:#f4f6f8;color:#475467;font-size:11px;line-height:1;">${escapeHtml(tag)}</span>`).join('')}
        </div>
        <div style="margin-top:8px;display:flex;flex-wrap:wrap;gap:10px;font-size:11px;color:var(--text-secondary);">
          <span>推荐类目：${escapeHtml(item.recommendedCategory?.categoryName || item.category || '—')}</span>
          <span>规格模板：${escapeHtml(item.specTemplate || item.specTemplateDetail || '—')}</span>
        </div>
        <div style="margin-top:8px;display:flex;flex-wrap:wrap;gap:6px;font-size:11px;">
          ${progressChip('采集', progress.capture)}
          ${progressChip('类目', progress.category)}
          ${progressChip('规格', progress.spec)}
          ${progressChip('复制草稿', progress.draft)}
        </div>
        <div style="margin-top:8px;font-size:11px;color:var(--text-muted);word-break:break-all;">${escapeHtml(item.editUrl || item.url || item.sourceUrl || '')}</div>
      </div>
    </div>
  `;
  }).join('');

  // 绑定复选框
  container.querySelectorAll('.collection-item-check').forEach(cb => {
    cb.addEventListener('change', (e) => {
      const item = collectionState.items[+e.target.dataset.idx];
      const id = getCollectionItemKey(item);
      if (e.target.checked && id) collectionState.selected.add(id);
      else collectionState.selected.delete(id);
      updateCollectionStats();
    });
  });

  // 绑定编辑按钮
  container.querySelectorAll('.collection-edit-btn').forEach(btn => {
    btn.addEventListener('click', (e) => {
      const idx = +e.target.dataset.idx;
      const item = collectionState.items[idx];
      if (item) {
        const nextTitle = window.prompt('修改商品名称', item.title || '');
        if (nextTitle !== null) item.title = nextTitle.trim() || item.title;
        const nextPrice = window.prompt('修改价格', item.price ?? '');
        if (nextPrice !== null && nextPrice.trim() !== '') item.price = nextPrice.trim();
        const nextCategory = window.prompt('修改分类', item.category || '');
        if (nextCategory !== null) item.category = nextCategory.trim() || item.category;
        renderCollectionResults();
        showToast('商品信息已更新', 'success');
      }
    });
  });

  container.querySelectorAll('.collection-open-draft-btn').forEach(btn => {
    btn.addEventListener('click', async (e) => {
      const item = collectionState.items[+e.target.dataset.idx];
      const url = item?.editUrl || '';
      if (!url) return showToast('暂无草稿链接', 'error');
      window.open(url, '_blank');
    });
  });

  container.querySelectorAll('.collection-remove-btn').forEach(btn => {
    btn.addEventListener('click', (e) => {
      const idx = +e.target.dataset.idx;
      const item = collectionState.items[idx];
      if (!item) return;
      const key = getCollectionItemKey(item);
      collectionState.items.splice(idx, 1);
      collectionState.selected.delete(key);
      renderCollectionResults();
      showToast('已移除该商品', 'info');
    });
  });
}

// 创建草稿
async function createCollectionDrafts() {
  if (!requireAdminFeature('店铺搬家')) return;
  const selectedItems = collectionState.items.filter(item => collectionState.selected.has(getCollectionItemKey(item)));
  if (selectedItems.length === 0) { showToast('请先选择要创建草稿的商品', 'error'); return; }
  if (!state.mallId) { showToast('请先连接目标店铺', 'error'); return; }
  resetCollectionRunCounters();

  const progressSection = document.getElementById('collectionProgressSection');
  const progressFill = document.getElementById('collectionProgressFill');
  const progressText = document.getElementById('collectionProgressText');
  const logList = document.getElementById('collectionLogList');
  if (progressSection) progressSection.style.display = '';
  if (progressFill) progressFill.style.width = '0%';
  if (progressText) progressText.textContent = `准备复制 ${selectedItems.length} 个商品...`;
  setCollectionProgressStage('准备凌风复制');
  if (logList) logList.innerHTML = '';

  let success = 0;
  let fail = 0;
  for (let i = 0; i < selectedItems.length; i++) {
    const item = selectedItems[i];
    const pct = Math.round(((i + 1) / selectedItems.length) * 100);
    if (progressFill) progressFill.style.width = pct + '%';
    if (progressText) progressText.textContent = `复制商品 ${i + 1}/${selectedItems.length}：${item.title || item.goodsId || '未知商品'}`;
    setCollectionProgressStage(`凌风复制 ${i + 1}/${selectedItems.length}`);
    item.collectionProgress = item.collectionProgress || {};
    item.collectionProgress.draft = 'running';

    const ready = validateCollectionReady(item, 'draft');
    if (!ready.ok) {
      fail += 1;
      collectionState.failCount += 1;
      item.collectionProgress.draft = 'error';
      addCollectionLog(logList, `❌ ${item.title || item.goodsId || '商品'} - ${ready.message}`, 'error');
      renderCollectionResults();
      continue;
    }

    const draftData = {
      ...buildCollectionPayload(item),
      openAfterCreate: selectedItems.length === 1
    };

    try {
      const draftResp = await safeRuntimeSendMessage({
        type: 'COPY_MOVE_PRODUCT_TO_DRAFT',
        productData: draftData
      }, { success: false, error: '复制商品到草稿失败' }, 60000);
      if (!draftResp?.success && draftResp?.error) throw new Error(draftResp.error);

      success += 1;
      collectionState.successCount += 1;
      collectionState.draftCount += 1;
      item.collectionProgress.draft = 'done';
      item.productDraftId = draftResp.productDraftId || draftResp.data?.productDraftId || '';
      item.editUrl = draftResp.editUrl || '';
      addCollectionLog(logList, `✅ ${item.title || item.goodsId || '商品'} - 已按凌风逻辑复制到草稿${item.productDraftId ? `（${item.productDraftId}）` : ''}`, 'success');
    } catch (err) {
      fail += 1;
      collectionState.failCount += 1;
      item.collectionProgress.draft = 'error';
      addCollectionLog(logList, `❌ ${item.title || item.goodsId || '商品'} - ${err.message}`, 'error');
      renderCollectionResults();
    }
  }

  if (progressText) progressText.textContent = `复制完成：成功 ${success} 个，失败 ${fail} 个`;
  setCollectionProgressStage(`凌风复制完成 成功 ${success} 失败 ${fail}`);
  renderCollectionResults();
  showToast(`复制完成：成功 ${success}，失败 ${fail}`, success > 0 ? 'success' : 'error');
}

// 清空复制数据
function clearCollection() {
  collectionState.items = [];
  collectionState.selected.clear();
  resetCollectionRunCounters();
  renderCollectionResults();
  showToast('复制任务已清空', 'info');
}

// ========== TEMU 批量上架测试 Tab ==========
const TEMU_UPLOAD_TEST_TEMPLATE_FIELDS = [
  { key: 'code', label: '编号' },
  { key: 'title', label: '上传标题' },
  { key: 'remark', label: '备注' }
];

const temuUploadTestState = {
  excelFile: null,
  imageFiles: [],
  imageGroups: new Map(),
  templates: [],
  selectedTemplateId: '',
  rows: [],
  selected: new Set(),
  addImageTargetIndex: -1,
  preview: { rowIndex: -1, imageIndex: 0 },
  editingTemplateId: '',
  initialized: false
};

function normalizeTemuTemplateSourceId(value) {
  const sourceProductId = String(value || '').trim();
  if (!/^\d+$/.test(sourceProductId)) throw new Error('请输入正确的数字商品 ID');
  return sourceProductId;
}

function getSelectedTemuUploadTemplate() {
  return temuUploadTestState.templates.find(item => String(item.id) === String(temuUploadTestState.selectedTemplateId)) || null;
}

function findTemuUploadProductInCustomizedCache(sourceId) {
  const id = String(sourceId || '').trim();
  if (!id) return null;
  const pools = [
    customizedProductState.allProducts,
    customizedProductState.viewProducts,
    customizedProductState.products
  ];
  for (const pool of pools) {
    const products = Array.isArray(pool) ? pool : [];
    const match = products.find(product => {
      const skuList = Array.isArray(product.skuList) ? product.skuList : [];
      const ids = [
        product.goodsId,
        product.productId,
        product.spuId,
        product.skcId,
        ...skuList.flatMap(sku => [sku?.skuId, sku?.productSkuId])
      ].map(value => String(value || '').trim()).filter(Boolean);
      return ids.includes(id);
    });
    if (match) return match;
  }
  return null;
}

function buildTemuUploadDetailFromCustomizedProduct(product = {}, sourceId = '', sourceIdType = 'spu') {
  const skuList = Array.isArray(product.skuList) ? product.skuList : [];
  const raw = product.raw && typeof product.raw === 'object' ? product.raw : {};
  const images = normalizeCollectionImages(product);
  const skcImage = product.mainImage || images[0] || '';
  const productSkcId = product.productId || product.skcId || '';
  const propertyList = Array.isArray(product.productPropertyList) ? product.productPropertyList : [];
  const productWhExtAttr = [
    product.productWhExtAttr,
    product.productWhExtAttrReq,
    raw.productWhExtAttr,
    raw.productWhExtAttrReq
  ].find(item => item && typeof item === 'object' && !Array.isArray(item));
  const productOuterPackage = [
    product.productOuterPackage,
    product.productOuterPackageReq,
    raw.productOuterPackage,
    raw.productOuterPackageReq,
    productWhExtAttr?.productOuterPackage,
    productWhExtAttr?.outerPackage
  ].find(item => item && typeof item === 'object' && !Array.isArray(item));
  const outerPackageImages = collectTemuUploadTemplateOuterPackageImagesFromSources([
    product.outerPackageImages,
    product.productOuterPackageImages,
    product.productOuterPackageImageReqs,
    product.productOuterPackageImageList,
    product.outerPackageImageReqs,
    product.outerPackageImageList,
    product.packageImageList,
    raw.outerPackageImages,
    raw.productOuterPackageImages,
    raw.productOuterPackageImageReqs,
    raw.productOuterPackageImageList,
    raw.outerPackageImageReqs,
    raw.outerPackageImageList,
    raw.packageImageList,
    productWhExtAttr?.outerPackageImages,
    productWhExtAttr?.productOuterPackageImages,
    productWhExtAttr?.productOuterPackageImageReqs,
    productWhExtAttr?.productOuterPackageImageList,
    productWhExtAttr?.outerPackageImageReqs,
    productWhExtAttr?.outerPackageImageList,
    productWhExtAttr?.packageImageList,
    productOuterPackage?.outerPackageImages,
    productOuterPackage?.productOuterPackageImages,
    productOuterPackage?.productOuterPackageImageReqs,
    productOuterPackage?.productOuterPackageImageList,
    productOuterPackage?.outerPackageImageReqs,
    productOuterPackage?.outerPackageImageList,
    productOuterPackage?.packageImageList,
    productOuterPackage?.packageImages,
    productOuterPackage?.imageList,
    productOuterPackage?.images
  ]);
  return {
    productId: product.goodsId || product.spuId || (sourceIdType === 'spu' ? sourceId : ''),
    goodsId: product.goodsId || product.spuId || '',
    productName: product.productName || product.title || '',
    categoryId: product.categoryId || '',
    categoryName: product.categoryName || '',
    carouselImageUrls: images,
    materialImgUrl: skcImage,
    productPropertyList: propertyList,
    productProperties: propertyList,
    missingProductAttributes: propertyList.length === 0,
    ...(productWhExtAttr ? { productWhExtAttr: cloneTemuUploadJsonSafe(productWhExtAttr) } : {}),
    ...(productOuterPackage ? { productOuterPackage: cloneTemuUploadJsonSafe(productOuterPackage) } : {}),
    ...(outerPackageImages.length ? { outerPackageImages: cloneTemuUploadJsonSafe(outerPackageImages) } : {}),
    ...(product.raw ? { raw: cloneTemuUploadJsonSafe(product.raw) } : {}),
    detailSource: 'temu_customized_product_cache',
    sourceId,
    sourceIdType,
    productSkcList: [{
      productSkcId,
      colorImageUrl: skcImage,
      previewImgUrls: images,
      productSkcImageExtList: images.map(url => ({ imageUrl: url })),
      productSkuList: skuList.map((sku, index) => {
        const specText = sku?.skuSpec || sku?.skuSpecText || sku?.name || sku?.skuName || `SKU ${index + 1}`;
        return {
          productSkuId: sku?.skuId || sku?.productSkuId || `${productSkcId || sourceId}_${index + 1}`,
          extCode: sku?.skuCode || sku?.extCode || '',
          supplierPrice: sku?.price || product.price || '',
          thumbUrl: sku?.skuImage || sku?.imageUrl || sku?.thumbUrl || sku?.productSkuImage || skcImage,
          productSkuQuantity: sku?.stockText || sku?.showStockText || sku?.stock || '',
          productSkuSpecList: [{ specValue: specText, value: specText, name: specText }]
        };
      })
    }]
  };
}

function setTemuUploadTemplateStatus(message, type = '') {
  const el = document.getElementById('temuUploadTemplateStatus');
  if (!el) return;
  el.textContent = message;
  el.style.color = type === 'error' ? '#b42318' : type === 'success' ? '#027a48' : '#64748b';
}

function getTemuUploadTemplateCategoryText(template = {}) {
  const data = template.data && typeof template.data === 'object' ? template.data : {};
  return String(
    data.categoryPath ||
    data.category ||
    data.categoryName ||
    data.sourceProductDetail?.categoryPath ||
    data.sourceProductDetail?.categoryName ||
    ''
  ).trim() || '未返回类目';
}

function getTemuUploadTemplateTimeText(template = {}) {
  const value = template.updatedAt || template.createdAt || template.data?.savedAt || '';
  return value ? formatTemuSnapshotTime(value) : '未返回时间';
}

function getTemuUploadTemplateSkus(template = {}) {
  const data = template.data && typeof template.data === 'object' ? template.data : {};
  const detail = getTemuUploadSourceDetail(template);
  if (hasTemuUploadOfficialTemplateDetail(detail)) {
    const summary = summarizeMoveProductDetail(detail, data);
    if (Array.isArray(summary.skus) && summary.skus.length) return summary.skus;
  }
  return Array.isArray(data.skus) ? data.skus : [];
}

function getTemuUploadSourceSkus(template = {}) {
  const detail = getTemuUploadSourceDetail(template);
  const skus = [];
  (Array.isArray(detail?.productSkcList) ? detail.productSkcList : []).forEach(skc => {
    (Array.isArray(skc?.productSkuList) ? skc.productSkuList : []).forEach(sku => {
      skus.push({ ...(sku && typeof sku === 'object' ? sku : {}), sourceSkc: skc });
    });
  });
  return skus;
}

function getTemuUploadSourceSkuForSummary(template = {}, summarySku = {}, index = 0) {
  const sourceSkus = getTemuUploadSourceSkus(template);
  const summaryId = String(summarySku.id || summarySku.skuId || summarySku.productSkuId || '').trim();
  if (summaryId) {
    const matched = sourceSkus.find(sku => String(sku.productSkuId || sku.skuId || sku.id || '').trim() === summaryId);
    if (matched) return matched;
  }
  return sourceSkus[index] || null;
}

function getTemuUploadSkuSpecParentName(spec = {}) {
  const explicitName = String(
    spec.parentSpecName
    || spec.parentName
    || spec.specGroupName
    || spec.groupName
    || spec.propName
    || spec.propertyName
    || ''
  ).trim();
  if (explicitName) return explicitName;
  const name = String(spec.name || spec.label || '').trim();
  const value = String(spec.specName || spec.specValue || spec.valueName || spec.displayName || spec.value || '').trim();
  return name && value && name !== value ? name : '';
}

function getTemuUploadSkuSpecValueName(spec = {}) {
  return String(
    spec.specName
    || spec.specValue
    || spec.valueName
    || spec.displayName
    || spec.value
    || spec.name
    || spec.label
    || spec.text
    || ''
  ).trim();
}

function getTemuUploadGeneratorSkuHeader(template = {}, summarySkus = []) {
  const names = [];
  summarySkus.forEach((summarySku, index) => {
    const sourceSku = getTemuUploadSourceSkuForSummary(template, summarySku, index);
    (Array.isArray(sourceSku?.productSkuSpecList) ? sourceSku.productSkuSpecList : []).forEach(spec => {
      const name = getTemuUploadSkuSpecParentName(spec);
      if (name && !names.includes(name)) names.push(name);
    });
  });
  return names.length ? names.join(' / ') : 'SKU 名称';
}

function getTemuUploadGeneratorSkuName(template = {}, summarySku = {}, index = 0) {
  const sourceSku = getTemuUploadSourceSkuForSummary(template, summarySku, index);
  const specList = Array.isArray(sourceSku?.productSkuSpecList) ? sourceSku.productSkuSpecList : [];
  const values = specList.map(getTemuUploadSkuSpecValueName).filter(Boolean);
  const parentNames = specList.map(getTemuUploadSkuSpecParentName).filter(Boolean);
  const cleanValues = values.filter(value => !parentNames.includes(value));
  if (cleanValues.length) return cleanValues.join(' / ');
  const sourceText = getTemuUploadSkuSpecTextFromSource(sourceSku || {});
  return sourceText || summarySku.name || `SKU ${index + 1}`;
}

function getTemuUploadSkuKey(sku = {}, index = 0) {
  return String(sku.id || sku.skuId || sku.productSkuId || sku.name || `sku_${index + 1}`).trim();
}

function getTemuUploadSkuImageMapping(template = {}) {
  const data = template.data && typeof template.data === 'object' ? template.data : {};
  return data.skuImageMapping && typeof data.skuImageMapping === 'object' ? data.skuImageMapping : {};
}

function getTemuUploadSkuImageIndex(template = {}, sku = {}, index = 0) {
  const mapping = getTemuUploadSkuImageMapping(template);
  const key = getTemuUploadSkuKey(sku, index);
  const raw = mapping[key];
  const num = Number(raw);
  return Number.isInteger(num) && num >= 0 ? num : -1;
}

function setTemuUploadSkuImageIndex(template = {}, sku = {}, index = 0, value = '') {
  if (!template.data || typeof template.data !== 'object') template.data = {};
  if (!template.data.skuImageMapping || typeof template.data.skuImageMapping !== 'object') template.data.skuImageMapping = {};
  const key = getTemuUploadSkuKey(sku, index);
  const num = Number(value);
  if (Number.isInteger(num) && num >= 0) template.data.skuImageMapping[key] = num;
  else delete template.data.skuImageMapping[key];
}

function setTemuUploadSkuImageDisplayIndex(template = {}, sku = {}, index = 0, value = '') {
  const num = Number(value);
  if (Number.isInteger(num) && num > 0) {
    setTemuUploadSkuImageIndex(template, sku, index, num - 1);
  } else {
    setTemuUploadSkuImageIndex(template, sku, index, '');
  }
}

function getTemuUploadSkuImageDisplayIndex(template = {}, sku = {}, index = 0, maxImageCount = 0) {
  const fixedIndex = getTemuUploadSkuImageIndex(template, sku, index);
  if (fixedIndex >= 0) return fixedIndex + 1;
  const total = Number(maxImageCount);
  return total > 0 ? (index % total) + 1 : index + 1;
}

function getTemuUploadMaxCarouselImageCount() {
  let max = 0;
  temuUploadTestState.imageGroups.forEach(images => {
    if (Array.isArray(images)) max = Math.max(max, images.length);
  });
  temuUploadTestState.rows.forEach(row => {
    if (Array.isArray(row.images)) max = Math.max(max, row.images.length);
  });
  return Math.max(20, max);
}

function buildTemuUploadSkuImagePlan(row = {}, template = getSelectedTemuUploadTemplate()) {
  const skus = getTemuUploadTemplateSkus(template);
  const imageCount = Array.isArray(row.images) ? row.images.length : 0;
  return skus.map((sku, index) => {
    const fixedIndex = getTemuUploadSkuImageIndex(template, sku, index);
    const carouselImageIndex = imageCount
      ? (fixedIndex >= 0 ? (fixedIndex < imageCount ? fixedIndex : -1) : index % imageCount)
      : -1;
    return {
      skuId: sku.id || sku.skuId || sku.productSkuId || '',
      skuName: sku.name || `SKU ${index + 1}`,
      carouselImageIndex,
      imageName: carouselImageIndex >= 0 ? row.images?.[carouselImageIndex]?.name || '' : '',
      imageRule: fixedIndex >= 0 ? fixedIndex + 1 : 'auto'
    };
  });
}

function applyTemuUploadTemplateToRow(row, template = getSelectedTemuUploadTemplate()) {
  if (!row) return row;
  const skus = getTemuUploadTemplateSkus(template);
  row.templateId = template?.id || '';
  row.templateName = template?.name || '';
  row.templateSkuCount = skus.length;
  row.skuImagePlan = buildTemuUploadSkuImagePlan(row, template);
  return row;
}

function renderTemuUploadTemplates() {
  const select = document.getElementById('temuUploadTemplateSelect');
  const list = document.getElementById('temuUploadTemplateList');
  const templates = temuUploadTestState.templates;
  if (select) {
    select.innerHTML = '<option value="">请选择商品模板</option>' + templates.map(template => {
      const skuCount = Array.isArray(template.data?.skus) ? template.data.skus.length : 0;
      const attrText = template.data?.missingProductAttributes ? ' · 属性未同步' : '';
      return `<option value="${escapeHtml(String(template.id))}" ${String(template.id) === String(temuUploadTestState.selectedTemplateId) ? 'selected' : ''}>${escapeHtml(template.name || '未命名模板')}（${skuCount} SKU${attrText}）</option>`;
    }).join('');
  }
  if (!list) return;
  if (!templates.length) {
    list.innerHTML = '<div class="empty">当前店铺暂无商品上架模板。</div>';
    return;
  }
  list.innerHTML = templates.map(template => {
    const images = Array.isArray(template.data?.images) ? template.data.images : [];
    const image = template.data?.mainImage || images[0] || '';
    const skuCount = Array.isArray(template.data?.skus) ? template.data.skus.length : 0;
    const sourceIdLabel = String(template.data?.sourceIdType || 'spu').toUpperCase();
    const attrText = template.data?.missingProductAttributes ? ' · 属性未同步' : '';
    const categoryText = getTemuUploadTemplateCategoryText(template);
    const timeText = getTemuUploadTemplateTimeText(template);
    const active = String(template.id) === String(temuUploadTestState.selectedTemplateId);
    return `
      <div class="temu-upload-template-item${active ? ' active' : ''}" data-template-id="${escapeHtml(String(template.id))}">
        ${image ? `<img src="${escapeHtml(image)}" alt="商品模板主图">` : '<span class="temu-upload-test-status">无图</span>'}
        <div>
          <strong title="${escapeHtml(template.name || '未命名模板')}">${escapeHtml(template.name || '未命名模板')}</strong>
          <span title="${escapeHtml(categoryText)}">类目：${escapeHtml(categoryText)}</span>
          <span>时间：${escapeHtml(timeText)} · ${escapeHtml(sourceIdLabel)} ${escapeHtml(template.sourceProductId || '-')} · ${skuCount} SKU${escapeHtml(attrText)}</span>
        </div>
        <div class="temu-upload-test-actions">
          <button class="btn btn-default btn-sm temu-upload-template-edit" data-id="${escapeHtml(String(template.id))}" type="button">查看属性</button>
          <button class="btn btn-primary btn-sm temu-upload-template-use" data-id="${escapeHtml(String(template.id))}" type="button">批量生成</button>
          <button class="btn btn-danger btn-sm temu-upload-template-delete" data-id="${escapeHtml(String(template.id))}" type="button">删除</button>
        </div>
      </div>`;
  }).join('');
}

async function loadTemuUploadTemplates() {
  const btn = document.getElementById('temuUploadTemplateRefreshBtn');
  const oldText = btn?.textContent || '刷新模板';
  try {
    if (btn) { btn.disabled = true; btn.textContent = '刷新中...'; }
    const result = await fetchTemuProductUploadTemplates();
    if (!result.success) throw new Error(result.error || '加载模板失败');
    temuUploadTestState.templates = result.templates || [];
    if (!temuUploadTestState.templates.some(item => String(item.id) === String(temuUploadTestState.selectedTemplateId))) {
      temuUploadTestState.selectedTemplateId = temuUploadTestState.templates[0]?.id || '';
    }
    renderTemuUploadTemplates();
    setTemuUploadTemplateStatus(temuUploadTestState.templates.length
      ? `已加载当前店铺 ${temuUploadTestState.templates.length} 个真实商品模板。`
      : '当前店铺暂无商品上架模板。');
  } catch (error) {
    setTemuUploadTemplateStatus(error.message || String(error), 'error');
  } finally {
    if (btn) { btn.disabled = false; btn.textContent = oldText; }
  }
}

function closeTemuUploadTemplateAddModal() {
  document.getElementById('temuUploadTemplateAddModal')?.classList.remove('is-open');
}

function closeTemuUploadTemplateEditModal() {
  document.getElementById('temuUploadTemplateEditModal')?.classList.remove('is-open');
  temuUploadTestState.editingTemplateId = '';
}

function getTemuUploadSourceDetail(template = {}) {
  const data = template.data && typeof template.data === 'object' ? template.data : {};
  const detail = data.sourceProductDetail || data.productDetail || data.rawProductDetail || null;
  return normalizeTemuUploadTemplateVisibleDetail(detail);
}

function hasTemuUploadOfficialTemplateDetail(detail = {}) {
  if (!detail || typeof detail !== 'object') return false;
  const hasSkus = Array.isArray(detail.productSkcList)
    && detail.productSkcList.some(skc => Array.isArray(skc?.productSkuList) && skc.productSkuList.length > 0);
  const hasEditExtAttrs = detail.productWhExtAttr && typeof detail.productWhExtAttr === 'object';
  const hasAttributes = Array.isArray(detail.productPropertyList)
    || Array.isArray(detail.productSpecPropertyVOS)
    || Boolean(detail.productSaleExtAttr && typeof detail.productSaleExtAttr === 'object')
    || Boolean(detail.productNonAuditExtAttr && typeof detail.productNonAuditExtAttr === 'object');
  return hasSkus && hasEditExtAttrs && hasAttributes;
}

function getTemuUploadTemplateImageUrlFromValue(value, depth = 0) {
  if (!value || depth > 4) return '';
  if (typeof value === 'string') {
    const text = value.trim();
    if (/^\/\//.test(text)) return `https:${text}`;
    return /^https?:\/\//i.test(text) ? text : '';
  }
  if (Array.isArray(value)) {
    for (const item of value) {
      const url = getTemuUploadTemplateImageUrlFromValue(item, depth + 1);
      if (url) return url;
    }
    return '';
  }
  if (typeof value !== 'object') return '';
  const direct = String(
    value.signedImageUrl ||
    value.imageUrl ||
    value.imgUrl ||
    value.url ||
    value.fileUrl ||
    value.file_url ||
    value.image_url ||
    value.picUrl ||
    value.pic_url ||
    value.thumbUrl ||
    ''
  ).trim();
  if (/^\/\//.test(direct)) return `https:${direct}`;
  if (/^https?:\/\//i.test(direct)) return direct;
  for (const item of Object.values(value)) {
    const url = getTemuUploadTemplateImageUrlFromValue(item, depth + 1);
    if (url) return url;
  }
  return '';
}

function getTemuUploadTemplateOuterPackageImages(detail = {}) {
  const raw = detail.raw && typeof detail.raw === 'object' ? detail.raw : {};
  const productWhExtAttr = detail.productWhExtAttr && typeof detail.productWhExtAttr === 'object' ? detail.productWhExtAttr : {};
  const productOuterPackage = [
    detail.productOuterPackage,
    detail.productOuterPackageReq,
    raw.productOuterPackage,
    raw.productOuterPackageReq,
    productWhExtAttr.productOuterPackage,
    productWhExtAttr.outerPackage
  ].find(item => item && typeof item === 'object' && !Array.isArray(item)) || {};
  return collectTemuUploadTemplateOuterPackageImagesFromSources([
    detail.outerPackageImages,
    detail.productOuterPackageImageReqs,
    detail.productOuterPackageImages,
    detail.productOuterPackageImageList,
    detail.outerPackageImageReqs,
    detail.outerPackageImageList,
    detail.packageImageList,
    raw.outerPackageImages,
    raw.productOuterPackageImageReqs,
    raw.productOuterPackageImages,
    raw.productOuterPackageImageList,
    raw.outerPackageImageReqs,
    raw.outerPackageImageList,
    raw.packageImageList,
    productWhExtAttr.outerPackageImages,
    productWhExtAttr.productOuterPackageImageReqs,
    productWhExtAttr.productOuterPackageImages,
    productWhExtAttr.productOuterPackageImageList,
    productWhExtAttr.outerPackageImageReqs,
    productWhExtAttr.outerPackageImageList,
    productWhExtAttr.packageImageList,
    productOuterPackage.outerPackageImages,
    productOuterPackage.productOuterPackageImageReqs,
    productOuterPackage.productOuterPackageImages,
    productOuterPackage.productOuterPackageImageList,
    productOuterPackage.outerPackageImageReqs,
    productOuterPackage.outerPackageImageList,
    productOuterPackage.packageImageList,
    productOuterPackage.packageImages,
    productOuterPackage.imageList,
    productOuterPackage.images
  ]);
}

function getTemuUploadPropertyName(item = {}) {
  return String(item.propName || item.name || item.propertyName || item.attrName || item.key || item.title || '').trim();
}

function getTemuUploadPropertyValue(item = {}) {
  return String(item.propValue ?? item.value ?? item.propertyValue ?? item.attrValue ?? item.valueName ?? item.text ?? '').trim();
}

function getTemuUploadEditableProperties(template = {}) {
  const detail = getTemuUploadSourceDetail(template);
  const data = template.data && typeof template.data === 'object' ? template.data : {};
  const list = Array.isArray(detail?.productPropertyList)
    ? detail.productPropertyList
    : (Array.isArray(data.productPropertyList) ? data.productPropertyList : (Array.isArray(data.productProperties) ? data.productProperties : []));
  return list.map(item => ({ ...(item && typeof item === 'object' ? item : {}), name: getTemuUploadPropertyName(item), value: getTemuUploadPropertyValue(item) }));
}

function getTemuUploadJsonText(value) {
  if (value === undefined || value === null) return '';
  try {
    return JSON.stringify(value, null, 2);
  } catch (_) {
    return String(value);
  }
}

function parseTemuUploadJsonText(text, label) {
  const raw = String(text || '').trim();
  if (!raw) return null;
  try {
    return JSON.parse(raw);
  } catch (error) {
    throw new Error(`${label} JSON 格式不正确：${error.message || error}`);
  }
}

function parseTemuUploadTemplateScalar(value, oldValue) {
  const text = String(value ?? '').trim();
  if (text === '') return oldValue === null ? null : '';
  if (oldValue && typeof oldValue === 'object') {
    try {
      return JSON.parse(text);
    } catch (_) {
      return text;
    }
  }
  if (typeof oldValue === 'number') {
    const num = Number(text);
    if (!Number.isFinite(num)) throw new Error(`数值格式不正确：${text}`);
    return num;
  }
  if (typeof oldValue === 'boolean') {
    return text === 'true' || text === '1' || text === '是';
  }
  return text;
}

function formatTemuUploadEditNumber(value, scale = 1, fractionDigits = 2) {
  if (value === undefined || value === null || value === '') return '';
  const num = Number(value);
  if (!Number.isFinite(num)) return String(value);
  const scaled = scale ? num / scale : num;
  if (!Number.isFinite(scaled)) return String(value);
  return scaled.toFixed(fractionDigits).replace(/\.?0+$/, '');
}

function parseTemuUploadEditScaledNumber(value, oldValue, scale = 1) {
  const text = String(value ?? '').trim();
  if (!text) return oldValue === null ? null : '';
  const num = Number(text);
  if (!Number.isFinite(num)) throw new Error(`数值格式不正确：${text}`);
  const raw = Math.round(num * scale);
  return typeof oldValue === 'number' ? raw : String(raw);
}

function formatTemuUploadEditPrice(value) {
  if (value === undefined || value === null || value === '') return '';
  const num = Number(value);
  if (!Number.isFinite(num)) return String(value);
  return num.toFixed(2);
}

function parseTemuUploadEditPrice(value, oldValue) {
  const text = String(value ?? '').trim();
  if (!text) return oldValue === null ? null : '';
  const num = Number(text);
  if (!Number.isFinite(num)) throw new Error(`数值格式不正确：${text}`);
  return typeof oldValue === 'number' ? num : String(num);
}

function formatTemuUploadEditDimension(value) {
  if (value === undefined || value === null || value === '') return '';
  const num = Number(value);
  if (!Number.isFinite(num)) return String(value);
  return num.toFixed(2).replace(/\.?0+$/, '');
}

function parseTemuUploadEditDimension(value, oldValue) {
  return parseTemuUploadEditScaledNumber(value, oldValue, 1);
}

function formatTemuUploadEditWeightGram(value) {
  return formatTemuUploadEditNumber(value, 1, 2);
}

function parseTemuUploadEditWeightGram(value, oldValue) {
  return parseTemuUploadEditScaledNumber(value, oldValue, 1);
}

function getTemuUploadEditDimensionInputKey(key = '') {
  if (key === 'len') return 'inputLen';
  if (key === 'width') return 'inputWidth';
  if (key === 'height') return 'inputHeight';
  return '';
}

function formatTemuUploadEditDimensionInput(volume = {}, key = '') {
  const inputKey = getTemuUploadEditDimensionInputKey(key);
  if (inputKey && volume && volume[inputKey] !== undefined && volume[inputKey] !== null && volume[inputKey] !== '') {
    return formatTemuUploadEditNumber(volume[inputKey], 1, 2);
  }
  return formatTemuUploadEditDimension(volume?.[key]);
}

function formatTemuUploadEditWeightInput(weight = {}) {
  if (weight && weight.inputValue !== undefined && weight.inputValue !== null && weight.inputValue !== '') {
    return formatTemuUploadEditNumber(weight.inputValue, 1, 2);
  }
  return formatTemuUploadEditWeightGram(weight?.value);
}

function getTemuUploadTemplateViewText(element) {
  if (!element) return '';
  if ('value' in element) return String(element.value ?? '').trim();
  const text = String(element.textContent ?? '').trim();
  return text === '—' ? '' : text;
}

function parseTemuUploadEditPlainNumber(value) {
  const text = String(value ?? '').trim();
  if (!text) return undefined;
  const num = Number(text);
  if (!Number.isFinite(num)) throw new Error(`数值格式不正确：${text}`);
  return num;
}

function applyTemuUploadEditDimensionInput(volume = {}, key = '', value) {
  const inputKey = getTemuUploadEditDimensionInputKey(key);
  if (!inputKey) return;
  const inputValue = parseTemuUploadEditPlainNumber(value);
  if (inputValue === undefined) return;
  volume[inputKey] = inputValue;
  volume[key] = typeof volume[key] === 'number' ? inputValue : String(inputValue);
}

function applyTemuUploadEditWeightInput(weight = {}, value) {
  const inputValue = parseTemuUploadEditPlainNumber(value);
  if (inputValue === undefined) return;
  weight.inputValue = inputValue;
  weight.value = typeof weight.value === 'number' ? inputValue : String(inputValue);
}

function getTemuUploadSkuSpecTextFromSource(sku = {}) {
  return (Array.isArray(sku.productSkuSpecList) ? sku.productSkuSpecList : [])
    .map(spec => [getTemuUploadSkuSpecParentName(spec), getTemuUploadSkuSpecValueName(spec)].filter(Boolean).join('：'))
    .filter(Boolean)
    .join(' / ');
}

function getTemuUploadTemplateEditInputValue(value) {
  if (value === undefined || value === null) return '';
  if (typeof value === 'object') return getTemuUploadJsonText(value);
  return String(value);
}

function renderTemuUploadTemplateEditField({ label, value = '', cls = '', id = '', field = '', placeholder = '', note = '', wide = false }) {
  return `
    <label class="temu-template-edit-field${wide ? ' is-wide' : ''}">
      <span>${escapeHtml(label)}</span>
      <div ${id ? `id="${escapeHtml(id)}"` : ''} ${cls ? `class="temu-template-view-value ${escapeHtml(cls)}"` : 'class="temu-template-view-value"'} ${field ? `data-field="${escapeHtml(field)}"` : ''}>${escapeHtml(getTemuUploadTemplateEditInputValue(value) || placeholder || '—')}</div>
      ${note ? `<em>${escapeHtml(note)}</em>` : ''}
    </label>`;
}

function pickTemuUploadFirstText(source = {}, keys = []) {
  if (!source || typeof source !== 'object') return '';
  for (const key of keys) {
    const value = source[key];
    if (value !== undefined && value !== null && value !== '') return String(value).trim();
  }
  return '';
}

function formatTemuUploadCountryDisplay(origin = {}) {
  const shortName = String(origin.countryShortName || '').trim().toUpperCase();
  const countryName = String(origin.countryName || '').trim();
  const text = pickTemuUploadFirstText(origin, ['countryNameCn', 'countryCnName', 'countryDisplayName', 'displayCountryName', 'countryName']);
  if (shortName === 'CN' || /^china$/i.test(countryName)) return '中国大陆';
  return text || shortName || '未返回';
}

function formatTemuUploadProvinceDisplay(origin = {}) {
  const text = pickTemuUploadFirstText(origin, [
    'region2Name',
    'regionName',
    'provinceName',
    'provinceCnName',
    'region2DisplayName',
    'displayRegionName',
    'stateName'
  ]);
  if (text) return text;
  return origin.region2Id || origin.regionId ? formatTemuUploadOfficialId(origin.region2Id || origin.regionId) : '未返回';
}

function hasTemuUploadVisibleValue(value) {
  if (value === undefined || value === null || value === '') return false;
  if (Array.isArray(value)) return value.length > 0;
  if (typeof value === 'object') return Object.keys(value).length > 0;
  return true;
}

const TEMU_UPLOAD_PACKAGE_TYPE_LABELS = {
  0: '硬包装'
};
const TEMU_UPLOAD_PACKAGE_SHAPE_LABELS = {
  0: '其他不规则形状'
};
const TEMU_UPLOAD_CUSTOM_TECH_TYPE_LABELS = {
  1: '单一工艺',
  2: '组合工艺'
};
const TEMU_UPLOAD_CUSTOM_TECH_DETAIL_LABELS = {
  20008: '丝网印刷-文字',
  20009: '丝网印刷-图片'
};

function formatTemuUploadOfficialId(value) {
  const text = String(value ?? '').trim();
  return text ? `官方仅返回 ID：${text}` : '未返回';
}

function formatTemuUploadOptionDisplay(value, fallback = '') {
  if (!hasTemuUploadVisibleValue(value)) return '未返回';
  if (typeof value === 'number') return fallback || formatTemuUploadOfficialId(value);
  if (typeof value === 'boolean') return value ? '是' : '否';
  if (Array.isArray(value)) {
    return value.map(item => formatTemuUploadOptionDisplay(item, fallback)).filter(Boolean).join('、') || fallback || '未返回';
  }
  if (typeof value === 'object') {
    return pickTemuUploadFirstText(value, [
      'name',
      'label',
      'text',
      'valueName',
      'displayName',
      'title',
      'cnName',
      'zhName',
      'showName',
      'optionName',
      'propertyValueName'
    ]) || fallback || getTemuUploadTemplateEditInputValue(value);
  }
  const text = String(value).trim();
  return /^\d+$/.test(text) ? (fallback || formatTemuUploadOfficialId(text)) : text;
}

function formatTemuUploadPackageDisplay(value, type = '') {
  if (!hasTemuUploadVisibleValue(value)) return '未返回';
  const id = String(value).trim();
  const verifiedLabels = type === 'shape' ? TEMU_UPLOAD_PACKAGE_SHAPE_LABELS : TEMU_UPLOAD_PACKAGE_TYPE_LABELS;
  if (/^\d+$/.test(id) && verifiedLabels[id]) return verifiedLabels[id];
  return formatTemuUploadOptionDisplay(value, '');
}

function formatTemuUploadCustomTechLabel(value) {
  if (!hasTemuUploadVisibleValue(value)) return '';
  const id = String(value).trim();
  if (TEMU_UPLOAD_CUSTOM_TECH_TYPE_LABELS[id]) return TEMU_UPLOAD_CUSTOM_TECH_TYPE_LABELS[id];
  if (TEMU_UPLOAD_CUSTOM_TECH_DETAIL_LABELS[id]) return TEMU_UPLOAD_CUSTOM_TECH_DETAIL_LABELS[id];
  return formatTemuUploadOptionDisplay(value, '');
}

function getTemuUploadPropertyDisplayValue(item = {}) {
  const value = item.propValue ?? item.value ?? item.propertyValue ?? item.attrValue ?? item.valueName ?? item.text ?? '';
  if (Array.isArray(value)) {
    const labels = value.map(entry => formatTemuUploadOptionDisplay(entry, '')).filter(Boolean);
    return labels.join('、') || getTemuUploadTemplateEditInputValue(value);
  }
  if (value && typeof value === 'object') return formatTemuUploadOptionDisplay(value, getTemuUploadTemplateEditInputValue(value));
  return String(value ?? '').trim();
}

function getTemuUploadSkuSensitiveYesNo(sensitive = {}) {
  if (!sensitive || typeof sensitive !== 'object') return '否';
  const hasYesValue = value => {
    if (value === true || value === 1 || value === '1' || value === 'true' || value === '是') return true;
    if (Array.isArray(value)) return value.some(hasYesValue);
    if (value && typeof value === 'object') return Object.values(value).some(hasYesValue);
    return false;
  };
  return Object.keys(sensitive).length && hasYesValue(sensitive) ? '是' : '否';
}

function getTemuUploadCustomizationInfo(detail = {}) {
  const skus = [];
  (Array.isArray(detail.productSkcList) ? detail.productSkcList : []).forEach(skc => {
    (Array.isArray(skc?.productSkuList) ? skc.productSkuList : []).forEach(sku => skus.push(sku));
  });
  const saleExt = detail.productSaleExtAttr && typeof detail.productSaleExtAttr === 'object' ? detail.productSaleExtAttr : {};
  const whExt = detail.productWhExtAttr && typeof detail.productWhExtAttr === 'object' ? detail.productWhExtAttr : {};
  const personalization = detail.productPersonalization || saleExt.productPersonalization || {};
  const skuTemplates = skus.map(sku => (
    sku?.productSkuCustomizationTmpl
    || sku?.productSkuSaleExtAttr?.productSkuCustomizationTmpl
    || sku?.productSkuSaleExtAttr?.customizationTmpl
    || sku?.customizationTmpl
    || null
  )).filter(Boolean);
  const switchValues = [
    personalization.personalizationSwitch,
    saleExt.personalizationSwitch,
    whExt.personalizationSwitch,
    saleExt.customizationSwitch,
    saleExt.isCustomized
  ];
  const isCustomized = switchValues.some(value => Number(value) === 1 || value === true || value === 'true')
    || skuTemplates.length > 0;
  const craftNames = [];
  const technology = saleExt.productCustomizationTechnology || saleExt.customizationTechnology || {};
  const mainTech = formatTemuUploadCustomTechLabel(technology.technologyType);
  if (mainTech) craftNames.push(mainTech);
  const firstTech = formatTemuUploadCustomTechLabel(technology.firstType);
  if (firstTech && !/官方仅返回 ID/.test(firstTech)) craftNames.push(firstTech);
  const secondTechList = Array.isArray(technology.twiceType) ? technology.twiceType : [];
  secondTechList.forEach(item => {
    const text = formatTemuUploadCustomTechLabel(item);
    if (text && !/官方仅返回 ID/.test(text)) craftNames.push(text);
  });
  const collectName = value => {
    if (!value) return;
    if (Array.isArray(value)) return value.forEach(collectName);
    if (typeof value === 'object') {
      const label = pickTemuUploadFirstText(value, ['name', 'label', 'text', 'title', 'templateName', 'craftName', 'technologyName']);
      if (label) craftNames.push(label);
      ['craftList', 'technologyList', 'processList', 'templateTypes', 'twiceTypeList'].forEach(key => collectName(value[key]));
      return;
    }
    const text = String(value).trim();
    if (text && !/^\d+$/.test(text)) craftNames.push(text);
  };
  skuTemplates.forEach(collectName);
  collectName(saleExt.productCustomizationTmpl || saleExt.customizationTmpl || saleExt.customizationTemplate);
  return {
    isCustomized,
    statusText: isCustomized ? '定制商品' : '非定制商品',
    craftText: Array.from(new Set(craftNames)).join('、') || '未返回'
  };
}

function renderTemuUploadCustomizationSection(detail = {}) {
  const info = getTemuUploadCustomizationInfo(detail);
  return `
    <section class="temu-template-edit-section">
      <div class="temu-template-edit-section-head">
        <div><strong>是否定制信息</strong><p>按 TEMU 后台样式展示定制状态与工艺摘要。</p></div>
      </div>
      <div class="temu-template-edit-custom-box">
        <div class="temu-template-edit-radio-row">
          <span class="${info.isCustomized ? 'active' : ''}">定制商品</span>
          <span class="${!info.isCustomized ? 'active' : ''}">非定制商品</span>
        </div>
        <div class="temu-template-edit-custom-tip">${info.isCustomized ? '定制开启后，模板会保留 TEMU 返回的定制工艺与 SKU 原始配置。' : '该模板未识别到官方定制配置。'}</div>
        <div class="temu-template-edit-field-grid">
          <label class="temu-template-edit-field is-wide">
            <span>工艺类型</span>
            <div class="temu-template-view-value">${escapeHtml(info.craftText || '—')}</div>
          </label>
        </div>
      </div>
    </section>`;
}

function getTemuUploadEditSkus(template = {}) {
  return getTemuUploadTemplateSkus(template).map((sku, index) => ({
    id: sku.id || sku.skuId || sku.productSkuId || '',
    name: sku.name || `SKU ${index + 1}`,
    price: sku.price || '',
    stock: sku.stock || '',
    image: sku.image || ''
  }));
}

function renderTemuUploadTemplateEditModal(template) {
  const modal = ensureTemuUploadTemplateEditModal();
  modal.querySelector('#temuUploadTemplateEditName').value = template?.name || '';
  const propertyBody = modal.querySelector('#temuUploadTemplateEditPropertyBody');
  const skuBody = modal.querySelector('#temuUploadTemplateEditSkuBody');
  const properties = getTemuUploadEditableProperties(template);
  const skus = getTemuUploadEditSkus(template);
  propertyBody.innerHTML = (properties.length ? properties : [{ name: '', value: '' }]).map((item, index) => `
    <tr data-property-index="${index}">
      <td><input class="temu-upload-template-edit-prop-name" value="${escapeHtml(item.name || '')}" placeholder="属性名"></td>
      <td><input class="temu-upload-template-edit-prop-value" value="${escapeHtml(item.value || '')}" placeholder="属性值"></td>
      <td style="width:70px;text-align:center;"><button class="btn btn-danger btn-sm temu-upload-template-edit-prop-delete" type="button">删除</button></td>
    </tr>
  `).join('');
  skuBody.innerHTML = skus.map((sku, index) => `
    <tr data-sku-index="${index}">
      <td title="${escapeHtml(sku.id || '')}">${escapeHtml(sku.id || `SKU ${index + 1}`)}</td>
      <td><input class="temu-upload-template-edit-sku-name" value="${escapeHtml(sku.name || '')}" placeholder="SKU名称/规格"></td>
      <td><input class="temu-upload-template-edit-sku-price" value="${escapeHtml(sku.price || '')}" placeholder="申报价"></td>
      <td><input class="temu-upload-template-edit-sku-stock" value="${escapeHtml(sku.stock || '')}" placeholder="库存"></td>
      <td><input class="temu-upload-template-edit-sku-image" value="${escapeHtml(sku.image || '')}" placeholder="SKU图URL"></td>
    </tr>
  `).join('') || '<tr><td colspan="5" class="empty">模板未返回 SKU，不能编辑。</td></tr>';
}

function ensureTemuUploadTemplateEditModal() {
  let modal = document.getElementById('temuUploadTemplateEditModal');
  if (modal) return modal;
  modal = document.createElement('div');
  modal.id = 'temuUploadTemplateEditModal';
  modal.className = 'temu-upload-test-preview-modal';
  modal.innerHTML = `
    <div class="temu-upload-test-preview-panel" style="width:min(960px,96vw);max-height:86vh;overflow:auto;">
      <div class="temu-upload-test-preview-head">
        <div><strong>编辑 TEMU 商品模板</strong><div class="temu-upload-test-note">只保存模板数据，不写回 TEMU 官方后台。</div></div>
        <button class="btn btn-default btn-sm" id="temuUploadTemplateEditCloseBtn" type="button">关闭</button>
      </div>
      <div class="temu-upload-template-controls" style="grid-template-columns:120px minmax(0,1fr);margin-bottom:12px;">
        <label class="temu-upload-test-note" for="temuUploadTemplateEditName">模板名称</label>
        <input id="temuUploadTemplateEditName" autocomplete="off">
      </div>
      <div class="temu-upload-test-note" style="margin-bottom:6px;">商品属性</div>
      <div class="temu-upload-test-table-wrap" style="max-height:220px;margin-bottom:10px;">
        <table class="temu-upload-test-table">
          <thead><tr><th style="width:220px;">属性名</th><th>属性值</th><th style="width:70px;">操作</th></tr></thead>
          <tbody id="temuUploadTemplateEditPropertyBody"></tbody>
        </table>
      </div>
      <button class="btn btn-default btn-sm" id="temuUploadTemplateEditAddPropBtn" type="button">添加属性</button>
      <div class="temu-upload-test-note" style="margin:14px 0 6px;">SKU 信息</div>
      <div class="temu-upload-test-table-wrap" style="max-height:260px;">
        <table class="temu-upload-test-table">
          <thead><tr><th style="width:130px;">SKU ID</th><th style="width:220px;">SKU名称/规格</th><th style="width:100px;">申报价</th><th style="width:90px;">库存</th><th>SKU图URL</th></tr></thead>
          <tbody id="temuUploadTemplateEditSkuBody"></tbody>
        </table>
      </div>
      <div class="temu-upload-test-actions" style="justify-content:flex-end;margin-top:14px;">
        <button class="btn btn-primary btn-sm" id="temuUploadTemplateEditSaveBtn" type="button">保存模板</button>
      </div>
    </div>`;
  modal.addEventListener('click', event => {
    if (event.target === modal || event.target?.closest?.('#temuUploadTemplateEditCloseBtn')) closeTemuUploadTemplateEditModal();
    if (event.target?.closest?.('#temuUploadTemplateEditAddPropBtn')) {
      const body = modal.querySelector('#temuUploadTemplateEditPropertyBody');
      body.insertAdjacentHTML('beforeend', `
        <tr>
          <td><input class="temu-upload-template-edit-prop-name" value="" placeholder="属性名"></td>
          <td><input class="temu-upload-template-edit-prop-value" value="" placeholder="属性值"></td>
          <td style="width:70px;text-align:center;"><button class="btn btn-danger btn-sm temu-upload-template-edit-prop-delete" type="button">删除</button></td>
        </tr>`);
    }
    if (event.target?.closest?.('.temu-upload-template-edit-prop-delete')) {
      event.target.closest('tr')?.remove();
    }
  });
  modal.querySelector('#temuUploadTemplateEditSaveBtn')?.addEventListener('click', saveTemuUploadTemplateEdit);
  document.body.appendChild(modal);
  return modal;
}

function openTemuUploadTemplateEditModal(templateId) {
  const template = temuUploadTestState.templates.find(item => String(item.id) === String(templateId));
  if (!template) return showToast('未找到要编辑的模板', 'error');
  temuUploadTestState.editingTemplateId = String(template.id);
  renderTemuUploadTemplateEditModal(template);
  ensureTemuUploadTemplateEditModal().classList.add('is-open');
}

function collectTemuUploadTemplateEditPayload(template) {
  const modal = ensureTemuUploadTemplateEditModal();
  const name = String(modal.querySelector('#temuUploadTemplateEditName')?.value || '').trim();
  if (!name) throw new Error('请填写模板名称');
  const data = JSON.parse(JSON.stringify(template.data || {}));
  data.templateName = name;
  const properties = Array.from(modal.querySelectorAll('#temuUploadTemplateEditPropertyBody tr')).map((tr, index) => {
    const old = getTemuUploadEditableProperties(template)[index] || {};
    const propName = String(tr.querySelector('.temu-upload-template-edit-prop-name')?.value || '').trim();
    const propValue = String(tr.querySelector('.temu-upload-template-edit-prop-value')?.value || '').trim();
    return { ...old, name: propName, value: propValue };
  }).filter(item => item.name || item.value);
  data.productPropertyList = properties;
  data.productProperties = properties;
  if (data.sourceProductDetail && typeof data.sourceProductDetail === 'object') {
    data.sourceProductDetail.productPropertyList = properties;
    data.sourceProductDetail.productProperties = properties;
  }
  const oldSkus = getTemuUploadTemplateSkus(template);
  const skus = Array.from(modal.querySelectorAll('#temuUploadTemplateEditSkuBody tr')).map((tr, index) => {
    const old = oldSkus[index] || {};
    return {
      ...old,
      name: String(tr.querySelector('.temu-upload-template-edit-sku-name')?.value || old.name || '').trim(),
      price: String(tr.querySelector('.temu-upload-template-edit-sku-price')?.value || '').trim(),
      stock: String(tr.querySelector('.temu-upload-template-edit-sku-stock')?.value || '').trim(),
      image: String(tr.querySelector('.temu-upload-template-edit-sku-image')?.value || '').trim()
    };
  }).filter((_, index) => oldSkus[index]);
  if (!skus.length) throw new Error('模板没有可保存的 SKU');
  data.skus = skus;
  const sourceSkus = [];
  (data.sourceProductDetail?.productSkcList || []).forEach(skc => {
    (skc.productSkuList || []).forEach(sku => sourceSkus.push(sku));
  });
  skus.forEach((sku, index) => {
    const sourceSku = sourceSkus[index];
    if (!sourceSku || typeof sourceSku !== 'object') return;
    sourceSku.supplierPrice = sku.price;
    sourceSku.productSkuQuantity = sku.stock;
    sourceSku.thumbUrl = sku.image || sourceSku.thumbUrl || '';
    if (Array.isArray(sourceSku.productSkuSpecList) && sourceSku.productSkuSpecList.length) {
      sourceSku.productSkuSpecList[0] = { ...sourceSku.productSkuSpecList[0], specValue: sku.name, value: sku.name, name: sku.name };
    } else {
      sourceSku.productSkuSpecList = [{ specValue: sku.name, value: sku.name, name: sku.name }];
    }
  });
  return {
    name,
    sourceUrl: template.sourceUrl || '',
    sourceProductId: template.sourceProductId || data.sourceProductId || data.productId || '',
    sourceIdType: data.sourceIdType || 'spu',
    data
  };
}

function renderTemuUploadTemplateEditModal(template) {
  const modal = ensureTemuUploadTemplateEditModal();
  const nameEl = modal.querySelector('#temuUploadTemplateViewName');
  if (nameEl) nameEl.textContent = `模板：${template?.name || '未命名模板'}`;
  const propertyBody = modal.querySelector('#temuUploadTemplateEditPropertyBody');
  const skuBody = modal.querySelector('#temuUploadTemplateEditSkuBody');
  const jsonBody = modal.querySelector('#temuUploadTemplateEditJsonBody');
  const detail = getTemuUploadSourceDetail(template);
  const properties = getTemuUploadEditableProperties(template);
  if (!hasTemuUploadOfficialTemplateDetail(detail)) {
    propertyBody.innerHTML = '<div class="temu-template-edit-empty temu-template-edit-warning">这个模板缺少 TEMU 官方商品详情结构，不能查看完整属性。请用当前店铺的 SPU/SKC 重新添加模板；新模板会保存商品产地、商品属性、包装、敏感属性、体积重量和 SKU 原始字段。</div>';
    skuBody.innerHTML = '<div class="temu-template-edit-empty">旧模板缺少官方 SKU 详情，请重新添加模板。</div>';
    if (jsonBody) jsonBody.innerHTML = '';
    return;
  }
  const origin = detail.productWhExtAttr?.productOrigin || {};
  const outerPackage = detail.productWhExtAttr?.productOuterPackage || {};
  const outerImages = getTemuUploadTemplateOuterPackageImages(detail);
  propertyBody.innerHTML = `
    <section class="temu-template-edit-section">
      <div class="temu-template-edit-section-head">
        <div><strong>商品基础信息</strong><p>按 TEMU 后台展示；内部区域 ID、外部链接只保存不展示。</p></div>
      </div>
      <div class="temu-template-edit-origin-row">
        <span class="temu-template-edit-required">商品产地</span>
        <div class="temu-template-edit-select-like">${escapeHtml(formatTemuUploadCountryDisplay(origin))}</div>
        <div class="temu-template-edit-select-like">${escapeHtml(formatTemuUploadProvinceDisplay(origin))}</div>
      </div>
      <div class="temu-template-edit-field-grid temu-template-edit-basic-grid">
        <label class="temu-template-edit-field">
          <span>外包装形状</span>
          <div class="temu-template-view-value">${escapeHtml(formatTemuUploadPackageDisplay(outerPackage.packageShape, 'shape'))}</div>
        </label>
        <label class="temu-template-edit-field">
          <span>外包装类型</span>
          <div class="temu-template-view-value">${escapeHtml(formatTemuUploadPackageDisplay(outerPackage.packageType, 'type'))}</div>
        </label>
      </div>
      <div class="temu-template-edit-image-list temu-template-edit-image-only-list">
        ${outerImages.length ? outerImages.map((item, index) => {
          const url = getTemuUploadTemplateImageUrlFromValue(item);
          return `
            <div class="temu-template-edit-image-card">
              <img src="${escapeHtml(url)}" alt="包装图 ${index + 1}" referrerpolicy="no-referrer" loading="lazy" onerror="this.style.display='none'">
              <span>包装图 ${index + 1}</span>
            </div>`;
        }).join('') : '<div class="temu-template-edit-empty">TEMU 未返回商品包装图片。</div>'}
      </div>
    </section>
    <section class="temu-template-edit-section">
      <div class="temu-template-edit-section-head">
        <div><strong>商品属性</strong><p>不同类目字段不同，按官方返回字段块展示。</p></div>
      </div>
      <div class="temu-template-edit-attr-grid">
        ${properties.length ? properties.map((item, index) => {
          const numberValue = item.numberInputValue;
          const unitValue = item.valueUnit;
          const hasNumber = hasTemuUploadVisibleValue(numberValue);
          const hasUnit = hasTemuUploadVisibleValue(unitValue);
          const displayValue = getTemuUploadPropertyDisplayValue(item);
          return `
            <label class="temu-template-edit-attr-field" data-property-index="${index}" data-original-prop-display="${escapeHtml(displayValue)}">
              <span>${escapeHtml(item.name || `属性 ${index + 1}`)}</span>
              <div class="temu-template-view-value temu-upload-template-edit-prop-value">${escapeHtml(displayValue || '—')}</div>
              ${(hasNumber || hasUnit) ? `<div class="temu-template-edit-attr-extra">
                ${hasNumber ? `<div class="temu-template-view-value temu-upload-template-edit-prop-number">${escapeHtml(getTemuUploadTemplateEditInputValue(numberValue) || '—')}</div>` : ''}
                ${hasUnit ? `<div class="temu-template-view-value temu-upload-template-edit-prop-unit">${escapeHtml(getTemuUploadTemplateEditInputValue(unitValue) || '—')}</div>` : ''}
              </div>` : ''}
            </label>`;
        }).join('') : '<div class="temu-template-edit-empty">TEMU 未返回商品属性列表。</div>'}
      </div>
    </section>
    ${renderTemuUploadCustomizationSection(detail)}`;
  const sourceSkus = [];
  (detail.productSkcList || []).forEach(skc => {
    (skc.productSkuList || []).forEach(sku => sourceSkus.push(sku));
  });
  skuBody.innerHTML = sourceSkus.map((sku, index) => {
    const volume = sku.productSkuWhExtAttr?.productSkuVolume || {};
    const weight = sku.productSkuWhExtAttr?.productSkuWeight || {};
    const sensitive = sku.productSkuWhExtAttr?.productSkuSensitiveAttr || {};
    return `
      <section class="temu-template-edit-sku-card" data-sku-index="${index}">
        <div class="temu-template-edit-sku-head">
          <div>
            <strong>${escapeHtml(getTemuUploadSkuSpecTextFromSource(sku) || `SKU ${index + 1}`)}</strong>
            <span>SKU ID：${escapeHtml(String(sku.productSkuId || '-'))}</span>
          </div>
        </div>
        <div class="temu-template-edit-field-grid temu-template-edit-sku-grid">
          ${renderTemuUploadTemplateEditField({ label: '申报价', cls: 'temu-upload-template-edit-sku-price', value: formatTemuUploadEditPrice(sku.supplierPrice), placeholder: '申报价' })}
          ${renderTemuUploadTemplateEditField({ label: '货号', cls: 'temu-upload-template-edit-sku-code', value: sku.extCode, placeholder: '货号' })}
          ${renderTemuUploadTemplateEditField({ label: '长(cm)', cls: 'temu-upload-template-edit-sku-len', value: formatTemuUploadEditDimensionInput(volume, 'len'), placeholder: '长' })}
          ${renderTemuUploadTemplateEditField({ label: '宽(cm)', cls: 'temu-upload-template-edit-sku-width', value: formatTemuUploadEditDimensionInput(volume, 'width'), placeholder: '宽' })}
          ${renderTemuUploadTemplateEditField({ label: '高(cm)', cls: 'temu-upload-template-edit-sku-height', value: formatTemuUploadEditDimensionInput(volume, 'height'), placeholder: '高' })}
          ${renderTemuUploadTemplateEditField({ label: '重量(g)', cls: 'temu-upload-template-edit-sku-weight', value: formatTemuUploadEditWeightInput(weight), placeholder: '重量' })}
          <label class="temu-template-edit-field">
            <span>敏感属性</span>
            <input value="${escapeHtml(getTemuUploadSkuSensitiveYesNo(sensitive))}" readonly>
          </label>
        </div>
      </section>`;
  }).join('') || '<div class="temu-template-edit-empty">模板未返回 SKU 信息。</div>';
  if (jsonBody) {
    jsonBody.innerHTML = '';
  }
}

function ensureTemuUploadTemplateEditModal() {
  let modal = document.getElementById('temuUploadTemplateEditModal');
  if (modal) return modal;
  modal = document.createElement('div');
  modal.id = 'temuUploadTemplateEditModal';
  modal.className = 'temu-upload-test-preview-modal';
  modal.innerHTML = `
    <div class="temu-upload-test-preview-panel temu-template-edit-panel">
      <div class="temu-upload-test-preview-head temu-template-edit-head">
        <div>
          <strong>查看 TEMU 商品模板属性</strong>
          <div class="temu-upload-test-note">只查看模板中保存的 TEMU 官方属性，不在网页中编辑属性，不写回 TEMU 官方后台。</div>
          <div class="temu-template-edit-current" id="temuUploadTemplateViewName">模板：—</div>
        </div>
        <button class="temu-template-edit-close" id="temuUploadTemplateEditCloseBtn" type="button">关闭</button>
      </div>
      <div class="temu-template-edit-body">
        <div id="temuUploadTemplateEditPropertyBody"></div>
        <section class="temu-template-edit-section">
          <div class="temu-template-edit-section-head">
            <div><strong>SKU 信息 / 尺寸重量 / 敏感属性</strong><p>不展示 SKU 图片；敏感属性仅显示是/否。</p></div>
          </div>
          <div id="temuUploadTemplateEditSkuBody" class="temu-template-edit-sku-list"></div>
        </section>
        <div id="temuUploadTemplateEditJsonBody" hidden></div>
      </div>
      <div class="temu-template-edit-footer">
        <button class="btn btn-primary btn-sm" id="temuUploadTemplateEditCloseFooterBtn" type="button">关闭</button>
      </div>
    </div>`;
  modal.addEventListener('click', event => {
    if (event.target === modal || event.target?.closest?.('#temuUploadTemplateEditCloseBtn') || event.target?.closest?.('#temuUploadTemplateEditCloseFooterBtn')) closeTemuUploadTemplateEditModal();
  });
  document.body.appendChild(modal);
  return modal;
}

function collectTemuUploadTemplateEditPayload(template) {
  const modal = ensureTemuUploadTemplateEditModal();
  const name = String(modal.querySelector('#temuUploadTemplateEditName')?.value || '').trim();
  if (!name) throw new Error('请填写模板名称');
  const data = JSON.parse(JSON.stringify(template.data || {}));
  const detail = data.sourceProductDetail || data.productDetail || data.rawProductDetail || null;
  if (!hasTemuUploadOfficialTemplateDetail(detail)) {
    throw new Error('这个模板缺少 TEMU 官方商品编辑详情结构，请重新添加模板后再编辑');
  }
  data.templateName = name;

  if (!detail.productWhExtAttr || typeof detail.productWhExtAttr !== 'object') detail.productWhExtAttr = {};
  if (!detail.productWhExtAttr.productOrigin || typeof detail.productWhExtAttr.productOrigin !== 'object') detail.productWhExtAttr.productOrigin = {};
  if (!detail.productWhExtAttr.productOuterPackage || typeof detail.productWhExtAttr.productOuterPackage !== 'object') detail.productWhExtAttr.productOuterPackage = {};
  modal.querySelectorAll('.temu-upload-template-edit-origin').forEach(input => {
    const field = input.dataset.field;
    if (!field) return;
    detail.productWhExtAttr.productOrigin[field] = parseTemuUploadTemplateScalar(input.value, detail.productWhExtAttr.productOrigin[field]);
  });
  modal.querySelectorAll('.temu-upload-template-edit-package').forEach(input => {
    const field = input.dataset.field;
    if (!field) return;
    detail.productWhExtAttr.productOuterPackage[field] = parseTemuUploadTemplateScalar(input.value, detail.productWhExtAttr.productOuterPackage[field]);
  });
  const outerGoodsUrlInput = modal.querySelector('#temuUploadTemplateEditOuterGoodsUrl');
  if (outerGoodsUrlInput) {
    detail.productWhExtAttr.outerGoodsUrl = String(outerGoodsUrlInput.value || '').trim();
  }

  const oldProperties = Array.isArray(detail.productPropertyList) ? detail.productPropertyList : [];
  modal.querySelectorAll('#temuUploadTemplateEditPropertyBody [data-property-index]').forEach(tr => {
    const index = Number(tr.dataset.propertyIndex);
    const old = oldProperties[index];
    if (!old || typeof old !== 'object') return;
    const propValue = String(tr.querySelector('.temu-upload-template-edit-prop-value')?.value ?? '').trim();
    const originalDisplay = String(tr.dataset.originalPropDisplay || '').trim();
    const oldRawValue = 'propValue' in old ? old.propValue : old.value;
    const shouldPreserveComplexValue = oldRawValue && typeof oldRawValue === 'object' && propValue === originalDisplay;
    if (!shouldPreserveComplexValue) {
      if ('propValue' in old) old.propValue = propValue;
      else old.value = propValue;
    }
    const numberInput = tr.querySelector('.temu-upload-template-edit-prop-number');
    if (numberInput) old.numberInputValue = String(numberInput.value ?? '').trim();
    const valueUnitInput = tr.querySelector('.temu-upload-template-edit-prop-unit');
    if (valueUnitInput) old.valueUnit = String(valueUnitInput.value ?? '').trim();
  });
  data.productPropertyList = oldProperties;
  data.productProperties = oldProperties;

  if (Array.isArray(detail.outerPackageImages)) {
    modal.querySelectorAll('.temu-upload-template-edit-outer-image').forEach(input => {
      const index = Number(input.dataset.index);
      const old = detail.outerPackageImages[index];
      if (!old || typeof old !== 'object') return;
      old.imageUrl = String(input.value || '').trim();
      if ('signedImageUrl' in old) old.signedImageUrl = old.imageUrl;
    });
  }

  detail.productSaleExtAttr = parseTemuUploadJsonText(modal.querySelector('#temuUploadTemplateEditSaleExtAttr')?.value, 'productSaleExtAttr');
  detail.productNonAuditExtAttr = parseTemuUploadJsonText(modal.querySelector('#temuUploadTemplateEditNonAuditExtAttr')?.value, 'productNonAuditExtAttr');

  const sourceSkus = [];
  (detail.productSkcList || []).forEach(skc => {
    (skc.productSkuList || []).forEach(sku => sourceSkus.push(sku));
  });
  modal.querySelectorAll('#temuUploadTemplateEditSkuBody [data-sku-index]').forEach(tr => {
    const index = Number(tr.dataset.skuIndex);
    const sku = sourceSkus[index];
    if (!sku || typeof sku !== 'object') return;
    sku.supplierPrice = parseTemuUploadEditPrice(tr.querySelector('.temu-upload-template-edit-sku-price')?.value, sku.supplierPrice);
    sku.extCode = String(tr.querySelector('.temu-upload-template-edit-sku-code')?.value || '').trim();
    const skuImageInput = tr.querySelector('.temu-upload-template-edit-sku-image');
    if (skuImageInput) sku.thumbUrl = String(skuImageInput.value || '').trim();
    if (!sku.productSkuWhExtAttr || typeof sku.productSkuWhExtAttr !== 'object') sku.productSkuWhExtAttr = {};
    if (!sku.productSkuWhExtAttr.productSkuVolume || typeof sku.productSkuWhExtAttr.productSkuVolume !== 'object') sku.productSkuWhExtAttr.productSkuVolume = {};
    if (!sku.productSkuWhExtAttr.productSkuWeight || typeof sku.productSkuWhExtAttr.productSkuWeight !== 'object') sku.productSkuWhExtAttr.productSkuWeight = {};
    const volume = sku.productSkuWhExtAttr.productSkuVolume;
    const weight = sku.productSkuWhExtAttr.productSkuWeight;
    applyTemuUploadEditDimensionInput(volume, 'len', getTemuUploadTemplateViewText(tr.querySelector('.temu-upload-template-edit-sku-len')));
    applyTemuUploadEditDimensionInput(volume, 'width', getTemuUploadTemplateViewText(tr.querySelector('.temu-upload-template-edit-sku-width')));
    applyTemuUploadEditDimensionInput(volume, 'height', getTemuUploadTemplateViewText(tr.querySelector('.temu-upload-template-edit-sku-height')));
    applyTemuUploadEditWeightInput(weight, getTemuUploadTemplateViewText(tr.querySelector('.temu-upload-template-edit-sku-weight')));
  });
  modal.querySelectorAll('#temuUploadTemplateEditSkuBody [data-sku-sensitive-index]').forEach(tr => {
    const index = Number(tr.dataset.skuSensitiveIndex);
    const sku = sourceSkus[index];
    if (!sku || typeof sku !== 'object') return;
    if (!sku.productSkuWhExtAttr || typeof sku.productSkuWhExtAttr !== 'object') sku.productSkuWhExtAttr = {};
    sku.productSkuWhExtAttr.productSkuSensitiveAttr = parseTemuUploadJsonText(
      tr.querySelector('.temu-upload-template-edit-sku-sensitive')?.value,
      `SKU ${sku.productSkuId || index + 1} 敏感属性`
    );
  });

  const visibleDetail = normalizeTemuUploadTemplateVisibleDetail(detail);
  const summary = summarizeMoveProductDetail(visibleDetail, data);
  data.sourceProductDetail = visibleDetail;
  data.skus = summary.skus;
  data.images = summary.images;
  data.mainImage = summary.mainImage;
  data.categoryId = summary.categoryId;
  data.category = summary.category;
  data.categoryPath = summary.categoryPath;
  data.missingProductAttributes = false;
  data.templateDetailSource = detail.detailSource || data.templateDetailSource || 'temu_product_query';
  return {
    name,
    sourceUrl: template.sourceUrl || '',
    sourceProductId: template.sourceProductId || data.sourceProductId || data.productId || '',
    sourceIdType: data.sourceIdType || 'spu',
    data
  };
}

async function saveTemuUploadTemplateEdit() {
  const template = temuUploadTestState.templates.find(item => String(item.id) === String(temuUploadTestState.editingTemplateId));
  if (!template) return showToast('未找到要保存的模板', 'error');
  const btn = document.getElementById('temuUploadTemplateEditSaveBtn');
  const oldText = btn?.textContent || '保存模板';
  try {
    if (btn) { btn.disabled = true; btn.textContent = '保存中...'; }
    const payload = collectTemuUploadTemplateEditPayload(template);
    const result = await updateTemuProductUploadTemplate(template.id, payload);
    if (!result.success) throw new Error(result.error || '保存模板失败');
    closeTemuUploadTemplateEditModal();
    await loadTemuUploadTemplates();
    temuUploadTestState.rows.forEach(row => {
      applyTemuUploadTemplateToRow(row);
      row.issues = buildTemuUploadTestIssues(row);
    });
    renderTemuUploadTestRows();
    showToast('模板已保存', 'success');
  } catch (error) {
    showToast(`保存模板失败：${error.message || String(error)}`, 'error');
  } finally {
    if (btn) { btn.disabled = false; btn.textContent = oldText; }
  }
}

function ensureTemuUploadTemplateAddModal() {
  let modal = document.getElementById('temuUploadTemplateAddModal');
  if (modal) return modal;
  modal = document.createElement('div');
  modal.id = 'temuUploadTemplateAddModal';
  modal.className = 'temu-upload-test-preview-modal';
  modal.innerHTML = `
    <div class="temu-upload-test-preview-panel" style="width:min(560px,96vw);">
      <div class="temu-upload-test-preview-head">
        <div><strong>添加 TEMU 商品模板</strong><div class="temu-upload-test-note">从当前已连接店铺读取真实商品详情。</div></div>
        <button class="btn btn-default btn-sm" id="temuUploadTemplateAddCloseBtn" type="button">关闭</button>
      </div>
      <div class="temu-upload-template-controls" style="grid-template-columns:130px minmax(0,1fr);">
        <input id="temuUploadTemplateName" style="grid-column:1 / -1;" placeholder="模板名称，例如：抱枕模板-40cm" autocomplete="off">
        <select id="temuUploadTemplateSourceType" aria-label="商品 ID 类型"><option value="spu">SPU ID</option><option value="skc">SKC ID</option></select>
        <input id="temuUploadTemplateSourceId" inputmode="numeric" placeholder="请输入数字 ID" autocomplete="off">
      </div>
      <p class="temu-upload-test-note">SPU ID 按 productId 查询，SKC ID 按 goodsId 查询；未返回完整属性和 SKU 时不会保存。</p>
      <div class="temu-upload-test-actions" style="justify-content:flex-end;"><button class="btn btn-primary btn-sm" id="temuUploadTemplateAddSaveBtn" type="button">读取并保存</button></div>
    </div>`;
  modal.addEventListener('click', event => {
    if (event.target === modal || event.target?.closest?.('#temuUploadTemplateAddCloseBtn')) closeTemuUploadTemplateAddModal();
  });
  modal.querySelector('#temuUploadTemplateAddSaveBtn')?.addEventListener('click', saveTemuUploadTemplateFromId);
  document.body.appendChild(modal);
  return modal;
}

function openTemuUploadTemplateAddModal() {
  const modal = ensureTemuUploadTemplateAddModal();
  const nameInput = modal.querySelector('#temuUploadTemplateName');
  const input = modal.querySelector('#temuUploadTemplateSourceId');
  if (nameInput) nameInput.value = '';
  if (input) input.value = '';
  modal.classList.add('is-open');
  nameInput?.focus();
}

async function saveTemuUploadTemplateFromId() {
  const modal = ensureTemuUploadTemplateAddModal();
  const sourceIdType = String(modal.querySelector('#temuUploadTemplateSourceType')?.value || 'spu').toLowerCase();
  const nameInput = modal.querySelector('#temuUploadTemplateName');
  const input = modal.querySelector('#temuUploadTemplateSourceId');
  const btn = modal.querySelector('#temuUploadTemplateAddSaveBtn');
  const oldText = btn?.textContent || '读取并保存';
  try {
    const templateName = String(nameInput?.value || '').trim();
    if (!templateName) throw new Error('请先填写模板名称');
    const sourceProductId = normalizeTemuTemplateSourceId(input?.value);
    if (!['spu', 'skc'].includes(sourceIdType)) throw new Error('请选择 SPU ID 或 SKC ID');
    if (!state.mallId) throw new Error('请先连接商品所属的 TEMU 店铺');
    if (btn) { btn.disabled = true; btn.textContent = '读取中...'; }
    setTemuUploadTemplateStatus('正在通过插件读取当前店铺商品完整详情...');
    const detailResp = await safeRuntimeSendMessage({
      type: 'FETCH_MOVE_PRODUCT_DETAIL',
      sourceIdType,
      ...(sourceIdType === 'skc' ? { goodsId: sourceProductId } : { productId: sourceProductId })
    }, null, 40000);
    let detail = detailResp?.success ? (detailResp.productDetail || detailResp.data || detailResp.result || null) : null;
    let detailWarning = '';
    if (!hasTemuUploadOfficialTemplateDetail(detail)) {
      if (!customizedProductState.cacheLoaded || !Array.isArray(customizedProductState.allProducts) || customizedProductState.allProducts.length === 0) {
        await loadCustomizedProductCache().catch(() => false);
      }
      const cachedProduct = findTemuUploadProductInCustomizedCache(sourceProductId);
      if (cachedProduct) {
        const cachedDetail = buildTemuUploadDetailFromCustomizedProduct(cachedProduct, sourceProductId, sourceIdType);
        if (hasTemuUploadOfficialTemplateDetail(cachedDetail)) {
          detail = cachedDetail;
          detailWarning = '完整商品详情接口未返回，已使用商品管理真实缓存保存。';
        }
      }
    }
    if (!detailResp?.success && !detail) throw new Error(detailResp?.error || '读取商品详情失败');
    if (!hasTemuUploadOfficialTemplateDetail(detail)) {
      throw new Error('TEMU 未返回完整商品编辑详情，模板未保存；请确认当前 Chrome 已登录该 TEMU 店铺，且该 SPU/SKC 属于当前店铺，并先在商品管理同步到包含属性、包装、SKU 的真实数据');
    }
    if (!detail || typeof detail !== 'object') throw new Error('TEMU 商品详情为空');
    const productPropertyList = Array.isArray(detail.productPropertyList)
      ? detail.productPropertyList
      : (Array.isArray(detail.productProperties) ? detail.productProperties : []);
    if (detail.missingProductAttributes) {
      throw new Error('TEMU 商品管理未返回可保存的商品属性，模板未保存；请先在商品管理同步到包含属性的数据后再添加模板');
    }
    detail = normalizeTemuUploadTemplateVisibleDetail(detail);
    const summary = summarizeMoveProductDetail(detail, { productId: sourceProductId });
    const detailed = {
      ...summary,
      sourceProductId,
      sourceProductDetail: detail,
      sourceIdType,
      productPropertyList,
      productProperties: productPropertyList,
      missingProductAttributes: false,
      templateDetailSource: detail.detailSource || detailResp?.resolvedBy || 'temu_product_query',
      lingfengReady: true
    };
    const mallId = await resolveCurrentMallIdForStorage();
    const data = normalizeProductMoveTemplatePayload(detailed, mallId);
    data.templatePurpose = 'temu_product_upload';
    data.templateName = templateName;
    data.sourceIdType = sourceIdType;
    data.productPropertyList = productPropertyList;
    data.productProperties = productPropertyList;
    data.missingProductAttributes = false;
    data.templateDetailSource = detail.detailSource || detailResp?.resolvedBy || 'temu_product_query';
    if (!data.sourceProductDetail || !Array.isArray(data.skus) || !data.skus.length) {
      throw new Error('TEMU 未返回完整商品详情或 SKU，模板未保存');
    }
    if (btn) btn.textContent = '保存中...';
    const result = await saveTemuProductUploadTemplate({
      name: templateName,
      sourceUrl: '',
      sourceProductId,
      sourceIdType,
      data
    });
    if (!result.success) throw new Error(result.error || '保存模板失败');
    if (nameInput) nameInput.value = '';
    if (input) input.value = '';
    closeTemuUploadTemplateAddModal();
    temuUploadTestState.selectedTemplateId = result.id || '';
    await loadTemuUploadTemplates();
    setTemuUploadTemplateStatus(`模板已保存：${templateName}，共 ${data.skus.length} 个 SKU。${detailWarning ? ` ${detailWarning}` : ''}`, 'success');
    showToast('TEMU 商品上架模板已保存', 'success');
  } catch (error) {
    const message = isExtensionContextInvalidatedError(error)
      ? getRuntimeReconnectMessage('商品模板读取')
      : (error.message || String(error));
    setTemuUploadTemplateStatus(message, 'error');
    showToast(`模板保存失败：${message}`, 'error');
  } finally {
    if (btn) { btn.disabled = false; btn.textContent = oldText; }
  }
}

function getTemuUploadTestCell(row, names = []) {
  if (!row || typeof row !== 'object') return '';
  for (const name of names) {
    if (row[name] !== undefined && row[name] !== null) return String(row[name]).trim();
  }
  const normalizedNames = names.map(name => String(name).replace(/\s+/g, '').toLowerCase());
  for (const [key, value] of Object.entries(row)) {
    const normalizedKey = String(key).replace(/\s+/g, '').toLowerCase();
    if (normalizedNames.includes(normalizedKey) && value !== undefined && value !== null) {
      return String(value).trim();
    }
  }
  return '';
}

function isTemuUploadImageFile(file) {
  if (!file) return false;
  if (String(file.type || '').startsWith('image/')) return true;
  return /\.(png|jpe?g|webp|gif|bmp|avif)$/i.test(file.name || '');
}

function getTemuUploadImageParentFolder(file) {
  const relativePath = String(file?.webkitRelativePath || file?.relativePath || file?.name || '');
  const parts = relativePath.split(/[\\/]+/).filter(Boolean);
  if (parts.length >= 2) return parts[parts.length - 2];
  return '';
}

function getTemuUploadImageSortValue(file) {
  const name = String(file?.name || '');
  const base = name.replace(/\.[^.]+$/, '');
  const numeric = Number(base.match(/\d+/)?.[0] || NaN);
  return Number.isFinite(numeric) ? numeric : Number.MAX_SAFE_INTEGER;
}

function groupTemuUploadImages(files = []) {
  const groups = new Map();
  Array.from(files).filter(isTemuUploadImageFile).forEach(file => {
    const folder = getTemuUploadImageParentFolder(file);
    if (!folder) return;
    if (!groups.has(folder)) groups.set(folder, []);
    groups.get(folder).push({ file, name: file.name, url: '' });
  });
  groups.forEach(images => {
    images.sort((a, b) => {
      const na = getTemuUploadImageSortValue(a.file);
      const nb = getTemuUploadImageSortValue(b.file);
      if (na !== nb) return na - nb;
      return String(a.name || '').localeCompare(String(b.name || ''), 'zh-Hans-CN', { numeric: true });
    });
  });
  return groups;
}

function cloneTemuUploadImageRows(images = []) {
  return (Array.isArray(images) ? images : [])
    .map(image => ({ file: image.file, name: image.name, url: '' }))
    .filter(image => image.file);
}

function matchTemuUploadRowImages(code) {
  const exactCode = String(code || '').trim();
  if (!exactCode || !(temuUploadTestState.imageGroups instanceof Map)) return [];
  return cloneTemuUploadImageRows(temuUploadTestState.imageGroups.get(exactCode) || []);
}

function getTemuUploadImageUrl(image) {
  if (!image?.file) return '';
  if (!image.url) image.url = URL.createObjectURL(image.file);
  return image.url;
}

function revokeTemuUploadImages(images = []) {
  images.forEach(image => {
    if (image?.url) URL.revokeObjectURL(image.url);
    if (image) image.url = '';
  });
}

function updateTemuUploadTestSummary() {
  const el = document.getElementById('temuUploadTestSummary');
  if (!el) return;
  const rowCount = temuUploadTestState.rows.length;
  const selected = temuUploadTestState.selected.size;
  const imageCount = temuUploadTestState.imageFiles.filter(isTemuUploadImageFile).length;
  const folderCount = temuUploadTestState.imageGroups.size;
  if (!rowCount) {
    el.textContent = `Excel：${temuUploadTestState.excelFile?.name || '未选择'}；图片：${imageCount ? `${imageCount} 张 / ${folderCount} 个编号文件夹` : '未选择'}`;
    return;
  }
  const okCount = temuUploadTestState.rows.filter(row => row.issues.length === 0).length;
  el.textContent = `已预检 ${rowCount} 行，已勾选 ${selected} 行，通过 ${okCount} 行，图片 ${imageCount} 张 / ${folderCount} 个编号文件夹。`;
}

function setTemuUploadFileLabel(id, text) {
  const el = document.getElementById(id);
  if (el) el.textContent = text;
}

function buildTemuUploadTestIssues(row) {
  const issues = [];
  if (!row.templateId || !row.templateSkuCount) issues.push('未选择有效商品模板');
  if (!row.code) issues.push('缺少编号');
  if (!row.title) issues.push('缺少上传标题');
  if (!Array.isArray(row.images) || row.images.length === 0) issues.push('未匹配到图片文件夹，请检查 Excel 编号和图片文件夹名是否完全一致');
  if (Array.isArray(row.images) && row.images.length > 0 && Array.isArray(row.skuImagePlan)
    && row.skuImagePlan.some(plan => Number(plan.carouselImageIndex) < 0)) {
    issues.push('SKU 图规则超出轮播图数量');
  }
  return issues;
}

function normalizeTemuUploadTestRow(rawRow, index) {
  const code = getTemuUploadTestCell(rawRow, ['编号', '数据编号', '文件夹名称', 'folder', 'code', 'id']);
  const images = matchTemuUploadRowImages(code);
  const row = {
    rowNo: index + 1,
    code,
    title: getTemuUploadTestCell(rawRow, ['上传标题', '标题', '商品标题', 'title']),
    price: getTemuUploadTestCell(rawRow, ['价格', '申报价', 'price']),
    currency: getTemuUploadTestCell(rawRow, ['币种', 'currency', '货币']),
    spuCode: getTemuUploadTestCell(rawRow, ['货号', '商品货号', 'SPU货号', 'spuCode']),
    sku: getTemuUploadTestCell(rawRow, ['SKU', 'sku', 'SKU货号']),
    stock: getTemuUploadTestCell(rawRow, ['库存', 'stock']),
    remark: getTemuUploadTestCell(rawRow, ['备注', 'remark']),
    images,
    templateId: '',
    templateName: '',
    templateSkuCount: 0,
    skuImagePlan: [],
    uploadStatus: '未上传',
    issues: []
  };
  applyTemuUploadTemplateToRow(row);
  row.issues = buildTemuUploadTestIssues(row);
  return row;
}

function updateTemuUploadTestRow(index, field, value) {
  const row = temuUploadTestState.rows[index];
  if (!row || !Object.prototype.hasOwnProperty.call(row, field)) return;
  row[field] = String(value ?? '').trim();
  row.issues = buildTemuUploadTestIssues(row);
  row.uploadStatus = row.uploadStatus === '未上传' ? row.uploadStatus : '已修改，未上传';
  updateTemuUploadTestSummary();
  renderTemuUploadTestRows();
}

function renderTemuUploadTestRows() {
  const tbody = document.getElementById('temuUploadTestTableBody');
  if (!tbody) return;
  const rows = temuUploadTestState.rows;
  if (!rows.length) {
    tbody.innerHTML = '<tr><td colspan="9" class="empty">请先在商品模板中点击“批量生成”，选择标题 Excel 和图片总文件夹后生成预检结果。</td></tr>';
    updateTemuUploadTestSummary();
    return;
  }
  tbody.innerHTML = rows.map((row, index) => {
    const firstImage = row.images?.[0];
    const imageUrl = firstImage ? getTemuUploadImageUrl(firstImage) : '';
    const selected = temuUploadTestState.selected.has(index);
    const statusClass = row.issues.length ? 'error' : 'ok';
    const statusText = row.issues.length ? row.issues.join('；') : '预检通过';
    const uploadClass = /^待接入|^未上传/.test(row.uploadStatus)
      ? ''
      : (/失败|错误|未进入/.test(row.uploadStatus) ? 'error' : 'ok');
    return `
      <tr data-temu-upload-row="${index}">
        <td style="text-align:center;"><input type="checkbox" class="temu-upload-test-check" data-index="${index}" ${selected ? 'checked' : ''}></td>
        <td style="text-align:center;">${escapeHtml(String(row.rowNo))}</td>
        <td><button class="btn btn-danger btn-sm temu-upload-test-remove-row" data-index="${index}" type="button">删除</button></td>
        <td>${escapeHtml(row.code || '-')}</td>
        <td>
          <div class="temu-upload-test-thumb-row">
            ${imageUrl ? `<img class="temu-upload-test-thumb temu-upload-test-open-preview" data-index="${index}" data-image-index="0" src="${imageUrl}" alt="预览图">` : '<span class="temu-upload-test-status error">无图片</span>'}
            <button class="btn btn-default btn-sm temu-upload-test-open-preview" data-index="${index}" data-image-index="0" type="button" ${imageUrl ? '' : 'disabled'}>${row.images.length || 0} 张</button>
            <button class="btn btn-default btn-sm temu-upload-test-add-image" data-index="${index}" type="button">增加</button>
          </div>
        </td>
        <td><strong>${escapeHtml(row.templateName || '未选择')}</strong></td>
        <td><textarea class="temu-upload-test-field" data-index="${index}" data-field="title">${escapeHtml(row.title)}</textarea></td>
        <td><span class="temu-upload-test-status ${statusClass}">${escapeHtml(statusText)}</span></td>
        <td><span class="temu-upload-test-status ${uploadClass}">${escapeHtml(row.uploadStatus || '未上传')}</span></td>
      </tr>
    `;
  }).join('');
  updateTemuUploadTestSummary();
}

async function readTemuUploadTestExcel(file) {
  const XLSX = await ensureXlsxLoaded();
  const data = await file.arrayBuffer();
  const wb = XLSX.read(data, { type: 'array' });
  const sheetName = wb.SheetNames?.[0];
  if (!sheetName) throw new Error('Excel 没有工作表');
  const ws = wb.Sheets[sheetName];
  return XLSX.utils.sheet_to_json(ws, { defval: '' });
}

async function precheckTemuUploadTest() {
  if (!getSelectedTemuUploadTemplate()) {
    showToast('请先选择有效的商品上架模板', 'error');
    return;
  }
  if (!temuUploadTestState.excelFile) {
    showToast('请先选择 Excel 数据表', 'error');
    return;
  }
  if (!temuUploadTestState.imageFiles.length) {
    showToast('请先选择图片总文件夹', 'error');
    return;
  }
  const btn = document.getElementById('temuUploadTestPrecheckBtn');
  const oldText = btn?.textContent || '';
  if (btn) {
    btn.disabled = true;
    btn.textContent = '预检中...';
  }
  try {
    const rawRows = await readTemuUploadTestExcel(temuUploadTestState.excelFile);
    if (!rawRows.length) throw new Error('Excel 表格没有数据行');
    temuUploadTestState.rows.forEach(row => revokeTemuUploadImages(row.images));
    temuUploadTestState.rows = rawRows.map(normalizeTemuUploadTestRow);
    temuUploadTestState.selected = new Set(temuUploadTestState.rows.map((_, index) => index));
    renderTemuUploadTestRows();
    const failCount = temuUploadTestState.rows.filter(row => row.issues.length).length;
    showToast(`预检完成：${temuUploadTestState.rows.length} 行，问题 ${failCount} 行`, failCount ? 'warning' : 'success');
  } catch (error) {
    console.error('[小青蛙电商助手] TEMU 批量上架测试预检失败:', error);
    showToast(`预检失败：${error?.message || String(error)}`, 'error');
  } finally {
    if (btn) {
      btn.disabled = false;
      btn.textContent = oldText || '预检数据';
    }
  }
}

function downloadTemuUploadTestTemplate() {
  try {
    const { blob } = createTemuXlsxBlob({
      sheetName: 'TEMU批量上架测试',
      fields: TEMU_UPLOAD_TEST_TEMPLATE_FIELDS,
      rows: []
    });
    triggerBrowserDownload('TEMU批量上架测试模板.xlsx', blob);
    showToast('Excel 模板已下载', 'success');
  } catch (error) {
    console.error('[小青蛙电商助手] TEMU 上架测试模板下载失败:', error);
    showToast(`下载失败：${error?.message || String(error)}`, 'error');
  }
}

function closeTemuUploadTemplateGenerator() {
  document.getElementById('temuUploadTemplateGenerateModal')?.classList.remove('is-open');
}

function renderTemuUploadTemplateGenerator() {
  const modal = document.getElementById('temuUploadTemplateGenerateModal');
  if (!modal) return;
  const template = getSelectedTemuUploadTemplate();
  const skuCount = Array.isArray(template?.data?.skus) ? template.data.skus.length : 0;
  const title = modal.querySelector('#temuUploadGenerateTitle');
  const meta = modal.querySelector('#temuUploadGenerateMeta');
  const excel = modal.querySelector('#temuUploadGenerateExcel');
  const images = modal.querySelector('#temuUploadGenerateImages');
  const skuBody = modal.querySelector('#temuUploadGenerateSkuBody');
  if (title) title.textContent = template?.name || '批量生成';
  if (meta) meta.textContent = template
    ? `源商品 ${template.sourceProductId || '-'} · 保留 ${skuCount} 个 SKU 的属性、尺寸重量与申报价`
    : '未选择模板';
  if (excel) excel.textContent = temuUploadTestState.excelFile?.name || '未选择标题 Excel';
  if (images) images.textContent = temuUploadTestState.imageFiles.length
    ? `${temuUploadTestState.imageFiles.length} 张图片 / ${temuUploadTestState.imageGroups.size} 个编号文件夹`
    : '未选择图片文件夹';
  if (skuBody) {
    const skus = getTemuUploadTemplateSkus(template);
    const skuHead = modal.querySelector('#temuUploadGenerateSkuNameHead');
    if (skuHead) skuHead.textContent = getTemuUploadGeneratorSkuHeader(template, skus);
    const maxImageCount = getTemuUploadMaxCarouselImageCount();
    skuBody.innerHTML = skus.map((sku, index) => {
      const displayIndex = getTemuUploadSkuImageDisplayIndex(template, sku, index, maxImageCount);
      const declarePrice = formatTemuUploadEditPrice(sku.supplierPrice ?? sku.price);
      const skuName = getTemuUploadGeneratorSkuName(template, sku, index);
      return `
        <tr>
          <td class="temu-upload-generate-sku-name-cell" title="${escapeHtml(skuName)}">${escapeHtml(skuName)}</td>
          <td class="temu-upload-generate-sku-index-col">
            <input class="temu-upload-generate-sku-image-input temu-upload-generate-sku-index-select" data-sku-index="${index}" type="number" min="1" step="1" inputmode="numeric" value="${escapeHtml(String(displayIndex))}">
          </td>
          <td class="temu-upload-generate-sku-price-col">${escapeHtml(declarePrice || '未返回')}</td>
        </tr>`;
    }).join('') || '<tr><td colspan="3" class="empty">模板未返回 SKU。</td></tr>';
  }
}

function ensureTemuUploadTemplateGenerator() {
  let modal = document.getElementById('temuUploadTemplateGenerateModal');
  if (modal) return modal;
  modal = document.createElement('div');
  modal.id = 'temuUploadTemplateGenerateModal';
  modal.className = 'temu-upload-test-preview-modal';
  modal.innerHTML = `
    <div class="temu-upload-test-preview-panel" style="width:min(720px,96vw);">
      <div class="temu-upload-test-preview-head">
        <div><strong id="temuUploadGenerateTitle">批量生成</strong><div class="temu-upload-test-note" id="temuUploadGenerateMeta"></div></div>
        <button class="btn btn-default btn-sm" id="temuUploadGenerateCloseBtn" type="button">关闭</button>
      </div>
      <div class="temu-upload-test-file-grid">
        <div class="temu-upload-test-file"><div><strong>标题 Excel</strong><span id="temuUploadGenerateExcel">未选择标题 Excel</span></div><button class="btn btn-default btn-sm" id="temuUploadGenerateExcelBtn" type="button">选择 Excel</button></div>
        <div class="temu-upload-test-file"><div><strong>图片总文件夹</strong><span id="temuUploadGenerateImages">未选择图片文件夹</span></div><button class="btn btn-default btn-sm" id="temuUploadGenerateImagesBtn" type="button">选择文件夹</button></div>
      </div>
      <p class="temu-upload-test-note">每个编号生成一个新商品；标题来自 Excel，轮播图来自同名图片文件夹。SKU 图索引请手动填写数字：1 表示第 1 张轮播图，2 表示第 2 张轮播图。</p>
      <div class="temu-upload-test-table-wrap temu-upload-generate-sku-wrap">
        <table class="temu-upload-test-table temu-upload-generate-sku-table">
          <thead><tr><th id="temuUploadGenerateSkuNameHead" class="temu-upload-generate-sku-name-col">SKU 名称</th><th class="temu-upload-generate-sku-index-col">SKU 图索引</th><th class="temu-upload-generate-sku-price-col">申报价格(CNY)</th></tr></thead>
          <tbody id="temuUploadGenerateSkuBody"></tbody>
        </table>
      </div>
      <div class="temu-upload-test-actions" style="justify-content:flex-end;">
        <button class="btn btn-primary btn-sm" id="temuUploadGeneratePrecheckBtn" type="button">预检生成数据</button>
      </div>
    </div>`;
  modal.addEventListener('click', event => {
    if (event.target === modal || event.target?.closest?.('#temuUploadGenerateCloseBtn')) closeTemuUploadTemplateGenerator();
  });
  modal.querySelector('#temuUploadGenerateExcelBtn')?.addEventListener('click', () => document.getElementById('temuUploadTestExcelInput')?.click());
  modal.querySelector('#temuUploadGenerateImagesBtn')?.addEventListener('click', () => document.getElementById('temuUploadTestImageInput')?.click());
  const handleSkuImageIndexChange = event => {
    const input = event.target?.closest?.('.temu-upload-generate-sku-image-input');
    if (!input) return;
    const template = getSelectedTemuUploadTemplate();
    const skus = getTemuUploadTemplateSkus(template);
    const skuIndex = Number(input.dataset.skuIndex);
    if (!template || !skus[skuIndex]) return;
    setTemuUploadSkuImageDisplayIndex(template, skus[skuIndex], skuIndex, input.value);
    temuUploadTestState.rows.forEach(row => {
      applyTemuUploadTemplateToRow(row, template);
      row.issues = buildTemuUploadTestIssues(row);
      row.uploadStatus = 'SKU图规则已更新，未上传';
    });
    renderTemuUploadTestRows();
    renderTemuUploadTemplateGenerator();
  };
  modal.addEventListener('change', handleSkuImageIndexChange);
  modal.querySelector('#temuUploadGeneratePrecheckBtn')?.addEventListener('click', async () => {
    await precheckTemuUploadTest();
    renderTemuUploadTemplateGenerator();
  });
  document.body.appendChild(modal);
  return modal;
}

function openTemuUploadTemplateGenerator(templateId) {
  temuUploadTestState.selectedTemplateId = String(templateId || '');
  temuUploadTestState.rows.forEach(row => {
    applyTemuUploadTemplateToRow(row);
    row.issues = buildTemuUploadTestIssues(row);
    row.uploadStatus = '已更换模板，未上传';
  });
  renderTemuUploadTemplates();
  renderTemuUploadTestRows();
  const modal = ensureTemuUploadTemplateGenerator();
  renderTemuUploadTemplateGenerator();
  modal.classList.add('is-open');
}

function openTemuUploadTestPreview(rowIndex, imageIndex = 0) {
  const row = temuUploadTestState.rows[rowIndex];
  if (!row?.images?.length) return;
  temuUploadTestState.preview = {
    rowIndex,
    imageIndex: Math.max(0, Math.min(imageIndex, row.images.length - 1))
  };
  renderTemuUploadTestPreview();
}

function closeTemuUploadTestPreview() {
  const modal = document.getElementById('temuUploadTestPreviewModal');
  if (modal) modal.classList.remove('is-open');
}

function ensureTemuUploadTestPreviewModal() {
  let modal = document.getElementById('temuUploadTestPreviewModal');
  if (modal) return modal;
  modal = document.createElement('div');
  modal.id = 'temuUploadTestPreviewModal';
  modal.className = 'temu-upload-test-preview-modal';
  modal.innerHTML = `
    <div class="temu-upload-test-preview-panel" role="dialog" aria-modal="true">
      <div class="temu-upload-test-preview-head">
        <div>
          <strong id="temuUploadTestPreviewTitle">图片预览</strong>
          <div class="temu-upload-test-note" id="temuUploadTestPreviewMeta"></div>
        </div>
        <div class="temu-upload-test-actions">
          <button class="btn btn-danger btn-sm" id="temuUploadTestPreviewDeleteBtn" type="button">删除当前图片</button>
          <button class="btn btn-default btn-sm" id="temuUploadTestPreviewCloseBtn" type="button">关闭</button>
        </div>
      </div>
      <div class="temu-upload-test-preview-stage">
        <button class="temu-upload-test-preview-arrow prev" id="temuUploadTestPreviewPrevBtn" type="button">‹</button>
        <img id="temuUploadTestPreviewImage" alt="图片预览">
        <button class="temu-upload-test-preview-arrow next" id="temuUploadTestPreviewNextBtn" type="button">›</button>
      </div>
    </div>
  `;
  modal.addEventListener('click', event => {
    if (event.target === modal || event.target?.closest?.('#temuUploadTestPreviewCloseBtn')) {
      closeTemuUploadTestPreview();
    }
  });
  modal.querySelector('#temuUploadTestPreviewPrevBtn')?.addEventListener('click', () => switchTemuUploadTestPreview(-1));
  modal.querySelector('#temuUploadTestPreviewNextBtn')?.addEventListener('click', () => switchTemuUploadTestPreview(1));
  modal.querySelector('#temuUploadTestPreviewDeleteBtn')?.addEventListener('click', deleteCurrentTemuUploadTestPreviewImage);
  document.body.appendChild(modal);
  return modal;
}

function renderTemuUploadTestPreview() {
  const modal = ensureTemuUploadTestPreviewModal();
  const { rowIndex, imageIndex } = temuUploadTestState.preview;
  const row = temuUploadTestState.rows[rowIndex];
  if (!row?.images?.length) {
    closeTemuUploadTestPreview();
    renderTemuUploadTestRows();
    return;
  }
  const safeIndex = Math.max(0, Math.min(imageIndex, row.images.length - 1));
  temuUploadTestState.preview.imageIndex = safeIndex;
  const image = row.images[safeIndex];
  const imgEl = modal.querySelector('#temuUploadTestPreviewImage');
  const titleEl = modal.querySelector('#temuUploadTestPreviewTitle');
  const metaEl = modal.querySelector('#temuUploadTestPreviewMeta');
  if (imgEl) imgEl.src = getTemuUploadImageUrl(image);
  if (titleEl) titleEl.textContent = `编号 ${row.code || row.rowNo}`;
  if (metaEl) metaEl.textContent = `第 ${safeIndex + 1} 张 / 共 ${row.images.length} 张：${image.name || ''}`;
  modal.classList.add('is-open');
}

function switchTemuUploadTestPreview(delta) {
  const row = temuUploadTestState.rows[temuUploadTestState.preview.rowIndex];
  if (!row?.images?.length) return;
  const total = row.images.length;
  temuUploadTestState.preview.imageIndex = (temuUploadTestState.preview.imageIndex + delta + total) % total;
  renderTemuUploadTestPreview();
}

function deleteCurrentTemuUploadTestPreviewImage() {
  const row = temuUploadTestState.rows[temuUploadTestState.preview.rowIndex];
  if (!row?.images?.length) return;
  const [removed] = row.images.splice(temuUploadTestState.preview.imageIndex, 1);
  revokeTemuUploadImages([removed]);
  applyTemuUploadTemplateToRow(row);
  row.issues = buildTemuUploadTestIssues(row);
  if (temuUploadTestState.preview.imageIndex >= row.images.length) {
    temuUploadTestState.preview.imageIndex = Math.max(0, row.images.length - 1);
  }
  renderTemuUploadTestRows();
  if (row.images.length) renderTemuUploadTestPreview();
  else closeTemuUploadTestPreview();
  showToast('当前图片已删除', 'info');
}

function clearTemuUploadTest() {
  temuUploadTestState.rows.forEach(row => revokeTemuUploadImages(row.images));
  temuUploadTestState.excelFile = null;
  temuUploadTestState.imageFiles = [];
  temuUploadTestState.imageGroups = new Map();
  temuUploadTestState.rows = [];
  temuUploadTestState.selected.clear();
  closeTemuUploadTestPreview();
  setTemuUploadFileLabel('temuUploadTestExcelName', '未选择 .xlsx / .xls / .csv');
  setTemuUploadFileLabel('temuUploadTestImageName', '未选择图片文件夹');
  renderTemuUploadTestRows();
  showToast('TEMU 批量上架测试数据已清空', 'info');
}

function cloneTemuUploadJsonSafe(value) {
  if (value === undefined || value === null) return value;
  try {
    return JSON.parse(JSON.stringify(value));
  } catch (_) {
    return null;
  }
}

function pickTemuUploadFirstDefined(...values) {
  for (const value of values) {
    if (value !== undefined && value !== null && value !== '') return value;
  }
  return '';
}

function getTemuUploadSourceSkuPriceValue(sourceSku = {}, summarySku = {}) {
  const sitePrice = Array.isArray(sourceSku.siteSupplierPrices) && sourceSku.siteSupplierPrices.length
    ? sourceSku.siteSupplierPrices[0]?.supplierPrice
    : '';
  return pickTemuUploadFirstDefined(
    sitePrice,
    sourceSku.supplierPrice,
    sourceSku.price,
    summarySku.supplierPrice,
    summarySku.price
  );
}

function readTemuUploadFileAsDataUrl(file) {
  return new Promise((resolve, reject) => {
    if (!file) {
      reject(new Error('图片文件无效'));
      return;
    }
    const reader = new FileReader();
    reader.onload = event => resolve(String(event.target?.result || ''));
    reader.onerror = () => reject(reader.error || new Error('读取图片失败'));
    reader.readAsDataURL(file);
  });
}

function getTemuUploadSubmitTimeoutMs(row = {}) {
  const imageCount = Array.isArray(row.images) ? row.images.length : 0;
  return Math.min(600000, Math.max(180000, 90000 + imageCount * 45000));
}

async function buildTemuUploadSubmitPayload(row = {}, template = getSelectedTemuUploadTemplate()) {
  const detail = getTemuUploadSourceDetail(template);
  if (!template) throw new Error('未选择商品模板');
  if (!hasTemuUploadOfficialTemplateDetail(detail)) {
    throw new Error('模板缺少 TEMU 官方完整商品详情，不能复制到草稿箱');
  }
  if (!String(row.title || '').trim()) throw new Error('缺少上传标题');
  const imageList = Array.isArray(row.images) ? row.images : [];
  if (!imageList.length) throw new Error('缺少轮播图图片');
  const skuImagePlan = Array.isArray(row.skuImagePlan) && row.skuImagePlan.length
    ? row.skuImagePlan
    : buildTemuUploadSkuImagePlan(row, template);
  if (skuImagePlan.some(plan => Number(plan.carouselImageIndex) < 0)) {
    throw new Error('SKU 图规则超出轮播图数量');
  }
  const images = [];
  for (let index = 0; index < imageList.length; index += 1) {
    const image = imageList[index] || {};
    const file = image.file;
    if (!file) throw new Error(`第 ${index + 1} 张图片缺少本地文件`);
    images.push({
      filename: file.name || image.name || `image-${index + 1}.jpg`,
      imageData: await readTemuUploadFileAsDataUrl(file)
    });
  }
  return {
    sourceProductDetail: cloneTemuUploadJsonSafe(detail),
    row: {
      rowNo: row.rowNo || '',
      code: row.code || '',
      title: String(row.title || '').trim(),
      spuCode: String(row.spuCode || '').trim(),
      remark: String(row.remark || '').trim()
    },
    images,
    skuImagePlan: cloneTemuUploadJsonSafe(skuImagePlan) || []
  };
}

async function createTemuUploadDraftForRow(row = {}, template = getSelectedTemuUploadTemplate()) {
  const payload = await buildTemuUploadSubmitPayload(row, template);
  const result = await safeRuntimeSendMessage({
    action: 'CREATE_TEMU_UPLOAD_PRODUCT_DRAFT',
    payload
  }, { success: false, error: '后台上传失败' }, getTemuUploadSubmitTimeoutMs(row));
  if (!result?.success) {
    const draftText = result?.productDraftId ? `（已创建草稿 ${result.productDraftId}，请到草稿箱检查）` : '';
    throw new Error(`${result?.error || 'TEMU 未返回成功结果'}${draftText}`);
  }
  return result;
}

function getTemuUploadOfficialKeepFields(detail = {}) {
  const keepKeys = [
    'catId',
    'categoryId',
    'categoryName',
    'categoryPath',
    'productPropertyList',
    'productPropertyReqs',
    'productSpecPropertyVOS',
    'productSaleExtAttr',
    'productNonAuditExtAttr',
    'productWhExtAttr',
    'productOuterPackage',
    'outerPackageImages',
    'goodsLayerDecorationReqs',
    'productI18nReqs',
    'productSemiManagedReq',
    'productSkcList',
    'productSkuList'
  ];
  const result = {};
  keepKeys.forEach(key => {
    if (detail && Object.prototype.hasOwnProperty.call(detail, key)) {
      result[key] = cloneTemuUploadJsonSafe(detail[key]);
    }
  });
  return result;
}

function buildTemuUploadDraftPreviewPayload(row = {}, template = getSelectedTemuUploadTemplate()) {
  const detail = getTemuUploadSourceDetail(template) || {};
  const summarySkus = getTemuUploadTemplateSkus(template);
  const imageList = Array.isArray(row.images) ? row.images : [];
  const skuImagePlan = Array.isArray(row.skuImagePlan) ? row.skuImagePlan : buildTemuUploadSkuImagePlan(row, template);
  const templateSource = template?.data && typeof template.data === 'object' ? template.data : {};
  const officialKeepFields = getTemuUploadOfficialKeepFields(detail);
  const missing = [];

  if (!template) missing.push('未选择商品模板');
  if (!hasTemuUploadOfficialTemplateDetail(detail)) missing.push('模板缺少 TEMU 官方完整商品编辑详情');
  if (!row.title) missing.push('缺少 Excel 上传标题');
  if (!imageList.length) missing.push('缺少轮播图本地图片');
  if (skuImagePlan.some(plan => Number(plan.carouselImageIndex) < 0)) missing.push('SKU 图索引未匹配到轮播图');

  return {
    mode: 'preview_only_not_submitted',
    submitStatus: '未提交 TEMU；仅用于核对待复制到草稿箱的数据',
    unresolvedBeforeRealSubmit: [
      '本地轮播图必须先上传到 TEMU 图片空间并取得真实 URL',
      'TEMU 后台草稿接口请求体仍需用官方后台/真实网络请求确认',
      '当前不自动上架；仅研究“复制到草稿箱”链路'
    ],
    template: {
      id: template?.id || '',
      name: template?.name || '',
      sourceProductId: template?.sourceProductId || templateSource.sourceId || '',
      sourceIdType: String(templateSource.sourceIdType || 'spu').toUpperCase(),
      category: getTemuUploadTemplateCategoryText(template),
      savedAt: getTemuUploadTemplateTimeText(template)
    },
    row: {
      rowNo: row.rowNo || '',
      code: row.code || '',
      titleFromExcel: row.title || '',
      spuCodeFromExcel: row.spuCode || '',
      remark: row.remark || ''
    },
    replacements: {
      productName: {
        fromTemplate: detail.productName || templateSource.productName || '',
        toExcelTitle: row.title || ''
      },
      carouselImages: imageList.map((image, index) => ({
        index: index + 1,
        fileName: image?.file?.name || image?.name || '',
        size: image?.file?.size || '',
        temuUrl: '',
        status: '待上传到 TEMU 图片空间'
      })),
      skuImages: summarySkus.map((summarySku, index) => {
        const sourceSku = getTemuUploadSourceSkuForSummary(template, summarySku, index) || {};
        const sourceSkc = sourceSku.sourceSkc || {};
        const plan = skuImagePlan[index] || {};
        const carouselIndex = Number(plan.carouselImageIndex);
        const selectedImage = Number.isInteger(carouselIndex) && carouselIndex >= 0 ? imageList[carouselIndex] : null;
        const priceValue = getTemuUploadSourceSkuPriceValue(sourceSku, summarySku);
        return {
          skuNo: index + 1,
          skuName: getTemuUploadGeneratorSkuName(template, summarySku, index),
          productSkcId: sourceSkc.productSkcId || summarySku.productSkcId || '',
          productSkuId: sourceSku.productSkuId || summarySku.productSkuId || summarySku.id || '',
          originalSkcExtCode: sourceSkc.extCode || '',
          originalSkuExtCode: sourceSku.extCode || summarySku.extCode || '',
          selectedCarouselImageIndex: selectedImage ? carouselIndex + 1 : '',
          selectedImageFileName: selectedImage?.file?.name || selectedImage?.name || '',
          skuImageTemuUrl: '',
          skuImageStatus: selectedImage ? '待上传到 TEMU 图片空间后引用' : '未匹配到图片',
          declarePriceCny: formatTemuUploadEditPrice(priceValue),
          declarePriceSource: '模板原始申报价'
        };
      })
    },
    officialTemplateFieldsKept: officialKeepFields,
    diagnostics: {
      canBuildPreview: missing.length === 0,
      missing,
      rowIssues: Array.isArray(row.issues) ? row.issues : []
    }
  };
}

function ensureTemuUploadDraftPreviewModal() {
  let modal = document.getElementById('temuUploadDraftPreviewModal');
  if (modal) return modal;
  modal = document.createElement('div');
  modal.id = 'temuUploadDraftPreviewModal';
  modal.className = 'temu-upload-test-preview-modal';
  modal.innerHTML = `
    <div class="temu-upload-test-preview-panel" style="width:min(960px,96vw);max-height:92vh;overflow:auto;">
      <div class="temu-upload-test-preview-head">
        <div>
          <strong>复制到草稿箱 - 草稿数据预览</strong>
          <div class="temu-upload-test-note">当前不会提交 TEMU，只展示准备提交前的数据与缺失项。</div>
        </div>
        <div class="temu-upload-test-actions">
          <button class="btn btn-default btn-sm" id="temuUploadDraftPreviewCopyBtn" type="button">复制 JSON</button>
          <button class="btn btn-default btn-sm" id="temuUploadDraftPreviewCloseBtn" type="button">关闭</button>
        </div>
      </div>
      <div id="temuUploadDraftPreviewSummary" class="temu-upload-test-note" style="margin:10px 0;"></div>
      <div id="temuUploadDraftPreviewIssues"></div>
      <div class="temu-upload-test-table-wrap" style="max-height:240px;margin:10px 0;">
        <table class="temu-upload-test-table">
          <thead>
            <tr><th>编号</th><th>模板</th><th>标题</th><th>轮播图</th><th>SKU</th><th>状态</th></tr>
          </thead>
          <tbody id="temuUploadDraftPreviewRows"></tbody>
        </table>
      </div>
      <pre id="temuUploadDraftPreviewJson" style="white-space:pre-wrap;word-break:break-word;background:#0f172a;color:#e2e8f0;border-radius:10px;padding:12px;max-height:360px;overflow:auto;font-size:12px;line-height:1.5;"></pre>
      <div class="temu-upload-test-note" style="margin-top:10px;">
        真实复制前还需要接入：图片上传到 TEMU 图片空间、草稿接口请求体确认、逐行提交队列与真实返回结果。
      </div>
    </div>`;
  modal.addEventListener('click', event => {
    if (event.target === modal || event.target?.closest?.('#temuUploadDraftPreviewCloseBtn')) {
      modal.classList.remove('is-open');
    }
  });
  modal.querySelector('#temuUploadDraftPreviewCopyBtn')?.addEventListener('click', async () => {
    const text = modal.querySelector('#temuUploadDraftPreviewJson')?.textContent || '';
    try {
      await navigator.clipboard.writeText(text);
      showToast('草稿预览 JSON 已复制', 'success');
    } catch (error) {
      console.warn('[小青蛙电商助手] 复制 TEMU 草稿预览失败:', error);
      showToast('复制失败，请手动选中 JSON', 'warning');
    }
  });
  document.body.appendChild(modal);
  return modal;
}

function openTemuUploadDraftPreview(rows = []) {
  const modal = ensureTemuUploadDraftPreviewModal();
  const template = getSelectedTemuUploadTemplate();
  const payloads = rows.map(row => buildTemuUploadDraftPreviewPayload(row, template));
  const invalidCount = payloads.filter(item => !item.diagnostics.canBuildPreview).length;
  const summary = modal.querySelector('#temuUploadDraftPreviewSummary');
  const issues = modal.querySelector('#temuUploadDraftPreviewIssues');
  const tbody = modal.querySelector('#temuUploadDraftPreviewRows');
  const pre = modal.querySelector('#temuUploadDraftPreviewJson');
  if (summary) {
    summary.textContent = `已生成 ${payloads.length} 条草稿数据预览；异常 ${invalidCount} 条；状态：未提交 TEMU。`;
  }
  if (issues) {
    const allIssues = Array.from(new Set(payloads.flatMap(item => item.diagnostics.missing || [])));
    issues.innerHTML = allIssues.length
      ? `<div class="temu-upload-test-status error" style="display:block;margin:8px 0;">待处理：${escapeHtml(allIssues.join('；'))}</div>`
      : '<div class="temu-upload-test-status pending" style="display:block;margin:8px 0;">预览可构建，但真实草稿接口仍未接入。</div>';
  }
  if (tbody) {
    tbody.innerHTML = payloads.map(item => `
      <tr>
        <td>${escapeHtml(item.row.code || item.row.rowNo || '-')}</td>
        <td>${escapeHtml(item.template.name || '-')}</td>
        <td>${escapeHtml(item.row.titleFromExcel || '-')}</td>
        <td>${item.replacements.carouselImages.length} 张</td>
        <td>${item.replacements.skuImages.length} 个</td>
        <td><span class="temu-upload-test-status ${item.diagnostics.canBuildPreview ? 'pending' : 'error'}">${item.diagnostics.canBuildPreview ? '预览已生成' : '缺字段'}</span></td>
      </tr>
    `).join('');
  }
  if (pre) {
    pre.textContent = JSON.stringify({
      generatedAt: new Date().toISOString(),
      note: 'preview_only_not_submitted',
      rows: payloads.slice(0, 3),
      omittedRows: Math.max(0, payloads.length - 3)
    }, null, 2);
  }
  modal.classList.add('is-open');
}

async function handleTemuUploadTestSubmit() {
  if (!temuUploadTestState.rows.length) {
    showToast('请先预检数据', 'warning');
    return;
  }
  const selectedEntries = temuUploadTestState.rows
    .map((row, index) => ({ row, index }))
    .filter(item => temuUploadTestState.selected.has(item.index));
  if (!selectedEntries.length) {
    showToast('请先勾选要处理的行', 'warning');
    return;
  }
  const template = getSelectedTemuUploadTemplate();
  if (!template) {
    showToast('请先选择商品模板', 'error');
    return;
  }
  const readyEntries = selectedEntries.filter(({ row }) => !row.issues.length);
  selectedEntries.forEach(({ row }) => {
    row.uploadStatus = row.issues.length
      ? '未进入复制：预检有问题'
      : '等待复制到草稿箱';
  });
  renderTemuUploadTestRows();
  if (!readyEntries.length) {
    showToast('选中行都有预检问题，不能复制到草稿箱', 'warning');
    return;
  }

  const submitBtn = document.getElementById('temuUploadTestSubmitBtn');
  const precheckBtn = document.getElementById('temuUploadTestPrecheckBtn');
  const oldSubmitText = submitBtn?.textContent || '确认上传';
  if (submitBtn) {
    submitBtn.disabled = true;
    submitBtn.textContent = '复制到草稿箱中...';
  }
  if (precheckBtn) precheckBtn.disabled = true;

  let successCount = 0;
  let failCount = 0;
  try {
    for (let i = 0; i < readyEntries.length; i += 1) {
      const { row } = readyEntries[i];
      row.uploadStatus = `复制到草稿箱中 ${i + 1}/${readyEntries.length}`;
      renderTemuUploadTestRows();
      try {
        const result = await createTemuUploadDraftForRow(row, template);
        successCount += 1;
        row.uploadResult = {
          productDraftId: result.productDraftId || '',
          editUrl: result.editUrl || '',
          uploadedAt: new Date().toISOString()
        };
        row.uploadStatus = result.productDraftId
          ? `已复制到草稿箱：${result.productDraftId}`
          : '已复制到草稿箱';
      } catch (error) {
        failCount += 1;
        const message = isExtensionContextInvalidatedError(error)
          ? getRuntimeReconnectMessage('TEMU 批量上架')
          : (error?.message || String(error));
        row.uploadStatus = `失败：${message}`;
        console.error('[小青蛙电商助手] TEMU 复制到草稿箱失败:', error);
      }
      renderTemuUploadTestRows();
    }
  } finally {
    if (submitBtn) {
      submitBtn.disabled = false;
      submitBtn.textContent = oldSubmitText;
    }
    if (precheckBtn) precheckBtn.disabled = false;
  }

  showToast(
    `复制到草稿箱完成：成功 ${successCount} 条，失败 ${failCount} 条`,
    failCount ? 'warning' : 'success'
  );
}

function bindTemuUploadTestTableEvents() {
  const tbody = document.getElementById('temuUploadTestTableBody');
  if (!tbody || tbody.dataset.temuUploadBound === '1') return;
  tbody.dataset.temuUploadBound = '1';
  tbody.addEventListener('change', event => {
    const checkbox = event.target?.closest?.('.temu-upload-test-check');
    if (checkbox) {
      const index = Number(checkbox.dataset.index);
      if (checkbox.checked) temuUploadTestState.selected.add(index);
      else temuUploadTestState.selected.delete(index);
      updateTemuUploadTestSummary();
      return;
    }
    const field = event.target?.closest?.('.temu-upload-test-field');
    if (field) updateTemuUploadTestRow(Number(field.dataset.index), field.dataset.field, field.value);
  });
  tbody.addEventListener('click', event => {
    const removeBtn = event.target?.closest?.('.temu-upload-test-remove-row');
    if (removeBtn) {
      const index = Number(removeBtn.dataset.index);
      const row = temuUploadTestState.rows[index];
      if (!row) return;
      revokeTemuUploadImages(row.images);
      temuUploadTestState.rows.splice(index, 1);
      temuUploadTestState.rows.forEach((item, idx) => { item.rowNo = idx + 1; });
      temuUploadTestState.selected = new Set([...temuUploadTestState.selected].filter(idx => idx !== index).map(idx => idx > index ? idx - 1 : idx));
      renderTemuUploadTestRows();
      showToast('已删除该行', 'info');
      return;
    }
    const preview = event.target?.closest?.('.temu-upload-test-open-preview');
    if (preview && !preview.disabled) {
      openTemuUploadTestPreview(Number(preview.dataset.index), Number(preview.dataset.imageIndex || 0));
      return;
    }
    const addImage = event.target?.closest?.('.temu-upload-test-add-image');
    if (addImage) {
      temuUploadTestState.addImageTargetIndex = Number(addImage.dataset.index);
      const input = document.getElementById('temuUploadTestAddImageInput');
      if (input) {
        input.value = '';
        input.click();
      }
    }
  });
}

function initTemuUploadTestTab() {
  if (temuUploadTestState.initialized) return;
  temuUploadTestState.initialized = true;
  const excelInput = document.getElementById('temuUploadTestExcelInput');
  const imageInput = document.getElementById('temuUploadTestImageInput');
  const addImageInput = document.getElementById('temuUploadTestAddImageInput');
  const templateSelect = document.getElementById('temuUploadTemplateSelect');
  const templateList = document.getElementById('temuUploadTemplateList');
  document.getElementById('temuUploadTestDownloadTplBtn')?.addEventListener('click', downloadTemuUploadTestTemplate);
  document.getElementById('temuUploadTemplateAddBtn')?.addEventListener('click', openTemuUploadTemplateAddModal);
  document.getElementById('temuUploadTemplateRefreshBtn')?.addEventListener('click', loadTemuUploadTemplates);
  templateSelect?.addEventListener('change', event => {
    temuUploadTestState.selectedTemplateId = event.target.value;
    temuUploadTestState.rows.forEach(row => {
      applyTemuUploadTemplateToRow(row);
      row.issues = buildTemuUploadTestIssues(row);
      row.uploadStatus = '已更换模板，未上传';
    });
    renderTemuUploadTemplates();
    renderTemuUploadTestRows();
  });
  templateList?.addEventListener('click', async event => {
    const editBtn = event.target?.closest?.('.temu-upload-template-edit');
    if (editBtn) {
      openTemuUploadTemplateEditModal(editBtn.dataset.id);
      return;
    }
    const useBtn = event.target?.closest?.('.temu-upload-template-use');
    if (useBtn) {
      openTemuUploadTemplateGenerator(useBtn.dataset.id);
      return;
    }
    const deleteBtn = event.target?.closest?.('.temu-upload-template-delete');
    if (!deleteBtn) return;
    const template = temuUploadTestState.templates.find(item => String(item.id) === String(deleteBtn.dataset.id));
    if (!template || !window.confirm(`确认删除模板“${template.name}”吗？`)) return;
    const result = await deleteTemuProductUploadTemplate(template.id);
    if (!result.success) return showToast(`删除模板失败：${result.error}`, 'error');
    temuUploadTestState.templates = temuUploadTestState.templates.filter(item => String(item.id) !== String(template.id));
    if (String(temuUploadTestState.selectedTemplateId) === String(template.id)) {
      temuUploadTestState.selectedTemplateId = temuUploadTestState.templates[0]?.id || '';
    }
    renderTemuUploadTemplates();
    showToast('TEMU 商品上架模板已删除', 'success');
  });
  document.getElementById('temuUploadTestExcelBtn')?.addEventListener('click', () => {
    if (excelInput) {
      excelInput.value = '';
      excelInput.click();
    }
  });
  document.getElementById('temuUploadTestImageBtn')?.addEventListener('click', () => {
    if (imageInput) {
      imageInput.value = '';
      imageInput.click();
    }
  });
  document.getElementById('temuUploadTestPrecheckBtn')?.addEventListener('click', precheckTemuUploadTest);
  document.getElementById('temuUploadTestSubmitBtn')?.addEventListener('click', handleTemuUploadTestSubmit);
  document.getElementById('temuUploadTestClearBtn')?.addEventListener('click', clearTemuUploadTest);
  document.getElementById('temuUploadTestSelectAllBtn')?.addEventListener('click', () => {
    if (!temuUploadTestState.rows.length) return;
    const allSelected = temuUploadTestState.selected.size === temuUploadTestState.rows.length;
    temuUploadTestState.selected = allSelected ? new Set() : new Set(temuUploadTestState.rows.map((_, index) => index));
    renderTemuUploadTestRows();
  });
  excelInput?.addEventListener('change', event => {
    const file = event.target.files?.[0] || null;
    temuUploadTestState.excelFile = file;
    setTemuUploadFileLabel('temuUploadTestExcelName', file ? file.name : '未选择 .xlsx / .xls / .csv');
    updateTemuUploadTestSummary();
    renderTemuUploadTemplateGenerator();
  });
  imageInput?.addEventListener('change', event => {
    const files = Array.from(event.target.files || []);
    temuUploadTestState.imageFiles = files.filter(isTemuUploadImageFile);
    temuUploadTestState.imageGroups = groupTemuUploadImages(temuUploadTestState.imageFiles);
    setTemuUploadFileLabel('temuUploadTestImageName', temuUploadTestState.imageFiles.length
      ? `已选择 ${temuUploadTestState.imageFiles.length} 张图片 / ${temuUploadTestState.imageGroups.size} 个编号文件夹`
      : '未选择图片文件夹');
    temuUploadTestState.rows.forEach(row => {
      revokeTemuUploadImages(row.images);
      row.images = matchTemuUploadRowImages(row.code);
      applyTemuUploadTemplateToRow(row);
      row.issues = buildTemuUploadTestIssues(row);
    });
    updateTemuUploadTestSummary();
    renderTemuUploadTestRows();
    renderTemuUploadTemplateGenerator();
  });
  addImageInput?.addEventListener('change', event => {
    const row = temuUploadTestState.rows[temuUploadTestState.addImageTargetIndex];
    const files = Array.from(event.target.files || []).filter(isTemuUploadImageFile);
    if (row && files.length) {
      row.images.push(...files.map(file => ({ file, name: file.name, url: '' })));
      row.images.sort((a, b) => getTemuUploadImageSortValue(a.file) - getTemuUploadImageSortValue(b.file));
      applyTemuUploadTemplateToRow(row);
      row.issues = buildTemuUploadTestIssues(row);
      renderTemuUploadTestRows();
      showToast(`已增加 ${files.length} 张图片`, 'success');
    }
    event.target.value = '';
    temuUploadTestState.addImageTargetIndex = -1;
  });
  loadTemuUploadTemplates();
  bindTemuUploadTestTableEvents();
  document.addEventListener('keydown', event => {
    if (event.key === 'Escape') closeTemuUploadTestPreview();
    if (!document.getElementById('temuUploadTestPreviewModal')?.classList.contains('is-open')) return;
    if (event.key === 'ArrowLeft') switchTemuUploadTestPreview(-1);
    if (event.key === 'ArrowRight') switchTemuUploadTestPreview(1);
  });
  renderTemuUploadTestRows();
}

// ========== 财务数据 Tab ==========
const financeState = {
  available: 0,
  frozen: 0,
  pending: 0,
  pendingAreas: { global: 0, eu: 0, us: 0 },
  stockValue: 0,
  returnValue: 0,
  source: '',
  partialErrors: [],
  history: []
};

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

async function persistFinanceSnapshot() {
  try {
    const token = await getServerAuthToken();
    if (!token || !state.mallId) return;
    await fetch(`${SERVER_URL}/data/finance`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        mallId: state.mallId,
        date: financeToday(),
        availableBalance: financeState.available,
        frozenBalance: financeState.frozen,
        pendingSettlement: financeState.pending
      })
    }).catch(() => null);
    await saveTemuSnapshot('finance', {
      available: financeState.available,
      frozen: financeState.frozen,
      pending: financeState.pending,
      pendingAreas: financeState.pendingAreas,
      stockValue: financeState.stockValue,
      returnValue: financeState.returnValue,
      source: financeState.source,
      partialErrors: financeState.partialErrors,
      history: financeState.history,
      date: financeToday()
    }, { itemCount: Array.isArray(financeState.history) ? financeState.history.length : 0 });
  } catch (err) {
    console.warn('[小青蛙电商助手] 保存财务快照失败:', err);
  }
}

async function refreshFinanceData() {
  if (!requireAdminFeature('财务数据')) return;
  showToast('正在获取财务数据...', 'info');
  
  try {
    // 通过 background.js 调用 TEMU 后台 API
    const result = await safeRuntimeSendMessage({
      type: 'GET_FINANCE_DATA'
    }, { success: false, error: '获取财务数据失败' }, 30000);

    if (result && result.success) {
      financeState.available = result.data.available || 0;
      financeState.frozen = result.data.frozen || 0;
      financeState.pending = result.data.pending || 0;
      financeState.pendingAreas = result.data.pendingAreas || { global: 0, eu: 0, us: 0 };
      financeState.stockValue = result.data.stockValue || 0;
      financeState.returnValue = result.data.returnValue || 0;
      financeState.source = result.data.source || '';
      financeState.partialErrors = Array.isArray(result.data.partialErrors) ? result.data.partialErrors : [];
      financeState.history = result.data.history || [];
      renderFinanceData();
      persistFinanceSnapshot();
      showToast('财务数据已更新', 'success');
    } else {
      showToast(result?.error || '获取财务数据失败', 'error');
    }
  } catch (err) {
    showToast('获取财务数据失败：' + err.message, 'error');
  }
}

async function queryFinanceHistory() {
  if (!requireAdminFeature('财务数据')) return;
  const startDate = document.getElementById('financeStartDate')?.value || '';
  const endDate = document.getElementById('financeEndDate')?.value || '';
  showToast('正在查询资金流水...', 'info');
  try {
    const result = await safeRuntimeSendMessage({
      type: 'GET_FINANCE_HISTORY',
      startDate,
      endDate,
      page: 1,
      pageSize: 100
    }, { success: false, error: '获取流水失败' }, 30000);
    if (!result?.success) throw new Error(result?.error || '获取流水失败');
    financeState.history = Array.isArray(result.data) ? result.data : [];
    renderFinanceData();
    showToast('资金流水已更新', 'success');
  } catch (err) {
    showToast('查询流水失败：' + (err.message || String(err)), 'error');
  }
}

function renderFinanceData() {
  const fmt = (cents) => '¥' + (cents / 100).toFixed(2);
  
  const availableEl = document.getElementById('financeAvailable');
  const frozenEl = document.getElementById('financeFrozen');
  const pendingEl = document.getElementById('financePending');
  const totalEl = document.getElementById('financeTotal');

  if (availableEl) availableEl.textContent = fmt(financeState.available);
  if (frozenEl) frozenEl.textContent = fmt(financeState.frozen);
  if (pendingEl) pendingEl.textContent = fmt(financeState.pending);
  if (totalEl) totalEl.textContent = fmt(financeState.available + financeState.frozen + financeState.pending);

  const chart = document.getElementById('financeBarChart');
  if (chart) {
    const pendingAreas = financeState.pendingAreas || {};
    chart.innerHTML = `
      <div class="finance-overview-grid">
        <div class="stat-card" style="box-shadow:none;"><div class="stat-card-label">备货单货值</div><div class="stat-card-value" style="color:var(--accent-red);font-size:18px;">${fmt(financeState.stockValue)}</div></div>
        <div class="stat-card" style="box-shadow:none;"><div class="stat-card-label">退货货值</div><div class="stat-card-value" style="color:var(--accent-amber);font-size:18px;">${fmt(financeState.returnValue)}</div></div>
        <div class="stat-card" style="box-shadow:none;"><div class="stat-card-label">美国待结算</div><div class="stat-card-value" style="color:var(--accent-blue);font-size:18px;">${fmt(pendingAreas.us || 0)}</div></div>
        <div class="stat-card" style="box-shadow:none;"><div class="stat-card-label">欧区待结算</div><div class="stat-card-value" style="color:var(--accent-blue);font-size:18px;">${fmt(pendingAreas.eu || 0)}</div></div>
        <div class="stat-card" style="box-shadow:none;"><div class="stat-card-label">全球待结算</div><div class="stat-card-value" style="color:var(--accent-blue);font-size:18px;">${fmt(pendingAreas.global || 0)}</div></div>
        <div style="grid-column:1/-1;color:var(--text-muted);font-size:12px;line-height:1.6;">
          数据来源：${escapeHtml(financeState.source || 'TEMU 财务接口')}
          ${financeState.partialErrors.length ? `；部分区域未登录或接口无响应：${escapeHtml(financeState.partialErrors.join('；'))}` : ''}
        </div>
      </div>
    `;
  }

  // 渲染流水表格
  const tbody = document.getElementById('financeTableBody');
  if (!tbody) return;

  if (financeState.history.length === 0) {
    tbody.innerHTML = '<tr><td colspan="5" class="empty">暂无流水数据</td></tr>';
    return;
  }

  tbody.innerHTML = financeState.history.map(item => `
    <tr>
      <td>${escapeHtml(item.date || '-')}</td>
      <td>${escapeHtml(item.type || '-')}</td>
      <td>${escapeHtml(item.description || '-')}</td>
      <td style="color:${item.amount >= 0 ? 'var(--accent-green)' : 'var(--accent-red)'}">${item.amount >= 0 ? '+' : ''}${fmt(item.amount)}</td>
      <td>${fmt(item.balance || 0)}</td>
    </tr>
  `).join('');
}

function exportFinanceHistory() {
  const rows = financeState.history || [];
  if (!rows.length) {
    showToast('暂无财务流水可导出', 'warning');
    return;
  }
  const escapeCsv = (value) => `"${String(value ?? '').replace(/"/g, '""')}"`;
  const fmtMoney = (cents) => ((Number(cents) || 0) / 100).toFixed(2);
  const csv = [
    ['日期', '类型', '描述', '金额', '余额'].map(escapeCsv).join(','),
    ...rows.map(item => [
      item.date || '',
      item.type || '',
      item.description || '',
      fmtMoney(item.amount),
      fmtMoney(item.balance)
    ].map(escapeCsv).join(','))
  ].join('\r\n');
  const blob = new Blob([`\uFEFF${csv}`], { type: 'text/csv;charset=utf-8' });
  triggerBrowserDownload(`xiaoqingwa-finance-${financeToday()}.csv`, blob);
  showToast('财务数据已导出', 'success');
}




