const $ = (selector) => document.querySelector(selector);
const API_BASE = "/api/cms";
const MAX_UPLOAD_SIZE = 20 * 1024 * 1024;
const DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
let categories = [];
let editingSlug = null;
let editingStatus = "new-unsaved";
let dirty = false;
let previewTimer = null;
let previewRequest = 0;
let captchaToken = "";
let captchaObjectUrl = "";
let captchaRequest = 0;
let currentUser = null;
let consultationTeachers = [];

async function api(path, options = {}) {
  const endpoint = path.startsWith("/") ? path : `/${path}`;
  const response = await fetch(`${API_BASE}${endpoint}`, {
    headers: { "Content-Type": "application/json" },
    ...options,
    body: options.body ? JSON.stringify(options.body) : undefined,
  });
  let data = {};
  try { data = await response.json(); } catch {}
  if (!response.ok) throw new Error(data.error || `请求失败（${response.status}）`);
  return data;
}

const show = (selector) => $(selector).classList.remove("hidden");
const hide = (selector) => $(selector).classList.add("hidden");
async function refreshCaptcha() {
  const request = ++captchaRequest;
  captchaToken = "";
  $("#captcha").value = "";
  $("#captcha-image").removeAttribute("src");
  try {
    const response = await fetch(`${API_BASE}/captcha?t=${Date.now()}`, { cache: "no-store" });
    if (!response.ok) throw new Error(`验证码加载失败（${response.status}）`);
    const token = response.headers.get("X-Captcha-Token") || "";
    const blob = await response.blob();
    if (request !== captchaRequest) return;
    if (!token) throw new Error("验证码令牌缺失");
    if (captchaObjectUrl) URL.revokeObjectURL(captchaObjectUrl);
    captchaObjectUrl = URL.createObjectURL(blob);
    captchaToken = token;
    $("#captcha-image").src = captchaObjectUrl;
  } catch (error) {
    if (request === captchaRequest) $("#login-error").textContent = error.message;
  }
}

function showLogin() {
  show("#login");
  hide("#app");
  refreshCaptcha();
  $("#username").value ||= "admin";
  $("#username").focus();
}

async function boot() {
  const session = await api("/session").catch(() => ({ authed: false }));
  if (session.authed) {
    hide("#login"); show("#app");
    await enterApp(session.user);
  }
  else showLogin();
}

$("#captcha-image").addEventListener("click", refreshCaptcha);

$("#login-form").addEventListener("submit", async (event) => {
  event.preventDefault();
  $("#login-error").textContent = "";
  try {
    const result = await api("/session", { method: "POST", body: { username: $("#username").value, password: $("#password").value, captcha: $("#captcha").value, captchaToken } });
    $("#password").value = "";
    $("#captcha").value = "";
    hide("#login"); show("#app");
    await enterApp(result.user);
  } catch (error) {
    $("#login-error").textContent = error.message;
    refreshCaptcha();
  }
});

$("#logout").addEventListener("click", async () => { await api("/session", { method: "DELETE" }); location.reload(); });

async function enterApp(user) {
  currentUser = user;
  const admin = user?.role === "admin";
  document.body.classList.toggle("teacher-session", !admin);
  document.querySelectorAll(".admin-only").forEach((element) => element.classList.toggle("hidden", !admin));
  $("#account-role").textContent = admin ? "管理员账号" : "咨询老师";
  $("#account-name").textContent = user?.name || "";
  $("#consultation-scope").textContent = admin ? "管理员可查看全部老师的咨询" : "这里只显示分配给你的咨询";
  await showAdminView("consultations");
  if (admin) {
    await loadCategories();
    loadArticles();
  }
}

const adminViews = {
  consultations: ["#consultations-view", "咨询记录", "查看和跟进官网咨询"],
  teachers: ["#teachers-view", "咨询老师", "配置账号、二维码与轮询顺序"],
  articles: ["#list-view", "学习资讯", "管理官网文章内容"],
};

async function showAdminView(name) {
  Object.values(adminViews).forEach(([selector]) => hide(selector));
  hide("#edit-view");
  const view = adminViews[name] || adminViews.consultations;
  show(view[0]);
  $("#page-title").textContent = view[1];
  $("#page-subtitle").textContent = view[2];
  document.querySelectorAll("[data-admin-nav]").forEach((button) => button.classList.toggle("active", button.dataset.adminNav === name));
  if (name === "consultations") await loadConsultations();
  if (name === "teachers") await loadTeachers();
  if (name === "articles") await loadArticles();
}

document.querySelectorAll("[data-admin-nav]").forEach((button) => button.addEventListener("click", () => { void showAdminView(button.dataset.adminNav); }));

const formatTime = (value) => new Intl.DateTimeFormat("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false }).format(new Date(value));

function updateConsultationMetrics(records) {
  $("#consultation-count").textContent = records.length;
  $("#metric-total").textContent = records.length;
  ["new", "contacted", "completed"].forEach((status) => {
    $(`#metric-${status}`).textContent = records.filter((item) => item.status === status).length;
  });
}

async function loadConsultations() {
  const result = await api("/consultations").catch((error) => { alert(error.message); return { consultations: [] }; });
  const records = result.consultations || [];
  updateConsultationMetrics(records);
  const list = $("#consultation-list");
  list.innerHTML = "";
  if (!records.length) {
    list.innerHTML = '<div class="empty">暂时没有咨询记录。官网用户提交手机号后会显示在这里。</div>';
    return;
  }
  const head = document.createElement("div");
  head.className = "data-row data-head consultation-row";
  head.innerHTML = "<span>手机号</span><span>提交时间</span><span>分配老师</span><span>跟进状态</span><span>跟进备注</span><span>操作</span>";
  list.append(head);
  records.forEach((record) => {
    const row = document.createElement("article");
    row.className = "data-row consultation-row";
    row.innerHTML = '<a class="phone-link"></a><time class="created"></time><span class="teacher-name"></span><form class="inline-follow-form"><select aria-label="咨询状态"><option value="new">待联系</option><option value="contacted">跟进中</option><option value="completed">已完成</option></select><div class="inline-note"><textarea maxlength="500" rows="2" placeholder="记录沟通结果或下次跟进时间"></textarea><button class="note-toggle" type="button">查看全部备注</button></div><div class="inline-follow-actions"><button class="primary small" type="submit">保存</button><small class="save-state"></small></div></form>';
    const phone = row.querySelector(".phone-link");
    phone.textContent = record.phone;
    phone.href = `tel:${record.phone}`;
    row.querySelector(".created").textContent = formatTime(record.createdAt);
    row.querySelector(".teacher-name").textContent = record.teacherName;
    const form = row.querySelector("form");
    const textarea = form.querySelector("textarea");
    const noteToggle = form.querySelector(".note-toggle");
    form.querySelector("select").value = record.status;
    textarea.value = record.note || "";
    noteToggle.addEventListener("click", () => {
      const expanded = textarea.classList.toggle("expanded");
      textarea.style.height = expanded ? `${Math.min(Math.max(textarea.scrollHeight, 96), 260)}px` : "";
      noteToggle.textContent = expanded ? "收起备注" : "查看全部备注";
      if (expanded) textarea.focus();
    });
    form.addEventListener("submit", async (event) => {
      event.preventDefault();
      const button = form.querySelector('button[type="submit"]');
      const state = form.querySelector(".save-state");
      button.disabled = true;
      state.textContent = "保存中…";
      try {
        const result = await api(`/consultations/${record.id}`, { method: "PATCH", body: { status: form.querySelector("select").value, note: form.querySelector("textarea").value } });
        record.status = result.consultation.status;
        record.note = result.consultation.note;
        updateConsultationMetrics(records);
        state.textContent = "已保存";
        setTimeout(() => { state.textContent = ""; }, 1600);
      } catch (error) { state.textContent = error.message; }
      finally { button.disabled = false; }
    });
    list.append(row);
  });
}

$("#refresh-consultations").addEventListener("click", loadConsultations);

async function loadTeachers() {
  const result = await api("/consultation-teachers").catch((error) => { alert(error.message); return { teachers: [] }; });
  consultationTeachers = result.teachers || [];
  const list = $("#teacher-list");
  list.innerHTML = "";
  if (!consultationTeachers.length) {
    list.innerHTML = '<div class="empty">还没有咨询老师。添加并启用老师后，前台手机号才能按顺序分配。</div>';
    return;
  }
  const head = document.createElement("div");
  head.className = "data-row data-head teacher-row";
  head.innerHTML = "<span>老师</span><span>登录账号</span><span>轮询</span><span>累计分配</span><span>状态</span><span>操作</span>";
  list.append(head);
  consultationTeachers.forEach((teacher) => {
    const row = document.createElement("article");
    row.className = "data-row teacher-row";
    row.innerHTML = '<div class="teacher-identity"><img alt="微信二维码" /><div><strong></strong><small></small></div></div><span class="username"></span><strong class="sort"></strong><span class="assigned"></span><span class="teacher-state"></span><div class="row-actions"></div>';
    row.querySelector("img").src = teacher.qrCodeUrl;
    row.querySelector(".teacher-identity strong").textContent = teacher.name;
    row.querySelector(".teacher-identity small").textContent = teacher.title;
    row.querySelector(".username").textContent = teacher.username;
    row.querySelector(".sort").textContent = teacher.sort;
    row.querySelector(".assigned").textContent = `${teacher.assignedCount} 次`;
    const state = row.querySelector(".teacher-state");
    state.textContent = teacher.enabled ? "启用" : "停用";
    state.className = `teacher-state ${teacher.enabled ? "enabled" : "disabled"}`;
    row.querySelector(".row-actions").append(
      listAction("编辑", "ghost small", () => openTeacherModal(teacher)),
      listAction("删除", "ghost danger small", async () => {
        if (!confirm(`确定删除咨询老师“${teacher.name}”吗？历史咨询仍会保留。`)) return;
        try { await api(`/consultation-teachers/${teacher.id}`, { method: "DELETE" }); await loadTeachers(); }
        catch (error) { alert(error.message); }
      }),
    );
    list.append(row);
  });
}

function closeTeacherModal() { hide("#teacher-modal"); $("#teacher-form").reset(); $("#teacher-error").textContent = ""; }

function openTeacherModal(teacher = null) {
  $("#teacher-form").reset();
  $("#teacher-error").textContent = "";
  $("#teacher-id").value = teacher?.id || "";
  $("#teacher-modal-title").textContent = teacher ? "编辑咨询老师" : "添加咨询老师";
  $("#teacher-name").value = teacher?.name || "";
  $("#teacher-title").value = teacher?.title || "教务老师｜一对一学情沟通";
  $("#teacher-username").value = teacher?.username || "";
  $("#teacher-wechat").value = teacher?.wechat || "";
  $("#teacher-sort").value = teacher?.sort || Math.max(1, ...consultationTeachers.map((item) => item.sort + 1));
  $("#teacher-enabled").checked = teacher?.enabled ?? true;
  $("#teacher-password").required = !teacher;
  $("#teacher-password-hint").textContent = teacher ? "留空则不修改密码" : "至少 6 个字符";
  $("#teacher-qr-url").value = teacher?.qrCodeUrl || "";
  $("#teacher-qr-preview").src = teacher?.qrCodeUrl || "";
  $("#teacher-qr-preview").classList.toggle("hidden", !teacher?.qrCodeUrl);
  $("#teacher-qr-label").textContent = teacher ? "点击更换二维码" : "点击上传二维码";
  show("#teacher-modal");
  $("#teacher-name").focus();
}

$("#new-teacher").addEventListener("click", () => openTeacherModal());
$("#close-teacher-modal").addEventListener("click", closeTeacherModal);
$("#cancel-teacher").addEventListener("click", closeTeacherModal);
$("#teacher-qr-button").addEventListener("click", () => $("#teacher-qr-file").click());
$("#teacher-qr-file").addEventListener("change", async (event) => {
  const file = event.target.files?.[0];
  event.target.value = "";
  if (!file) return;
  if (file.size > MAX_UPLOAD_SIZE) return $("#teacher-error").textContent = "二维码图片不能超过 20MB";
  $("#teacher-qr-label").textContent = "上传中…";
  try {
    const dataUrl = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(file); });
    const result = await api("/uploads", { method: "POST", body: { dataUrl } });
    $("#teacher-qr-url").value = result.url;
    $("#teacher-qr-preview").src = result.url;
    show("#teacher-qr-preview");
    $("#teacher-qr-label").textContent = "点击更换二维码";
  } catch (error) { $("#teacher-error").textContent = error.message; $("#teacher-qr-label").textContent = "点击上传二维码"; }
});

$("#teacher-form").addEventListener("submit", async (event) => {
  event.preventDefault();
  const id = $("#teacher-id").value;
  const body = { name: $("#teacher-name").value, title: $("#teacher-title").value, username: $("#teacher-username").value, wechat: $("#teacher-wechat").value, sort: Number($("#teacher-sort").value), password: $("#teacher-password").value, qrCodeUrl: $("#teacher-qr-url").value, enabled: $("#teacher-enabled").checked };
  const submit = event.submitter;
  submit.disabled = true;
  $("#teacher-error").textContent = "";
  try {
    await api(id ? `/consultation-teachers/${id}` : "/consultation-teachers", { method: id ? "PUT" : "POST", body });
    closeTeacherModal();
    await loadTeachers();
  } catch (error) { $("#teacher-error").textContent = error.message; }
  finally { submit.disabled = false; }
});

function closePasswordModal() {
  hide("#password-modal");
  $("#password-form").reset();
  $("#password-error").textContent = "";
}

$("#change-password").addEventListener("click", () => {
  $("#password-form").reset();
  $("#password-error").textContent = "";
  show("#password-modal");
  $("#current-password").focus();
});

$("#close-password-modal").addEventListener("click", closePasswordModal);
$("#cancel-password").addEventListener("click", closePasswordModal);

$("#password-form").addEventListener("submit", async (event) => {
  event.preventDefault();
  const currentPassword = $("#current-password").value;
  const newPassword = $("#new-password").value;
  const confirmPassword = $("#confirm-password").value;
  const submit = event.submitter;
  $("#password-error").textContent = "";
  if (newPassword !== confirmPassword) {
    $("#password-error").textContent = "两次输入的新密码不一致";
    return;
  }
  if (submit) submit.disabled = true;
  try {
    await api("/account/password", { method: "PUT", body: { currentPassword, newPassword } });
    closePasswordModal();
    alert("密码修改成功，请重新登录后台");
    showLogin();
  } catch (error) {
    $("#password-error").textContent = error.message;
  } finally {
    if (submit) submit.disabled = false;
  }
});

const statusMap = {
  "new-unsaved": ["尚未保存", "new-draft", "填写完成后先保存草稿，线上网站不会发生变化。"],
  "new-draft": ["新建未发布", "new-draft", "这篇文章目前只有草稿版本，发布后才会出现在官网。"],
  "edited-draft": ["修改未发布", "edited-draft", "草稿已经保存，官网仍在展示上一次发布的版本。"],
  live: ["已上线", "live", "当前编辑内容与官网展示的版本一致。"],
};

function applyStatus(element, status) {
  const [label, className] = statusMap[status] || statusMap["new-draft"];
  element.className = `status ${className}`;
  element.textContent = label;
}

async function loadArticles() {
  const articles = await api("/articles").catch((error) => { alert(error.message); return []; });
  const list = $("#article-list");
  list.innerHTML = "";
  const pending = articles.filter((item) => item.publishStatus !== "live").length;
  $("#pending-badge").textContent = `${pending} 篇草稿待发布`;
  $("#pending-badge").classList.toggle("hidden", pending === 0);
  $("#sidebar-pending").textContent = pending;
  $("#sidebar-pending").classList.toggle("hidden", pending === 0);
  if (!articles.length) { list.innerHTML = '<div class="empty">还没有文章，点击“新建文章”开始。</div>'; return; }
  for (const article of articles) {
    const card = document.createElement("article");
    card.className = "article-row";
    card.innerHTML = '<div class="article-summary"><h2></h2><p><span class="category"></span><span class="updated"></span><span class="status"></span></p></div><div class="row-actions"></div>';
    card.querySelector("h2").textContent = article.title;
    card.querySelector(".category").textContent = article.category;
    card.querySelector(".updated").textContent = `更新于 ${article.updated}`;
    applyStatus(card.querySelector(".status"), article.publishStatus);
    const actions = card.querySelector(".row-actions");
    const edit = listAction("编辑", "ghost small", () => openEditor(article.slug));
    const publish = listAction("发布", "primary small", () => publishFromList(article));
    publish.disabled = article.publishStatus === "live";
    publish.title = publish.disabled ? "文章当前已上线" : "发布到官网";
    actions.append(edit, publish, listMoreActions(article));
    list.append(card);
  }
}

function listAction(label, className, action) {
  const button = document.createElement("button");
  button.type = "button";
  button.className = className;
  button.textContent = label;
  button.onclick = action;
  return button;
}

function listMenuAction(label, className, action) {
  return listAction(label, `menu-action ${className}`.trim(), async (event) => {
    event.currentTarget.closest("details").open = false;
    await action();
  });
}

function listMoreActions(article) {
  const details = document.createElement("details");
  details.className = "more-actions row-more-actions";
  const summary = document.createElement("summary");
  summary.textContent = "更多";
  summary.setAttribute("aria-label", `${article.title}的更多操作`);
  const menu = document.createElement("div");
  menu.className = "action-menu";
  if (article.publishStatus === "edited-draft") {
    menu.append(listMenuAction("放弃未发布修改", "", () => discardFromList(article)));
  }
  if (["live", "edited-draft"].includes(article.publishStatus)) {
    menu.append(listMenuAction("下架文章", "danger", () => unpublishFromList(article)));
  }
  menu.append(listMenuAction("删除文章", "danger", () => deleteFromList(article)));
  details.append(summary, menu);
  details.addEventListener("toggle", () => {
    if (!details.open) return;
    document.querySelectorAll(".row-more-actions[open]").forEach((item) => {
      if (item !== details) item.open = false;
    });
  });
  return details;
}

async function publishFromList(article) {
  try {
    await triggerBuild(`/articles/${article.slug}/publish`);
    await loadArticles();
  } catch {}
}

async function discardFromList(article) {
  if (!confirm(`确定放弃《${article.title}》未发布的修改，恢复为当前线上版本吗？`)) return;
  try {
    await api(`/articles/${article.slug}/draft`, { method: "DELETE" });
    await loadArticles();
  } catch (error) { alert(error.message); }
}

async function unpublishFromList(article) {
  const detail = article.publishStatus === "edited-draft" ? "未发布的修改会继续保留为草稿。" : "文章内容会继续保留为草稿。";
  if (!confirm(`确定将《${article.title}》从官网下架吗？${detail}`)) return;
  try {
    await triggerBuild(`/articles/${article.slug}/unpublish`, { action: "下架" });
    await loadArticles();
  } catch {}
}

async function deleteFromList(article) {
  const prompt = article.publishStatus === "new-draft"
    ? `确定彻底删除草稿《${article.title}》吗？删除后无法恢复。`
    : `确定彻底删除《${article.title}》吗？线上版本和草稿都会删除，且无法恢复。`;
  if (!confirm(prompt)) return;
  try {
    await triggerBuild(`/articles/${article.slug}`, { action: "删除", method: "DELETE" });
    await loadArticles();
  } catch {}
}

function fillCategories(selected) {
  const current = selected || $("#category").value || categories[0] || "";
  const options = $("#category-options");
  options.innerHTML = "";
  categories.forEach((name) => {
    const option = document.createElement("button");
    option.type = "button";
    option.className = "select-option";
    option.dataset.value = name;
    option.setAttribute("role", "option");
    option.setAttribute("aria-selected", String(name === current));
    option.innerHTML = '<span></span><span class="option-check" aria-hidden="true">✓</span>';
    option.querySelector("span").textContent = name;
    option.addEventListener("click", () => {
      selectCategory(name, true);
      closeCategoryMenu();
    });
    options.append(option);
  });
  selectCategory(categories.includes(current) ? current : categories[0] || "", false);
}

function selectCategory(name, markDirty) {
  $("#category").value = name;
  $("#category-value").textContent = name || "请选择分类";
  $("#category-options").querySelectorAll(".select-option").forEach((option) => {
    option.setAttribute("aria-selected", String(option.dataset.value === name));
  });
  if (markDirty) dirty = true;
}

function closeCategoryMenu() {
  $("#category-menu").classList.add("hidden");
  $("#category-trigger").setAttribute("aria-expanded", "false");
  $("#category-select").classList.remove("open");
}

function toggleCategoryMenu() {
  const opening = $("#category-menu").classList.contains("hidden");
  if (!opening) return closeCategoryMenu();
  $("#category-menu").classList.remove("hidden");
  $("#category-trigger").setAttribute("aria-expanded", "true");
  $("#category-select").classList.add("open");
  $("#category-error").textContent = "";
}

$("#category-trigger").addEventListener("click", (event) => { event.stopPropagation(); toggleCategoryMenu(); });
$("#category-menu").addEventListener("click", (event) => event.stopPropagation());
document.addEventListener("click", closeCategoryMenu);
document.addEventListener("click", (event) => {
  document.querySelectorAll(".row-more-actions[open]").forEach((details) => {
    if (!details.contains(event.target)) details.open = false;
  });
});
document.addEventListener("keydown", (event) => { if (event.key === "Escape") closeCategoryMenu(); });

async function createCategory() {
  const input = $("#new-category");
  const name = input.value.trim();
  $("#category-error").textContent = "";
  try {
    await triggerBuild("/categories", { action: "添加分类", body: { name } });
    await loadCategories();
    input.value = "";
    fillCategories(name);
    dirty = true;
    closeCategoryMenu();
    if (!$("#category-modal").classList.contains("hidden")) await loadCategoryManager();
  } catch (error) { $("#category-error").textContent = error.message; }
}

$("#add-category").addEventListener("click", createCategory);
$("#new-category").addEventListener("keydown", (event) => {
  if (event.key !== "Enter") return;
  event.preventDefault();
  createCategory();
});

async function loadCategories() {
  const result = await api("/categories");
  categories = result.categories;
  fillCategories($("#category").value);
  return result;
}

async function loadCategoryManager() {
  const result = await loadCategories();
  renderCategoryManager(result.stats || []);
}

function clearCategoryPanels() {
  $("#category-manager-list").querySelectorAll(".category-inline-panel").forEach((panel) => panel.remove());
}

function categoryPanel(row, text) {
  clearCategoryPanels();
  const panel = document.createElement("div");
  panel.className = "category-inline-panel";
  const note = document.createElement("p");
  note.textContent = text;
  panel.append(note);
  row.append(panel);
  return panel;
}

async function runCategoryBuild(action, method, body) {
  $("#manager-category-error").textContent = "";
  try {
    await triggerBuild("/categories", { action, method, body });
    await loadCategoryManager();
    await loadArticles();
  } catch (error) {
    $("#manager-category-error").textContent = error.message;
    await loadCategoryManager().catch(() => {});
  }
}

function showRenameCategory(row, stat) {
  const panel = categoryPanel(row, `重命名后，${stat.count} 篇文章会同步更新分类。`);
  const form = document.createElement("div");
  form.className = "category-rename-form";
  const input = document.createElement("input");
  input.maxLength = 20;
  input.value = stat.name;
  input.setAttribute("aria-label", `重命名 ${stat.name}`);
  const save = document.createElement("button");
  save.type = "button";
  save.className = "primary";
  save.textContent = "保存名称";
  const cancel = document.createElement("button");
  cancel.type = "button";
  cancel.className = "ghost";
  cancel.textContent = "取消";
  save.onclick = () => {
    const name = input.value.trim();
    if (!name || name === stat.name) return clearCategoryPanels();
    runCategoryBuild("更新分类", "PATCH", { current: stat.name, name });
  };
  cancel.onclick = clearCategoryPanels;
  input.addEventListener("keydown", (event) => {
    if (event.key === "Enter") { event.preventDefault(); save.click(); }
    if (event.key === "Escape") clearCategoryPanels();
  });
  form.append(input, save, cancel);
  panel.append(form);
  input.focus();
  input.select();
}

function showDeleteCategory(row, stat) {
  const panel = categoryPanel(
    row,
    stat.count ? `该分类下有 ${stat.count} 篇文章，请选择删除后要迁移到的分类。` : "该分类下没有文章，可以直接删除。",
  );
  const actions = document.createElement("div");
  actions.className = stat.count ? "replacement-options" : "category-inline-actions";
  const choices = stat.count ? categories.filter((name) => name !== stat.name) : [""];
  choices.forEach((replacement) => {
    const button = document.createElement("button");
    button.type = "button";
    button.className = stat.count ? "" : "confirm-danger";
    button.textContent = stat.count ? `迁移到“${replacement}”并删除` : "确认删除分类";
    button.onclick = () => runCategoryBuild("删除分类", "DELETE", { name: stat.name, replacement });
    actions.append(button);
  });
  const cancel = document.createElement("button");
  cancel.type = "button";
  cancel.className = "ghost";
  cancel.textContent = "取消";
  cancel.onclick = clearCategoryPanels;
  actions.append(cancel);
  panel.append(actions);
}

function renderCategoryManager(stats) {
  const list = $("#category-manager-list");
  list.innerHTML = "";
  stats.forEach((stat) => {
    const row = document.createElement("div");
    row.className = "category-manager-row";
    const main = document.createElement("div");
    main.className = "category-row-main";
    const name = document.createElement("div");
    name.className = "category-row-name";
    const strong = document.createElement("strong");
    strong.textContent = stat.name;
    const count = document.createElement("span");
    count.textContent = stat.count ? `${stat.count} 篇文章` : "暂无文章";
    name.append(strong, count);
    const actions = document.createElement("div");
    actions.className = "category-row-actions";
    const rename = document.createElement("button");
    rename.type = "button";
    rename.className = "ghost";
    rename.textContent = "重命名";
    rename.onclick = () => showRenameCategory(row, stat);
    const remove = document.createElement("button");
    remove.type = "button";
    remove.className = "ghost danger";
    remove.textContent = "删除";
    remove.disabled = categories.length <= 1;
    remove.title = remove.disabled ? "至少需要保留一个分类" : "删除分类";
    remove.onclick = () => showDeleteCategory(row, stat);
    actions.append(rename, remove);
    main.append(name, actions);
    row.append(main);
    list.append(row);
  });
}

async function openCategoryManager() {
  $("#manager-category-error").textContent = "";
  show("#category-modal");
  try { await loadCategoryManager(); }
  catch (error) { $("#manager-category-error").textContent = error.message; }
}

$("#manage-categories").addEventListener("click", openCategoryManager);
$("#close-category-modal").addEventListener("click", () => hide("#category-modal"));
$("#manager-add-category").addEventListener("click", async () => {
  const input = $("#manager-new-category");
  const name = input.value.trim();
  $("#manager-category-error").textContent = "";
  try {
    await triggerBuild("/categories", { action: "添加分类", body: { name } });
    input.value = "";
    await loadCategoryManager();
  } catch (error) { $("#manager-category-error").textContent = error.message; }
});
$("#manager-new-category").addEventListener("keydown", (event) => {
  if (event.key === "Enter") { event.preventDefault(); $("#manager-add-category").click(); }
});

function setView(editing) {
  $("#list-view").classList.toggle("hidden", editing);
  $("#edit-view").classList.toggle("hidden", !editing);
}

function setEditorStatus(status) {
  editingStatus = status;
  applyStatus($("#editor-status"), status);
  $("#version-note").textContent = statusMap[status]?.[2] || "";
  const persisted = status !== "new-unsaved";
  $("#more-actions").classList.toggle("hidden", !persisted);
  $("#more-actions").open = false;
  $("#discard").classList.toggle("hidden", status !== "edited-draft");
  $("#unpublish").classList.toggle("hidden", !["live", "edited-draft"].includes(status));
  $("#delete-article").classList.toggle("hidden", !persisted);
}

async function openEditor(slug) {
  editingSlug = slug;
  dirty = false;
  fillCategories();
  $("#preview").innerHTML = "";
  $("#message").textContent = "";
  $("#upload-status").textContent = "";
  if (slug) {
    const article = await api(`/articles/${slug}`);
    $("#edit-heading").textContent = "编辑文章";
    $("#title").value = article.title;
    fillCategories(article.category);
    $("#body").value = article.body;
    setEditorStatus(article.publishStatus);
  } else {
    $("#edit-heading").textContent = "新建文章";
    $("#title").value = "";
    $("#body").value = "";
    setEditorStatus("new-unsaved");
  }
  setView(true);
  schedulePreview(0);
  $("#title").focus();
}

$("#new-article").addEventListener("click", () => openEditor(null));
$("#import-article").addEventListener("click", () => $("#article-file").click());
$("#article-file").addEventListener("change", async (event) => {
  const file = event.target.files?.[0];
  event.target.value = "";
  if (!file) return;
  if (file.size > MAX_UPLOAD_SIZE) {
    $("#import-status").textContent = "文件不能超过 20MB";
    return;
  }

  const extension = file.name.toLowerCase().match(/\.[^.]+$/)?.[0];
  if (![".md", ".docx"].includes(extension)) {
    $("#import-status").textContent = "仅支持 Markdown（.md）或 Word（.docx）文件";
    return;
  }

  $("#import-status").textContent = `正在导入 ${file.name}…`;
  try {
    let result;
    if (extension === ".md") {
      let body = await file.text();
      const heading = body.match(/^#\s+(.+)$/m);
      const title = heading?.[1].trim() || file.name.replace(/\.md$/i, "");
      if (heading?.index !== undefined) {
        body = `${body.slice(0, heading.index)}${body.slice(heading.index + heading[0].length)}`
          .trim()
          .replace(/\n{3,}/g, "\n\n");
      }
      if (!body) throw new Error("Markdown 文件中没有可导入的正文内容");
      result = await api("/articles", { method: "POST", body: { title, category: categories[0], body } });
    } else {
      const dataUrl = await new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result);
        reader.onerror = reject;
        reader.readAsDataURL(file);
      });
      result = await api("/imports/word", {
        method: "POST",
        body: { fileName: file.name, category: categories[0], dataUrl: String(dataUrl).replace(/^data:[^;,]*;/, `data:${DOCX_MIME};`) },
      });
    }
    const successMessage = extension === ".docx"
      ? `导入成功：已创建草稿并提取 ${result.imageCount} 张图片`
      : "导入成功：已创建 Markdown 草稿";
    $("#import-status").textContent = successMessage;
    await openEditor(result.slug);
    $("#message").textContent = successMessage;
  } catch (error) {
    $("#import-status").textContent = error.message;
  }
});
$("#back").addEventListener("click", () => {
  if (dirty && !confirm("还有未保存的内容，确定返回文章列表吗？")) return;
  setView(false);
  loadArticles();
});

function payload() {
  return {
    title: $("#title").value.trim(),
    category: $("#category").value,
    body: $("#body").value,
  };
}

async function save() {
  const result = editingSlug
    ? await api(`/articles/${editingSlug}`, { method: "PUT", body: payload() })
    : await api("/articles", { method: "POST", body: payload() });
  editingSlug = result.slug;
  dirty = false;
  setEditorStatus(result.publishStatus);
  return result.slug;
}

$("#edit-form").addEventListener("submit", async (event) => {
  event.preventDefault();
  $("#message").textContent = "正在保存…";
  try {
    await save();
    $("#message").textContent = editingStatus === "live" ? "内容与线上版本一致。" : "草稿已保存，线上版本没有改变。";
  } catch (error) { $("#message").textContent = error.message; }
});

$("#publish").addEventListener("click", async () => {
  $("#message").textContent = "正在保存草稿…";
  try {
    const slug = await save();
    await triggerBuild(`/articles/${slug}/publish`);
    setEditorStatus("live");
    $("#message").textContent = "发布成功，官网已更新。";
  } catch (error) { $("#message").textContent = error.message; }
});

$("#discard").addEventListener("click", async () => {
  if (!editingSlug) return;
  if (!confirm("确定放弃未发布的修改，恢复为当前线上版本吗？")) return;
  try {
    const result = await api(`/articles/${editingSlug}/draft`, { method: "DELETE" });
    dirty = false;
    if (result.removed) { setView(false); loadArticles(); }
    else await openEditor(editingSlug);
  } catch (error) { $("#message").textContent = error.message; }
});

$("#unpublish").addEventListener("click", async () => {
  if (!editingSlug) return;
  const detail = editingStatus === "edited-draft" ? "未发布的修改会继续保留为草稿。" : "文章内容会继续保留为草稿。";
  if (!confirm(`确定将这篇文章从官网下架吗？${detail}`)) return;
  $("#more-actions").open = false;
  $("#message").textContent = "正在下架文章…";
  try {
    await triggerBuild(`/articles/${editingSlug}/unpublish`, { action: "下架" });
    setEditorStatus("new-draft");
    $("#message").textContent = "文章已下架，内容已保留为草稿。";
  } catch (error) { $("#message").textContent = error.message; }
});

$("#delete-article").addEventListener("click", async () => {
  if (!editingSlug) return;
  const prompt = editingStatus === "new-draft"
    ? "确定彻底删除这篇草稿吗？删除后无法恢复。"
    : "确定彻底删除这篇文章吗？线上版本和草稿都会删除，且无法恢复。";
  if (!confirm(prompt)) return;
  $("#more-actions").open = false;
  $("#message").textContent = "正在删除文章…";
  try {
    await triggerBuild(`/articles/${editingSlug}`, { action: "删除", method: "DELETE" });
    dirty = false;
    editingSlug = null;
    setView(false);
    await loadArticles();
  } catch (error) { $("#message").textContent = error.message; }
});

async function renderPreview() {
  const body = $("#body").value.trim();
  if (!body) { $("#preview").innerHTML = ""; return; }
  const request = ++previewRequest;
  try {
    const result = await api("/preview", { method: "POST", body: { body } });
    if (request === previewRequest) $("#preview").innerHTML = result.html;
  } catch {
    if (request === previewRequest) $("#preview").textContent = "预览暂时不可用";
  }
}

function schedulePreview(delay = 350) {
  clearTimeout(previewTimer);
  previewTimer = setTimeout(renderPreview, delay);
}

$("#title").addEventListener("input", () => { dirty = true; });
$("#body").addEventListener("input", () => { dirty = true; schedulePreview(); });

$("#upload-button").addEventListener("click", () => $("#file").click());
$("#file").addEventListener("change", async (event) => {
  const file = event.target.files?.[0];
  event.target.value = "";
  if (!file) return;
  if (file.size > MAX_UPLOAD_SIZE) {
    $("#upload-status").textContent = "图片不能超过 20MB";
    return;
  }
  $("#upload-status").textContent = "上传中…";
  try {
    const dataUrl = await new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
    const result = await api("/uploads", { method: "POST", body: { dataUrl } });
    const textarea = $("#body");
    const start = textarea.selectionStart;
    const markdown = `\n![${file.name}](${result.url})\n`;
    textarea.value = textarea.value.slice(0, start) + markdown + textarea.value.slice(textarea.selectionEnd);
    textarea.dispatchEvent(new Event("input"));
    $("#upload-status").textContent = "图片已插入";
  } catch (error) { $("#upload-status").textContent = error.message; }
});

async function triggerBuild(endpoint, { action = "发布", method = "POST", body } = {}) {
  show("#build-modal");
  hide("#close-modal");
  $("#build-title").textContent = `正在${action}…`;
  $("#build-log").textContent = "系统正在更新官网，请稍候。";
  try {
    await api(endpoint, { method, body });
  } catch (error) {
    $("#build-title").textContent = `${action}失败`;
    $("#build-log").textContent = error.message;
    show("#close-modal");
    throw error;
  }
  while (true) {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    const state = await api("/build");
    if (state.status === "building") continue;
    if (state.ok) { hide("#build-modal"); return; }
    $("#build-title").textContent = `${action}失败`;
    $("#build-log").textContent = state.log || `${action}失败，请联系技术人员。`;
    show("#close-modal");
    throw new Error(`${action}失败，原文章已恢复，请检查操作日志。`);
  }
}

$("#close-modal").addEventListener("click", () => hide("#build-modal"));

boot();
