const DASHBOARD_URL =
  "https://qpmcn.zhuanspirit.com/#/data-manage/n-specific_conversion";
const LEGACY_DASHBOARD_URL =
  "http://data.qpmcn.com/zlj/qianshuju/#/data-manage/n-specific_conversion";
const RUNNER_URL = "http://127.0.0.1:4318";
const HELPER_SOURCE = "nt-chrome-helper";

let activeRun = null;

const delay = (milliseconds) =>
  new Promise((resolve) => setTimeout(resolve, milliseconds));

async function notify(workbenchTabId, type, payload = {}) {
  try {
    await chrome.tabs.sendMessage(workbenchTabId, {
      source: HELPER_SOURCE,
      type,
      payload,
    });
  } catch {
    // The local page may have been reloaded; the runner result remains available.
  }
}

async function readLatestDataSnapshot() {
  try {
    const response = await fetch(`${RUNNER_URL}/api/data/latest`, {
      cache: "no-store",
    });
    if (!response.ok) {
      return { available: false };
    }
    return await response.json();
  } catch {
    return { available: false };
  }
}

async function readLatestTaskSnapshot() {
  try {
    const response = await fetch(`${RUNNER_URL}/api/tasks/latest`, {
      cache: "no-store",
    });
    if (!response.ok) {
      return null;
    }
    return await response.json();
  } catch {
    return null;
  }
}

function progress(workbenchTabId, patch) {
  return notify(workbenchTabId, "NT_TASK_PROGRESS", patch);
}

async function allowDashboardDownloads() {
  for (const primaryPattern of [
    "https://qpmcn.zhuanspirit.com/*",
    "https://ldpqbi.zhuanspirit.com/*",
    "https://*.zhuanspirit.com/*",
    "http://qpmcn.zhuanspirit.com/*",
    "http://ldpqbi.zhuanspirit.com/*",
    "http://*.zhuanspirit.com/*",
    "https://data.qpmcn.com/*",
    "http://data.qpmcn.com/*",
  ]) {
    await chrome.contentSettings.automaticDownloads.set({
      primaryPattern,
      setting: "allow",
    });
  }
}

async function focusTab(tab) {
  if (tab.windowId) {
    await chrome.windows.update(tab.windowId, { focused: true }).catch(() => {});
  }
  await chrome.tabs.update(tab.id, { active: true });
}

async function findOrOpenDashboardTab() {
  const allTabs = await chrome.tabs.query({});
  const getTabUrl = (tab) => tab.url || tab.pendingUrl || "";
  const getTabTitle = (tab) => tab.title || "";
  const isQianDataTab = (tab) =>
    getTabUrl(tab).includes("zhuanspirit.com") ||
    getTabUrl(tab).includes("data.qpmcn.com") ||
    getTabTitle(tab).includes("乾数据") ||
    getTabTitle(tab).includes("转化看板");
  const qianDataTabs = allTabs.filter(isQianDataTab);
  const dashboardTabs = qianDataTabs.filter((tab) =>
    getTabUrl(tab).includes("n-specific_conversion") ||
    getTabTitle(tab).includes("N特转化") ||
    getTabTitle(tab).includes("n特转化"),
  );
  const openedDashboard =
    dashboardTabs.find((tab) => tab.active) ??
    dashboardTabs.find((tab) => tab.highlighted) ??
    dashboardTabs[0];
  if (openedDashboard?.id) {
    if (!getTabUrl(openedDashboard).includes("n-specific_conversion")) {
      await chrome.tabs.update(openedDashboard.id, {
        active: true,
        url: getTabUrl(openedDashboard).includes("data.qpmcn.com")
          ? LEGACY_DASHBOARD_URL
          : DASHBOARD_URL,
      });
      if (openedDashboard.windowId) {
        await chrome.windows
          .update(openedDashboard.windowId, { focused: true })
          .catch(() => {});
      }
    } else {
      await focusTab(openedDashboard);
    }
    return {
      tabId: openedDashboard.id,
      mode: "reused_dashboard",
      foundTabs: qianDataTabs.length,
    };
  }

  const openedQianData =
    qianDataTabs.find((tab) => tab.active) ??
    qianDataTabs.find((tab) => tab.highlighted) ??
    qianDataTabs.find((tab) => getTabUrl(tab).includes("data.qpmcn.com")) ??
    qianDataTabs.find((tab) => getTabUrl(tab).includes("qpmcn.zhuanspirit.com")) ??
    qianDataTabs[0];
  if (openedQianData?.id) {
    await chrome.tabs.update(openedQianData.id, {
      active: true,
      url: getTabUrl(openedQianData).includes("data.qpmcn.com")
        ? LEGACY_DASHBOARD_URL
        : DASHBOARD_URL,
    });
    if (openedQianData.windowId) {
      await chrome.windows
        .update(openedQianData.windowId, { focused: true })
        .catch(() => {});
    }
    return {
      tabId: openedQianData.id,
      mode: "reused_qiandata",
      foundTabs: qianDataTabs.length,
    };
  }

  const created = await chrome.tabs.create({
    active: true,
    url: DASHBOARD_URL,
  });
  if (!created.id) {
    throw new Error("没有成功打开乾数据平台。");
  }
  return { tabId: created.id, mode: "created", foundTabs: 0 };
}

async function executeDashboardAction(tabId, action, payload = {}) {
  let results;
  try {
    results = await chrome.scripting.executeScript({
      target: { tabId, allFrames: true },
      world: "MAIN",
      args: [action, payload],
      func: async (requestedAction, requestedPayload) => {
      const normalize = (value) =>
        String(value ?? "")
          .normalize("NFKC")
          .replace(/\s+/g, "");
      const sleep = (milliseconds) =>
        new Promise((resolve) => setTimeout(resolve, milliseconds));
      const visible = (element) => {
        if (!(element instanceof HTMLElement)) {
          return false;
        }
        const style = getComputedStyle(element);
        const rect = element.getBoundingClientRect();
        return (
          style.display !== "none" &&
          style.visibility !== "hidden" &&
          rect.width > 0 &&
          rect.height > 0
        );
      };
      const waitFor = async (factory, timeout = 20_000) => {
        const deadline = Date.now() + timeout;
        while (Date.now() < deadline) {
          const value = factory();
          if (value) {
            return value;
          }
          await sleep(200);
        }
        return null;
      };
      const click = (element) => {
        if (!(element instanceof HTMLElement)) {
          throw new Error("页面按钮不存在。");
        }
        element.scrollIntoView({ block: "center", inline: "center" });
        element.dispatchEvent(
          new MouseEvent("mousedown", {
            bubbles: true,
            cancelable: true,
            view: window,
          }),
        );
        element.dispatchEvent(
          new MouseEvent("mouseup", {
            bubbles: true,
            cancelable: true,
            view: window,
          }),
        );
        element.click();
      };
      const hover = (element) => {
        if (!(element instanceof HTMLElement)) {
          return;
        }
        element.scrollIntoView({ block: "center", inline: "center" });
        for (const type of [
          "pointerover",
          "mouseover",
          "mouseenter",
          "pointermove",
          "mousemove",
        ]) {
          const EventConstructor =
            type.startsWith("pointer") &&
            typeof PointerEvent === "function"
              ? PointerEvent
              : MouseEvent;
          element.dispatchEvent(
            new EventConstructor(type, {
              bubbles: type !== "mouseenter",
              cancelable: true,
              view: window,
            }),
          );
        }
      };
      const exactVisibleText = (selector, value, root = document) =>
        Array.from(root.querySelectorAll(selector)).find(
          (element) =>
            visible(element) &&
            normalize(element.textContent) === normalize(value),
        );
      const waitForIdle = async () => {
        await sleep(700);
        await waitFor(
          () =>
            !Array.from(
              document.querySelectorAll(".ant-spin-spinning"),
            ).some(visible),
          30_000,
        );
        await sleep(400);
      };
      const findCard = (title) => {
        const caption = Array.from(
          document.querySelectorAll(".caption"),
        ).find(
          (element) => normalize(element.textContent) === normalize(title),
        );
        return caption?.closest(".component-card") ?? null;
      };
      const getPageScroller = () =>
        document.querySelector(".root-container") ??
        document.scrollingElement ??
        document.documentElement;
      const findCardWithScroll = async (title, attempts = 20) => {
        const scrollingElement = getPageScroller();
        for (let index = 0; index < attempts; index += 1) {
          const card = findCard(title);
          if (card) {
            card.scrollIntoView({
              block: "center",
              inline: "nearest",
            });
            await sleep(350);
            return findCard(title) ?? card;
          }
          scrollingElement.scrollTop += Math.max(window.innerHeight, 800);
          await sleep(180);
        }
        return null;
      };
      const findQueryButton = () =>
        Array.from(document.querySelectorAll("button")).find(
          (button) =>
            normalize(button.textContent) === "查询" && visible(button),
        );
      const findCardValue = (card, value) =>
        exactVisibleText(".cell-raw-data", value, card);
      const isSelectedCell = (cell, card) => {
        let ancestor = cell;
        while (ancestor && ancestor !== card) {
          if (
            getComputedStyle(ancestor).backgroundColor ===
            "rgb(213, 227, 255)"
          ) {
            return true;
          }
          ancestor = ancestor.parentElement;
        }
        return false;
      };
      const waitForCardValue = async (title, value) => {
        const deadline = Date.now() + 55_000;
        let refreshedAt = 0;
        let refreshAttempts = 0;
        let queryRetried = false;
        const startedAt = Date.now();

        while (Date.now() < deadline) {
          const card = findCard(title);
          if (card) {
            const cell = findCardValue(card, value);
            if (cell) {
              return { card, cell };
            }

            const refresh = card.querySelector(
              ".component-loader-error .refresh-data",
            );
            if (
              refresh &&
              refreshAttempts < 2 &&
              Date.now() - refreshedAt > 1_500
            ) {
              click(refresh);
              refreshAttempts += 1;
              refreshedAt = Date.now();
              await sleep(700);
              continue;
            }
          }

          if (!queryRetried && Date.now() - startedAt > 16_000) {
            const queryButton = findQueryButton();
            if (queryButton) {
              click(queryButton);
              queryRetried = true;
              await waitForIdle();
              continue;
            }
          }
          await sleep(350);
        }
        return null;
      };
      const waitForExportCard = async (title) => {
        const deadline = Date.now() + 55_000;
        let refreshedAt = 0;
        let refreshAttempts = 0;

        while (Date.now() < deadline) {
          const card = findCard(title);
          if (card) {
            const refresh = card.querySelector(
              ".component-loader-error .refresh-data",
            );
            if (
              refresh &&
              refreshAttempts < 2 &&
              Date.now() - refreshedAt > 1_500
            ) {
              click(refresh);
              refreshAttempts += 1;
              refreshedAt = Date.now();
              await sleep(700);
              continue;
            }

            const loading = card.querySelector(
              ".component-loader-loading",
            );
            const failed = card.querySelector(
              ".component-loader-error",
            );
            const hasRows = card.querySelector(
              ".cell-raw-data, .table-cell",
            );
            if (!loading && !failed && hasRows) {
              return card;
            }
          }
          await sleep(350);
        }
        return null;
      };

      const pageText = normalize(document.body?.innerText ?? "");
      const wrapperCount = document.querySelectorAll(
        ".query-field-wrapper",
      ).length;
      const isDashboardFrame = Boolean(
        wrapperCount > 0 ||
          (pageText.includes("统计日期") &&
            pageText.includes("品类") &&
            pageText.includes("查询")) ||
          pageText.includes("指标趋势") ||
          location.href.includes("n-specific_conversion"),
      );

      try {
        if (!isDashboardFrame) {
          return {
            handled: false,
            ready: false,
            title: document.title,
            url: location.href,
            wrapperCount,
            textPreview: pageText.slice(0, 80),
          };
        }

        if (requestedAction === "probe") {
          return {
            handled: true,
            ready:
              wrapperCount >= 10 ||
              (pageText.includes("统计日期") &&
                pageText.includes("订单话术") &&
                pageText.includes("查询")),
            title: document.title,
            url: location.href,
            wrapperCount,
            textPreview: pageText.slice(0, 80),
          };
        }

        if (requestedAction === "prepare") {
          const wrappers = Array.from(
            document.querySelectorAll(".query-field-wrapper"),
          );
          if (wrappers.length < 13) {
            throw new Error("乾数据筛选区还没有加载完成。");
          }

          const dateInputs = Array.from(
            wrappers[3].querySelectorAll("input"),
          );
          if (dateInputs.length < 2) {
            throw new Error("没有找到统计日期输入框。");
          }

          if (
            dateInputs[0].value !== requestedPayload.start ||
            dateInputs[1].value !== requestedPayload.end
          ) {
            click(dateInputs[0]);
            const picker = await waitFor(() => {
              const candidate = document.querySelector(
                ".ant-picker-dropdown",
              );
              return visible(candidate) ? candidate : null;
            });
            if (!picker) {
              throw new Error("统计日期日历没有打开。");
            }

            const findDateCell = (date) =>
              Array.from(
                picker.querySelectorAll(
                  `.ant-picker-cell[title="${date}"].ant-picker-cell-in-view`,
                ),
              ).find(visible);
            const navigateToDate = async (date) => {
              for (let attempts = 0; attempts < 30; attempts += 1) {
                const targetCell = findDateCell(date);
                if (targetCell) {
                  return targetCell;
                }
                const inViewDates = Array.from(
                  picker.querySelectorAll(
                    ".ant-picker-cell.ant-picker-cell-in-view[title]",
                  ),
                )
                  .filter(visible)
                  .map((cell) => cell.getAttribute("title"))
                  .filter(Boolean)
                  .sort();
                if (!inViewDates.length) {
                  throw new Error("日期日历没有显示可选日期。");
                }
                const targetIsEarlier = date < inViewDates[0];
                const selector = targetIsEarlier
                  ? ".ant-picker-header-prev-btn"
                  : ".ant-picker-header-next-btn";
                const navigationButton = Array.from(
                  picker.querySelectorAll(selector),
                ).find(visible);
                if (!navigationButton) {
                  throw new Error("日期日历无法切换月份。");
                }
                click(navigationButton);
                await sleep(120);
              }
              throw new Error(`日期日历无法定位到 ${date}。`);
            };

            click(await navigateToDate(requestedPayload.start));
            await sleep(180);
            click(await navigateToDate(requestedPayload.end));
            await sleep(250);
          }

          if (
            dateInputs[0].value !== requestedPayload.start ||
            dateInputs[1].value !== requestedPayload.end
          ) {
            throw new Error(
              `统计日期没有成功设置为 ${requestedPayload.start} 至 ${requestedPayload.end}。`,
            );
          }

          const categoryWrapper = wrappers[4];
          const currentCategory = categoryWrapper.querySelector(
            ".single-select-value",
          )?.textContent;
          if (
            normalize(currentCategory) !==
            normalize(requestedPayload.category)
          ) {
            const categoryControl =
              categoryWrapper.querySelector(
                ".ant-select-selector, .single-select-content, input",
              ) ?? categoryWrapper;
            click(categoryControl);
            const option = await waitFor(() =>
              exactVisibleText(
                ".dropdown-item, .ant-select-item-option-content, [role='option']",
                requestedPayload.category,
              ),
            );
            if (!option) {
              throw new Error(
                `没有找到品类“${requestedPayload.category}”。`,
              );
            }
            click(option);
          }

          const queryCard = document.querySelectorAll(".component-card")[0];
          const queryButton = queryCard
            ? Array.from(queryCard.querySelectorAll("button")).find(
                (button) => normalize(button.textContent) === "查询",
              )
            : null;
          if (!queryButton) {
            throw new Error("没有找到查询按钮。");
          }
          click(queryButton);
          await waitForIdle();
          return { handled: true, ready: true };
        }

        if (requestedAction === "select_dimensions") {
          const scrollingElement = getPageScroller();
          scrollingElement.scrollTop = 0;
          await sleep(120);

          let videoTag = null;
          for (let attempts = 0; attempts < 20; attempts += 1) {
            videoTag = exactVisibleText(
              ".bi-tab-item-text[title='视频标签'], button, [role='tab'], span, div",
              "视频标签",
            );
            if (videoTag) {
              break;
            }
            scrollingElement.scrollTop += 700;
            await sleep(120);
          }
          if (!videoTag) {
            throw new Error("没有找到“视频标签”入口。");
          }
          click(videoTag);
          await sleep(700);

          for (const selection of [
            { card: "平台", value: "抖音" },
            { card: "定制/贴片/图文", value: "定制" },
          ]) {
            const initialCard = await findCardWithScroll(selection.card);
            if (!initialCard) {
              throw new Error(`没有找到“${selection.card}”数据卡片。`);
            }
            const found = await waitForCardValue(
              selection.card,
              selection.value,
            );
            if (!found) {
              throw new Error(
                `“${selection.card}”数据加载失败，自动刷新后仍未找到“${selection.value}”。`,
              );
            }
            if (!isSelectedCell(found.cell, found.card)) {
              click(found.cell);
              await waitForIdle();
            } else {
              await sleep(120);
            }
          }
          return { handled: true, ready: true };
        }

        if (requestedAction === "export") {
          const initialCard = await findCardWithScroll(
            requestedPayload.title,
            28,
          );
          if (!initialCard) {
            throw new Error(
              `没有找到“${requestedPayload.title}”导出卡片。`,
            );
          }
          const card = await waitForExportCard(requestedPayload.title);
          if (!card) {
            throw new Error(
              `“${requestedPayload.title}”数据加载失败，自动刷新后仍无法导出。`,
            );
          }
          const more = card.querySelector(
            "i.ant-dropdown-trigger.common-more-outlined",
          );
          if (!more) {
            throw new Error(
              `没有找到“${requestedPayload.title}”导出菜单。`,
            );
          }
          let exportItem = null;
          for (let attempts = 0; attempts < 3; attempts += 1) {
            hover(card);
            hover(more);
            await sleep(120);
            click(more);
            exportItem = await waitFor(
              () =>
                exactVisibleText(
                  "span.control-menu-item, .ant-dropdown-menu-item",
                  "导出",
                ),
              2_500,
            );
            if (exportItem) {
              break;
            }
            await sleep(250);
          }
          if (!exportItem) {
            throw new Error("导出菜单没有打开。");
          }
          click(exportItem);

          const confirm = await waitFor(() => {
            const modals = Array.from(
              document.querySelectorAll(".ant-modal"),
            ).filter(visible);
            for (const modal of modals) {
              const button = Array.from(
                modal.querySelectorAll("button"),
              ).find(
                (item) => normalize(item.textContent) === "确定",
              );
              if (button) {
                return button;
              }
            }
            return null;
          });
          if (!confirm) {
            throw new Error("导出确认窗口没有出现。");
          }
          click(confirm);
          return { handled: true, ready: true };
        }

        return { handled: true, ready: false };
      } catch (error) {
        return {
          handled: isDashboardFrame,
          ready: false,
          error:
            error instanceof Error ? error.message : String(error),
        };
      }
      },
    });
  } catch (error) {
    const tab = await chrome.tabs.get(tabId).catch(() => null);
    const message = error instanceof Error ? error.message : String(error);
    if (action === "probe") {
      return {
        handled: false,
        ready: false,
        diagnostics: [
          {
            title: tab?.title ?? "未读取到标题",
            url: tab?.url ?? tab?.pendingUrl ?? "未读取到URL",
            error: message,
          },
        ],
      };
    }
    throw new Error(
      `无法控制乾数据页面。请检查插件是否有乾数据网站权限。浏览器返回：${message}`,
    );
  }

  const readyHandled = results.find(
    (item) => item.result?.handled && item.result?.ready,
  );
  if (readyHandled) {
    return readyHandled.result;
  }

  const errorHandled = results.find(
    (item) => item.result?.handled && item.result?.error,
  );
  if (errorHandled) {
    throw new Error(errorHandled.result.error);
  }

  const handled = results.find((item) => item.result?.handled);
  if (!handled) {
    if (action === "probe") {
      return {
        handled: false,
        ready: false,
        diagnostics: results
          .map((item) => item.result)
          .filter(Boolean)
          .slice(0, 5),
      };
    }
    return null;
  }
  return handled.result;
}

async function executeDashboardActionWithRetry(
  tabId,
  action,
  payload = {},
  attempts = 5,
) {
  let lastError = null;
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
    try {
      const result = await executeDashboardAction(tabId, action, payload);
      if (result?.ready) {
        return result;
      }
      lastError = new Error(`${action} 暂未就绪`);
    } catch (error) {
      lastError = error;
    }
    await delay(1_500);
  }
  throw lastError ?? new Error(`${action} 没有执行成功。`);
}

async function waitForNewXlsx(startedAt, existingDownloadIds) {
  const startedAfter = new Date(startedAt - 1_000).toISOString();
  const deadline = Date.now() + 60_000;
  while (Date.now() < deadline) {
    const downloads = await chrome.downloads.search({
      startedAfter,
      orderBy: ["-startTime"],
      limit: 20,
    });
    const completed = downloads.find(
      (item) =>
        !existingDownloadIds.has(item.id) &&
        item.state === "complete" &&
        !item.error &&
        item.filename.toLowerCase().endsWith(".xlsx"),
    );
    if (completed?.filename) {
      return completed.filename;
    }
    await delay(400);
  }
  throw new Error("等待 Excel 下载超时，请检查 Chrome 下载列表。");
}

async function exportOne(tabId, title) {
  const startedAt = Date.now();
  const existingDownloads = await chrome.downloads.search({
    orderBy: ["-startTime"],
    limit: 100,
  });
  const downloadPromise = waitForNewXlsx(
    startedAt,
    new Set(existingDownloads.map((item) => item.id)),
  );
  const action = await executeDashboardAction(tabId, "export", { title });
  if (!action?.ready) {
    throw new Error(`没有成功触发“${title}”导出。`);
  }
  return downloadPromise;
}

async function importFiles(files, input) {
  const response = await fetch(`${RUNNER_URL}/api/tasks/import`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      files,
      budgets: input.budgets,
      unitPrices: input.unitPrices,
      period: input.period,
    }),
  });
  const body = await response.json();
  if (!response.ok) {
    throw new Error(body.error || "本机计算器没有接收导出文件。");
  }
  return body;
}

async function recalculateLatest(input) {
  const response = await fetch(`${RUNNER_URL}/api/tasks/recalculate`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(input),
  });
  const body = await response.json();
  if (!response.ok) {
    throw new Error(body.error || "无法复用上次数据。");
  }
  return body;
}

async function searchStreamingEvents(month) {
  const response = await fetch(
    `${RUNNER_URL}/api/streaming/events?month=${encodeURIComponent(month)}`,
    { cache: "no-store" },
  );
  const body = await response.json();
  if (!response.ok) {
    throw new Error(body.error || "赛事检索失败。");
  }
  return body;
}

async function runAutomaticTask(workbenchTabId, input) {
  await allowDashboardDownloads();
  await progress(workbenchTabId, {
    stage: "opening_chrome",
    step: 2,
    percent: 4,
    message: "正在复用已打开的乾数据平台；如未打开则自动进入看板…",
  });
  const dashboardTab = await findOrOpenDashboardTab();
  const dashboardTabId = dashboardTab.tabId;
  await progress(workbenchTabId, {
    stage: "opening_chrome",
    step: 2,
    percent: 6,
    message:
      dashboardTab.mode === "reused_dashboard"
        ? `已复用当前 Chrome 里打开的乾数据看板（识别到 ${dashboardTab.foundTabs} 个乾数据标签）。`
        : dashboardTab.mode === "reused_qiandata"
          ? `已复用当前 Chrome 里打开的乾数据标签，并进入看板（识别到 ${dashboardTab.foundTabs} 个乾数据标签）。`
          : "未找到已打开的乾数据标签，已新打开看板。",
  });
  await delay(1_500);

  const files = {};
  const categories = [
    {
      key: "billiards",
      name: "台球",
      progress: [10, 20, 29, 39],
    },
    {
      key: "cycling",
      name: "骑行",
      progress: [50, 59, 69, 79],
    },
  ];

  for (const category of categories) {
    await progress(workbenchTabId, {
      stage: "filtering",
      step: 2,
      percent: category.progress[0],
      message: `正在设置${category.name}的日期与品类筛选…`,
    });
    const prepared = await executeDashboardActionWithRetry(
      dashboardTabId,
      "prepare",
      {
        category: category.name,
        start: input.period.start,
        end: input.period.end,
      },
      8,
    );
    if (!prepared?.ready) {
      throw new Error(`${category.name}筛选条件没有设置成功。`);
    }

    await progress(workbenchTabId, {
      stage: "filtering",
      step: 2,
      percent: category.progress[1],
      message: `正在选择${category.name} · 抖音 · 定制视频…`,
    });
    const selected = await executeDashboardActionWithRetry(
      dashboardTabId,
      "select_dimensions",
      {},
      5,
    );
    if (!selected?.ready) {
      throw new Error(`${category.name}平台或视频类型没有选择成功。`);
    }

    await progress(workbenchTabId, {
      stage: "downloading",
      step: 2,
      percent: category.progress[2],
      message: `正在下载${category.name} SOP 数据…`,
    });
    files[`${category.key}_sop`] = await exportOne(
      dashboardTabId,
      "广告话术",
    );

    await progress(workbenchTabId, {
      stage: "downloading",
      step: 2,
      percent: category.progress[3],
      message: `正在下载${category.name} SKU 数据…`,
    });
    files[`${category.key}_sku`] = await exportOne(
      dashboardTabId,
      "投放品类",
    );
  }

  await progress(workbenchTabId, {
    stage: "calculating",
    step: 3,
    percent: 88,
    message: "四份数据已下载，正在清洗并计算 SOP、SKU 分配…",
  });
  return importFiles(files, input);
}

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message?.type === "NT_RUNNER_HEALTH") {
    Promise.all([
      fetch(`${RUNNER_URL}/health`, { cache: "no-store" }),
      readLatestDataSnapshot(),
      readLatestTaskSnapshot(),
    ])
      .then(([response, latestData, latestTask]) =>
        sendResponse({ ok: response.ok, latestData, latestTask }),
      )
      .catch(() =>
        sendResponse({
          ok: false,
          latestData: { available: false },
          latestTask: null,
        }),
      );
    return true;
  }

  if (message?.type === "NT_GET_LATEST_TASK") {
    Promise.all([
      fetch(`${RUNNER_URL}/health`, { cache: "no-store" }),
      readLatestDataSnapshot(),
      readLatestTaskSnapshot(),
    ])
      .then(([response, latestData, latestTask]) =>
        sendResponse({ ok: response.ok, latestData, latestTask }),
      )
      .catch(() =>
        sendResponse({
          ok: false,
          latestData: { available: false },
          latestTask: null,
        }),
      );
    return true;
  }

  if (message?.type === "NT_RECALCULATE_TASK" && sender.tab?.id) {
    if (activeRun) {
      sendResponse({ error: "已有一个自动任务正在运行。" });
      return false;
    }
    const workbenchTabId = sender.tab.id;
    activeRun = { workbenchTabId, startedAt: Date.now() };
    void notify(workbenchTabId, "NT_TASK_ACCEPTED", {
      message: "Chrome 助手已接收复用计算任务。",
    });
    recalculateLatest(message.payload)
      .then((task) =>
        notify(workbenchTabId, "NT_TASK_CREATED", { task }),
      )
      .catch((error) =>
        notify(workbenchTabId, "NT_TASK_ERROR", {
          error: error instanceof Error ? error.message : String(error),
        }),
      )
      .finally(() => {
        activeRun = null;
      });
    sendResponse({ ok: true });
    return false;
  }

  if (message?.type === "NT_SEARCH_STREAMING_EVENTS" && sender.tab?.id) {
    const workbenchTabId = sender.tab.id;
    const month = message.payload?.month ?? "";
    searchStreamingEvents(month)
      .then((result) =>
        notify(workbenchTabId, "NT_STREAMING_EVENTS_RESULT", {
          streamingEventsResult: result,
        }),
      )
      .catch((error) =>
        notify(workbenchTabId, "NT_STREAMING_EVENTS_ERROR", {
          error: error instanceof Error ? error.message : String(error),
        }),
      );
    sendResponse({ ok: true });
    return false;
  }

  if (message?.type !== "NT_START_TASK" || !sender.tab?.id) {
    return false;
  }

  if (activeRun) {
    sendResponse({ error: "已有一个自动抓数任务正在运行。" });
    return false;
  }

  const workbenchTabId = sender.tab.id;
  const input = message.payload;
  activeRun = { workbenchTabId, startedAt: Date.now() };
  void notify(workbenchTabId, "NT_TASK_ACCEPTED", {
    message: "Chrome 助手已接收任务。",
  });

  runAutomaticTask(workbenchTabId, input)
    .then((task) =>
      notify(workbenchTabId, "NT_TASK_CREATED", { task }),
    )
    .catch((error) =>
      notify(workbenchTabId, "NT_TASK_ERROR", {
        error: error instanceof Error ? error.message : String(error),
      }),
    )
    .finally(() => {
      activeRun = null;
    });

  sendResponse({ ok: true });
  return false;
});

chrome.runtime.onInstalled.addListener(() => {
  chrome.action.setBadgeBackgroundColor({ color: "#5B4CE1" });
  chrome.action.setBadgeText({ text: "NT" });
});
