"use strict";

importScripts("cloud-config.js", "capture-config.js", "capture-queue.js", "extension-auth.js", "price-parser.js", "amazon-parser.js", "collector-v2.js");

const config = globalThis.SDMCaptureConfig;
const queueApi = globalThis.SDMCaptureQueue;
const amazonParser = globalThis.SDMAmazonParser;
const collectorV2 = globalThis.SDMCollectorV2;
const SETTINGS = config.SETTINGS;
const STATUS = queueApi.STATUS;
const captureQueue = queueApi.createCaptureQueue(chrome.storage.local, SETTINGS);
const extensionAuth = globalThis.SDMExtensionAuth.createExtensionAuth(chrome.storage.local, SETTINGS, globalThis.fetch);
const cancelledBatchIds = new Set();
const taskAbortControllers = new Map();
const cancelledEditorTaskIds = new Set();
const DASHBOARD_URL = chrome.runtime.getURL("dashboard.html");
const AI_URLS = {
  deepseek: "https://chat.deepseek.com/",
  doubao: "https://www.doubao.com/chat/",
  chatgpt: "https://chatgpt.com/"
};

class CaptureError extends Error {
  constructor(message, code = "CAPTURE_FAILED", retryable = false, retryAfterMs = 0) {
    super(message);
    this.name = "CaptureError";
    this.code = code;
    this.retryable = retryable;
    this.retryAfterMs = retryAfterMs;
  }
}

function delay(milliseconds) {
  return new Promise(resolve => setTimeout(resolve, Math.max(0, milliseconds || 0)));
}

function errorMessage(error) {
  return error?.message || String(error || "未知错误");
}

function captureError(error) {
  if (error instanceof CaptureError) return error;
  if (error?.name === "AbortError") return new CaptureError("亚马逊详情请求超时", "REQUEST_TIMEOUT", true);
  return new CaptureError(errorMessage(error), error?.code || "CAPTURE_FAILED", Boolean(error?.retryable));
}

async function captureOptions() {
  const result = await chrome.storage.local.get(SETTINGS.OPTIONS_STORAGE_KEY);
  const stored = result?.[SETTINGS.OPTIONS_STORAGE_KEY];
  return {
    includeVariants: stored?.includeVariants == null ? SETTINGS.DEFAULT_INCLUDE_VARIANTS : Boolean(stored.includeVariants),
    collectorV2Enabled: stored?.collectorV2Enabled == null ? SETTINGS.COLLECTOR_V2_ENABLED : Boolean(stored.collectorV2Enabled),
    allowLegacyTabFallback: stored?.allowLegacyTabFallback == null ? SETTINGS.ALLOW_LEGACY_TAB_FALLBACK : Boolean(stored.allowLegacyTabFallback)
  };
}

async function setCaptureOptions(patch) {
  const current = await captureOptions();
  const updated = {
    ...current,
    ...patch,
    includeVariants: Boolean(patch?.includeVariants ?? current.includeVariants),
    collectorV2Enabled: Boolean(patch?.collectorV2Enabled ?? current.collectorV2Enabled),
    allowLegacyTabFallback: Boolean(patch?.allowLegacyTabFallback ?? current.allowLegacyTabFallback)
  };
  await chrome.storage.local.set({ [SETTINGS.OPTIONS_STORAGE_KEY]: updated });
  return updated;
}

async function authState(requested = {}) {
  return extensionAuth.resolve(requested);
}

async function erpRequest(path, taskAccount, options = {}) {
  const auth = await authState({ profileId: options.profileId, account: taskAccount, storeId: options.storeId });
  if (!auth.token) throw new CaptureError("请先在易联跨境插件中登录账号", "ERP_AUTH_REQUIRED", false);
  const headers = { ...(options.headers || {}), "Authorization": `Bearer ${auth.token}`, "X-Data-Account": taskAccount || auth.account, "X-Store-Id": options.storeId || auth.storeId };
  if (options.body != null && !headers["Content-Type"]) headers["Content-Type"] = "application/json";
  let response;
  try {
    response = await fetch(`${SETTINGS.ERP_BASE_URL}${path}`, { ...options, headers });
  } catch (error) {
    throw new CaptureError(`无法连接易联跨境 ERP：${errorMessage(error)}`, "ERP_UNAVAILABLE", true);
  }
  const payload = await response.json().catch(() => ({}));
  if (response.status === 401 || response.status === 403) {
    if (response.status === 401 && auth.profileId) await extensionAuth.markExpired(auth.profileId).catch(() => {});
    const detail = typeof payload.detail === "string" ? payload.detail : payload.msg;
    throw new CaptureError(detail || "登录已失效或当前账号没有采集库权限", "ERP_AUTH_REQUIRED", false);
  }
  if (!response.ok) {
    const detail = typeof payload.detail === "string" ? payload.detail : payload.msg;
    throw new CaptureError(detail || `易联跨境 ERP 返回 HTTP ${response.status}`, "ERP_ERROR", response.status >= 500);
  }
  return payload;
}

let activeHttpRequests = 0;
const httpWaiters = [];
let nextRequestAt = 0;

async function acquireHttpSlot() {
  if (activeHttpRequests >= SETTINGS.HTTP_CONCURRENCY) {
    await new Promise(resolve => httpWaiters.push(resolve));
  }
  activeHttpRequests += 1;
}

function releaseHttpSlot() {
  activeHttpRequests = Math.max(0, activeHttpRequests - 1);
  const next = httpWaiters.shift();
  if (next) next();
}

async function withHttpSlot(operation) {
  await acquireHttpSlot();
  try {
    const wait = Math.max(0, nextRequestAt - Date.now());
    if (wait) await delay(wait);
    nextRequestAt = Date.now() + SETTINGS.MIN_REQUEST_GAP_MS;
    return await operation();
  } finally {
    releaseHttpSlot();
  }
}

async function matchingAmazonTab(target) {
  const tabs = await chrome.tabs.query({});
  return tabs.find(tab => {
    const current = config.targetFromUrl(tab.url || "");
    return current?.asin === target.asin && current?.domain === config.normalizeHost(target.domain);
  }) || null;
}

async function ensureStableCollector(tabId) {
  await chrome.scripting.executeScript({
    target: { tabId },
    files: ["collector-v2.js", "content-script.js"]
  });
}

function normalizeDomProduct(rawProduct, target) {
  const product = rawProduct && typeof rawProduct === "object" ? { ...rawProduct } : {};
  const site = config.siteForHost(target.domain || product.domain || product.url);
  const asin = config.normalizeAsin(product.asin || target.asin);
  const url = config.canonicalProductUrl(target.domain || product.domain, asin) || product.url || target.url;
  const warnings = [];
  if (!product.title) warnings.push("标题为空");
  if (!Array.isArray(product.images) || !product.images.length) warnings.push("高清图片为空");
  if (!Array.isArray(product.bulletPoints) || !product.bulletPoints.length) warnings.push("五点描述为空");
  const normalized = {
    ...product,
    asin,
    rawTitle: product.rawTitle || product.title || "",
    cleanedTitle: product.cleanedTitle || product.title || "",
    site: site?.site || product.site || "",
    country: site?.country || product.country || "",
    countries: [site?.country || product.country || ""].filter(Boolean),
    domain: config.normalizeHost(target.domain || product.domain || url),
    url,
    sourceUrl: url,
    image: product.image || product.images?.[0] || "",
    images: Array.isArray(product.images) ? product.images : [],
    variantRows: Array.isArray(product.variantRows) ? product.variantRows : [],
    variants: Array.isArray(product.variants) ? product.variants : [],
    captureSource: "易联跨境 Collector V2（页面内置数据优先 + 静默 fetch 补全）",
    dataCompleteness: warnings.length ? "部分完整" : "完整",
    collectedAt: product.collectedAt || new Date().toISOString()
  };
  return { ok: true, product: normalized, warnings, partial: warnings.length > 0 };
}

async function scrapeProductFromTab(tabId, target, timeoutMs = SETTINGS.CAPTURE_TAB_TIMEOUT_MS, messageType = "SCRAPE_PRODUCT") {
  const started = Date.now();
  let lastMessage = "等待 8 月 3 日前稳定版采集脚本读取当前页面";
  while (Date.now() - started < timeoutMs) {
    try {
      let response;
      try {
        response = await chrome.tabs.sendMessage(tabId, { type: messageType, target });
      } catch (messageError) {
        await ensureStableCollector(tabId);
        response = await chrome.tabs.sendMessage(tabId, { type: messageType, target });
      }
      if (response?.ok && response.product) return normalizeDomProduct(response.product, target);
      lastMessage = response?.error || lastMessage;
      if (response?.captcha) throw new CaptureError(response.error || "亚马逊要求验证码", "CAPTCHA_REQUIRED", false);
    } catch (error) {
      if (error instanceof CaptureError) throw error;
      lastMessage = errorMessage(error);
    }
    await delay(Math.max(500, SETTINGS.CAPTURE_TAB_POLL_MS));
  }
  throw new CaptureError(`${lastMessage}；请确认页面已正常显示商品标题`, "DOM_CAPTURE_TIMEOUT", true);
}

function collectorLog(event, details = {}) {
  const safe = Object.fromEntries(Object.entries(details).filter(([key]) => !/cookie|token|password/i.test(key)));
  console.info("[易联跨境 Collector V2]", { event, at: new Date().toISOString(), ...safe });
}

function parsedHtmlProduct(html, target, status = 200) {
  const parsed = amazonParser.parseAmazonProduct(html, { domain: target.domain, url: target.url, asin: target.asin, status });
  if (!parsed?.ok || !parsed.product) throw new CaptureError(parsed?.error || "亚马逊商品 HTML 解析失败", parsed?.code || "STRUCTURE_CHANGED", Boolean(parsed?.retryable));
  const embedded = collectorV2.extractEmbeddedVariationData(html, parsed.product.asin || target.asin);
  const product = collectorV2.mergeVariantData(parsed.product, embedded);
  collectorLog("embedded_parsed", {
    asin: product.asin,
    parentAsin: product.parentAsin,
    variantCount: product.variantRows?.length || 0,
    imageCounts: embedded.diagnostics?.embeddedImageCounts || {},
    embeddedFields: embedded.diagnostics?.embeddedFields || []
  });
  return normalizeDomProduct(product, target);
}

async function silentFetchProduct(target, signal) {
  const url = config.canonicalProductUrl(target.domain, target.asin);
  if (!url) throw new CaptureError("ASIN 或亚马逊站点无效", "INVALID_TARGET", false);
  let lastError = null;
  for (let attempt = 0; attempt <= SETTINGS.V2_FETCH_RETRIES; attempt += 1) {
    if (signal?.aborted) throw new DOMException("采集已取消", "AbortError");
    try {
      return await withHttpSlot(async () => {
        const controller = new AbortController();
        const abort = () => controller.abort();
        signal?.addEventListener("abort", abort, { once: true });
        const timer = setTimeout(() => controller.abort(), SETTINGS.V2_FETCH_TIMEOUT_MS);
        collectorLog("fetch_started", { asin: target.asin, attempt: attempt + 1 });
        try {
          const response = await fetch(url, { credentials: "include", cache: "no-store", redirect: "follow", signal: controller.signal });
          const html = await response.text();
          if (response.status === 403) throw new CaptureError("亚马逊拒绝静默请求（HTTP 403），请检查站点登录或验证状态", "AMAZON_FORBIDDEN", false);
          const result = parsedHtmlProduct(html, { ...target, url }, response.status);
          const returnedAsin = config.normalizeAsin(result.product?.asin);
          if (returnedAsin && returnedAsin !== target.asin) {
            throw new CaptureError(`亚马逊返回了其他变体 ${returnedAsin}，将重试目标 ${target.asin}`, "VARIANT_REDIRECTED", true);
          }
          const imageCount = result.product.images?.length || 0;
          const hasPrice = result.product.price !== "" && result.product.price != null;
          collectorLog("fetch_completed", { asin: target.asin, status: response.status, imageCount, hasPrice });
          return result;
        } finally {
          clearTimeout(timer);
          signal?.removeEventListener("abort", abort);
        }
      });
    } catch (rawError) {
      const error = captureError(rawError);
      lastError = error;
      collectorLog("fetch_failed", { asin: target.asin, attempt: attempt + 1, code: error.code, error: error.message });
      if (!error.retryable || attempt >= SETTINGS.V2_FETCH_RETRIES || signal?.aborted) throw error;
      await delay(SETTINGS.RETRY_BASE_DELAY_MS * (attempt + 1));
    }
  }
  throw lastError || new CaptureError("静默请求失败", "SILENT_FETCH_FAILED", true);
}

function exactProductQuality(result, target) {
  const product = result?.product || {};
  const exactRow = (product.variantRows || []).find(row => row.asin === target.asin);
  const exactImages = exactRow?.images?.length
    ? exactRow.images
    : (product.asin === target.asin ? (product.images || []) : []);
  const price = exactRow?.price ?? exactRow?.originalPrice ?? product.price;
  return {
    imageCount: collectorV2.dedupeImageRecords(exactImages).length,
    hasPrice: price !== "" && price != null
  };
}

async function renderedProductFallback(target, signal) {
  if (signal?.aborted) throw new DOMException("采集已取消", "AbortError");
  const url = config.canonicalProductUrl(target.domain, target.asin);
  const tab = await chrome.tabs.create({ url, active: false });
  try {
    return await scrapeProductFromTab(tab.id, target, SETTINGS.CAPTURE_TAB_TIMEOUT_MS, "COLLECT_PRODUCT_V2");
  } finally {
    if (tab?.id) await chrome.tabs.remove(tab.id).catch(() => {});
  }
}

async function fetchCompleteProductDetail(target, signal) {
  const fetched = await silentFetchProduct(target, signal);
  const quality = exactProductQuality(fetched, target);
  if (quality.imageCount > 1 && quality.hasPrice) {
    return { ...fetched, detailSource: "silent-fetch" };
  }
  collectorLog("rendered_fallback_started", {
    asin: target.asin,
    imageCount: quality.imageCount,
    hasPrice: quality.hasPrice
  });
  try {
    const rendered = await renderedProductFallback(target, signal);
    const renderedQuality = exactProductQuality(rendered, target);
    collectorLog("rendered_fallback_completed", {
      asin: target.asin,
      imageCount: renderedQuality.imageCount,
      hasPrice: renderedQuality.hasPrice
    });
    return { ...rendered, detailSource: "detail-dom" };
  } catch (error) {
    collectorLog("rendered_fallback_failed", {
      asin: target.asin,
      code: error?.code || "RENDERED_FALLBACK_FAILED",
      error: errorMessage(error)
    });
    return {
      ...fetched,
      detailSource: "silent-fetch",
      detailWarning: `渲染页未能补全图片：${errorMessage(error)}`
    };
  }
}

async function completeProductWithSilentFetch(result, target, signal) {
  const product = result.product;
  product.variantRows = (product.variantRows || []).map(row => ({
    ...row,
    detailFetched: Boolean(row.detailFetched || row.asin === product.asin)
  }));
  const candidates = (product.variantRows || [])
    .filter(row => row.asin !== product.asin && collectorV2.variantNeedsFetch(row))
    .slice(0, SETTINGS.MAX_VARIANTS_PER_TASK - 1);
  const batchSize = Math.max(1, Number(SETTINGS.VARIANT_FETCH_BATCH_SIZE || 20));
  const missing = candidates.slice(0, batchSize);
  collectorLog("missing_variants", {
    asin: product.asin,
    total: (product.variantRows || []).length,
    pending: candidates.length,
    batch: missing.map(row => row.asin)
  });
  const fetched = new Map();
  const errors = [];
  const detailWarnings = [];
  await mapLimit(missing, SETTINGS.V2_FETCH_CONCURRENCY, async row => {
    try {
      const detail = await fetchCompleteProductDetail({ ...target, asin: row.asin, url: config.canonicalProductUrl(target.domain, row.asin) }, signal);
      fetched.set(row.asin, detail);
      if (detail.detailWarning) detailWarnings.push({ asin: row.asin, error: detail.detailWarning, code: "VARIANT_GALLERY_INCOMPLETE" });
    } catch (error) {
      errors.push({ asin: row.asin, error: errorMessage(error), code: error?.code || "SILENT_FETCH_FAILED" });
    }
  });
  product.variantRows = (product.variantRows || []).map(row => {
    const detailResult = fetched.get(row.asin);
    const detail = detailResult?.product;
    if (!detailResult || !detail) {
      const failure = errors.find(item => item.asin === row.asin);
      return failure ? {
        ...row,
        detailFetchFailed: true,
        detailFetchError: failure.error,
        detailFetched: false
      } : row;
    }
    const detailRow = detail.variantRows?.find(candidate => candidate.asin === row.asin);
    // A fetched detail document is authoritative for this exact ASIN.  Never
    // merge the previously seeded colour-level gallery back in, otherwise a
    // deleted/recollected product can appear to revive old sibling images.
    const exactImages = detailRow?.images?.length
      ? detailRow.images
      : (detail.asin === row.asin ? (detail.images || []) : []);
    const images = collectorV2.dedupeImageRecords(exactImages).map(record => record.url);
    return {
      ...row,
      title: detail.title || row.title || "",
      price: detailRow?.price ?? detail.price ?? row.price ?? row.originalPrice ?? "",
      originalPrice: detailRow?.price ?? detail.price ?? row.originalPrice ?? row.price ?? "",
      priceCurrency: detailRow?.priceCurrency || detailRow?.currency || detail.currency || row.priceCurrency || row.currency || product.currency,
      currency: detailRow?.currency || detail.currency || row.currency || product.currency,
      availability: detail.availability || row.availability || "",
      images,
      image: images[0] || "",
      detailFetched: true,
      detailFetchFailed: false,
      detailFetchError: "",
      source: {
        ...(row.source || {}),
        variation: "embedded",
        images: detailResult.detailSource === "detail-dom" ? "detail-dom" : "fetch",
        details: detailResult.detailSource === "detail-dom" ? "detail-dom" : "fetch"
      }
    };
  });
  product.variants = product.variantRows.map(row => ({ ...row, value: [row.color, row.size].filter(Boolean).join(" / "), dimension: "组合变体" }));
  const previousDiagnostics = product.collectorV2Diagnostics || {};
  const previousFetched = Array.isArray(previousDiagnostics.fetchedAsins) ? previousDiagnostics.fetchedAsins : [];
  const previousErrors = Array.isArray(previousDiagnostics.fetchErrors) ? previousDiagnostics.fetchErrors : [];
  const fetchedAsins = [...new Set([...previousFetched, ...fetched.keys()])];
  const fetchErrors = [...previousErrors, ...errors, ...detailWarnings].filter((item, index, rows) =>
    rows.findIndex(candidate => candidate.asin === item.asin) === index
  );
  const remaining = (product.variantRows || [])
    .filter(row => row.asin !== product.asin && collectorV2.variantNeedsFetch(row));
  const continuation = remaining.length > 0;
  const validation = continuation
    ? { complete: false, problems: [`variant_details_pending:${remaining.length}`] }
    : collectorV2.validateCollectionResult(product);
  product.collectorV2Diagnostics = {
    ...previousDiagnostics,
    fetchedAsins,
    fetchErrors,
    validation,
    variantProgress: {
      total: (product.variantRows || []).length,
      completed: Math.max(1, (product.variantRows || []).filter(row => row.detailFetched === true).length),
      pending: remaining.length,
      failed: fetchErrors.length
    }
  };
  return {
    ...result,
    product,
    continuation,
    partial: continuation ? false : (result.partial || !validation.complete || fetchErrors.length > 0),
    warnings: [...new Set([...(result.warnings || []), ...fetchErrors.map(item => `${item.asin}: ${item.error}`)])]
  };
}

async function collectProductV2(target, signal) {
  if (target.variantProgress?.product?.asin === target.asin) {
    return completeProductWithSilentFetch({
      product: target.variantProgress.product,
      warnings: target.variantProgress.warnings || [],
      partial: false
    }, target, signal);
  }
  if (target.seedIsCurrentPage && target.seedProduct?.asin === target.asin) {
    // COLLECT_CURRENT has already read the fully rendered detail page. Reuse it
    // directly and silently fetch only sibling ASINs; querying tabs again is
    // racy during navigation and must not cause the current ASIN to be fetched
    // twice or replaced with a sibling document.
    const seeded = normalizeDomProduct(target.seedProduct, target);
    return completeProductWithSilentFetch(seeded, target, signal);
  }
  // Search/category cards are only discovery seeds. Even when their status is
  // labelled complete, they do not contain a reliable per-variant gallery or
  // price, so never treat them as the canonical detail document.
  const existing = await matchingAmazonTab(target);
  if (existing?.id) {
    const rendered = await scrapeProductFromTab(existing.id, target, SETTINGS.CAPTURE_TAB_TIMEOUT_MS, "COLLECT_PRODUCT_V2");
    return completeProductWithSilentFetch(rendered, target, signal);
  }
  const fetched = await fetchCompleteProductDetail(target, signal);
  return completeProductWithSilentFetch(fetched, target, signal);
}

async function legacyCollector(target) {
  if (target.seedProduct?.title) return normalizeDomProduct(target.seedProduct, target);
  const existing = await matchingAmazonTab(target);
  if (existing?.id) {
    return scrapeProductFromTab(existing.id, target, 15000);
  }
  const url = config.canonicalProductUrl(target.domain, target.asin);
  if (!url) throw new CaptureError("ASIN 或亚马逊站点无效", "INVALID_TARGET", false);
  return withHttpSlot(async () => {
    const tab = await chrome.tabs.create({ url, active: false });
    try {
      return await scrapeProductFromTab(tab.id, target);
    } finally {
      if (tab?.id) await chrome.tabs.remove(tab.id).catch(() => {});
    }
  });
}

async function requestProduct(target, signal) {
  const options = await captureOptions();
  if (options.collectorV2Enabled) {
    try {
      return await collectProductV2(target, signal);
    } catch (error) {
      collectorLog("collector_v2_failed", { asin: target.asin, code: error?.code || "COLLECTOR_V2_FAILED", error: errorMessage(error), legacyFallback: options.allowLegacyTabFallback });
      if (!options.allowLegacyTabFallback) throw error;
    }
  }
  collectorLog("legacy_collector", { asin: target.asin, enabled: true });
  return legacyCollector(target);
}

async function productForTarget(target, { forceRefresh = false, signal } = {}) {
  // 每次都重新读取 Amazon 当前页面或静默请求结果，绝不复用扩展缓存。
  void forceRefresh;
  return requestProduct(target, signal);
}

function decorateProduct(product, task, relation = {}) {
  const status = relation.captureStatus || STATUS.SUCCESS;
  return {
    ...product,
    dataAccount: task.account,
    captureTaskId: task.id,
    idempotencyKey: task.idempotencyKey,
    captureStatus: status,
    collectionStatus: status === STATUS.PARTIAL ? "部分成功" : "已采集",
    mainCaptureAsin: task.asin,
    isPrimary: relation.isPrimary !== false,
    relationshipType: relation.relationshipType || "primary",
    parentAsin: relation.parentAsin || product.parentAsin || "",
    updatedAt: new Date().toISOString()
  };
}

async function mapLimit(items, limit, worker) {
  const results = new Array(items.length);
  let cursor = 0;
  async function run() {
    while (cursor < items.length) {
      const index = cursor++;
      results[index] = await worker(items[index], index);
    }
  }
  await Promise.all(Array.from({ length: Math.min(limit, items.length) }, run));
  return results;
}

async function collectTaskProducts(task, signal) {
  const mainResult = await productForTarget(task, { forceRefresh: Boolean(task.includeVariants), signal });
  const mainProduct = mainResult.product;
  if (mainResult.continuation) {
    return {
      continuation: true,
      variantProgress: { product: mainProduct, warnings: mainResult.warnings || [] },
      progress: mainProduct.collectorV2Diagnostics?.variantProgress || {}
    };
  }
  const parentAsin = mainProduct.parentAsin || mainProduct.asin;
  const partialErrors = Array.isArray(mainProduct.collectorV2Diagnostics?.fetchErrors)
    ? [...mainProduct.collectorV2Diagnostics.fetchErrors]
    : [];
  const collectedByAsin = new Map([[mainProduct.asin, mainProduct]]);

  const completeVariantRows = (mainProduct.variantRows || []).map(row => {
    const collected = collectedByAsin.get(row.asin);
    return {
      ...row,
      parentAsin,
      title: collected?.title || "",
      price: collected?.price ?? row.price ?? row.originalPrice ?? "",
      originalPrice: collected?.price ?? row.originalPrice ?? row.price ?? "",
      priceCurrency: collected?.currency || row.priceCurrency || row.currency || mainProduct.currency,
      currency: collected?.currency || mainProduct.currency,
      images: Array.isArray(collected?.images) && collected.images.filter(Boolean).length ? collected.images.filter(Boolean) : (Array.isArray(row.images) ? row.images.filter(Boolean) : []),
      image: collected?.image || collected?.images?.find(Boolean) || row.image || row.images?.find(Boolean) || ""
    };
  });
  for (const product of collectedByAsin.values()) {
    product.variantRows = completeVariantRows.map(row => ({
      ...row,
      selected: row.asin === product.asin,
      images: [...(row.images || [])]
    }));
    product.variants = product.variantRows.map(row => ({
      ...row,
      value: [row.color, row.size].filter(Boolean).join(" / "),
      dimension: "组合变体"
    }));
  }

  const partial = Boolean(mainResult.partial || partialErrors.length);
  const finalStatus = partial ? STATUS.PARTIAL : STATUS.SUCCESS;
  const products = [decorateProduct(mainProduct, task, {
    isPrimary: true,
    relationshipType: mainProduct.asin === parentAsin ? "parent" : "primary",
    parentAsin: mainProduct.asin === parentAsin ? "" : parentAsin,
    captureStatus: finalStatus
  })];
  // 同一商品始终作为一条父商品记录写入 ERP，避免为每个变体重复开页签和建任务。
  return { products, product: products[0], status: finalStatus, partialErrors, warnings: mainResult.warnings || [] };
}

async function updateErpTask(task, patch) {
  return erpRequest(`/crawl/tasks/${encodeURIComponent(task.id)}`, task.account, {
    profileId: task.profileId,
    storeId: task.storeId,
    method: "PUT",
    body: JSON.stringify(patch)
  });
}

async function processTask(task) {
  const controller = new AbortController();
  taskAbortControllers.set(task.id, controller);
  try {
    const beforeStart = (await captureQueue.list(task.account, SETTINGS.MAX_TASKS)).find(item => item.id === task.id);
    if (beforeStart?.errorCode === "USER_CANCELLED") return;
    await updateErpTask(task, { status: STATUS.COLLECTING, attempts: task.attempts, error: "" });
    let activeTask = task;
    let result;
    while (true) {
      result = await collectTaskProducts(activeTask, controller.signal);
      if (!result.continuation) break;
      const progress = result.progress || {};
      const patch = {
        status: STATUS.COLLECTING,
        variantProgress: result.variantProgress,
        error: `正在补全变体详情 ${Number(progress.completed || 0)}/${Number(progress.total || 0)}`,
        errorCode: "",
        nextAttemptAt: 0,
        resultSummary: { asin: activeTask.asin, progress }
      };
      await captureQueue.update(task.id, patch);
      await updateErpTask(task, {
        status: STATUS.COLLECTING,
        attempts: task.attempts,
        error: "",
        result: { progress }
      }).catch(() => {});
      activeTask = { ...activeTask, ...patch };
      // Persist before yielding so MV3 service-worker restarts resume this family.
      await delay(Math.max(100, Number(SETTINGS.MIN_REQUEST_GAP_MS || 0)));
    }
    const afterCollect = (await captureQueue.list(task.account, SETTINGS.MAX_TASKS)).find(item => item.id === task.id);
    if (afterCollect?.errorCode === "USER_CANCELLED") return;
    await updateErpTask(task, {
      status: result.status,
      attempts: task.attempts,
      error: "",
      partialErrors: result.partialErrors,
      result: { product: result.product, products: result.products, warnings: result.warnings }
    });
    // ERP 已成功接收后立即移除扩展任务；扩展不保留商品、图片或完成历史。
    await captureQueue.remove(task.id);
  } catch (rawError) {
    if (task.batchId && cancelledBatchIds.has(task.batchId)) return;
    const latest = (await captureQueue.list(task.account, SETTINGS.MAX_TASKS)).find(item => item.id === task.id);
    if (latest?.errorCode === "USER_CANCELLED") return;
    const error = captureError(rawError);
    if (error.code === "ERP_AUTH_REQUIRED") {
      await captureQueue.update(task.id, { status: STATUS.WAITING, error: error.message, errorCode: error.code, nextAttemptAt: Date.now() + 60000 });
      scheduleQueue(60000);
      return;
    }
    const attempts = Number(latest?.attempts || task.attempts || 1);
    const maxRetries = Math.min(
      Number(latest?.maxRetries || task.maxRetries || SETTINGS.REQUEST_RETRIES),
      SETTINGS.REQUEST_RETRIES
    );
    const shouldRetry = error.retryable && attempts < maxRetries;
    if (shouldRetry) {
      const wait = error.retryAfterMs || SETTINGS.RETRY_BASE_DELAY_MS * (2 ** Math.max(0, attempts - 1));
      await captureQueue.update(task.id, {
        status: STATUS.WAITING,
        error: `${error.message}；将在后台自动重试（第${attempts}/${maxRetries}次）`,
        errorCode: error.code,
        nextAttemptAt: Date.now() + wait
      });
      await updateErpTask(task, { status: STATUS.WAITING, attempts, error: error.message }).catch(() => {});
      scheduleQueue(wait);
      return;
    }
    await updateErpTask(task, { status: STATUS.FAILED, attempts, error: error.message }).catch(() => {});
    // 最终失败原因已经回写 ERP，扩展端不继续保存失败历史。
    await captureQueue.remove(task.id);
  } finally {
    taskAbortControllers.delete(task.id);
  }
}

let processingQueue = false;
async function processQueue() {
  if (processingQueue) return;
  processingQueue = true;
  try {
    while (true) {
      const tasks = await captureQueue.claim(SETTINGS.TASK_CONCURRENCY);
      if (!tasks.length) break;
      await Promise.all(tasks.map(processTask));
    }
  } finally {
    processingQueue = false;
  }
}

function scheduleQueue(delayMs = 250) {
  const when = Date.now() + Math.max(250, Number(delayMs) || 250);
  chrome.alarms.create(SETTINGS.ALARM_NAME, { when });
  if (delayMs < 30000) setTimeout(() => processQueue().catch(() => {}), Math.max(250, delayMs));
}

async function registerTask(task) {
  return erpRequest("/crawl/tasks", task.account, {
    profileId: task.profileId,
    storeId: task.storeId,
    method: "POST",
    body: JSON.stringify({
      taskId: task.id,
      idempotencyKey: task.idempotencyKey,
      storeId: task.storeId,
      asin: task.asin,
      site: task.site,
      domain: task.domain,
      url: task.url,
      includeVariants: task.includeVariants,
      preview: task.previewProduct || {},
      status: STATUS.WAITING,
      attempts: task.attempts,
      maxRetries: task.maxRetries
    })
  });
}

async function enqueueTargets(rawTargets, requestedOptions = {}) {
  const auth = await authState();
  if (!auth.token || !auth.account) throw new CaptureError("请先在易联跨境插件中登录账号", "ERP_AUTH_REQUIRED", false);
  if (!auth.storeId) throw new CaptureError("请先在易联跨境插件中选择一个已授权店铺", "ERP_STORE_REQUIRED", false);
  const options = await captureOptions();
  const targets = (Array.isArray(rawTargets) ? rawTargets : [rawTargets])
    .map(raw => raw?.asin ? raw : config.targetFromUrl(raw?.url || raw))
    .filter(Boolean);
  if (!targets.length) throw new CaptureError("没有可识别的亚马逊 ASIN", "INVALID_TARGET", false);
  const queued = await captureQueue.enqueue(targets, {
    account: auth.account,
    storeId: auth.storeId,
    profileId: auth.profileId,
    includeVariants: requestedOptions.includeVariants == null ? options.includeVariants : Boolean(requestedOptions.includeVariants),
    force: Boolean(requestedOptions.force),
    batchId: String(requestedOptions.batchId || "")
  });
  const accepted = [];
  const rejected = [];
  const duplicates = [];
  let warning = "";
  for (const task of queued.accepted) {
    if (task.batchId && cancelledBatchIds.has(task.batchId)) break;
    try {
      await registerTask(task);
      accepted.push(task);
    } catch (rawError) {
      const error = captureError(rawError);
      if (error.code === "ERP_AUTH_REQUIRED") {
        await captureQueue.remove(task.id);
        rejected.push({ task, error: error.message });
      } else {
        await captureQueue.update(task.id, { error: "ERP 暂不可达，任务已保存在本地队列", errorCode: error.code });
        accepted.push(task);
        warning = "ERP 暂不可达，任务已保存在本地队列并会自动重试";
      }
    }
  }
  for (const task of queued.duplicates) {
    try {
      const registration = await registerTask({ ...task, profileId: auth.profileId, storeId: auth.storeId });
      if (registration?.data?.accepted) {
        const revived = await captureQueue.enqueue([{ asin: task.asin, domain: task.domain, url: task.url, previewProduct: task.previewProduct }], {
          account: auth.account,
          storeId: auth.storeId,
          profileId: auth.profileId,
          includeVariants: requestedOptions.includeVariants == null ? options.includeVariants : Boolean(requestedOptions.includeVariants),
          force: true,
          batchId: String(requestedOptions.batchId || task.batchId || "")
        });
        if (revived.accepted[0]) accepted.push(revived.accepted[0]);
        else duplicates.push(task);
      } else {
        duplicates.push(task);
      }
    } catch {
      duplicates.push(task);
    }
  }
  if (requestedOptions.batchId && cancelledBatchIds.has(requestedOptions.batchId)) {
    await captureQueue.cancelBatch(requestedOptions.batchId, auth.account);
  }
  if (!accepted.length && !duplicates.length && rejected.length) {
    throw new CaptureError(rejected[0].error, "ERP_AUTH_REQUIRED", false);
  }
  if (accepted.length && !cancelledBatchIds.has(requestedOptions.batchId)) scheduleQueue();
  return { accepted, duplicates, rejected, warning, batchId: requestedOptions.batchId || "" };
}

async function currentPageTarget(sourceTab) {
  const tab = sourceTab?.id ? sourceTab : (await chrome.tabs.query({ active: true, currentWindow: true }))[0];
  if (!tab?.id || !config.isAmazonHost(tab.url || "")) throw new CaptureError("请先打开亚马逊商品详情页", "INVALID_TARGET", false);
  try {
    const response = await chrome.tabs.sendMessage(tab.id, { type: "GET_PAGE_TARGET" });
    if (response?.ok && response.target) return response.target;
  } catch {}
  const fallback = config.targetFromUrl(tab.url || "");
  if (!fallback) throw new CaptureError("当前页面未识别到商品 ASIN", "INVALID_TARGET", false);
  return fallback;
}

async function currentPageProduct(sourceTab) {
  const tab = sourceTab?.id ? sourceTab : (await chrome.tabs.query({ active: true, currentWindow: true }))[0];
  if (!tab?.id || !config.isAmazonHost(tab.url || "")) throw new CaptureError("请先打开亚马逊商品详情页", "INVALID_TARGET", false);
  const target = config.targetFromUrl(tab.url || "");
  if (!target) throw new CaptureError("当前页面未识别到商品 ASIN", "INVALID_TARGET", false);
  const result = await scrapeProductFromTab(tab.id, target, 15000, "COLLECT_PRODUCT_V2");
  return { ...target, seedProduct: result.product, seedIsCurrentPage: true };
}

async function queueSummary() {
  const auth = await authState();
  if (!auth.account) return { counts: { waiting: 0, collecting: 0, success: 0, partial_success: 0, failed: 0 }, total: 0, cacheSize: 0, tasks: [] };
  return captureQueue.summary(auth.account);
}

async function initializeQueue(wakeWaiting = false) {
  await captureQueue.clearCache();
  await captureQueue.purgeFinal();
  await chrome.storage.local.remove("capturedQueue");
  const recovered = await captureQueue.recoverInterrupted(wakeWaiting);
  if (recovered) {
    const auth = await authState();
    if (auth.account) {
      const tasks = await captureQueue.list(auth.account, SETTINGS.MAX_TASKS);
      await mapLimit(tasks.filter(task => task.status === STATUS.WAITING && task.error === "扩展后台已重启，任务正在恢复"), 2, async task => {
        await updateErpTask(task, {
          status: STATUS.WAITING,
          attempts: task.attempts,
          error: "扩展后台已重启，任务正在恢复"
        }).catch(() => {});
      });
    }
  }
  scheduleQueue(600);
}

async function runAiTask(task) {
  if (!task?.id || !task?.prompt || !AI_URLS[task.tool]) throw new Error("AI任务参数不完整");
  if (cancelledEditorTaskIds.has(task.id)) return { ok: true, cancelled: true };
  const preparedTask = { ...task, conversationTitle: task.conversationTitle || "优化亚马逊产品信息" };
  await chrome.storage.local.set({ activeAiTask: preparedTask });
  if (cancelledEditorTaskIds.has(task.id)) {
    await chrome.storage.local.remove("activeAiTask");
    return { ok: true, cancelled: true };
  }
  const host = new URL(AI_URLS[task.tool]).hostname;
  const tabs = await chrome.tabs.query({});
  const candidates = tabs.filter(tab => {
    try { return new URL(tab.url || "").hostname === host; } catch { return false; }
  });
  const existing = candidates.find(tab => (tab.title || "").includes(preparedTask.conversationTitle)) || candidates[0];
  if (existing?.id) {
    // AI sites throttle inactive tabs.  Keep the provider in the foreground
    // while it generates, then AI_TASK_RESULT returns focus to the editor.
    await chrome.tabs.update(existing.id, { active: true });
    try {
      await chrome.tabs.sendMessage(existing.id, { type: "START_AI_TASK", task: preparedTask });
    } catch {
      await chrome.tabs.update(existing.id, { url: existing.url || AI_URLS[task.tool], active: true });
    }
    return { ok: true, reused: true };
  }
  await chrome.tabs.create({ url: AI_URLS[task.tool], active: true });
  return { ok: true, reused: false };
}

async function waitForTabComplete(tabId, timeoutMs = 30000, expectedHost = "") {
  const started = Date.now();
  while (Date.now() - started < timeoutMs) {
    const tab = await chrome.tabs.get(tabId);
    let host = "";
    try { host = new URL(tab.url || "").hostname; } catch {}
    if (tab.status === "complete" && (!expectedHost || host === expectedHost)) return tab;
    await delay(250);
  }
  throw new Error("翻译页签加载超时");
}

async function runTranslationTab(task) {
  if (!task?.id || !task?.content) throw new Error("翻译任务参数不完整");
  if (cancelledEditorTaskIds.has(task.id)) return { ok: true, cancelled: true };
  const toolUrl = task.translationTool?.url || "https://translate.google.com/?sl=auto&tl=zh-CN&op=translate";
  let parsed;
  try { parsed = new URL(toolUrl); } catch { throw new Error("翻译软件网址无效"); }
  if (!/^https?:$/.test(parsed.protocol)) throw new Error("翻译软件网址必须以 http 或 https 开头");
  const tab = await chrome.tabs.create({ url: "about:blank", active: false });
  const originalActiveTabs = await chrome.tabs.query({ active: true, currentWindow: true }).catch(() => []);
  const restoreTabId = task.returnTabId || originalActiveTabs[0]?.id || 0;
  const storedTask = { ...task, translationTabId: tab.id, toolUrl };
  await chrome.storage.local.set({ activeGoogleTask: storedTask });
  if (cancelledEditorTaskIds.has(task.id)) {
    await chrome.storage.local.remove("activeGoogleTask");
    await chrome.tabs.remove(tab.id).catch(() => {});
    return { ok: true, cancelled: true };
  }
  await chrome.tabs.update(tab.id, { url: toolUrl, active: false });
  const injectBridge = async () => {
    let lastError = null;
    for (let attempt = 0; attempt < 3; attempt++) {
      try {
        await waitForTabComplete(tab.id, 30000, parsed.hostname);
        await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ["google-translate-bridge.js"] });
        return;
      } catch (error) {
        lastError = error;
        if (attempt < 2) {
          await chrome.tabs.update(tab.id, { url: toolUrl, active: false }).catch(() => {});
          await delay(1200 * (attempt + 1));
        }
      }
    }
    throw lastError || new Error("翻译页桥接脚本加载失败");
  };
  try {
    // Chrome 会强烈节流完全隐藏的翻译页。加载完成后由扩展自动激活，
    // 无需用户手工点击；译文返回或失败后再自动切回原产品页。
    await waitForTabComplete(tab.id, 30000, parsed.hostname);
    await chrome.tabs.update(tab.id, { active: true });
    await delay(50);
    await injectBridge();
    const started = Date.now();
    while (Date.now() - started < 95000) {
      const { activeGoogleTask, lastGoogleTranslateResult } = await chrome.storage.local.get(["activeGoogleTask", "lastGoogleTranslateResult"]);
      if (lastGoogleTranslateResult?.taskId === task.id) {
        if (restoreTabId) await chrome.tabs.update(restoreTabId, { active: true }).catch(() => {});
        return { ok: true, completed: true };
      }
      if (cancelledEditorTaskIds.has(task.id) || activeGoogleTask?.id !== task.id) {
        if (restoreTabId) await chrome.tabs.update(restoreTabId, { active: true }).catch(() => {});
        return { ok: true, cancelled: true };
      }
      await chrome.tabs.sendMessage(tab.id, { type: "WAKE_GOOGLE_TRANSLATION", taskId: task.id }).catch(() => {});
      await delay(350);
    }
    throw new Error("翻译页在后台等待超时");
  } catch (error) {
    if (cancelledEditorTaskIds.has(task.id)) {
      if (restoreTabId) await chrome.tabs.update(restoreTabId, { active: true }).catch(() => {});
      return { ok: true, cancelled: true };
    }
    await chrome.storage.local.remove("activeGoogleTask");
    const failure = { type: "GOOGLE_TRANSLATE_RESULT", taskId: task.id, returnTabId: task.returnTabId, ok: false, error: error.message };
    await chrome.storage.local.set({ lastGoogleTranslateResult: failure });
    if (restoreTabId) await chrome.tabs.update(restoreTabId, { active: true }).catch(() => {});
    throw error;
  }
}

async function cancelEditorTask(taskId) {
  const id = String(taskId || "").trim();
  if (!id) throw new Error("缺少需要终止的任务编号");
  cancelledEditorTaskIds.add(id);
  const stored = await chrome.storage.local.get([
    "activeAiTask", "activeGoogleTask", "lastAiTaskResult", "lastGoogleTranslateResult"
  ]);
  const activeAiTask = stored.activeAiTask?.id === id ? stored.activeAiTask : null;
  const activeGoogleTask = stored.activeGoogleTask?.id === id ? stored.activeGoogleTask : null;
  const removeKeys = [];
  if (activeAiTask) removeKeys.push("activeAiTask");
  if (activeGoogleTask) removeKeys.push("activeGoogleTask");
  if (stored.lastAiTaskResult?.taskId === id) removeKeys.push("lastAiTaskResult");
  if (stored.lastGoogleTranslateResult?.taskId === id) removeKeys.push("lastGoogleTranslateResult");
  if (removeKeys.length) await chrome.storage.local.remove(removeKeys);

  const tabs = await chrome.tabs.query({}).catch(() => []);
  const aiHosts = new Set(Object.values(AI_URLS).map(value => new URL(value).hostname));
  await Promise.all(tabs.map(async tab => {
    let host = "";
    try { host = new URL(tab.url || "").hostname; } catch {}
    if (!tab.id || (!aiHosts.has(host) && host !== "translate.google.com")) return;
    const type = host === "translate.google.com" ? "CANCEL_GOOGLE_TRANSLATION" : "CANCEL_AI_TASK";
    await chrome.tabs.sendMessage(tab.id, { type, taskId: id }).catch(() => {});
  }));

  if (activeGoogleTask?.translationTabId) {
    await chrome.tabs.remove(activeGoogleTask.translationTabId).catch(() => {});
    if (activeGoogleTask.returnTabId) {
      await chrome.tabs.update(activeGoogleTask.returnTabId, { active: true }).catch(() => {});
    }
  }
  return { cancelled: Boolean(activeAiTask || activeGoogleTask), taskId: id };
}

function respond(sendResponse, operation) {
  Promise.resolve(operation)
    .then(data => sendResponse({ ok: true, ...data }))
    .catch(error => sendResponse({ ok: false, error: errorMessage(error), code: error?.code || "" }));
  return true;
}

function isExtensionPageSender(sender) {
  const extensionRoot = chrome.runtime.getURL("");
  return sender?.id === chrome.runtime.id && String(sender?.url || "").startsWith(extensionRoot);
}

chrome.runtime.onInstalled.addListener(({ reason }) => {
  // initializeQueue 只清理旧商品缓存和已结束记录，保留尚未完成的动作，
  // 避免扩展更新或 service worker 重启时把正在采集的任务误删。
  initializeQueue().catch(() => {});
  if (reason === "install") chrome.tabs.create({ url: DASHBOARD_URL });
});
chrome.runtime.onStartup.addListener(() => initializeQueue().catch(() => {}));
chrome.alarms.onAlarm.addListener(alarm => {
  if (alarm.name === SETTINGS.ALARM_NAME) processQueue().catch(() => {});
});

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if ([
    "GET_EXTENSION_ACCOUNTS",
    "LOGIN_EXTENSION_ACCOUNT",
    "SWITCH_EXTENSION_ACCOUNT",
    "SELECT_EXTENSION_STORE",
    "REMOVE_EXTENSION_ACCOUNT"
  ].includes(message?.type) && !isExtensionPageSender(sender)) {
    return respond(sendResponse, Promise.reject(new CaptureError("该账号操作只能在易联跨境插件窗口中执行", "INVALID_SENDER", false)));
  }
  if (message?.type === "ENQUEUE_CAPTURE") {
    return respond(sendResponse, enqueueTargets([message.target], { includeVariants: message.includeVariants }).then(result => ({
      accepted: result.accepted.length,
      duplicate: result.duplicates.length > 0,
      task: result.accepted[0] || result.duplicates[0],
      warning: result.warning
    })));
  }
  if (message?.type === "ENQUEUE_CAPTURE_BATCH") {
    return respond(sendResponse, enqueueTargets(message.targets, { includeVariants: message.includeVariants, batchId: message.batchId }).then(result => ({
      accepted: result.accepted.length,
      duplicates: result.duplicates.length,
      rejected: result.rejected.length,
      batchId: result.batchId,
      warning: result.warning
    })));
  }
  if (message?.type === "STOP_CAPTURE_BATCH") {
    return respond(sendResponse, authState().then(async auth => {
      const batchId = String(message.batchId || "");
      if (!batchId) throw new CaptureError("批量任务编号无效", "INVALID_BATCH", false);
      cancelledBatchIds.add(batchId);
      const cancelled = await captureQueue.cancelBatch(batchId, auth.account);
      cancelled.forEach(task => taskAbortControllers.get(task.id)?.abort());
      await Promise.all(cancelled.map(task => updateErpTask(task, { status: STATUS.FAILED, attempts: task.attempts, error: "用户已停止本次批量采集" }).catch(() => {})));
      return { batchId, stopped: cancelled.length };
    }));
  }
  if (message?.type === "GET_CAPTURE_BATCH_STATUS") {
    return respond(sendResponse, authState().then(auth => captureQueue.batchSummary(message.batchId, auth.account)));
  }
  if (message?.type === "COLLECT_CURRENT") {
    return respond(sendResponse, currentPageProduct(sender.tab).then(target => enqueueTargets([target], { includeVariants: message.includeVariants })).then(result => ({
      accepted: true,
      duplicate: result.duplicates.length > 0,
      task: result.accepted[0] || result.duplicates[0]
    })));
  }
  if (message?.type === "COLLECT_URL" || message?.type === "COLLECT_CARD" || message?.type === "RECOLLECT_URL") {
    const baseTarget = message.target || config.targetFromUrl(message.url || "");
    const target = message.type === "COLLECT_CARD" && baseTarget
      ? { ...baseTarget, previewProduct: message.preview && typeof message.preview === "object" ? message.preview : null }
      : baseTarget;
    return respond(sendResponse, enqueueTargets([target], { includeVariants: message.includeVariants, force: message.type === "RECOLLECT_URL" }).then(result => ({
      accepted: true,
      duplicate: result.duplicates.length > 0,
      task: result.accepted[0] || result.duplicates[0]
    })));
  }
  if (message?.type === "QUEUE_CAPTURED_PRODUCT" || message?.type === "QUEUE_CAPTURED_PRODUCTS") {
    const products = message.type === "QUEUE_CAPTURED_PRODUCT" ? [message.product] : message.products;
    const targets = (products || []).map(product => ({
      asin: product?.asin,
      domain: product?.domain || product?.url,
      url: product?.url,
      seedProduct: product?.status === "complete" ? product : null
    }));
    return respond(sendResponse, enqueueTargets(targets).then(result => ({ accepted: result.accepted.length, duplicates: result.duplicates.length })));
  }
  if (message?.type === "GET_CAPTURE_TASKS") return respond(sendResponse, queueSummary());
  if (message?.type === "GET_EXTENSION_ACCOUNTS") return respond(sendResponse, extensionAuth.ensureLegacyProfile());
  if (message?.type === "LOGIN_EXTENSION_ACCOUNT") {
    return respond(sendResponse, extensionAuth.login(message.username, message.password).then(account => {
      scheduleQueue();
      return { account };
    }));
  }
  if (message?.type === "SWITCH_EXTENSION_ACCOUNT") {
    return respond(sendResponse, extensionAuth.switchAccount(message.accountId).then(account => {
      scheduleQueue();
      return { account };
    }));
  }
  if (message?.type === "SELECT_EXTENSION_STORE") {
    return respond(sendResponse, extensionAuth.selectStore(message.storeId).then(account => ({ account })));
  }
  if (message?.type === "REMOVE_EXTENSION_ACCOUNT") {
    return respond(sendResponse, extensionAuth.removeAccount(message.accountId));
  }
  if (message?.type === "GET_CAPTURE_OPTIONS") return respond(sendResponse, captureOptions().then(options => ({ options })));
  if (message?.type === "SET_CAPTURE_OPTIONS") return respond(sendResponse, setCaptureOptions(message.options).then(options => ({ options })));
  if (message?.type === "RETRY_CAPTURE_TASK") {
    return respond(sendResponse, authState().then(auth => captureQueue.retryFailed(message.taskId || "", auth.account)).then(tasks => {
      if (tasks.length) scheduleQueue();
      return { retried: tasks.length };
    }));
  }
  if (message?.type === "RETRY_FAILED_CAPTURE_TASKS") {
    return respond(sendResponse, authState().then(auth => captureQueue.retryFailed("", auth.account)).then(tasks => {
      if (tasks.length) scheduleQueue();
      return { retried: tasks.length };
    }));
  }
  if (message?.type === "RELEASE_CAPTURE_PRODUCTS") {
    return respond(sendResponse, authState().then(async auth => {
      const targets = Array.isArray(message.targets) ? message.targets : [];
      let tasks = 0;
      let cache = 0;
      for (const target of targets) {
        const released = await captureQueue.releaseProduct({ ...target, account: auth.account });
        tasks += released.tasks || 0;
        cache += released.cache || 0;
      }
      return { released: targets.length, tasks, cache };
    }));
  }
  if (message?.type === "CLEAR_CAPTURE_CACHE") return respond(sendResponse, authState().then(auth => captureQueue.clearCache(auth.account)).then(() => ({ cleared: true })));
  if (message?.type === "OPEN_DASHBOARD") {
    chrome.tabs.create({ url: SETTINGS.WORKBENCH_URL });
    sendResponse({ ok: true });
  }
  if (message?.type === "SYNC_WORKFLOW_CONTEXT") {
    return respond(sendResponse, extensionAuth.syncContext({
      token: message.token,
      dataAccount: message.account,
      storeId: message.storeId
    }).then(account => ({ synced: true, account })));
  }
  if (message?.type === "RUN_AI_TASK") return respond(sendResponse, runAiTask({ ...message.task, returnTabId: sender.tab?.id }));
  if (message?.type === "CANCEL_EDITOR_TASK") return respond(sendResponse, cancelEditorTask(message.taskId));
  if (message?.type === "RUN_TRANSLATION_TAB" || message?.type === "RUN_GOOGLE_TRANSLATE_TAB") {
    return respond(sendResponse, runTranslationTab({
      id: message.taskId,
      content: message.content,
      translationTool: message.translationTool,
      returnTabId: sender.tab?.id,
      createdAt: new Date().toISOString()
    }));
  }
  if (message?.type === "GOOGLE_TRANSLATE_RESULT" && sender.tab?.id) {
    chrome.storage.local.set({ lastGoogleTranslateResult: message }).catch(() => {});
    if (message.ok) setTimeout(() => chrome.tabs.remove(sender.tab.id).catch(() => {}), 800);
  }
  if (message?.type === "AI_TASK_RESULT") {
    chrome.storage.local.set({ lastAiTaskResult: message }).catch(() => {});
    if (message.returnTabId && !message.keepAiTabActive) {
      chrome.tabs.update(message.returnTabId, { active: true }).catch(() => {});
    }
  }
  if (message?.type === "GET_CURRENT_TAB_ID") sendResponse({ ok: true, tabId: sender.tab?.id });
});

initializeQueue().catch(() => {});
