const $ = (sel) => document.querySelector(sel);

let tagGroups = null;
let subsByParent = new Map();
let selectedCategoryId = null;
let isSubmittingDraft = false;

const PASSERBY_CODE = "passerby";
const SLOT_ORDER = ["tag1", "tag2", "tag3", "emoji"];
const draft = { tag1: null, tag2: null, tag3: null, emoji: null };

const SLOT_LABELS = {
  tag1: "一级",
  tag2: "二级",
  tag3: "风格",
  emoji: "Emoji",
};

/** 弹窗内可折叠标签组（默认收起） */
const expandedPopupGroups = new Set();

const BLOCK_MAX = 3;
/** @type {{ id: number, code: string, name: string, tag_kind?: string }[]} */
let blockSelected = [];

function slotForKind(kind) {
  const map = { category: "tag1", sub: "tag2", style: "tag3", emoji: "emoji" };
  return map[kind] || null;
}

function isPasserbyDraft() {
  return draft.tag1?.code === PASSERBY_CODE;
}

function requiredSlots() {
  return isPasserbyDraft() ? ["tag1", "emoji"] : SLOT_ORDER;
}

function isDraftComplete() {
  return requiredSlots().every((s) => draft[s]);
}

function clearDraft() {
  draft.tag1 = null;
  draft.tag2 = null;
  draft.tag3 = null;
  draft.emoji = null;
}

function updateDraftBar() {
  const bar = $("#tag-draft-bar");
  if (!bar) return;
  const need = requiredSlots();
  bar.innerHTML = SLOT_ORDER.map((slot) => {
    const tag = draft[slot];
    const skip = isPasserbyDraft() && (slot === "tag2" || slot === "tag3");
    const label = SLOT_LABELS[slot];
    if (skip) {
      return `<span class="draft-slot">${label}：路人无需</span>`;
    }
    const text = tag ? BiliIndex.formatTagLabel(tag) : "—";
    return `<span class="draft-slot${tag ? " done" : ""}">${label}：${text}</span>`;
  }).join("");
}

function refreshSelectionStyles() {
  document.querySelectorAll(".tag-l1-item").forEach((btn) => {
    const id = Number(btn.dataset.id);
    btn.classList.toggle("selected", draft.tag1?.id === id);
    btn.classList.toggle("active", selectedCategoryId === id);
  });
  document.querySelectorAll(".tag-chip[data-tag-id]").forEach((btn) => {
    const id = Number(btn.dataset.tagId);
    const slot = btn.dataset.slot;
    btn.classList.toggle("selected", draft[slot]?.id === id);
  });
  document.querySelectorAll(".tag-group[data-slot-group]").forEach((el) => {
    const g = el.dataset.slotGroup;
    const skip = isPasserbyDraft() && (g === "tag2" || g === "tag3");
    el.classList.toggle("is-passerby-skip", skip);
  });
}

function nextMissingSlotLabel() {
  const slot = requiredSlots().find((s) => !draft[s]);
  return slot ? SLOT_LABELS[slot] : "";
}

function tagKindOf(t) {
  let kind =
    t.tag_kind ||
    (t.level === 1 ? "category" : t.icon ? "emoji" : t.level === 3 ? "style" : "sub");
  if (BiliIndex.isEmojiTag?.(t)) kind = "emoji";
  return kind;
}

function buildSubsByParent(tree) {
  const map = new Map();
  for (const t of tree || []) {
    if (tagKindOf(t) !== "sub" || !t.parent_id) continue;
    if (!map.has(t.parent_id)) map.set(t.parent_id, []);
    map.get(t.parent_id).push(t);
  }
  const sortFn = (a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0) || a.id - b.id;
  for (const list of map.values()) list.sort(sortFn);
  return map;
}

function renderSubPane(catId, categories) {
  const pane = document.getElementById("tag-sub-pane");
  if (!pane) return;
  pane.innerHTML = "";
  if (!catId) {
    pane.innerHTML = '<p class="tag-pane-placeholder">点击左侧一级分类</p>';
    return;
  }
  const cat = categories.find((c) => c.id === catId);
  const subs = subsByParent.get(catId) || [];
  if (!subs.length) {
    pane.innerHTML = `<p class="tag-pane-placeholder">${cat ? `「${cat.name}」暂无二级标签` : "暂无二级标签"}</p>`;
    return;
  }
  const chips = document.createElement("div");
  chips.className = "tag-chips tag-chips-pane";
  for (const tag of subs) {
    const btn = document.createElement("button");
    btn.type = "button";
    btn.className = "tag-chip";
    btn.textContent = BiliIndex.formatTagLabel(tag);
    btn.title = `code: ${tag.code}`;
    btn.dataset.tagId = String(tag.id);
    btn.dataset.slot = "tag2";
    btn.addEventListener("click", () => void selectTagForSlot(tag, "sub"));
    chips.appendChild(btn);
  }
  pane.appendChild(chips);
}

function renderCategorySubPicker(categories) {
  const block = document.createElement("div");
  block.className = "tag-group tag-l1-l2-picker";
  block.innerHTML =
    '<div class="tag-group-title">一级分类（职业） · 仅归类，点击可快速填入参考</div>';

  const picker = document.createElement("div");
  picker.className = "tag-dual-pane";

  const left = document.createElement("div");
  left.className = "tag-pane-l1";
  left.setAttribute("role", "listbox");
  left.setAttribute("aria-label", "一级分类");

  const right = document.createElement("div");
  right.className = "tag-pane-l2";
  right.id = "tag-sub-pane";
  right.setAttribute("role", "region");
  right.setAttribute("aria-label", "二级标签");

  if (!categories.length) {
    left.innerHTML = '<p class="tag-pane-placeholder">暂无一级分类</p>';
    right.innerHTML = '<p class="tag-pane-placeholder">—</p>';
  } else {
    for (const cat of categories) {
      const btn = document.createElement("button");
      btn.type = "button";
      btn.className = "tag-l1-item";
      btn.textContent = cat.name;
      btn.dataset.id = String(cat.id);
      btn.setAttribute("role", "option");
      btn.addEventListener("click", () => {
        selectedCategoryId = cat.id;
        renderSubPane(cat.id, categories);
        void selectTagForSlot(cat, "category");
      });
      left.appendChild(btn);
    }
    const stillValid = categories.some((c) => c.id === selectedCategoryId);
    if (!stillValid) selectedCategoryId = categories[0].id;
    const activeBtn = left.querySelector(`[data-id="${selectedCategoryId}"]`);
    if (activeBtn) activeBtn.classList.add("active");
    renderSubPane(selectedCategoryId, categories);
  }

  picker.appendChild(left);
  picker.appendChild(right);
  block.appendChild(picker);
  return block;
}

function appendTagChips(container, sec, list, slot) {
  container.className = "tag-chips";
  if (!list.length) {
    container.innerHTML = '<span class="hint">暂无</span>';
    return;
  }
  for (const tag of list) {
    const btn = document.createElement("button");
    btn.type = "button";
    const isEmoji = sec.key === "emoji";
    btn.className = `tag-chip${isEmoji ? " emoji tag-chip-emoji-only" : ""}`;
    if (isEmoji) {
      const icon = BiliIndex.resolveTagIcon(tag);
      const name = (tag.name || tag.code || "").trim();
      btn.textContent = icon || "·";
      btn.title = name;
      btn.setAttribute("aria-label", name);
    } else {
      btn.textContent = BiliIndex.formatTagLabel(tag);
      btn.title = `code: ${tag.code}`;
    }
    if (slot) {
      btn.dataset.tagId = String(tag.id);
      btn.dataset.slot = slot;
    }
    btn.addEventListener("click", () => void selectTagForSlot(tag, sec.key));
    container.appendChild(btn);
  }
}

function renderCollapsibleChipGroup(sec, list) {
  const slot = slotForKind(sec.key);
  const expanded = expandedPopupGroups.has(sec.key);
  const block = document.createElement("div");
  block.className = "tag-group tag-group-collapsible";
  if (slot) block.dataset.slotGroup = slot;

  const head = document.createElement("button");
  head.type = "button";
  head.className = "tag-group-collapse-head";
  head.setAttribute("aria-expanded", String(expanded));
  const arrow = document.createElement("span");
  arrow.className = `collapse-arrow${expanded ? " expanded" : ""}`;
  arrow.setAttribute("aria-hidden", "true");
  arrow.textContent = "▶";
  const title = document.createElement("span");
  title.className = "tag-group-collapse-title";
  title.textContent = `${sec.title}${sec.hint ? ` · ${sec.hint}` : ""}`;
  head.append(arrow, title);

  const body = document.createElement("div");
  body.className = "tag-group-collapse-body";
  body.hidden = !expanded;
  const chips = document.createElement("div");
  appendTagChips(chips, sec, list, slot);
  body.appendChild(chips);

  head.addEventListener("click", () => {
    const open = !expandedPopupGroups.has(sec.key);
    if (open) expandedPopupGroups.add(sec.key);
    else expandedPopupGroups.delete(sec.key);
    body.hidden = !open;
    head.setAttribute("aria-expanded", String(open));
    arrow.classList.toggle("expanded", open);
  });

  block.append(head, body);
  return block;
}

function renderChipGroup(sec, list) {
  const slot = slotForKind(sec.key);
  const block = document.createElement("div");
  block.className = "tag-group";
  if (slot) block.dataset.slotGroup = slot;
  block.innerHTML = `<div class="tag-group-title">${sec.title}${sec.hint ? ` · ${sec.hint}` : ""}</div>`;
  const chips = document.createElement("div");
  appendTagChips(chips, sec, list, slot);
  block.appendChild(chips);
  return block;
}

function showAuthError(msg, isError = true) {
  const el = $("#auth-error");
  el.textContent = msg;
  el.classList.toggle("hidden", !msg);
  el.classList.toggle("error", !!msg && isError);
  el.classList.toggle("ok", !!msg && !isError);
}

function switchTab(tab) {
  document.querySelectorAll(".tab").forEach((b) => {
    b.classList.toggle("active", b.dataset.tab === tab);
  });
  $("#form-login").classList.toggle("hidden", tab !== "login");
  $("#form-register").classList.toggle("hidden", tab !== "register");
  $("#form-reset").classList.toggle("hidden", tab !== "reset");
  if (typeof BiliIndexCaptcha !== "undefined") {
    BiliIndexCaptcha.setVisible(tab === "login" || tab === "register");
  }
  showAuthError("");
}

document.querySelectorAll(".tab").forEach((btn) => {
  btn.addEventListener("click", () => switchTab(btn.dataset.tab));
});

$("#remember-password").addEventListener("change", () => {
  if (!$("#remember-password").checked) {
    $("#auto-login").checked = false;
  }
});

$("#auto-login").addEventListener("change", () => {
  if ($("#auto-login").checked) {
    $("#remember-password").checked = true;
  }
});

async function initAuthForm() {
  const prefs = await BiliIndex.getLoginPrefs();
  $("#remember-password").checked = prefs.rememberPassword;
  $("#auto-login").checked = prefs.autoLogin;
  const form = $("#form-login");
  if (prefs.rememberPassword && prefs.savedUsername) {
    form.username.value = prefs.savedUsername;
    if (prefs.savedPassword) {
      form.password.value = prefs.savedPassword;
    }
  }
}

async function tryAutoLogin() {
  const token = await BiliIndex.getToken();
  if (token) return false;
  const prefs = await BiliIndex.getLoginPrefs();
  if (
    !prefs.autoLogin ||
    !prefs.rememberPassword ||
    !prefs.savedUsername ||
    !prefs.savedPassword
  ) {
    return false;
  }
  switchTab("login");
  if (typeof BiliIndexCaptcha !== "undefined") {
    BiliIndexCaptcha.setVisible(true);
  }
  showAuthError("已填入账号，请完成滑块验证后点击登录", false);
  return false;
}

async function showBanner(membership) {
  const banner = $("#banner");
  if (!membership.is_member) {
    if (membership.label === "免费用户") {
      banner.textContent =
        "您的会员试用期已到期，部分功能已受限，请购买会员或使用积分兑换继续使用。";
      banner.className = "banner danger";
      banner.classList.remove("hidden");
    } else {
      banner.classList.add("hidden");
    }
    return;
  }
  if (
    membership.member_type === "trial" &&
    membership.remaining_days !== null &&
    membership.remaining_days <= 1
  ) {
    banner.textContent =
      "您的会员试用期将于明天到期，到期后您将无法使用高级屏蔽规则和无限打标功能，如需继续使用请购买会员或使用积分兑换。";
    banner.className = "banner warn";
    banner.classList.remove("hidden");
    return;
  }
  banner.classList.add("hidden");
}

function renderTagSections(groups, tree) {
  const root = $("#tag-sections");
  root.innerHTML = "";

  const categories = groups.category || [];
  const subs = groups.sub || [];
  root.appendChild(renderCategorySubPicker(categories));

  const tailSections = [
    { key: "style", title: "三级标签（风格）", hint: null },
    { key: "emoji", title: "Emoji 图标标签", hint: null },
  ];
  for (const sec of tailSections) {
    const list = groups[sec.key] || [];
    if (sec.key === "style") {
      root.appendChild(renderCollapsibleChipGroup(sec, list));
    } else {
      root.appendChild(renderChipGroup(sec, list));
    }
  }

  updateDraftBar();
  refreshSelectionStyles();

  return categories.length + subs.length + (groups.style?.length || 0) + (groups.emoji?.length || 0);
}

async function submitDraft(upMid) {
  if (isSubmittingDraft) return;
  isSubmittingDraft = true;
  const slots = requiredSlots();
  const wasPasserby = isPasserbyDraft();
  $("#action-msg").textContent = `提交中…（${slots.length} 个槽位）`;
  const ledgerIds = [];
  let expectedPoints = 0;
  try {
    for (const slot of slots) {
      const tag = draft[slot];
      if (!tag) continue;
      const res = await BiliIndex.quickTag(upMid, tag.code, { slot });
      if (!res?.ok) {
        $("#action-msg").textContent = res?.error || `打标失败（${SLOT_LABELS[slot]}）`;
        return;
      }
      if (res.points_ledger_id) {
        ledgerIds.push(res.points_ledger_id);
        expectedPoints += Number(res.points_earned) || 0;
      }
      if (res.activation_ledger_id) {
        ledgerIds.push(res.activation_ledger_id);
        expectedPoints += Number(res.activation_bonus) || 0;
      }
    }
    clearDraft();
    selectedCategoryId = null;
    updateDraftBar();
    refreshSelectionStyles();
    const groups = tagGroups;
    if (groups?.category?.length) {
      selectedCategoryId = groups.category[0].id;
      renderSubPane(selectedCategoryId, groups.category);
    }
    let pointsNote = "";
    if (ledgerIds.length) {
      try {
        const verify = await BiliIndex.verifyPointsCredit(ledgerIds, expectedPoints);
        if (verify.verified) {
          pointsNote = `，积分已到账 +${verify.actual_delta}`;
        } else {
          pointsNote = "，积分到账校验未通过，请稍后查看积分";
        }
        if (verify.user_points != null) {
          $("#points-status").textContent = `积分：${verify.user_points}`;
        }
      } catch {
        pointsNote = "，请刷新后查看积分是否到账";
      }
    }
    $("#action-msg").textContent = `已对 ${upMid} 完成打标（${
      wasPasserby ? "路人 + Emoji" : "一级 + 二级 + 风格 + Emoji"
    }）${pointsNote}。请刷新 B 站页面后生效`;
    const me = await BiliIndex.fetchMe();
    $("#points-status").textContent = `积分：${me.points}`;
  } catch (e) {
    $("#action-msg").textContent = e.message || "打标失败";
  } finally {
    isSubmittingDraft = false;
  }
}

async function selectTagForSlot(tag, kind) {
  const upMid = $("#tag-up-mid").value.trim();
  if (!upMid) {
    $("#action-msg").textContent = "请先填写或读取 UP主 UID";
    return;
  }

  const slot = slotForKind(kind);
  if (!slot) return;

  if (slot === "tag2" && !draft.tag1) {
    $("#action-msg").textContent = "请先选择一级分类";
    return;
  }
  if (slot === "tag2" && draft.tag1 && tag.parent_id != null && tag.parent_id !== draft.tag1.id) {
    $("#action-msg").textContent = `二级标签须属于「${draft.tag1.name}」`;
    return;
  }

  if (slot === "tag1") {
    draft.tag1 = tag;
    selectedCategoryId = tag.id;
    if (tag.code === PASSERBY_CODE) {
      draft.tag2 = null;
      draft.tag3 = null;
    } else if (draft.tag2 && draft.tag2.parent_id !== tag.id) {
      draft.tag2 = null;
    }
    renderSubPane(tag.id, tagGroups?.category || []);
  } else {
    draft[slot] = tag;
  }

  updateDraftBar();
  refreshSelectionStyles();

  const need = requiredSlots();
  const done = need.filter((s) => draft[s]).length;
  if (!isDraftComplete()) {
    const next = nextMissingSlotLabel();
    $("#action-msg").textContent = `已选 ${done}/${need.length}，请继续选择${next ? `「${next}」` : ""}`;
    return;
  }

  await submitDraft(upMid);
}

async function loadTags(force = false) {
  const status = $("#tags-load-status");
  status.textContent = "正在从服务器加载标签…";
  try {
    let tree = [];
    let emojiItems = [];
    try {
      if (force) {
        await BiliIndex.refreshTagCaches(true);
      }
      tree = await BiliIndex.getTagTree(force);
      const emojiRes = await BiliIndex.fetchPublic("/api/tag/quick-list?kind=emoji");
      emojiItems = emojiRes.items || [];
    } catch (e) {
      status.textContent = `后台加载失败，尝试直连… (${e.message})`;
      const [treeData, emojiRes] = await Promise.all([
        BiliIndex.fetchPublic("/api/tag/tree"),
        BiliIndex.fetchPublic("/api/tag/quick-list?kind=emoji"),
      ]);
      tree = treeData.items || [];
      emojiItems = emojiRes.items || [];
      await BiliIndex.storageSet({
        tagTree: tree,
        quickTags: tree.filter((t) => (t.tag_kind || "") !== "category" && t.level > 1),
        tagsCachedAt: Date.now(),
      });
    }

    const byCode = new Map(tree.map((t) => [t.code, t]));
    for (const t of emojiItems) {
      const prev = byCode.get(t.code);
      if (prev) {
        if (t.icon) prev.icon = BiliIndex.resolveTagIcon(t);
        if (t.tag_kind) prev.tag_kind = t.tag_kind;
      } else {
        byCode.set(t.code, { ...t, icon: BiliIndex.resolveTagIcon(t), tag_kind: "emoji" });
      }
    }
    tree = [...byCode.values()];

    if (!tree.length) {
      status.textContent = "未获取到标签。请确认 API 运行在 http://127.0.0.1:8000";
      $("#tag-sections").innerHTML =
        '<p class="hint">启动命令：uvicorn app.main:app --reload --port 8000</p>';
      return;
    }

    tagGroups = BiliIndex.groupTags(tree);
    subsByParent = buildSubsByParent(tree);
    const n = renderTagSections(tagGroups, tree);
    const quickN =
      (tagGroups.sub?.length || 0) +
      (tagGroups.style?.length || 0) +
      (tagGroups.emoji?.length || 0);
    status.textContent = `已加载 ${n} 个标签（可打标 ${quickN} 个）· 缓存 1 小时`;
  } catch (e) {
    status.textContent = `加载失败：${e.message}`;
    $("#tag-sections").innerHTML =
      '<p class="hint">请检查 API 地址与密钥，并确认 biliindex-api 已启动。</p>';
  }
}

function sendTabMessage(tabId, payload) {
  return new Promise((resolve, reject) => {
    chrome.tabs.sendMessage(tabId, payload, (res) => {
      const err = chrome.runtime.lastError;
      if (err) {
        reject(new Error(err.message));
        return;
      }
      resolve(res);
    });
  });
}

async function pickUpMidFromPage() {
  const btn = $("#btn-pick-up-mid");
  const msg = $("#action-msg");
  msg.textContent = "正在读取当前页…";
  if (btn) btn.disabled = true;
  try {
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    if (!tab?.id) {
      msg.textContent = "无法获取当前标签页";
      return;
    }
    if (!tab.url || !/bilibili\.com/i.test(tab.url)) {
      msg.textContent = "请先切换到 B 站页面（视频页 / 空间页等）";
      return;
    }

    let res = null;
    for (let attempt = 0; attempt < 3; attempt += 1) {
      try {
        res = await sendTabMessage(tab.id, { type: "GET_LAST_UP_MID" });
        break;
      } catch (e) {
        if (attempt < 2) {
          await new Promise((r) => setTimeout(r, 350));
          continue;
        }
        msg.textContent =
          "无法连接页面脚本，请在 B 站标签页按 Ctrl+F5 刷新后重试";
        console.warn("[BiliIndex] GET_LAST_UP_MID failed", e);
        return;
      }
    }

    if (res?.upMid) {
      $("#tag-up-mid").value = String(res.upMid);
      msg.textContent = `已读取 UID：${res.upMid}`;
    } else {
      msg.textContent =
        "未能识别 UP 主。请打开视频页或空间页，或点击页面上任意标签槽后再试";
    }
  } catch (e) {
    msg.textContent = e?.message || "读取失败";
  } finally {
    if (btn) btn.disabled = false;
  }
}

function renderBlockSelected() {
  const el = $("#block-selected");
  if (!el) return;
  if (!blockSelected.length) {
    el.innerHTML = '<span class="hint">未选择屏蔽标签</span>';
    return;
  }
  el.innerHTML = blockSelected
    .map(
      (t) =>
        `<span class="block-picked">${BiliIndex.formatTagLabel(t)} <button type="button" data-code="${t.code}" class="block-remove" aria-label="移除">×</button></span>`
    )
    .join("");
  el.querySelectorAll(".block-remove").forEach((btn) => {
    btn.addEventListener("click", () => {
      const code = btn.dataset.code;
      blockSelected = blockSelected.filter((t) => t.code !== code);
      renderBlockSelected();
      renderBlockPool();
    });
  });
}

function renderBlockPool() {
  const pool = $("#block-tag-pool");
  if (!pool) return;
  pool.innerHTML = "";
  const groups = tagGroups || { category: [], emoji: [] };
  const all = [...(groups.category || []), ...(groups.emoji || [])].filter(
    (t) => t.code !== PASSERBY_CODE
  );
  if (!all.length) {
    pool.innerHTML = '<span class="hint">请先加载标签</span>';
    return;
  }
  const picked = new Set(blockSelected.map((t) => t.code));
  for (const tag of all) {
    const btn = document.createElement("button");
    btn.type = "button";
    const active = picked.has(tag.code);
    btn.className = `block-pool-chip${active ? " active" : ""}`;
    btn.textContent = BiliIndex.formatTagLabel(tag);
    btn.disabled = active;
    btn.addEventListener("click", () => {
      if (blockSelected.length >= BLOCK_MAX) {
        $("#block-save-status").textContent = `最多 ${BLOCK_MAX} 个`;
        return;
      }
      if (blockSelected.some((t) => t.code === tag.code)) return;
      blockSelected.push({
        id: tag.id,
        code: tag.code,
        name: tag.name,
        tag_kind: tag.tag_kind,
        icon: tag.icon,
      });
      renderBlockSelected();
      renderBlockPool();
      $("#block-save-status").textContent = "";
    });
    pool.appendChild(btn);
  }
}

async function renderBlockPanel(membership, blockData) {
  const panel = $("#block-panel");
  if (!panel) return;
  if (!membership?.is_member) {
    panel.classList.add("hidden");
    return;
  }
  panel.classList.remove("hidden");
  blockSelected = (blockData?.tags || []).map((t) => ({
    id: t.id,
    code: t.code,
    name: t.name,
    tag_kind: t.tag_kind,
    icon: t.icon,
  }));
  renderBlockSelected();
  renderBlockPool();
}

async function loadAndRenderBlockPanel(membership) {
  let blockData = { tags: [], codes: [] };
  if (membership?.is_member) {
    try {
      blockData = await BiliIndex.fetchBlockTags();
    } catch (e) {
      $("#block-save-status").textContent = e.message || "屏蔽规则加载失败";
    }
  }
  await renderBlockPanel(membership, blockData);
}

async function renderLoggedIn() {
  const token = await BiliIndex.getToken();
  if (!token) {
    $("#logged-out").classList.remove("hidden");
    $("#logged-in").classList.add("hidden");
    $("#block-panel")?.classList.add("hidden");
    const activeTab = document.querySelector(".tab.active")?.dataset?.tab || "login";
    if (typeof BiliIndexCaptcha !== "undefined") {
      BiliIndexCaptcha.setVisible(activeTab === "login" || activeTab === "register");
    }
    return;
  }
  if (typeof BiliIndexCaptcha !== "undefined") {
    BiliIndexCaptcha.setVisible(false);
  }

  try {
    const me = await BiliIndex.fetchMe();
    const membership = await BiliIndex.fetchMembership();
    $("#logged-out").classList.add("hidden");
    $("#logged-in").classList.remove("hidden");
    $("#username-label").textContent = me.username;
    $("#membership-status").textContent = membership.label || "免费用户";
    $("#points-status").textContent = `积分：${me.points}`;
    await showBanner(membership);
    await checkBugNotifications();
    await loadTags(false);
    await loadAndRenderBlockPanel(membership);
  } catch (e) {
    await BiliIndex.clearSession();
    await BiliIndex.invalidateTagCache();
    renderLoggedIn();
  }
}

$("#form-login").addEventListener("submit", async (e) => {
  e.preventDefault();
  showAuthError("");
  const fd = new FormData(e.target);
  const username = String(fd.get("username") || "").trim();
  const password = String(fd.get("password") || "");
  const rememberPassword = $("#remember-password").checked;
  const autoLogin = $("#auto-login").checked;
  try {
    const captcha = BiliIndexCaptcha.getPayload();
    await BiliIndex.login(username, password, captcha);
    BiliIndexCaptcha.resetAfterSubmit();
    await BiliIndex.saveLoginPrefs({
      username,
      password,
      rememberPassword,
      autoLogin,
    });
    await BiliIndex.refreshTagCaches(true);
    await renderLoggedIn();
  } catch (err) {
    showAuthError(err.message);
    if (
      typeof BiliIndexCaptcha !== "undefined" &&
      BiliIndexCaptcha.isCaptchaError(err.message)
    ) {
      void BiliIndexCaptcha.refresh();
    }
  }
});

$("#form-reset").addEventListener("submit", async (e) => {
  e.preventDefault();
  showAuthError("");
  const fd = new FormData(e.target);
  const username = String(fd.get("username") || "").trim();
  const resetKey = String(fd.get("reset_key") || "").replace(/\D/g, "");
  const password = String(fd.get("password") || "");
  const password2 = String(fd.get("password2") || "");
  if (resetKey.length !== 16) {
    showAuthError("重置密钥须为 16 位数字");
    return;
  }
  if (password !== password2) {
    showAuthError("两次输入的新密码不一致");
    return;
  }
  try {
    const res = await BiliIndex.resetPassword(username, resetKey, password);
    showAuthError(res.message || "密码已重置，请使用新密码登录", false);
    e.target.reset();
    switchTab("login");
    $("#form-login input[name=username]").value = username;
  } catch (err) {
    showAuthError(err.message);
  }
});

$("#form-register").addEventListener("submit", async (e) => {
  e.preventDefault();
  showAuthError("");
  const fd = new FormData(e.target);
  try {
    const captcha = BiliIndexCaptcha.getPayload();
    await BiliIndex.register(fd.get("username"), fd.get("password"), captcha);
    BiliIndexCaptcha.resetAfterSubmit();
    await BiliIndex.refreshTagCaches(true);
    await renderLoggedIn();
  } catch (err) {
    showAuthError(err.message);
    if (
      typeof BiliIndexCaptcha !== "undefined" &&
      BiliIndexCaptcha.isCaptchaError(err.message)
    ) {
      void BiliIndexCaptcha.refresh();
    }
  }
});

$("#btn-logout").addEventListener("click", async () => {
  await BiliIndex.clearSession();
  await BiliIndex.invalidateTagCache();
  $("#action-msg").textContent = "";
  await renderLoggedIn();
});

$("#btn-exchange").addEventListener("click", async () => {
  $("#action-msg").textContent = "兑换中…";
  try {
    const res = await BiliIndex.exchangeMembership();
    $("#action-msg").textContent = res.message || "兑换成功";
    await renderLoggedIn();
  } catch (err) {
    $("#action-msg").textContent = err.message;
  }
});

$("#btn-refresh-tags").addEventListener("click", async () => {
  await loadTags(true);
  const membership = await BiliIndex.fetchMembership();
  renderBlockPool();
});

$("#btn-save-block-tags")?.addEventListener("click", async () => {
  const status = $("#block-save-status");
  status.textContent = "保存中…";
  try {
    const codes = blockSelected.map((t) => t.code);
    const res = await BiliIndex.saveBlockTags(codes);
    await chrome.storage.local.set({
      blockTagCodes: res.codes || codes,
      blockIsMember: !!res.is_member,
    });
    blockSelected = (res.tags || []).map((t) => ({
      id: t.id,
      code: t.code,
      name: t.name,
      tag_kind: t.tag_kind,
      icon: t.icon,
    }));
    renderBlockSelected();
    renderBlockPool();
    status.textContent = "已保存。请刷新 B 站页面后生效";
  } catch (e) {
    status.textContent = e.message || "保存失败";
  }
});

$("#btn-pick-up-mid").addEventListener("click", pickUpMidFromPage);

$("#api-base").addEventListener("change", async (e) => {
  await BiliIndex.storageSet({ apiBase: e.target.value.trim() });
  await BiliIndex.invalidateTagCache();
});

const POPUP_HEIGHT_KEY = "popupHeight";
const POPUP_HEIGHT_MIN = 400;
/** Chrome 扩展图标弹窗高度上限约 600px */
const POPUP_HEIGHT_MAX_ACTION = 600;
const POPUP_HEIGHT_MAX_STANDALONE = 1300;
const POPUP_HEIGHT_DEFAULT = 580;

const isStandaloneMode = () =>
  new URLSearchParams(location.search).get("mode") === "window";

function getPopupHeightMax() {
  return isStandaloneMode() ? POPUP_HEIGHT_MAX_STANDALONE : POPUP_HEIGHT_MAX_ACTION;
}

function clampPopupHeight(px) {
  return Math.min(getPopupHeightMax(), Math.max(POPUP_HEIGHT_MIN, Math.round(px)));
}

function applyPopupHeight(px) {
  const height = clampPopupHeight(px);
  document.documentElement.style.height = `${height}px`;
  document.body.style.height = `${height}px`;
  return height;
}

function updateResizeHint() {
  const hint = document.getElementById("popup-resize-hint");
  if (!hint) return;
  if (isStandaloneMode()) {
    hint.textContent = "大窗口模式：可拖至约 1300px 高";
    return;
  }
  hint.textContent = `浏览器限制图标弹窗最高约 ${POPUP_HEIGHT_MAX_ACTION}px，需要更高请点「大窗口」`;
}

function openStandaloneWindow() {
  const url = chrome.runtime.getURL("popup/popup.html?mode=window");
  chrome.tabs.create({ url });
}

function initPopupResize() {
  if (isStandaloneMode()) {
    document.documentElement.classList.add("popup-standalone-mode");
    document.documentElement.style.height = "100vh";
    document.body.style.height = "100vh";
    updateResizeHint();
    return;
  }

  const handle = document.getElementById("popup-resize-handle");
  if (!handle) return;
  updateResizeHint();

  chrome.storage.local.get([POPUP_HEIGHT_KEY], (stored) => {
    const h = stored[POPUP_HEIGHT_KEY];
    const raw = typeof h === "number" ? h : POPUP_HEIGHT_DEFAULT;
    applyPopupHeight(Math.min(raw, POPUP_HEIGHT_MAX_ACTION));
  });

  const onMove = (e) => {
    applyPopupHeight(e.clientY);
  };

  const onUp = () => {
    document.body.classList.remove("is-resizing-popup");
    document.removeEventListener("mousemove", onMove);
    document.removeEventListener("mouseup", onUp);
    let height = applyPopupHeight(document.documentElement.clientHeight);
    if (!isStandaloneMode()) {
      height = Math.min(height, POPUP_HEIGHT_MAX_ACTION);
      applyPopupHeight(height);
    }
    chrome.storage.local.set({ [POPUP_HEIGHT_KEY]: height });
  };

  handle.addEventListener("mousedown", (e) => {
    e.preventDefault();
    document.body.classList.add("is-resizing-popup");
    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);
  });
}

function openPopupModal(title, html) {
  const modal = document.getElementById("popup-modal");
  const titleEl = document.getElementById("popup-modal-title");
  const bodyEl = document.getElementById("popup-modal-body");
  if (!modal || !titleEl || !bodyEl) return;
  titleEl.textContent = title;
  bodyEl.innerHTML = html;
  modal.classList.remove("hidden");
  modal.setAttribute("aria-hidden", "false");
}

function closePopupModal() {
  const modal = document.getElementById("popup-modal");
  if (!modal) return;
  modal.classList.add("hidden");
  modal.setAttribute("aria-hidden", "true");
}

function initPopupModals() {
  document.getElementById("btn-usage-guide")?.addEventListener("click", async () => {
    openPopupModal("使用说明", '<p class="empty-hint">加载中…</p>');
    try {
      const base = await BiliIndex.getApiBase();
      const res = await fetch(`${base}/api/content/usage-guide`);
      const data = await res.json().catch(() => ({}));
      if (!res.ok) {
        throw new Error(data.detail || data.message || res.statusText);
      }
      const html = (data.body || "").trim();
      const bodyEl = document.getElementById("popup-modal-body");
      if (!bodyEl) return;
      if (!html) {
        bodyEl.innerHTML = '<p class="empty-hint">暂无使用说明，请在管理后台编辑。</p>';
        return;
      }
      bodyEl.innerHTML = html;
    } catch (e) {
      const bodyEl = document.getElementById("popup-modal-body");
      if (bodyEl) {
        bodyEl.innerHTML = `<p class="empty-hint">加载失败：${e.message}</p>`;
      }
    }
  });

  document.getElementById("link-privacy-policy")?.addEventListener("click", async (e) => {
    e.preventDefault();
    openPopupModal("隐私协议", '<p class="empty-hint">加载中…</p>');
    try {
      const base = await BiliIndex.getApiBase();
      const res = await fetch(`${base}/api/content/privacy-policy`);
      const data = await res.json().catch(() => ({}));
      if (!res.ok) {
        throw new Error(data.detail || data.message || res.statusText);
      }
      const html = (data.body || "").trim();
      const bodyEl = document.getElementById("popup-modal-body");
      if (!bodyEl) return;
      if (!html) {
        bodyEl.innerHTML =
          '<p class="empty-hint">暂无隐私协议，请在管理后台「隐私协议」中编辑。</p>';
        return;
      }
      if (/<[a-z][\s\S]*>/i.test(html)) {
        bodyEl.innerHTML = html;
      } else {
        const esc = (s) =>
          String(s)
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");
        bodyEl.innerHTML = `<div class="author-note-text">${esc(html)}</div>`;
      }
    } catch (err) {
      const bodyEl = document.getElementById("popup-modal-body");
      if (bodyEl) {
        bodyEl.innerHTML = `<p class="empty-hint">加载失败：${err.message}</p>`;
      }
    }
  });

  document.getElementById("btn-author-note")?.addEventListener("click", async () => {
    openPopupModal("作者的话", '<p class="empty-hint">加载中…</p>');
    try {
      const base = await BiliIndex.getApiBase();
      const res = await fetch(`${base}/api/content/author-note`);
      const data = await res.json().catch(() => ({}));
      if (!res.ok) {
        throw new Error(data.detail || data.message || res.statusText);
      }
      const text = (data.body || "").trim();
      const bodyEl = document.getElementById("popup-modal-body");
      if (!bodyEl) return;
      if (!text) {
        bodyEl.innerHTML =
          '<p class="empty-hint">作者尚未发布内容，请稍后再看。</p>';
        return;
      }
      const esc = (s) =>
        String(s)
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;");
      bodyEl.innerHTML = `<div class="author-note-text">${esc(text)}</div>`;
    } catch (e) {
      const bodyEl = document.getElementById("popup-modal-body");
      if (bodyEl) {
        bodyEl.innerHTML = `<p class="empty-hint">加载失败：${e.message}</p>`;
      }
    }
  });

  document.getElementById("btn-buy-member")?.addEventListener("click", () => {
    $("#action-msg").textContent = "购买会员功能暂不开放，请使用积分兑换或联系管理员";
  });

  document.getElementById("popup-modal-close")?.addEventListener("click", closePopupModal);
  document.querySelectorAll("[data-modal-close]").forEach((el) => {
    el.addEventListener("click", closePopupModal);
  });
}

async function checkBugNotifications() {
  const banner = document.getElementById("bug-notify-banner");
  if (!banner) return;
  const token = await BiliIndex.getToken();
  if (!token) {
    banner.classList.add("hidden");
    return;
  }
  try {
    const data = await BiliIndex.apiFetch("/api/user/bugs/notifications");
    const items = data.items || [];
    if (!items.length) {
      banner.classList.add("hidden");
      return;
    }
    const latest = items[0];
    banner.classList.remove("hidden");
    banner.textContent = `管理员已回复你的 BUG #${latest.id}，点击查看`;
    banner.onclick = () => {
      void showBugReplyModal(latest);
    };
  } catch {
    banner.classList.add("hidden");
  }
}

async function showBugReplyModal(item) {
  const esc = (s) =>
    String(s)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;");
  openPopupModal(
    `BUG 反馈 #${item.id} · 管理员回复`,
    `<p><strong>你的描述：</strong></p><p>${esc(item.body)}</p>
     <p><strong>管理员回复：</strong></p><p class="author-note-text">${esc(item.admin_reply || "")}</p>
     <p class="hint">时间：${item.replied_at || "—"}</p>`
  );
  try {
    await BiliIndex.apiFetch(`/api/user/bugs/${item.id}/read`, { method: "POST" });
    document.getElementById("bug-notify-banner")?.classList.add("hidden");
  } catch {
    /* ignore */
  }
}

function initBugReport() {
  document.getElementById("btn-open-standalone")?.addEventListener("click", () => {
    openStandaloneWindow();
  });

  document.getElementById("btn-bug-report")?.addEventListener("click", async () => {
    const token = await BiliIndex.getToken();
    if (!token) {
      $("#action-msg").textContent = "请先登录后再提交 BUG";
      return;
    }
    openPopupModal(
      "BUG 提交",
      `<p class="hint">请描述问题、复现步骤与浏览器版本，便于排查。</p>
       <label>BUG 描述<textarea id="bug-report-input" class="bug-form" placeholder="例如：评论区标签槽不显示…"></textarea></label>
       <button type="button" id="bug-report-submit" class="primary" style="width:100%;margin-top:8px">提交</button>`
    );
    document.getElementById("bug-report-submit")?.addEventListener("click", async () => {
      const text = document.getElementById("bug-report-input")?.value?.trim() || "";
      if (text.length < 5) {
        alert("请至少输入 5 个字");
        return;
      }
      try {
        const res = await BiliIndex.apiFetch("/api/user/bugs", {
          method: "POST",
          body: JSON.stringify({ body: text }),
        });
        closePopupModal();
        $("#action-msg").textContent = res.message || "BUG 已提交";
      } catch (e) {
        alert(e.message || "提交失败");
      }
    });
  });
}

initPopupResize();
initPopupModals();
initBugReport();

(async () => {
  const base = await BiliIndex.getApiBase();
  $("#api-base").value = base;
  if (typeof BiliIndexCaptcha !== "undefined") {
    BiliIndexCaptcha.mount(document.getElementById("auth-captcha-wrap"));
  }
  await initAuthForm();
  await tryAutoLogin();
  await renderLoggedIn();
})();
