var MMW_ADMIN_DEBUG = (() => {
  const STORAGE_KEYS = {
    enabled: "adminDebugEnabled",
    pageProbeEnabled: "adminDebugPageProbeEnabled",
    screenObserverEnabled: "adminDebugScreenObserverEnabled",
    referenceCaptureEnabled: "adminDebugReferenceCaptureEnabled",
    referenceCaptureRules: "adminDebugReferenceCaptureRules",
    referenceCapturePackage: "adminDebugReferenceCapturePackage",
    referenceCaptureSession: "adminDebugReferenceCaptureSession",
    logs: "adminDebugLogs"
  };
  const MAX_LOGS = 2000;
  const MAX_ARRAY = 80;
  const MAX_OBJECT_KEYS = 120;
  const MAX_STRING = 4000;
  const MAX_DEPTH = 14;
  const PAGE_PROBE_MIN_GAP_MS = 250;
  const PAGE_PROBE_MAX_PER_MINUTE = 240;
  const PAGE_PROBE_REFERENCE_MIN_GAP_MS = 25;
  const PAGE_PROBE_REFERENCE_MAX_PER_MINUTE = 1200;
  const REDACTED = "[redacted]";
  const SENSITIVE_KEY_RE = /token|authorization|cookie|password|passwd|secret|session|csrf|credential|access[_-]?key|refresh[_-]?key/i;

  let loaded = false;
  let logs = [];
  let persistTimer = 0;
  let lastPageProbeAt = 0;
  let lastReferencePageProbeAt = 0;
  let referenceCaptureSession = null;
  const pageProbeWindow = [];
  const referencePageProbeWindow = [];

  async function getStatus() {
    await load();
    const values = await chrome.storage.local.get([
      STORAGE_KEYS.enabled,
      STORAGE_KEYS.pageProbeEnabled,
      STORAGE_KEYS.screenObserverEnabled,
      STORAGE_KEYS.referenceCaptureEnabled,
      STORAGE_KEYS.referenceCaptureRules,
      STORAGE_KEYS.referenceCapturePackage,
      STORAGE_KEYS.referenceCaptureSession
    ]).catch(() => ({}));
    return {
      enabled: values[STORAGE_KEYS.enabled] === true,
      pageProbeEnabled: values[STORAGE_KEYS.pageProbeEnabled] === true,
      screenObserverEnabled: values[STORAGE_KEYS.screenObserverEnabled] === true,
      referenceCaptureEnabled: values[STORAGE_KEYS.referenceCaptureEnabled] === true,
      referenceCaptureRules: normalizeReferenceRules(values[STORAGE_KEYS.referenceCaptureRules]),
      referenceCapturePackage: normalizeReferencePackage(values[STORAGE_KEYS.referenceCapturePackage]),
      referenceCaptureSession: normalizeReferenceSession(values[STORAGE_KEYS.referenceCaptureSession] || referenceCaptureSession),
      referenceCaptureSessions: summarizeReferenceSessions(logs, values[STORAGE_KEYS.referenceCaptureSession] || referenceCaptureSession),
      logs: logs.slice(),
      count: logs.length,
      maxLogs: MAX_LOGS
    };
  }

  async function setEnabled(enabled) {
    await chrome.storage.local.set({ [STORAGE_KEYS.enabled]: enabled === true });
    await add({
      source: "admin-debug",
      event: enabled ? "capture-enabled" : "capture-disabled",
      level: "info",
      payload: { enabled: enabled === true }
    }, { force: true });
    return getStatus();
  }

  async function setPageProbeEnabled(enabled) {
    await chrome.storage.local.set({ [STORAGE_KEYS.pageProbeEnabled]: enabled === true });
    await add({
      source: "admin-debug",
      event: enabled ? "page-probe-enabled" : "page-probe-disabled",
      level: "info",
      payload: { pageProbeEnabled: enabled === true }
    }, { force: true });
    return getStatus();
  }

  async function setScreenObserverEnabled(enabled) {
    await chrome.storage.local.set({ [STORAGE_KEYS.screenObserverEnabled]: enabled === true });
    await add({
      source: "admin-debug",
      event: enabled ? "screen-observer-enabled" : "screen-observer-disabled",
      level: "info",
      payload: { screenObserverEnabled: enabled === true }
    }, { force: true });
    return getStatus();
  }

  async function setReferenceCapture(options = {}) {
    const enabled = options.enabled === true;
    const rules = normalizeReferenceRules(options.rules);
    const packageSummary = normalizeReferencePackage(options.packageSummary || options.package);
    let sessionEvent = null;
    if (enabled) {
      referenceCaptureSession = buildReferenceCaptureSession(options.session, packageSummary, rules, referenceCaptureSession);
      sessionEvent = {
        source: "admin-debug",
        event: "reference-session-started",
        level: "info",
        session: referenceCaptureSession,
        payload: {
          session: publicReferenceCaptureSession(referenceCaptureSession),
          package: packageSummary,
          rules: summarizeReferenceRules(rules)
        }
      };
    } else if (referenceCaptureSession) {
      const stoppedSession = stopReferenceCaptureSession(referenceCaptureSession, options.session);
      sessionEvent = {
        source: "admin-debug",
        event: "reference-session-stopped",
        level: "info",
        session: stoppedSession,
        payload: {
          session: publicReferenceCaptureSession(stoppedSession),
          package: packageSummary,
          rules: summarizeReferenceRules(rules)
        }
      };
      referenceCaptureSession = null;
    }
    await chrome.storage.local.set({
      [STORAGE_KEYS.referenceCaptureEnabled]: enabled,
      [STORAGE_KEYS.referenceCaptureRules]: rules,
      [STORAGE_KEYS.referenceCapturePackage]: packageSummary,
      [STORAGE_KEYS.referenceCaptureSession]: referenceCaptureSession
    });
    if (sessionEvent) {
      await add(sessionEvent, { force: true });
    }
    await add({
      source: "admin-debug",
      event: enabled ? "reference-capture-enabled" : "reference-capture-disabled",
      level: "info",
      session: referenceCaptureSession || sessionEvent?.session || null,
      payload: {
        enabled,
        session: publicReferenceCaptureSession(referenceCaptureSession || sessionEvent?.session || null),
        package: packageSummary,
        rules: summarizeReferenceRules(rules)
      }
    }, { force: true });
    return getStatus();
  }

  async function add(entry, options = {}) {
    await load();
    if (!options.force && !await isEnabled()) return { ok: true, skipped: true };
    if (!options.force && entry?.source === "page-probe" && !await isPageProbeEnabled()) {
      return { ok: true, skipped: true };
    }
    if (!options.force && entry?.source === "page-probe" && shouldThrottlePageProbe(entry)) {
      return { ok: true, skipped: true, throttled: true };
    }

    const normalized = normalizeEntry(entry, options);
    logs.push(normalized);
    if (logs.length > MAX_LOGS) logs = logs.slice(-MAX_LOGS);
    schedulePersist();
    return { ok: true, entry: normalized };
  }

  async function clear() {
    logs = [];
    loaded = true;
    await chrome.storage.local.set({ [STORAGE_KEYS.logs]: [] });
    return getStatus();
  }

  async function isEnabled() {
    const values = await chrome.storage.local.get([STORAGE_KEYS.enabled]).catch(() => ({}));
    return values[STORAGE_KEYS.enabled] === true;
  }

  async function isPageProbeEnabled() {
    const values = await chrome.storage.local.get([STORAGE_KEYS.pageProbeEnabled]).catch(() => ({}));
    return values[STORAGE_KEYS.pageProbeEnabled] === true;
  }

  async function load() {
    if (loaded) return;
    const values = await chrome.storage.local.get([STORAGE_KEYS.logs, STORAGE_KEYS.referenceCaptureSession]).catch(() => ({}));
    logs = Array.isArray(values[STORAGE_KEYS.logs]) ? values[STORAGE_KEYS.logs].slice(-MAX_LOGS) : [];
    referenceCaptureSession = normalizeReferenceSession(values[STORAGE_KEYS.referenceCaptureSession]);
    loaded = true;
  }

  function schedulePersist() {
    clearTimeout(persistTimer);
    persistTimer = setTimeout(() => {
      chrome.storage.local.set({
        [STORAGE_KEYS.logs]: logs.slice(-MAX_LOGS),
        [STORAGE_KEYS.referenceCaptureSession]: referenceCaptureSession
      }).catch(() => {});
    }, 1200);
  }

  function shouldThrottlePageProbe(entry) {
    const isReference = String(entry?.event || "").startsWith("reference-");
    const minGap = isReference ? PAGE_PROBE_REFERENCE_MIN_GAP_MS : PAGE_PROBE_MIN_GAP_MS;
    const maxPerMinute = isReference ? PAGE_PROBE_REFERENCE_MAX_PER_MINUTE : PAGE_PROBE_MAX_PER_MINUTE;
    const windowRef = isReference ? referencePageProbeWindow : pageProbeWindow;
    const now = Date.now();
    const lastAt = isReference ? lastReferencePageProbeAt : lastPageProbeAt;
    if (now - lastAt < minGap) return true;
    if (isReference) lastReferencePageProbeAt = now;
    else lastPageProbeAt = now;
    while (windowRef.length && now - windowRef[0] > 60000) windowRef.shift();
    if (windowRef.length >= maxPerMinute) return true;
    windowRef.push(now);
    return false;
  }

  function normalizeEntry(entry, options = {}) {
    const now = Date.now();
    const raw = entry && typeof entry === "object" ? entry : { payload: entry };
    const session = resolveEntrySession(raw.session, options, now);
    return {
      id: `${now}-${Math.random().toString(16).slice(2, 8)}`,
      time: now,
      iso: new Date(now).toISOString(),
      level: String(raw.level || "info").slice(0, 24),
      source: String(raw.source || "plugin").slice(0, 80),
      event: String(raw.event || raw.label || "log").slice(0, 120),
      pageUrl: truncateString(raw.pageUrl || raw.url || "", 1200),
      tabId: Number.isFinite(Number(raw.tabId)) ? Number(raw.tabId) : undefined,
      sessionId: session?.id || "",
      sessionSeq: Number.isFinite(Number(session?.seq)) ? Number(session.seq) : undefined,
      session: session ? publicReferenceCaptureSession(session) : null,
      payload: sanitizeValue(raw.payload ?? raw.data ?? raw.message ?? raw, 0)
    };
  }

  function resolveEntrySession(rawSession, options, now) {
    const explicit = normalizeReferenceSession(rawSession);
    if (explicit) {
      if (options.advanceSession !== false) explicit.seq = Math.max(0, Number(explicit.seq || 0) || 0) + 1;
      explicit.updatedAt = new Date(now).toISOString();
      if (referenceCaptureSession?.id === explicit.id) referenceCaptureSession = { ...referenceCaptureSession, ...explicit };
      return explicit;
    }
    if (!referenceCaptureSession) return null;
    referenceCaptureSession.seq = Math.max(0, Number(referenceCaptureSession.seq || 0) || 0) + 1;
    referenceCaptureSession.updatedAt = new Date(now).toISOString();
    return { ...referenceCaptureSession };
  }

  function sanitizeValue(value, depth, key = "") {
    if (SENSITIVE_KEY_RE.test(key)) return REDACTED;
    if (value === null || value === undefined) return value;
    if (typeof value === "string") return truncateString(value, MAX_STRING);
    if (typeof value === "number" || typeof value === "boolean") return value;
    if (typeof value === "bigint") return String(value);
    if (typeof value === "function") return "[function]";
    if (depth >= MAX_DEPTH) return "[depth-limit]";

    if (Array.isArray(value)) {
      const items = value.slice(0, MAX_ARRAY).map((item) => sanitizeValue(item, depth + 1, key));
      if (value.length > MAX_ARRAY) items.push(`[${value.length - MAX_ARRAY} more]`);
      return items;
    }

    if (typeof value === "object") {
      const result = {};
      const entries = Object.entries(value).slice(0, MAX_OBJECT_KEYS);
      for (const [itemKey, itemValue] of entries) {
        result[itemKey] = sanitizeValue(itemValue, depth + 1, itemKey);
      }
      const extra = Object.keys(value).length - entries.length;
      if (extra > 0) result.__truncated_keys__ = extra;
      return result;
    }

    return String(value);
  }

  function truncateString(value, max) {
    const text = String(value || "");
    return text.length > max ? `${text.slice(0, max)}...[truncated ${text.length - max}]` : text;
  }

  function normalizeReferenceRules(value) {
    const source = value && typeof value === "object" ? value : {};
    return {
      packageName: truncateString(source.packageName || "", 160),
      packageVersion: truncateString(source.packageVersion || "", 80),
      scannedFiles: Math.max(0, Number(source.scannedFiles || 0) || 0),
      manifestMatches: cleanStringList(source.manifestMatches, 80, 240),
      urlKeywords: cleanStringList(source.urlKeywords, 120, 220),
      bodyKeywords: cleanStringList(source.bodyKeywords, 160, 220),
      messageKeywords: cleanStringList(source.messageKeywords, 120, 160),
      storageKeys: cleanStringList(source.storageKeys, 80, 120),
      hookSelectors: cleanStringList(source.hookSelectors, 60, 120),
      notes: cleanStringList(source.notes, 20, 240)
    };
  }

  function normalizeReferencePackage(value) {
    const source = value && typeof value === "object" ? value : {};
    return {
      name: truncateString(source.name || source.packageName || "", 160),
      version: truncateString(source.version || source.packageVersion || "", 80),
      fileCount: Math.max(0, Number(source.fileCount || 0) || 0),
      scannedFiles: Math.max(0, Number(source.scannedFiles || 0) || 0),
      loadedAt: truncateString(source.loadedAt || "", 80),
      warnings: cleanStringList(source.warnings, 12, 240)
    };
  }

  function buildReferenceCaptureSession(value, packageSummary, rules, previousSession) {
    const source = value && typeof value === "object" ? value : {};
    const now = new Date().toISOString();
    const packageName = truncateString(source.packageName || packageSummary.name || rules.packageName || "参考抓包", 160);
    const packageVersion = truncateString(source.packageVersion || packageSummary.version || rules.packageVersion || "", 80);
    const startedAt = truncateString(source.startedAt || now, 80);
    const label = truncateString(
      source.name
        || source.label
        || `${packageName}${packageVersion ? ` v${packageVersion}` : ""} ${formatSessionStamp(startedAt)}`,
      160
    );
    return {
      id: truncateString(source.id || `ref_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, 96),
      name: label,
      packageName,
      packageVersion,
      startedAt,
      updatedAt: truncateString(source.updatedAt || now, 80),
      endedAt: "",
      seq: 0,
      previousId: truncateString(source.previousId || previousSession?.id || "", 96)
    };
  }

  function stopReferenceCaptureSession(session, override = {}) {
    const normalized = normalizeReferenceSession(session);
    if (!normalized) return null;
    const source = override && typeof override === "object" ? override : {};
    return {
      ...normalized,
      updatedAt: truncateString(source.updatedAt || new Date().toISOString(), 80),
      endedAt: truncateString(source.endedAt || new Date().toISOString(), 80)
    };
  }

  function normalizeReferenceSession(value) {
    const source = value && typeof value === "object" ? value : null;
    if (!source) return null;
    const id = truncateString(source.id || "", 96);
    if (!id) return null;
    return {
      id,
      name: truncateString(source.name || source.label || "", 160),
      packageName: truncateString(source.packageName || "", 160),
      packageVersion: truncateString(source.packageVersion || "", 80),
      startedAt: truncateString(source.startedAt || "", 80),
      updatedAt: truncateString(source.updatedAt || "", 80),
      endedAt: truncateString(source.endedAt || "", 80),
      seq: Math.max(0, Number(source.seq || 0) || 0),
      previousId: truncateString(source.previousId || "", 96)
    };
  }

  function publicReferenceCaptureSession(value) {
    const session = normalizeReferenceSession(value);
    if (!session) return null;
    return {
      id: session.id,
      name: session.name,
      packageName: session.packageName,
      packageVersion: session.packageVersion,
      startedAt: session.startedAt,
      updatedAt: session.updatedAt,
      endedAt: session.endedAt,
      seq: session.seq,
      active: !session.endedAt
    };
  }

  function summarizeReferenceSessions(logEntries, activeSessionInput) {
    const activeSession = normalizeReferenceSession(activeSessionInput || referenceCaptureSession);
    const sessionMap = new Map();
    for (const entry of Array.isArray(logEntries) ? logEntries : []) {
      const session = normalizeReferenceSession(entry?.session);
      if (!session) continue;
      if (!sessionMap.has(session.id)) {
        sessionMap.set(session.id, {
          ...publicReferenceCaptureSession(session),
          eventCount: 0,
          firstAt: Number(entry?.time || 0) || 0,
          lastAt: Number(entry?.time || 0) || 0,
          tabs: new Set(),
          events: new Set()
        });
      }
      const summary = sessionMap.get(session.id);
      summary.eventCount += 1;
      summary.firstAt = summary.firstAt ? Math.min(summary.firstAt, Number(entry?.time || 0) || summary.firstAt) : Number(entry?.time || 0) || 0;
      summary.lastAt = Math.max(summary.lastAt, Number(entry?.time || 0) || 0);
      if (Number.isFinite(Number(entry?.tabId))) summary.tabs.add(Number(entry.tabId));
      if (entry?.event) summary.events.add(String(entry.event));
      if (!summary.startedAt && session.startedAt) summary.startedAt = session.startedAt;
      if (!summary.name && session.name) summary.name = session.name;
      if (!summary.packageName && session.packageName) summary.packageName = session.packageName;
      if (!summary.packageVersion && session.packageVersion) summary.packageVersion = session.packageVersion;
      if (session.endedAt) summary.endedAt = session.endedAt;
      if (session.updatedAt) summary.updatedAt = session.updatedAt;
    }
    if (activeSession && !sessionMap.has(activeSession.id)) {
      sessionMap.set(activeSession.id, {
        ...publicReferenceCaptureSession(activeSession),
        eventCount: 0,
        firstAt: 0,
        lastAt: 0,
        tabs: new Set(),
        events: new Set()
      });
    }
    return Array.from(sessionMap.values())
      .map((summary) => ({
        ...summary,
        active: activeSession?.id === summary.id && !summary.endedAt,
        tabCount: summary.tabs.size,
        tabs: Array.from(summary.tabs).slice(0, 40),
        sampleEvents: Array.from(summary.events).slice(0, 12)
      }))
      .sort((left, right) => {
        const leftTime = Number(left.lastAt || left.firstAt || 0) || 0;
        const rightTime = Number(right.lastAt || right.firstAt || 0) || 0;
        return rightTime - leftTime;
      })
      .slice(0, 20);
  }

  function formatSessionStamp(value) {
    const date = new Date(value || Date.now());
    if (Number.isNaN(date.getTime())) return "会话";
    const pad = (input) => String(input).padStart(2, "0");
    return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
  }

  function summarizeReferenceRules(rules) {
    const clean = normalizeReferenceRules(rules);
    return {
      packageName: clean.packageName,
      packageVersion: clean.packageVersion,
      scannedFiles: clean.scannedFiles,
      manifestMatches: clean.manifestMatches.slice(0, 8),
      urlKeywords: clean.urlKeywords.slice(0, 12),
      bodyKeywords: clean.bodyKeywords.slice(0, 16),
      messageKeywords: clean.messageKeywords.slice(0, 10),
      storageKeys: clean.storageKeys.slice(0, 10),
      hookSelectors: clean.hookSelectors.slice(0, 8),
      notes: clean.notes.slice(0, 6)
    };
  }

  function cleanStringList(value, maxItems, maxLength) {
    const result = [];
    const seen = new Set();
    const source = Array.isArray(value) ? value : [];
    for (const item of source) {
      const text = truncateString(String(item || "").trim(), maxLength);
      const key = text.toLowerCase();
      if (!text || seen.has(key)) continue;
      seen.add(key);
      result.push(text);
      if (result.length >= maxItems) break;
    }
    return result;
  }

  return {
    getStatus,
    setEnabled,
    setPageProbeEnabled,
    setScreenObserverEnabled,
    setReferenceCapture,
    add,
    clear,
    sanitizeValue
  };
})();
