(() => {
  const GUARD = "__MMW_ECOMMERCE_FLOW_RECORDER_BACKGROUND__";
  if (globalThis[GUARD]) return;
  globalThis[GUARD] = true;

  const core = globalThis.MMWEcommerceFlowRecorderCore;
  if (!core) throw new Error("MMW ecommerce flow recorder core is missing");

  const META_KEY = "mmwEcommerceFlowRecorderMeta";
  const EVENTS_KEY = "mmwEcommerceFlowRecorderEvents";
  const AUTH_CONTEXT_KEYS = ["pluginAuth", "pluginEntitlement", "activationInfo"];
  const MAX_EVENTS = 3000;
  const ACTIONS = {
    start: "MMW_ECOMMERCE_FLOW_RECORDER_START",
    stopExport: "MMW_ECOMMERCE_FLOW_RECORDER_STOP_EXPORT",
    exportLast: "MMW_ECOMMERCE_FLOW_RECORDER_EXPORT_LAST",
    status: "MMW_ECOMMERCE_FLOW_RECORDER_STATUS",
    event: "MMW_ECOMMERCE_FLOW_RECORDER_EVENT"
  };

  const state = {
    meta: null,
    events: [],
    trackedTabs: new Set(),
    requests: new Map(),
    persistChain: Promise.resolve()
  };

  const ready = chrome.storage.local.get([META_KEY, EVENTS_KEY]).then((stored) => {
    state.meta = stored[META_KEY] || null;
    state.events = Array.isArray(stored[EVENTS_KEY]) ? stored[EVENTS_KEY].slice(-MAX_EVENTS) : [];
    for (const tabId of state.meta?.trackedTabIds || []) {
      if (Number.isInteger(tabId)) state.trackedTabs.add(tabId);
    }
  }).catch(() => {});

  function isActive() {
    return state.meta?.active === true;
  }

  function sessionId() {
    return String(state.meta?.id || "");
  }

  function createSessionId() {
    return `move_${new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}_${Math.random().toString(36).slice(2, 8)}`;
  }

  function senderInfo(sender = {}) {
    return {
      tabId: Number.isInteger(sender.tab?.id) ? sender.tab.id : -1,
      frameId: Number.isInteger(sender.frameId) ? sender.frameId : 0,
      documentId: String(sender.documentId || ""),
      url: core.sanitizeUrl(sender.url || sender.tab?.url || "")
    };
  }

  function publicStatus() {
    return {
      active: isActive(),
      sessionId: sessionId(),
      startedAt: state.meta?.startedAt || "",
      stoppedAt: state.meta?.stoppedAt || "",
      eventCount: state.events.length,
      droppedEvents: Number(state.meta?.droppedEvents || 0),
      hasTrace: state.events.length > 0
    };
  }

  function persist() {
    if (state.meta) state.meta.trackedTabIds = Array.from(state.trackedTabs);
    const snapshot = {
      [META_KEY]: state.meta,
      [EVENTS_KEY]: state.events
    };
    state.persistChain = state.persistChain
      .catch(() => {})
      .then(() => chrome.storage.local.set(snapshot))
      .catch(() => {});
    return state.persistChain;
  }

  function broadcastStatus() {
    const message = { type: "MMW_ECOMMERCE_FLOW_RECORDER_STATE", ...publicStatus() };
    chrome.tabs.query({}).then((tabs) => Promise.all((tabs || []).map((tab) => (
      Number.isInteger(tab.id) ? chrome.tabs.sendMessage(tab.id, message).catch(() => {}) : null
    )))).catch(() => {});
  }

  async function append(category, name, data = {}, context = {}) {
    await ready;
    if (!isActive()) return false;
    const startedAt = Date.parse(state.meta.startedAt) || Date.now();
    const event = {
      seq: Number(state.meta.nextSeq || 1),
      at: new Date().toISOString(),
      elapsedMs: Math.max(0, Date.now() - startedAt),
      category: String(category || "event"),
      name: String(name || "unknown"),
      tabId: Number.isInteger(context.tabId) ? context.tabId : -1,
      frameId: Number.isInteger(context.frameId) ? context.frameId : 0,
      data: core.sanitizeData(data, { maxDepth: 7, maxKeys: 160, maxArray: 60, maxString: 1000 })
    };
    state.meta.nextSeq = event.seq + 1;
    state.events.push(event);
    if (state.events.length > MAX_EVENTS) {
      const removed = state.events.length - MAX_EVENTS;
      state.events.splice(0, removed);
      state.meta.droppedEvents = Number(state.meta.droppedEvents || 0) + removed;
    }
    await persist();
    if (event.seq % 10 === 0) broadcastStatus();
    return true;
  }

  function trackTab(tabId) {
    if (Number.isInteger(tabId) && tabId >= 0) state.trackedTabs.add(tabId);
  }

  function isTrackedTab(tabId) {
    return Number.isInteger(tabId) && state.trackedTabs.has(tabId);
  }

  function sameSessionWindow(windowId) {
    return Number.isInteger(windowId) && windowId === Number(state.meta?.windowId);
  }

  async function captureCookieMetadata(tab = {}) {
    const url = String(tab.url || "");
    if (!/^https?:\/\//i.test(url) || !chrome.cookies?.getAll) return [];
    const cookies = await chrome.cookies.getAll({ url }).catch(() => []);
    return (cookies || []).slice(0, 120).map((cookie) => ({
      name: core.redactText(cookie.name || "", 120),
      domain: core.redactText(cookie.domain || "", 180),
      path: core.redactText(cookie.path || "", 180),
      secure: cookie.secure === true,
      httpOnly: cookie.httpOnly === true,
      sameSite: String(cookie.sameSite || ""),
      isSessionCookie: cookie.session === true,
      expirationDate: Number(cookie.expirationDate || 0) || null,
      valueRecorded: false
    }));
  }

  async function captureLoginContext(tab = {}) {
    const stored = await chrome.storage.local.get(AUTH_CONTEXT_KEYS).catch(() => ({}));
    const auth = core.sanitizeData(stored.pluginAuth || {}, { maxDepth: 7, maxKeys: 180, maxArray: 60, maxString: 1000 });
    return {
      capturedAt: new Date().toISOString(),
      loggedIn: Boolean(stored.pluginAuth?.active || stored.pluginAuth?.loggedIn || stored.pluginAuth?.token),
      auth,
      entitlement: core.sanitizeData(stored.pluginEntitlement || {}, { maxDepth: 5 }),
      activation: core.sanitizeData(stored.activationInfo || {}, { maxDepth: 5 }),
      cookieMetadata: await captureCookieMetadata(tab)
    };
  }

  async function start(sender) {
    await ready;
    const senderTab = sender?.tab;
    const activeTabs = senderTab?.id == null
      ? await chrome.tabs.query({ active: true, lastFocusedWindow: true }).catch(() => [])
      : [];
    const tab = senderTab || activeTabs[0] || {};
    const now = new Date().toISOString();
    state.trackedTabs.clear();
    trackTab(tab.id);
    state.requests.clear();
    state.events = [];
    state.meta = {
      schema: "mmw.ecommerce-flow-trace/v1",
      id: createSessionId(),
      active: true,
      startedAt: now,
      stoppedAt: "",
      extensionVersion: chrome.runtime.getManifest().version,
      windowId: Number.isInteger(tab.windowId) ? tab.windowId : -1,
      startTabId: Number.isInteger(tab.id) ? tab.id : -1,
      startUrl: core.sanitizeUrl(tab.url || sender?.url || ""),
      nextSeq: 1,
      droppedEvents: 0,
      privacy: {
        credentials: "redacted",
        inputValues: "business-selectors-only",
        requestBodies: "sanitized-business-samples",
        responseBodies: "sanitized-runtime-bridge-samples",
        cookies: "names-and-session-attributes-only"
      },
      loginContext: await captureLoginContext(tab),
      trackedTabIds: Array.from(state.trackedTabs)
    };
    await persist();
    await append("session", "recording-started", {
      extensionVersion: state.meta.extensionVersion,
      url: state.meta.startUrl,
      mode: "passive-business-flow"
    }, { tabId: state.meta.startTabId, frameId: sender?.frameId || 0 });
    broadcastStatus();
    return publicStatus();
  }

  function exportFilename() {
    const stamp = String(state.meta?.startedAt || new Date().toISOString()).replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
    return `mmw-ecommerce-flow-trace-${stamp}.json`;
  }

  async function exportTrace() {
    await ready;
    if (!state.events.length || !state.meta) throw new Error("暂无可导出的搬家流程记录");
    const payload = {
      schema: "mmw.ecommerce-flow-trace/v1",
      exportedAt: new Date().toISOString(),
      session: { ...state.meta, trackedTabIds: Array.from(state.trackedTabs) },
      summary: core.buildSummary(state.events),
      events: state.events
    };
    const filename = exportFilename();
    const url = `data:application/json;charset=utf-8,${encodeURIComponent(JSON.stringify(payload, null, 2))}`;
    const downloadId = await chrome.downloads.download({ url, filename, saveAs: true });
    return { filename, downloadId, eventCount: state.events.length };
  }

  async function stopAndExport(sender) {
    await ready;
    if (!isActive()) return exportTrace();
    await append("session", "recording-stopped", {
      url: core.sanitizeUrl(sender?.url || sender?.tab?.url || ""),
      loginContext: await captureLoginContext(sender?.tab || {})
    }, senderInfo(sender));
    state.meta.active = false;
    state.meta.stoppedAt = new Date().toISOString();
    state.requests.clear();
    await persist();
    broadcastStatus();
    return exportTrace();
  }

  function serviceName(config = {}) {
    const direct = config.name || config.service || config.api || config.action || "";
    if (direct) return core.redactText(direct, 160);
    try {
      const url = new URL(String(config.url || ""), "https://invalid.local/");
      return core.redactText(url.searchParams.get("s") || url.searchParams.get("service") || url.pathname, 160);
    } catch (_) {
      return "";
    }
  }

  function summarizeBridge(stage, payload = {}) {
    const config = payload.config && typeof payload.config === "object" ? payload.config : {};
    return {
      stage: String(stage || ""),
      action: core.redactText(payload.action || "", 120),
      service: serviceName(config),
      method: String(config.method || config.type || "GET").toUpperCase(),
      url: core.sanitizeUrl(config.url || ""),
      request: core.summarizeValue(config.data ?? config.params ?? config.body),
      response: core.summarizeValue(payload.data),
      error: core.redactText(payload.error || "", 240)
    };
  }

  function recordRuntimeBridge(stage, payload, sender) {
    const info = senderInfo(sender);
    trackTab(info.tabId);
    return append("runtime", String(stage || "original-bridge"), summarizeBridge(stage, payload), info);
  }

  globalThis.MMWEcommerceFlowRecorder = {
    actions: ACTIONS,
    recordRuntimeBridge,
    status: publicStatus
  };

  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    const type = String(message?.type || "");
    if (type === ACTIONS.start) {
      start(sender).then((status) => sendResponse({ ok: true, ...status })).catch((error) => sendResponse({ ok: false, error: error.message }));
      return true;
    }
    if (type === ACTIONS.stopExport) {
      stopAndExport(sender).then((result) => sendResponse({ ok: true, ...result })).catch((error) => sendResponse({ ok: false, error: error.message }));
      return true;
    }
    if (type === ACTIONS.exportLast) {
      exportTrace().then((result) => sendResponse({ ok: true, ...result })).catch((error) => sendResponse({ ok: false, error: error.message }));
      return true;
    }
    if (type === ACTIONS.status) {
      ready.then(() => sendResponse({ ok: true, ...publicStatus() })).catch((error) => sendResponse({ ok: false, error: error.message }));
      return true;
    }
    if (type === ACTIONS.event) {
      const info = senderInfo(sender);
      trackTab(info.tabId);
      append(
        core.redactText(message.category || "page", 40),
        core.redactText(message.name || "page-event", 80),
        message.data && typeof message.data === "object" ? message.data : {},
        info
      ).then(() => sendResponse({ ok: true })).catch((error) => sendResponse({ ok: false, error: error.message }));
      return true;
    }

    if (isActive()) {
      const action = String(message?.action || message?.name || message?.type || "");
      if (/ecommerce|original|shop.?move|goods|product|newPageTask|moveGoods/i.test(action)) {
        const info = senderInfo(sender);
        trackTab(info.tabId);
        append("runtime", "extension-message", {
          action: core.redactText(action, 140),
          shape: core.summarizeValue(message)
        }, info).catch(() => {});
      }
    }
    return false;
  });

  chrome.tabs.onCreated.addListener((tab) => {
    ready.then(() => {
      if (!isActive() || !sameSessionWindow(tab.windowId)) return;
      trackTab(tab.id);
      return append("tab", "tab-created", {
        openerTabId: Number.isInteger(tab.openerTabId) ? tab.openerTabId : -1,
        url: core.sanitizeUrl(tab.pendingUrl || tab.url || ""),
        active: tab.active === true
      }, { tabId: tab.id });
    }).catch(() => {});
  });

  chrome.tabs.onActivated.addListener((activeInfo) => {
    ready.then(() => {
      if (!isActive() || !sameSessionWindow(activeInfo.windowId)) return;
      trackTab(activeInfo.tabId);
      return chrome.tabs.get(activeInfo.tabId).catch(() => null).then((tab) => append("tab", "tab-activated", {
        url: core.sanitizeUrl(tab?.url || ""),
        title: core.redactText(tab?.title || "", 140)
      }, { tabId: activeInfo.tabId }));
    }).catch(() => {});
  });

  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    ready.then(() => {
      if (!isActive() || (!isTrackedTab(tabId) && !sameSessionWindow(tab?.windowId))) return;
      trackTab(tabId);
      if (!changeInfo.url && !changeInfo.status && !changeInfo.title) return;
      return append("tab", "tab-updated", {
        status: String(changeInfo.status || ""),
        url: core.sanitizeUrl(changeInfo.url || tab?.url || ""),
        title: core.redactText(changeInfo.title || tab?.title || "", 140)
      }, { tabId });
    }).catch(() => {});
  });

  chrome.tabs.onRemoved.addListener((tabId, removeInfo) => {
    ready.then(() => {
      if (!isActive() || !isTrackedTab(tabId)) return;
      state.trackedTabs.delete(tabId);
      return append("tab", "tab-removed", {
        windowId: Number(removeInfo?.windowId || -1),
        windowClosing: removeInfo?.isWindowClosing === true
      }, { tabId });
    }).catch(() => {});
  });

  function recordNavigation(name, details) {
    ready.then(() => {
      if (!isActive() || !isTrackedTab(details.tabId)) return;
      return append("navigation", name, {
        url: core.sanitizeUrl(details.url || ""),
        transitionType: String(details.transitionType || ""),
        transitionQualifiers: Array.isArray(details.transitionQualifiers) ? details.transitionQualifiers.slice(0, 8) : []
      }, { tabId: details.tabId, frameId: details.frameId });
    }).catch(() => {});
  }

  chrome.webNavigation?.onCommitted?.addListener((details) => recordNavigation("navigation-committed", details));
  chrome.webNavigation?.onHistoryStateUpdated?.addListener((details) => recordNavigation("history-state-updated", details));
  chrome.webNavigation?.onReferenceFragmentUpdated?.addListener((details) => recordNavigation("fragment-updated", details));

  chrome.webRequest.onBeforeRequest.addListener((details) => {
    if (!isActive() || !isTrackedTab(details.tabId) || !core.isRelevantNetwork(details)) return;
    state.requests.set(details.requestId, {
      startedAt: Date.now(),
      method: String(details.method || "GET").toUpperCase(),
      type: String(details.type || ""),
      url: core.sanitizeUrl(details.url || ""),
      initiator: core.sanitizeUrl(details.initiator || details.documentUrl || ""),
      requestFields: core.requestBodyFields(details.requestBody),
      requestSample: core.requestBodySample(details.requestBody),
      uploadBytes: Array.isArray(details.requestBody?.raw)
        ? details.requestBody.raw.reduce((total, item) => total + Number(item?.bytes?.byteLength || 0), 0)
        : 0,
      tabId: details.tabId,
      frameId: details.frameId
    });
  }, { urls: ["<all_urls>"] }, ["requestBody"]);

  function finishRequest(name, details, error = "") {
    const meta = state.requests.get(details.requestId);
    if (!meta) return;
    state.requests.delete(details.requestId);
    append("network", name, {
      method: meta.method,
      type: meta.type,
      url: meta.url,
      initiator: meta.initiator,
      status: Number(details.statusCode || 0),
      durationMs: Math.max(0, Date.now() - meta.startedAt),
      requestFields: meta.requestFields,
      requestSample: meta.requestSample,
      uploadBytes: meta.uploadBytes,
      responseType: core.redactText(details.responseHeaders?.find?.((item) => String(item.name || "").toLowerCase() === "content-type")?.value || "", 100),
      error: core.redactText(error, 200)
    }, { tabId: meta.tabId, frameId: meta.frameId }).catch(() => {});
  }

  chrome.webRequest.onCompleted.addListener(
    (details) => finishRequest("request-completed", details),
    { urls: ["<all_urls>"] },
    ["responseHeaders"]
  );
  chrome.webRequest.onErrorOccurred.addListener(
    (details) => finishRequest("request-failed", details, details.error || "request failed"),
    { urls: ["<all_urls>"] }
  );
})();
