(function bootLoginPortal(global) {
  "use strict";

  const THEME_KEY = "xiaobaiTheme";
  const api = global.XiaobaiAPI;
  const card = document.querySelector("#loginCard");
  const themeButton = document.querySelector("#loginThemeToggle");
  const state = {
    view: "first",
    mode: "login",
    accounts: [],
    currentUserId: null,
    loading: false,
    confirmingId: null,
    error: "",
    username: "",
    password: "",
    passwordVisible: false,
  };

  function escapeHtml(value) {
    return String(value ?? "").replace(/[&<>"']/g, (ch) => (
      { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[ch]
    ));
  }

  function preferredTheme() {
    try {
      const stored = global.localStorage.getItem(THEME_KEY);
      if (stored === "dark" || stored === "light") return stored;
    } catch (_error) {
      // Fall through to the system preference.
    }
    return global.matchMedia && global.matchMedia("(prefers-color-scheme: dark)").matches
      ? "dark"
      : "light";
  }

  function applyTheme(theme, persist) {
    const normalized = theme === "dark" ? "dark" : "light";
    document.documentElement.dataset.theme = normalized;
    document.documentElement.style.colorScheme = normalized;
    themeButton.textContent = normalized === "dark" ? "☀ 日间" : "🌙 夜间";
    themeButton.setAttribute("aria-label", normalized === "dark" ? "切换到日间模式" : "切换到夜间模式");
    if (persist) {
      try {
        global.localStorage.setItem(THEME_KEY, normalized);
      } catch (_error) {
        // Theme still applies for the current page when storage is unavailable.
      }
    }
  }

  function setError(message) {
    state.error = message || "";
  }

  function formatLastUsed(value) {
    if (!value) return "";
    const parsed = new Date(value);
    if (Number.isNaN(parsed.getTime())) return "";
    const now = new Date();
    const hh = String(parsed.getHours()).padStart(2, "0");
    const mm = String(parsed.getMinutes()).padStart(2, "0");
    if (parsed.toDateString() === now.toDateString()) return `今天 ${hh}:${mm}`;
    const yesterday = new Date(now);
    yesterday.setDate(now.getDate() - 1);
    if (parsed.toDateString() === yesterday.toDateString()) return `昨天 ${hh}:${mm}`;
    return `${parsed.getMonth() + 1}月${parsed.getDate()}日`;
  }

  function chipsFor(account, current) {
    const chips = [];
    if (current) chips.push('<span class="login-chip login-chip-current">当前</span>');
    if (account.role === "admin") chips.push('<span class="login-chip">管理员</span>');
    if (account.membership?.subscribed) chips.push('<span class="login-chip login-chip-member">会员</span>');
    else if (account.role !== "admin") chips.push('<span class="login-chip">普通用户</span>');
    return chips.join("");
  }

  function returnPath() {
    const raw = new URLSearchParams(global.location.search).get("next") || "";
    if (!raw) return "/";
    try {
      const url = new URL(raw, global.location.origin);
      if (url.origin !== global.location.origin) return "/";
      const path = url.pathname || "/";
      if (path === "/login" || path.startsWith("/login/")) return "/";
      return `${path}${url.search}${url.hash}` || "/";
    } catch (_error) {
      return "/";
    }
  }

  function enterApp() {
    global.location.replace(returnPath());
  }

  function formMarkup(options) {
    const registering = state.mode === "register";
    const submitLabel = options.submitLabel
      || (state.loading ? "正在登录..." : registering ? "注册并进入" : options.add ? "添加并进入" : "登录");
    const lead = options.lead;
    const hint = options.hint;
    const invalid = state.error ? " is-invalid" : "";
    const passwordType = state.passwordVisible ? "text" : "password";
    const passwordToggle = state.passwordVisible ? "隐藏" : "显示";
    return [
      options.back
        ? '<button class="login-back" type="button" data-login-action="picker">返回账号列表</button>'
        : "",
      `<h2 class="login-card-title">${escapeHtml(options.title)}</h2>`,
      `<p class="login-card-lead">${escapeHtml(lead)}</p>`,
      '<div class="login-tabs" role="tablist">',
      `<button class="login-tab${state.mode === "login" ? " is-active" : ""}" type="button" data-auth-mode="login">登录</button>`,
      `<button class="login-tab${state.mode === "register" ? " is-active" : ""}" type="button" data-auth-mode="register">注册</button>`,
      "</div>",
      '<form class="login-form" id="loginForm">',
      `<label class="form-field"><span>账号名</span><input id="loginUsername" type="text" minlength="3" maxlength="30" autocomplete="username" placeholder="请输入账号名" value="${escapeHtml(state.username)}" required></label>`,
      `<label class="form-field"><span>密码</span><span class="login-password-wrap"><input id="loginPassword" class="${invalid.trim()}" type="${passwordType}" minlength="8" maxlength="128" autocomplete="${registering ? "new-password" : "current-password"}" placeholder="请输入密码" value="${escapeHtml(state.password)}" required><button class="login-password-toggle" type="button" data-login-action="toggle-password" aria-pressed="${state.passwordVisible ? "true" : "false"}" aria-label="${state.passwordVisible ? "隐藏密码" : "显示密码"}">${passwordToggle}</button></span></label>`,
      `<label class="form-field" id="loginConfirmField"${registering ? "" : " hidden"}><span>确认密码</span><input id="loginPasswordConfirm" type="password" minlength="8" maxlength="128" autocomplete="new-password"${registering ? " required" : ""}></label>`,
      state.error ? `<p class="login-error">${escapeHtml(state.error)}</p>` : '<p class="login-error" hidden></p>',
      `<button class="button primary login-submit" type="submit"${state.loading ? " disabled" : ""}>`,
      state.loading ? '<span class="login-spinner" aria-hidden="true"></span>' : "",
      `<span>${escapeHtml(submitLabel)}</span></button>`,
      "</form>",
      `<p class="login-hint">${escapeHtml(hint)}</p>`,
    ].join("");
  }

  function accountRow(account) {
    const current = Number(account.user_id) === Number(state.currentUserId);
    const confirming = Number(state.confirmingId) === Number(account.user_id);
    const managing = state.view === "manage";
    const classes = [
      "login-account-row",
      current ? "is-current" : "",
      confirming ? "is-confirming" : "",
      !managing ? "is-switchable" : "",
    ].filter(Boolean).join(" ");
    if (managing && confirming) {
      return [
        `<div class="${classes}" data-user-id="${account.user_id}">`,
        `<p class="login-confirm-copy">移除「${escapeHtml(account.username)}」的本机记录？</p>`,
        '<div class="login-confirm-actions">',
        `<button class="button danger-button" type="button" data-forget-id="${account.user_id}">移除</button>`,
        '<button class="button" type="button" data-login-action="cancel-forget">取消</button>',
        "</div></div>",
      ].join("");
    }
    const glyph = escapeHtml(String(account.username || "账").slice(0, 1));
    const tone = Number(account.user_id || 0) % 4;
    const used = formatLastUsed(account.last_used_at);
    const action = managing
      ? `<button class="login-account-remove" type="button" data-confirm-id="${account.user_id}" aria-label="移除 ${escapeHtml(account.username)}"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M6 2h4l.5 1H14v1H2V3h3.5L6 2zm1 4v6H6V6h1zm3 0v6H9V6h1zM3.5 5H13l-.7 8.2A1.5 1.5 0 0 1 10.81 14H5.19a1.5 1.5 0 0 1-1.49-1.8L3.5 5z"></path></svg></button>`
      : current
        ? '<span class="login-account-action"><span class="login-account-check" aria-hidden="true">✓</span>继续使用</span>'
        : "";
    const switchAttr = !managing && !current ? ` data-switch-id="${account.user_id}"` : "";
    const resumeAttr = !managing && current ? ` data-resume-id="${account.user_id}"` : "";
    return [
      `<div class="${classes}" data-user-id="${account.user_id}"${switchAttr}${resumeAttr}>`,
      `<span class="login-avatar tone-${tone}" aria-hidden="true">${glyph}</span>`,
      '<div class="login-account-meta">',
      '<div class="login-account-name">',
      `<strong>${escapeHtml(account.username)}</strong>`,
      chipsFor(account, current),
      "</div>",
      used ? `<span class="login-account-used">上次登录 ${escapeHtml(used)}</span>` : "",
      "</div>",
      action,
      "</div>",
    ].join("");
  }

  function pickerMarkup() {
    const count = state.accounts.length;
    const managing = state.view === "manage";
    return [
      managing
        ? ""
        : '<button class="login-back" type="button" data-login-action="resume">返回复盘</button>',
      `<h2 class="login-card-title">${managing ? "管理账号记录" : "选择账号"}</h2>`,
      `<p class="login-card-lead">${managing
        ? "移除只删除这台电脑上的登录记录，不会注销账号"
        : `这台电脑已记录 ${count} 个账号，点选即可进入，无需再次输入密码。`}</p>`,
      managing
        ? '<div class="login-manage-toolbar"><p class="login-manage-hint">点击右侧图标移除对应记录</p><button class="login-manage-done" type="button" data-login-action="picker">完成</button></div>'
        : "",
      `<div class="login-account-list">${state.accounts.map(accountRow).join("")}</div>`,
      managing
        ? ""
        : '<button class="login-add" type="button" data-login-action="add">＋ 添加账号</button>',
      managing
        ? ""
        : '<button class="login-manage" type="button" data-login-action="manage">管理已记录的账号</button>',
      state.error ? `<p class="login-error">${escapeHtml(state.error)}</p>` : "",
      managing
        ? '<p class="login-privacy"><span class="login-lock" aria-hidden="true">🔒</span>移除后再次登录该账号需重新输入密码</p>'
        : '<p class="login-privacy"><span class="login-lock" aria-hidden="true">🔒</span>账号记录仅保存在这台电脑的浏览器中</p>',
    ].join("");
  }

  function render() {
    card.classList.toggle("is-loading", state.loading);
    if (state.view === "first" || state.view === "add") {
      card.innerHTML = formMarkup({
        title: state.view === "add" ? "添加账号" : "欢迎回来",
        lead: state.view === "add" ? "登录另一个账号，添加后可随时一键切换" : "登录后进入你的复盘空间",
        hint: state.view === "add"
          ? "添加后账号会保存在这台电脑，方便随时切换。"
          : "密码连续输错 5 次将锁定 10 分钟。还没有账号？切换到「注册」创建。",
        add: state.view === "add",
        back: state.view === "add",
      });
    } else {
      card.innerHTML = pickerMarkup();
    }
    bindCard();
    if (mascots) mascots.sync();
  }

  function bindCard() {
    card.querySelectorAll("[data-auth-mode]").forEach((button) => {
      button.addEventListener("click", () => {
        state.mode = button.dataset.authMode === "register" ? "register" : "login";
        setError("");
        render();
      });
    });
    card.querySelectorAll("[data-login-action]").forEach((button) => {
      button.addEventListener("click", () => {
        const action = button.dataset.loginAction;
        if (action === "toggle-password") {
          togglePasswordVisible();
          return;
        }
        if (action === "resume") {
          resumeCurrentAccount();
          return;
        }
        if (action === "picker") {
          state.view = state.accounts.length ? "picker" : "first";
          state.confirmingId = null;
        } else if (action === "add") {
          state.view = "add";
          state.mode = "login";
        } else if (action === "manage") {
          state.view = "manage";
        } else if (action === "cancel-forget") {
          state.confirmingId = null;
        }
        setError("");
        render();
      });
    });
    card.querySelectorAll("[data-switch-id]").forEach((button) => {
      button.addEventListener("click", () => switchAccount(Number(button.dataset.switchId)));
    });
    card.querySelectorAll("[data-resume-id]").forEach((button) => {
      button.addEventListener("click", () => resumeCurrentAccount());
    });
    card.querySelectorAll("[data-confirm-id]").forEach((button) => {
      button.addEventListener("click", (event) => {
        event.stopPropagation();
        state.confirmingId = Number(button.dataset.confirmId);
        render();
      });
    });
    card.querySelectorAll("[data-forget-id]").forEach((button) => {
      button.addEventListener("click", () => forgetAccount(Number(button.dataset.forgetId)));
    });
    const form = card.querySelector("#loginForm");
    if (form) form.addEventListener("submit", submitCredentials);
  }

  async function loadAccounts() {
    const payload = await api.request("/api/auth/accounts");
    state.accounts = payload.accounts || [];
    state.currentUserId = payload.current_user_id ?? null;
    const params = new URLSearchParams(global.location.search);
    if (params.get("mode") === "register") state.mode = "register";
    if (params.get("notice")) setError(params.get("notice"));
    if (state.accounts.length) state.view = "picker";
    else state.view = "first";
  }

  async function submitCredentials(event) {
    event.preventDefault();
    const username = document.querySelector("#loginUsername").value.trim();
    const password = document.querySelector("#loginPassword").value;
    state.username = username;
    state.password = password;
    if (state.mode === "register" && password !== document.querySelector("#loginPasswordConfirm").value) {
      setError("两次输入的密码不一致。");
      render();
      return;
    }
    state.loading = true;
    setError("");
    render();
    try {
      await api.request(`/api/auth/${state.mode}`, "POST", { username, password });
      await celebrateLogin();
      enterApp();
    } catch (error) {
      state.loading = false;
      setError(error.message || "账号操作失败");
      render();
      mascots.fail();
    }
  }

  async function switchAccount(userId) {
    state.loading = true;
    setError("");
    render();
    try {
      await api.request("/api/auth/switch", "POST", { user_id: userId });
      enterApp();
    } catch (error) {
      state.loading = false;
      setError(error.message || "该账号需重新验证");
      render();
    }
  }

  async function resumeCurrentAccount() {
    state.loading = true;
    setError("");
    render();
    try {
      const session = await api.request("/api/auth/me");
      const sessionUserId = session.user?.id;
      const matches = Boolean(session.authenticated) && (
        !state.currentUserId || Number(sessionUserId) === Number(state.currentUserId)
      );
      if (!matches) {
        throw new Error("当前会话已失效，请重新登录");
      }
      enterApp();
    } catch (error) {
      state.loading = false;
      setError(error.message || "当前会话已失效，请重新登录");
      render();
    }
  }

  async function forgetAccount(userId) {
    try {
      await api.request("/api/auth/forget", "POST", { user_id: userId });
      state.accounts = state.accounts.filter((item) => Number(item.user_id) !== Number(userId));
      state.confirmingId = null;
      if (!state.accounts.length) state.view = "first";
      setError("");
      render();
    } catch (error) {
      setError(error.message || "移除失败");
      render();
    }
  }

  function togglePasswordVisible() {
    state.passwordVisible = !state.passwordVisible;
    const input = document.querySelector("#loginPassword");
    const button = document.querySelector(".login-password-toggle");
    if (input) {
      input.type = state.passwordVisible ? "text" : "password";
      input.focus();
    }
    if (button) {
      button.textContent = state.passwordVisible ? "隐藏" : "显示";
      button.setAttribute("aria-pressed", state.passwordVisible ? "true" : "false");
      button.setAttribute("aria-label", state.passwordVisible ? "隐藏密码" : "显示密码");
    }
    mascots.sync();
  }

  function reducedMotion() {
    return Boolean(global.matchMedia && global.matchMedia("(prefers-reduced-motion: reduce)").matches);
  }

  function finePointer() {
    return Boolean(global.matchMedia && global.matchMedia("(pointer: fine)").matches);
  }

  function wait(ms) {
    return new Promise((resolve) => global.setTimeout(resolve, ms));
  }

  async function celebrateLogin() {
    mascots.succeed();
    if (!reducedMotion()) await wait(720);
  }

  const mascots = (() => {
    const root = document.querySelector("#loginMascots");
    const red = root && root.querySelector(".login-mascot.is-red");
    const green = root && root.querySelector(".login-mascot.is-green");
    const motion = {
      pupilX: 0,
      pupilY: 0,
      targetX: 0,
      targetY: 0,
      leanRed: 0,
      leanGreen: 0,
      targetLeanRed: 0,
      targetLeanGreen: 0,
    };
    let mood = "idle";
    let locked = "";
    let blinkTimer = 0;
    let failTimer = 0;
    let raf = 0;

    function setVars() {
      if (!red || !green) return;
      const pupilX = `${motion.pupilX.toFixed(2)}px`;
      const pupilY = `${motion.pupilY.toFixed(2)}px`;
      red.style.setProperty("--pupil-x", pupilX);
      red.style.setProperty("--pupil-y", pupilY);
      green.style.setProperty("--pupil-x", pupilX);
      green.style.setProperty("--pupil-y", pupilY);
      red.style.setProperty("--lean", `${motion.leanRed.toFixed(2)}deg`);
      green.style.setProperty("--lean", `${motion.leanGreen.toFixed(2)}deg`);
    }

    function poseFor(next) {
      if (next === "account" || next === "busy") {
        motion.targetX = 3.2;
        motion.targetY = 0.8;
        motion.targetLeanRed = 7;
        motion.targetLeanGreen = 5.6;
        return;
      }
      if (next === "password") {
        motion.targetX = 0;
        motion.targetY = 0;
        motion.targetLeanRed = 0;
        motion.targetLeanGreen = 0;
        return;
      }
      if (next === "fail") {
        motion.targetX = 0;
        motion.targetY = 2.8;
        motion.targetLeanRed = 8;
        motion.targetLeanGreen = 8;
        return;
      }
      if (next === "success") {
        motion.targetX = 0;
        motion.targetY = 0;
        motion.targetLeanRed = 0;
        motion.targetLeanGreen = 0;
        return;
      }
      if (!finePointer()) {
        motion.targetX = 2.4;
        motion.targetY = 0.4;
        motion.targetLeanRed = 4;
        motion.targetLeanGreen = 3.2;
        return;
      }
      motion.targetLeanRed = 0;
      motion.targetLeanGreen = 0;
    }

    function applyMood(next) {
      if (!root) return;
      const changed = next !== mood;
      if (changed) {
        mood = next;
        root.dataset.mood = next;
        poseFor(next);
      } else if (next !== "idle") {
        poseFor(next);
      }
      if (reducedMotion()) {
        motion.pupilX = motion.targetX;
        motion.pupilY = motion.targetY;
        motion.leanRed = motion.targetLeanRed;
        motion.leanGreen = motion.targetLeanGreen;
        setVars();
      }
    }

    function focusedControl() {
      const active = document.activeElement;
      if (!active || !card.contains(active)) return "";
      if (active.classList.contains("login-password-toggle")) return "loginPassword";
      return active.id || "";
    }

    function desiredMood() {
      if (locked === "success") return "success";
      if (state.loading) return "busy";
      const focused = focusedControl();
      if (focused === "loginPassword" || focused === "loginPasswordConfirm") return "password";
      if (focused === "loginUsername") return "account";
      if (locked === "fail") return "fail";
      return "idle";
    }

    function sync() {
      applyMood(desiredMood());
    }

    function succeed() {
      locked = "success";
      applyMood("success");
    }

    function fail() {
      locked = "fail";
      applyMood("fail");
      global.clearTimeout(failTimer);
      failTimer = global.setTimeout(() => {
        if (locked === "fail") locked = "";
        sync();
      }, 900);
    }

    function blink() {
      if (!root || reducedMotion()) return;
      if (mood !== "idle" && mood !== "account" && mood !== "busy") return;
      root.classList.remove("is-blinking");
      void root.offsetWidth;
      root.classList.add("is-blinking");
      global.setTimeout(() => root.classList.remove("is-blinking"), 160);
    }

    function scheduleBlink() {
      global.clearTimeout(blinkTimer);
      if (reducedMotion()) return;
      const waitMs = 4000 + Math.random() * 2000;
      blinkTimer = global.setTimeout(() => {
        blink();
        scheduleBlink();
      }, waitMs);
    }

    function onMouseMove(event) {
      if (reducedMotion() || !finePointer()) return;
      if (desiredMood() !== "idle") return;
      const rect = root.getBoundingClientRect();
      const cx = rect.left + rect.width * 0.42;
      const cy = rect.top + rect.height * 0.42;
      const dx = event.clientX - cx;
      const dy = event.clientY - cy;
      const dist = Math.hypot(dx, dy) || 1;
      const cap = 3.5;
      motion.targetX = (dx / dist) * Math.min(cap, Math.abs(dx) / 90);
      motion.targetY = (dy / dist) * Math.min(cap, Math.abs(dy) / 90);
      const tilt = Math.max(-5, Math.min(5, (dx / Math.max(global.innerWidth, 1)) * 10));
      motion.targetLeanRed = tilt;
      motion.targetLeanGreen = tilt * 0.8;
    }

    function tick() {
      if (!root) return;
      if (!reducedMotion()) {
        motion.pupilX += (motion.targetX - motion.pupilX) * 0.18;
        motion.pupilY += (motion.targetY - motion.pupilY) * 0.18;
        motion.leanRed += (motion.targetLeanRed - motion.leanRed) * 0.18;
        motion.leanGreen += (motion.targetLeanGreen - motion.leanGreen) * 0.18;
        setVars();
      } else {
        motion.pupilX = motion.targetX;
        motion.pupilY = motion.targetY;
        motion.leanRed = motion.targetLeanRed;
        motion.leanGreen = motion.targetLeanGreen;
        setVars();
      }
      raf = global.requestAnimationFrame(tick);
    }

    function onFocusChange() {
      global.requestAnimationFrame(sync);
    }

    if (root) {
      document.addEventListener("focusin", onFocusChange);
      document.addEventListener("focusout", onFocusChange);
      global.addEventListener("mousemove", onMouseMove, { passive: true });
      if (!reducedMotion()) {
        raf = global.requestAnimationFrame(tick);
        scheduleBlink();
      } else {
        poseFor("idle");
        setVars();
      }
      sync();
    }

    return { sync, succeed, fail };
  })();

  themeButton.addEventListener("click", () => {
    applyTheme(document.documentElement.dataset.theme === "dark" ? "light" : "dark", true);
  });
  applyTheme(preferredTheme(), false);

  loadAccounts()
    .then(render)
    .catch((error) => {
      setError(error.message || "无法连接本地服务");
      state.view = "first";
      render();
    });
})(window);
