const state = {
  user: null,
  posts: [],
  currentPost: null,
};

const el = {
  loginView: document.querySelector("#loginView"),
  appView: document.querySelector("#appView"),
  loginForm: document.querySelector("#loginForm"),
  loginUsername: document.querySelector("#loginUsername"),
  loginPassword: document.querySelector("#loginPassword"),
  loginMessage: document.querySelector("#loginMessage"),
  userBadge: document.querySelector("#userBadge"),
  logoutButton: document.querySelector("#logoutButton"),
  newPostButton: document.querySelector("#newPostButton"),
  statusFilter: document.querySelector("#statusFilter"),
  postList: document.querySelector("#postList"),
  postForm: document.querySelector("#postForm"),
  editorMode: document.querySelector("#editorMode"),
  editorTitle: document.querySelector("#editorTitle"),
  titleInput: document.querySelector("#titleInput"),
  slugInput: document.querySelector("#slugInput"),
  statusInput: document.querySelector("#statusInput"),
  coverInput: document.querySelector("#coverInput"),
  tagsInput: document.querySelector("#tagsInput"),
  summaryInput: document.querySelector("#summaryInput"),
  bodyInput: document.querySelector("#bodyInput"),
  publishButton: document.querySelector("#publishButton"),
  editorMessage: document.querySelector("#editorMessage"),
};

async function api(path, options = {}) {
  const response = await fetch(`/api/admin${path}`, {
    credentials: "include",
    headers: {
      "Content-Type": "application/json",
      ...(options.headers ?? {}),
    },
    ...options,
  });

  if (response.status === 204) {
    return null;
  }

  const text = await response.text();
  const data = text ? JSON.parse(text) : {};
  if (!response.ok) {
    throw new Error(data.error ?? `HTTP ${response.status}`);
  }
  return data;
}

function showLogin() {
  el.loginView.hidden = false;
  el.appView.hidden = true;
  el.loginPassword.focus();
}

function showApp() {
  el.loginView.hidden = true;
  el.appView.hidden = false;
  el.userBadge.textContent = state.user?.username ?? "";
}

async function bootstrap() {
  try {
    const data = await api("/me");
    state.user = data.user;
    showApp();
    await loadPosts();
    resetEditor();
  } catch {
    showLogin();
  }
}

async function loadPosts() {
  const status = el.statusFilter.value;
  const query = status ? `?status=${encodeURIComponent(status)}` : "";
  const data = await api(`/posts${query}`);
  state.posts = data.posts ?? [];
  renderPostList();
}

function renderPostList() {
  el.postList.innerHTML = "";

  if (state.posts.length === 0) {
    const empty = document.createElement("p");
    empty.className = "message";
    empty.textContent = "暂无文章";
    el.postList.append(empty);
    return;
  }

  for (const post of state.posts) {
    const button = document.createElement("button");
    button.type = "button";
    button.className = "post-item";
    if (state.currentPost?.id === post.id) {
      button.classList.add("active");
    }
    button.innerHTML = `
      <span class="post-item-title"></span>
      <span class="post-item-meta"></span>
    `;
    button.querySelector(".post-item-title").textContent = post.title || "未命名";
    button.querySelector(".post-item-meta").textContent = `${statusText(post.status)} / ${formatDate(post.updatedAt)}`;
    button.addEventListener("click", () => selectPost(post.id));
    el.postList.append(button);
  }
}

async function selectPost(id) {
  const data = await api(`/posts/${id}`);
  state.currentPost = data.post;
  fillEditor(data.post);
  renderPostList();
}

function resetEditor() {
  state.currentPost = null;
  el.editorMode.textContent = "新文章";
  el.editorTitle.textContent = "开始写作";
  el.postForm.reset();
  el.statusInput.value = "draft";
  el.editorMessage.textContent = "";
  renderPostList();
}

function fillEditor(post) {
  el.editorMode.textContent = `版本 ${post.version}`;
  el.editorTitle.textContent = post.title || "未命名";
  el.titleInput.value = post.title ?? "";
  el.slugInput.value = post.slug ?? "";
  el.statusInput.value = post.status ?? "draft";
  el.coverInput.value = post.cover ?? "";
  el.tagsInput.value = (post.tags ?? []).join(", ");
  el.summaryInput.value = post.summary ?? "";
  el.bodyInput.value = post.bodyMarkdown ?? "";
  el.editorMessage.textContent = "";
}

function readPostInput() {
  return {
    title: el.titleInput.value.trim(),
    slug: el.slugInput.value.trim(),
    status: el.statusInput.value,
    cover: el.coverInput.value.trim(),
    tags: parseTags(el.tagsInput.value),
    summary: el.summaryInput.value.trim(),
    bodyMarkdown: el.bodyInput.value,
    slugSource: "manual",
    slugLocked: true,
  };
}

async function savePost() {
  const input = readPostInput();
  const path = state.currentPost ? `/posts/${state.currentPost.id}` : "/posts";
  const method = state.currentPost ? "PUT" : "POST";
  const data = await api(path, {
    method,
    body: JSON.stringify(input),
  });
  state.currentPost = data.post;
  fillEditor(data.post);
  await loadPosts();
  el.editorMessage.textContent = "已保存";
}

async function publishPost() {
  if (!state.currentPost) {
    await savePost();
  }

  const data = await api(`/posts/${state.currentPost.id}/publish`, { method: "POST" });
  state.currentPost = data.post;
  fillEditor(data.post);
  await loadPosts();
  el.editorMessage.textContent = "已开始构建";

  if (data.buildJob?.id) {
    pollBuildJob(data.buildJob.id);
  }
}

async function pollBuildJob(id) {
  for (;;) {
    await wait(1400);
    const data = await api(`/build-jobs/${id}`);
    const job = data.buildJob;
    el.editorMessage.textContent = `构建状态：${buildStatusText(job.status)}`;
    if (["success", "failed", "cancelled"].includes(job.status)) {
      if (job.error) {
        el.editorMessage.textContent = `构建失败：${job.error}`;
      }
      return;
    }
  }
}

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

function statusText(status) {
  return {
    draft: "草稿",
    published: "已发布",
    archived: "归档",
    deleted: "已删除",
  }[status] ?? status;
}

function buildStatusText(status) {
  return {
    queued: "等待中",
    running: "构建中",
    success: "成功",
    failed: "失败",
    cancelled: "已取消",
  }[status] ?? status;
}

function formatDate(value) {
  if (!value) {
    return "无时间";
  }
  return new Intl.DateTimeFormat("zh-CN", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
  }).format(new Date(value));
}

function slugify(value) {
  return value
    .trim()
    .toLowerCase()
    .replace(/[\s_]+/g, "-")
    .replace(/[^a-z0-9\u4e00-\u9fa5-]+/g, "")
    .replace(/-+/g, "-")
    .replace(/^-|-$/g, "");
}

function parseTags(value) {
  const seen = new Set();
  return value
    .split(/[,，]/)
    .map((tag) => tag.trim())
    .filter((tag) => {
      const key = tag.toLowerCase();
      if (!tag || seen.has(key)) {
        return false;
      }
      seen.add(key);
      return true;
    });
}

el.loginForm.addEventListener("submit", async (event) => {
  event.preventDefault();
  el.loginMessage.textContent = "";

  try {
    const data = await api("/login", {
      method: "POST",
      body: JSON.stringify({
        username: el.loginUsername.value.trim(),
        password: el.loginPassword.value,
      }),
    });
    state.user = data.user;
    showApp();
    await loadPosts();
    resetEditor();
  } catch (error) {
    el.loginMessage.textContent = error.message;
  }
});

el.logoutButton.addEventListener("click", async () => {
  await api("/logout", { method: "POST" });
  state.user = null;
  state.posts = [];
  state.currentPost = null;
  showLogin();
});

el.newPostButton.addEventListener("click", resetEditor);
el.statusFilter.addEventListener("change", loadPosts);

el.titleInput.addEventListener("input", () => {
  if (!state.currentPost && !el.slugInput.value.trim()) {
    el.slugInput.value = slugify(el.titleInput.value);
  }
});

el.postForm.addEventListener("submit", async (event) => {
  event.preventDefault();
  try {
    await savePost();
  } catch (error) {
    el.editorMessage.textContent = error.message;
  }
});

el.publishButton.addEventListener("click", async () => {
  try {
    await publishPost();
  } catch (error) {
    el.editorMessage.textContent = error.message;
  }
});

bootstrap();
