/**
 * 视频页评论区自动扫描路人（弹窗勾选开启）。
 * 每 4 秒处理 1 人；库内已存在则快速跳过。
 */
(function () {
  "use strict";

  if (window.self !== window.top) return;
  if (typeof BiliDom === "undefined") {
    console.warn("[BiliIndex] passerby-scan: 缺少 bili-dom.js，已跳过");
    return;
  }

  const SCAN_GAP_MS = 4000;
  const SKIP_GAP_MS = 400;
  const DISCOVER_INTERVAL_MS = 20000;
  const START_DELAY_MS = 8000;
  const PATH_POLL_MS = 2500;
  const MAX_SCAN_RETRIES = 3;
  /** 主评论 + 楼中楼回复 */
  const RENDERER_SEL =
    "bili-comment-renderer, bili-comment-reply-renderer";
  const USER_LINK_SEL =
    "a[href*='space.bilibili.com'], a[href*='/space/']";
  const queryCommentDeep = BiliDom.queryCommentDeep;
  const extractMid = BiliDom.extractMid;
  const midFromData = BiliDom.midFromCommentElement;

  let enabled = false;
  let stopped = true;
  let processing = false;
  let discovering = false;
  const queue = [];
  const seenOnPage = new Set();
  const scanFailCounts = new Map();
  let discoverTimer = null;
  let pathTimer = null;
  let nextScanTimer = null;
  let startDelayTimer = null;
  let lastPath = "";
  let statusEl = null;
  let pageHideBound = false;

  function isVideoPage() {
    return /\/video\/(BV[\w]+|av\d+)/i.test(location.pathname || "");
  }

  function notifyPasserbyDisplay(upMid, display) {
    if (!upMid) return;
    document.dispatchEvent(
      new CustomEvent("biliindex-passerby-marked", {
        detail: { up_mid: String(upMid), display: display || null },
      })
    );
  }

  function nicknameFromData(el) {
    const d = el?.__data;
    if (!d) return null;
    const name =
      d.member?.uname ??
      d.uname ??
      d.name ??
      d.reply?.member?.uname ??
      d.user?.name;
    const s = name != null ? String(name).trim() : "";
    return s || null;
  }

  function collectCommentUsers() {
    const users = new Map();
    for (const el of queryCommentDeep(RENDERER_SEL)) {
      let mid = midFromData(el);
      let nickname = nicknameFromData(el);
      if (!mid) {
        const link = el.querySelector?.(USER_LINK_SEL);
        mid = extractMid(link?.getAttribute?.("href") || link?.href);
        if (!nickname && link) {
          nickname = (link.textContent || "").trim() || null;
        }
      }
      if (!nickname) {
        const link = el.querySelector?.(USER_LINK_SEL);
        if (link) nickname = (link.textContent || "").trim() || null;
      }
      if (!mid || !/^\d+$/.test(mid)) continue;
      if (!users.has(mid)) {
        users.set(mid, { up_mid: mid, nickname: nickname || null });
      }
    }
    if (!users.size) {
      for (const link of queryCommentDeep(USER_LINK_SEL)) {
        const mid = extractMid(link.getAttribute("href"));
        if (!mid || !/^\d+$/.test(mid)) continue;
        if (!users.has(mid)) {
          users.set(mid, {
            up_mid: mid,
            nickname: (link.textContent || "").trim() || null,
          });
        }
      }
    }
    return [...users.values()];
  }

  function runDiscoverIdle() {
    if (discovering || stopped || !enabled || !isVideoPage()) return;
    if (typeof BiliIndex !== "undefined" && BiliIndex.isCommentsSectionLoading?.()) {
      return;
    }
    discovering = true;
    const work = () => {
      try {
        if (
          typeof BiliIndex !== "undefined" &&
          BiliIndex.isCommentsSectionLoading?.()
        ) {
          return;
        }
        if (!stopped && enabled && isVideoPage()) {
          const users = collectCommentUsers();
          if (users.length) enqueueUsers(users);
          else setStatus("扫描待命（评论区加载后会自动发现用户）");
        }
      } finally {
        discovering = false;
      }
    };
    if (typeof requestIdleCallback === "function") {
      requestIdleCallback(work, { timeout: 2500 });
    } else {
      setTimeout(work, 50);
    }
  }

  const HINT_DIALOG_ID = "biliindex-passerby-hint-dialog";

  function hidePasserbyHintDialog() {
    document.getElementById(HINT_DIALOG_ID)?.remove();
  }

  function showPasserbyHintDialog() {
    if (document.getElementById(HINT_DIALOG_ID)) return;
    const hintHtml =
      (typeof BiliIndex !== "undefined" && BiliIndex.PASSERBY_SCAN_HINT_HTML) ||
      "视频页自动扫描路人：每 4 秒处理 1 人；粉丝≤100 或投稿≤1 则保留并 +1 积分（每日 100 次）。";
    const overlay = document.createElement("div");
    overlay.id = HINT_DIALOG_ID;
    overlay.setAttribute("data-biliindex", "passerby-scan-hint");
    Object.assign(overlay.style, {
      position: "fixed",
      inset: "0",
      zIndex: "2147483646",
      background: "rgba(0,0,0,0.45)",
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      padding: "16px",
      fontFamily: "system-ui,sans-serif",
    });
    const box = document.createElement("div");
    Object.assign(box.style, {
      maxWidth: "400px",
      width: "100%",
      background: "#fff",
      color: "#18191c",
      borderRadius: "10px",
      padding: "16px 18px",
      boxShadow: "0 8px 32px rgba(0,0,0,0.2)",
      fontSize: "13px",
      lineHeight: "1.55",
    });
    const title = document.createElement("div");
    title.textContent = "视频页自动扫描路人";
    Object.assign(title.style, {
      fontWeight: "700",
      fontSize: "15px",
      marginBottom: "10px",
    });
    const body = document.createElement("div");
    body.innerHTML = hintHtml;
    const actions = document.createElement("div");
    Object.assign(actions.style, {
      marginTop: "14px",
      textAlign: "right",
    });
    const btn = document.createElement("button");
    btn.type = "button";
    btn.textContent = "知道了，开始扫描";
    Object.assign(btn.style, {
      border: "none",
      borderRadius: "6px",
      padding: "8px 14px",
      background: "#00aeec",
      color: "#fff",
      fontSize: "13px",
      cursor: "pointer",
    });
    btn.addEventListener("click", () => hidePasserbyHintDialog());
    overlay.addEventListener("click", (e) => {
      if (e.target === overlay) hidePasserbyHintDialog();
    });
    actions.appendChild(btn);
    box.append(title, body, actions);
    overlay.appendChild(box);
    document.documentElement.appendChild(overlay);
  }

  async function maybeShowPasserbyHintDialog() {
    try {
      const { passerbyScanHintPending } = await BiliIndex.storageGet([
        "passerbyScanHintPending",
      ]);
      if (!passerbyScanHintPending) return;
      await BiliIndex.storageSet({ passerbyScanHintPending: false });
      showPasserbyHintDialog();
    } catch {
      showPasserbyHintDialog();
    }
  }

  function scanStatusBadgeStyle() {
    const base = {
      position: "fixed",
      right: "12px",
      maxWidth: "240px",
      padding: "6px 10px",
      borderRadius: "8px",
      background: "rgba(0,0,0,0.72)",
      color: "#fff",
      fontSize: "12px",
      lineHeight: "1.4",
      pointerEvents: "none",
      fontFamily: "system-ui,sans-serif",
      zIndex: "40",
    };
    if (isVideoPage()) {
      return { ...base, top: "72px", bottom: "auto" };
    }
    return { ...base, bottom: "72px", top: "auto" };
  }

  function ensureStatusBadge() {
    if (statusEl?.isConnected) {
      Object.assign(statusEl.style, scanStatusBadgeStyle());
      return statusEl;
    }
    statusEl = document.createElement("div");
    statusEl.id = "biliindex-passerby-scan-status";
    statusEl.setAttribute("data-biliindex", "passerby-scan");
    Object.assign(statusEl.style, scanStatusBadgeStyle());
    document.documentElement.appendChild(statusEl);
    return statusEl;
  }

  function setStatus(text) {
    if (!enabled || stopped) {
      statusEl?.remove();
      statusEl = null;
      return;
    }
    ensureStatusBadge().textContent = text;
  }

  function enqueueUsers(users) {
    let added = 0;
    for (const u of users) {
      if (seenOnPage.has(u.up_mid)) continue;
      seenOnPage.add(u.up_mid);
      queue.push(u);
      added += 1;
    }
    if (added && !processing) {
      setStatus(`扫描中 · 队列 ${queue.length} 人 · 每 4 秒 1 人`);
      scheduleDrain();
    }
  }

  function scheduleDrain() {
    if (nextScanTimer || processing || stopped || !enabled || !queue.length) {
      return;
    }
    void drainQueue();
  }

  async function drainQueue() {
    if (processing || stopped || !enabled || !queue.length) return;
    processing = true;
    let scanWasSkip = false;
    const item = queue[0];
    setStatus(`正在扫描 UID ${item.up_mid}…`);
    try {
      const token = await BiliIndex.getToken();
      if (!token) {
        stopScanning("未登录，已停止扫描");
        return;
      }
      const res = await BiliIndex.passerbyAutoScan(item.up_mid, item.nickname);
      scanWasSkip = !!(res.skipped || res.reason === "already_in_db");
      scanFailCounts.delete(item.up_mid);
      queue.shift();
      const quotaPatch =
        res.daily_limit != null
          ? {
              passerbyScanQuotaCache: {
                day: (() => {
                  const d = new Date();
                  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
                })(),
                daily_used:
                  res.daily_used ??
                  Math.max(0, (res.daily_limit || 100) - (res.daily_remaining ?? 0)),
                daily_limit: res.daily_limit,
                daily_remaining: res.daily_remaining,
                scan_points_unlimited: !!res.scan_points_unlimited,
                ts: Date.now(),
              },
            }
          : {};
      await BiliIndex.storageSet({
        lastPasserbyScanResult: {
          ts: Date.now(),
          up_mid: item.up_mid,
          message: res.message || "",
          kept: !!res.kept,
          skipped: !!res.skipped,
          points_earned: res.points_earned || 0,
          daily_remaining: res.daily_remaining,
          daily_used: res.daily_used,
          daily_limit: res.daily_limit,
        },
        ...quotaPatch,
      });
      if (res.kept || res.skipped) {
        notifyPasserbyDisplay(item.up_mid, res.display);
        if (
          (res.passerby_mark_count >= 1 || res.display?.passerby) &&
          BiliIndex.rememberPasserbyMid
        ) {
          void BiliIndex.rememberPasserbyMid(item.up_mid);
        }
      }
      const remain = res.daily_remaining ?? "—";
      if (res.kept && res.points_earned) {
        setStatus(
          `完成 ${item.up_mid}：保留路人 +${res.points_earned} 分 · 今日剩余 ${remain}`
        );
      } else if (res.skipped) {
        setStatus(`跳过 ${item.up_mid}：${res.message || "已是路人"}`);
      } else if (res.revoked) {
        const fc = res.fan_count != null ? `粉丝${res.fan_count}` : "";
        const ac =
          res.archive_count != null ? `投稿${res.archive_count}` : "";
        const extra = [fc, ac].filter(Boolean).join(" ");
        setStatus(
          `完成 ${item.up_mid}：不符合路人，已撤回${extra ? `（${extra}）` : ""}`
        );
      } else {
        setStatus(res.message || `完成 ${item.up_mid}`);
      }
      if (res.daily_remaining === 0 && !res.scan_points_unlimited) {
        await exhaustDailyQuota();
        return;
      }
    } catch (e) {
      const msg = e?.message || String(e);
      if (/会员/.test(msg)) {
        queue.shift();
        scanFailCounts.delete(item.up_mid);
        await BiliIndex.storageSet({ passerbyScanEnabled: false });
        stopScanning(msg);
        enabled = false;
        return;
      }
      if (/上限|429/.test(msg)) {
        queue.shift();
        scanFailCounts.delete(item.up_mid);
        await exhaustDailyQuota();
        return;
      }
      const fails = (scanFailCounts.get(item.up_mid) || 0) + 1;
      scanFailCounts.set(item.up_mid, fails);
      if (fails < MAX_SCAN_RETRIES) {
        setStatus(
          `扫描失败（${fails}/${MAX_SCAN_RETRIES}）：${msg} · ${Math.round(SCAN_GAP_MS / 1000)} 秒后重试`
        );
      } else {
        queue.shift();
        scanFailCounts.delete(item.up_mid);
        setStatus(`已跳过 ${item.up_mid}：连续失败 ${MAX_SCAN_RETRIES} 次`);
      }
    } finally {
      processing = false;
      if (!stopped && enabled && queue.length) {
        const gap = scanWasSkip ? SKIP_GAP_MS : SCAN_GAP_MS;
        setStatus(
          `扫描中 · 队列 ${queue.length} 人 · ${Math.round(gap / 1000)} 秒后下一个`
        );
        nextScanTimer = setTimeout(() => {
          nextScanTimer = null;
          void drainQueue();
        }, gap);
      } else if (!stopped && enabled) {
        setStatus(
          `扫描待命 · 每 ${Math.round(DISCOVER_INTERVAL_MS / 1000)} 秒检查新评论用户`
        );
      }
    }
  }

  function stopScanning(reason) {
    stopped = true;
    if (startDelayTimer) {
      clearTimeout(startDelayTimer);
      startDelayTimer = null;
    }
    if (discoverTimer) {
      clearInterval(discoverTimer);
      discoverTimer = null;
    }
    if (nextScanTimer) {
      clearTimeout(nextScanTimer);
      nextScanTimer = null;
    }
    queue.length = 0;
    scanFailCounts.clear();
    processing = false;
    discovering = false;
    statusEl?.remove();
    statusEl = null;
    hidePasserbyHintDialog();
    if (reason) {
      void BiliIndex.storageSet({
        lastPasserbyScanResult: {
          ts: Date.now(),
          message: reason,
          stopped: true,
        },
      });
    }
  }

  function startScanning() {
    if (!enabled || !isVideoPage()) return;
    stopped = false;
    seenOnPage.clear();
    queue.length = 0;
    void maybeShowPasserbyHintDialog();
    setStatus(`扫描将于 ${START_DELAY_MS / 1000} 秒后启动（先让标签槽加载）`);
    if (startDelayTimer) clearTimeout(startDelayTimer);
    startDelayTimer = setTimeout(() => {
      startDelayTimer = null;
      if (stopped || !enabled || !isVideoPage()) return;
      setStatus("扫描已开启，正在发现评论区用户…");
      runDiscoverIdle();
      if (!discoverTimer) {
        discoverTimer = setInterval(runDiscoverIdle, DISCOVER_INTERVAL_MS);
      }
    }, START_DELAY_MS);
  }

  function onPageContextChange() {
    const path = `${location.pathname}${location.search}`;
    if (path === lastPath) return;
    lastPath = path;
    stopScanning();
    seenOnPage.clear();
    if (enabled && isVideoPage()) startScanning();
  }

  function bindPathWatcher() {
    if (pathTimer) return;
    lastPath = `${location.pathname}${location.search}`;
    pathTimer = setInterval(onPageContextChange, PATH_POLL_MS);
  }

  function unbindPathWatcher() {
    if (pathTimer) {
      clearInterval(pathTimer);
      pathTimer = null;
    }
  }

  /** 今日配额用尽：同步 storage（passerbyScanEnabled=false + 次日 0 点恢复） */
  async function exhaustDailyQuota() {
    let msg = "今日已满 100 次，已自动关闭";
    try {
      const until = await BiliIndex.pausePasserbyScanUntilMidnight();
      const hint = BiliIndex.formatLocalMidnightHint(until);
      msg = `${msg}，${hint} 重新开启`;
    } catch {
      await BiliIndex.storageSet({ passerbyScanEnabled: false });
    }
    enabled = false;
    stopScanning(msg);
  }

  async function syncDailyQuotaPause() {
    try {
      const token = await BiliIndex.getToken();
      if (!token) return;
      const st = await BiliIndex.fetchPasserbyScanStatus();
      if (st.scan_points_unlimited) {
        await BiliIndex.storageSet({ passerbyScanPausedUntil: null });
        return;
      }
      if (st.daily_remaining === 0) {
        await BiliIndex.pausePasserbyScanUntilMidnight();
        enabled = false;
      }
    } catch {
      /* ignore */
    }
  }

  async function ensurePasserbyScanSession() {
    try {
      return !!(await BiliIndex.getToken());
    } catch {
      return false;
    }
  }

  async function resolveScanMode() {
    const st = await chrome.storage.local.get([
      "passerbyScanMode",
      "passerbyScanEnabled",
      "passerbyEcoScanEnabled",
    ]);
    if (st.passerbyScanMode === "eco" || st.passerbyEcoScanEnabled) return "eco";
    if (st.passerbyScanMode === "classic" || st.passerbyScanEnabled) {
      return "classic";
    }
    return "";
  }

  async function applyEnabledFlag() {
    const mode = await resolveScanMode();
    if (mode === "eco") {
      stopScanning();
      unbindPathWatcher();
      return;
    }
    if (mode !== "classic") {
      stopScanning();
      unbindPathWatcher();
      return;
    }
    if (BiliIndex.reconcilePasserbyScanSchedule) {
      const sched = await BiliIndex.reconcilePasserbyScanSchedule();
      enabled = sched.enabled;
      if (sched.pausedUntil) {
        const hint = BiliIndex.formatLocalMidnightHint(sched.pausedUntil);
        stopScanning(`今日已满 100 次，${hint} 自动重新开启`);
        unbindPathWatcher();
        return;
      }
    } else {
      const { passerbyScanEnabled } = await BiliIndex.storageGet([
        "passerbyScanEnabled",
      ]);
      enabled = !!passerbyScanEnabled;
    }
    await syncDailyQuotaPause();
    if (enabled && BiliIndex.reconcilePasserbyScanSchedule) {
      const again = await BiliIndex.reconcilePasserbyScanSchedule();
      enabled = again.enabled;
      if (again.pausedUntil) {
        const hint = BiliIndex.formatLocalMidnightHint(again.pausedUntil);
        stopScanning(`今日已满 100 次，${hint} 自动重新开启`);
        unbindPathWatcher();
        return;
      }
    }
    if (!enabled) {
      stopScanning();
      unbindPathWatcher();
      return;
    }
    const loggedIn = await ensurePasserbyScanSession();
    if (!loggedIn) {
      await BiliIndex.storageSet({ passerbyScanEnabled: false });
      enabled = false;
      stopScanning("请先登录后再使用自动扫描路人");
      unbindPathWatcher();
      return;
    }
    bindPathWatcher();
    if (!pageHideBound) {
      window.addEventListener("pagehide", onPageHide);
      pageHideBound = true;
    }
    if (isVideoPage()) startScanning();
    else stopScanning();
  }

  function onPageHide() {
    stopScanning();
  }

  chrome.storage.onChanged.addListener((changes, area) => {
    if (area !== "local") return;
    if (
      changes.passerbyScanEnabled ||
      changes.passerbyScanHintPending ||
      changes.passerbyScanMode ||
      changes.passerbyEcoScanEnabled
    ) {
      void applyEnabledFlag();
    }
  });

  document.addEventListener("biliindex:comments-scanned", () => {
    if (!enabled || stopped || !isVideoPage()) return;
    runDiscoverIdle();
  });

  void applyEnabledFlag();
})();
