const state = {
  items: [],
  itemsById: new Map(),
  navItemMap: new Map(),
  defaultSections: new Set(),
  filters: {
    query: "",
    sections: new Set(),
    characters: new Set(),
    events: new Set(),
    chapterMin: "",
    chapterMax: "",
  },
  matchIds: new Set(),
  selectedId: null,
  readSet: new Set(),
  currentItem: null,
  linkMap: new Map(),
};

const dom = {
  searchInput: document.getElementById("search-input"),
  chapterMin: document.getElementById("chapter-min"),
  chapterMax: document.getElementById("chapter-max"),
  sectionFilters: document.getElementById("section-filters"),
  characterFilters: document.getElementById("character-filters"),
  eventFilters: document.getElementById("event-filters"),
  clearFilters: document.getElementById("clear-filters"),
  resultsPanel: document.getElementById("results-panel"),
  resultsList: document.getElementById("results-list"),
  matchCount: document.getElementById("match-count"),
  navTree: document.getElementById("nav-tree"),
  contentTitle: document.getElementById("content-title"),
  contentBody: document.getElementById("content-body"),
  contentSummary: document.getElementById("content-summary"),
  contentPath: document.getElementById("content-path"),
  metaToggle: document.getElementById("meta-toggle"),
  metaPanel: document.getElementById("meta-panel"),
  markRead: document.getElementById("mark-read"),
  prevChapter: document.getElementById("prev-chapter"),
  nextChapter: document.getElementById("next-chapter"),
  currentInfo: document.getElementById("current-info"),
  topStats: document.getElementById("top-stats"),
  refreshIndex: document.getElementById("refresh-index"),
  resetProgress: document.getElementById("reset-progress"),
};

function escapeHtml(text) {
  return text
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/\"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

function escapeRegExp(text) {
  return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function debounce(fn, wait = 200) {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn(...args), wait);
  };
}

function loadReadSet() {
  const raw = localStorage.getItem("xsNovelRead");
  if (!raw) return;
  try {
    const ids = JSON.parse(raw);
    if (Array.isArray(ids)) {
      state.readSet = new Set(ids);
    }
  } catch (err) {
    state.readSet = new Set();
  }
}

function saveReadSet() {
  localStorage.setItem("xsNovelRead", JSON.stringify(Array.from(state.readSet)));
}

function setChipActive(chip, active) {
  chip.classList.toggle("is-active", active);
}

function createChip(label, onToggle) {
  const chip = document.createElement("button");
  chip.type = "button";
  chip.className = "chip";
  chip.textContent = label;
  chip.addEventListener("click", () => onToggle(chip, label));
  return chip;
}

function buildFilterChips(list, container, setRef, allowEmpty = true) {
  container.innerHTML = "";
  list.forEach((label) => {
    const chip = createChip(label, (chipEl, value) => {
      if (setRef.has(value)) {
        if (!allowEmpty && setRef.size === 1) {
          return;
        }
        setRef.delete(value);
        setChipActive(chipEl, false);
      } else {
        setRef.add(value);
        setChipActive(chipEl, true);
      }
      applyFilters();
    });
    if (setRef.has(label)) {
      setChipActive(chip, true);
    }
    container.appendChild(chip);
  });
}

function buildNavTree(items) {
  state.navItemMap.clear();
  dom.navTree.innerHTML = "";

  const chaptersBySection = { "正文": [], "草稿": [] };
  const infoGroups = { "角色": [], "事件": [], "物品": [], "场景": [], "大纲": [], "主题": [] };

  items.forEach((item) => {
    if (item.type === "chapter") {
      if (!chaptersBySection[item.section]) {
        chaptersBySection[item.section] = [];
      }
      chaptersBySection[item.section].push(item);
    } else {
      if (infoGroups[item.section]) {
        infoGroups[item.section].push(item);
      }
    }
  });

  const navGroup = document.createElement("div");
  navGroup.className = "nav-group";

  ["正文", "草稿"].forEach((section) => {
    const sectionItems = chaptersBySection[section] || [];
    const details = document.createElement("details");
    details.open = section === "正文";
    const summary = document.createElement("summary");
    summary.textContent = `${section} (${sectionItems.length})`;
    details.appendChild(summary);
    const list = document.createElement("ul");
    list.className = "nav-list";
    sectionItems.forEach((item) => {
      const li = document.createElement("li");
      li.className = "nav-item";
      li.textContent = item.label;
      li.dataset.id = item.id;
      li.addEventListener("click", () => selectItem(item.id));
      list.appendChild(li);
      state.navItemMap.set(item.id, li);
    });
    if (!sectionItems.length) {
      const empty = document.createElement("div");
      empty.className = "nav-item";
      empty.textContent = "暂无内容";
      list.appendChild(empty);
    }
    details.appendChild(list);
    navGroup.appendChild(details);
  });

  const infoDetails = document.createElement("details");
  infoDetails.open = false;
  const infoSummary = document.createElement("summary");
  infoSummary.textContent = "资料库";
  infoDetails.appendChild(infoSummary);

  const infoContainer = document.createElement("div");
  Object.entries(infoGroups).forEach(([section, list]) => {
    if (!list.length) return;
    const block = document.createElement("div");
    block.style.marginTop = "10px";
    const title = document.createElement("div");
    title.textContent = section;
    title.style.fontWeight = "600";
    title.style.fontSize = "13px";
    title.style.marginBottom = "6px";
    const ul = document.createElement("ul");
    ul.className = "nav-list";
    list.forEach((item) => {
      const li = document.createElement("li");
      li.className = "nav-item";
      li.textContent = item.label;
      li.dataset.id = item.id;
      li.addEventListener("click", () => selectItem(item.id));
      ul.appendChild(li);
      state.navItemMap.set(item.id, li);
    });
    block.appendChild(title);
    block.appendChild(ul);
    infoContainer.appendChild(block);
  });

  if (!infoContainer.childElementCount) {
    const empty = document.createElement("div");
    empty.className = "nav-item";
    empty.textContent = "暂无资料";
    infoContainer.appendChild(empty);
  }

  infoDetails.appendChild(infoContainer);
  navGroup.appendChild(infoDetails);

  dom.navTree.appendChild(navGroup);
}

function normalizeKey(value) {
  return value ? String(value).trim() : "";
}

function buildLinkMap(items) {
  state.linkMap = new Map();
  items.forEach((item) => {
    if (item.type !== "info") return;
    const titleKey = normalizeKey(item.title);
    if (titleKey) state.linkMap.set(titleKey, item.id);
    const labelKey = normalizeKey(item.label);
    if (labelKey) state.linkMap.set(labelKey, item.id);
  });
}

function updateTopStats(totals) {
  if (!totals || !totals.sections) return;
  const parts = [];
  const order = ["正文", "草稿", "角色", "事件", "物品", "场景", "大纲", "主题"];
  order.forEach((section) => {
    if (totals.sections[section]) {
      parts.push(`${section} ${totals.sections[section]}`);
    }
  });
  dom.topStats.textContent = parts.join(" · ") || "暂无统计";
}

function updateProgress() {
  const chapters = state.items.filter((item) => item.type === "chapter");
  const totals = { "正文": { count: 0, words: 0 }, "草稿": { count: 0, words: 0 } };
  chapters.forEach((item) => {
    if (!totals[item.section]) return;
    totals[item.section].count += 1;
    totals[item.section].words += item.word_count || 0;
  });

  const readCounts = { "正文": { count: 0, words: 0 }, "草稿": { count: 0, words: 0 } };
  chapters.forEach((item) => {
    if (!totals[item.section]) return;
    if (state.readSet.has(item.id)) {
      readCounts[item.section].count += 1;
      readCounts[item.section].words += item.word_count || 0;
    }
  });

  const totalAll = { count: totals["正文"].count + totals["草稿"].count, words: totals["正文"].words + totals["草稿"].words };
  const readAll = { count: readCounts["正文"].count + readCounts["草稿"].count, words: readCounts["正文"].words + readCounts["草稿"].words };

  const items = document.querySelectorAll(".progress-item");
  items.forEach((node) => {
    const section = node.dataset.section;
    let total = { count: 0, words: 0 };
    let read = { count: 0, words: 0 };
    if (section === "总计") {
      total = totalAll;
      read = readAll;
    } else if (totals[section]) {
      total = totals[section];
      read = readCounts[section];
    }
    const percent = total.count ? Math.round((read.count / total.count) * 100) : 0;
    const bar = node.querySelector(".progress-bar span");
    const meta = node.querySelector(".progress-meta");
    bar.style.width = `${percent}%`;
    meta.textContent = `${read.count}/${total.count} 章 · ${read.words}/${total.words} 字 · ${percent}%`;
  });
}

function updateNavVisibility(active) {
  const restrictAll = isQueryActive() || isSectionFilterActive();
  state.navItemMap.forEach((el, id) => {
    if (!active) {
      el.classList.remove("is-hidden");
      return;
    }
    const item = state.itemsById.get(id);
    if (item && !restrictAll && item.type !== "chapter") {
      el.classList.remove("is-hidden");
      return;
    }
    if (state.matchIds.has(id)) {
      el.classList.remove("is-hidden");
    } else {
      el.classList.add("is-hidden");
    }
  });
}

function renderResults(items) {
  dom.resultsList.innerHTML = "";
  items.forEach((item, idx) => {
    const card = document.createElement("div");
    card.className = "result-item";
    card.style.animationDelay = `${idx * 0.02}s`;
    card.addEventListener("click", () => selectItem(item.id));

    const title = document.createElement("div");
    title.className = "result-title";
    title.textContent = item.label;

    const meta = document.createElement("div");
    meta.className = "result-meta";
    meta.textContent = `${item.section}${item.chapter_number ? " · 第" + item.chapter_number + "章" : ""}`;

    const snippet = document.createElement("div");
    snippet.className = "result-meta";
    if (item.excerpt) {
      const terms = state.filters.query.trim().split(/\s+/).filter(Boolean);
      let html = escapeHtml(item.excerpt);
      terms.forEach((term) => {
        const re = new RegExp(escapeRegExp(term), "ig");
        html = html.replace(re, (match) => `<mark>${match}</mark>`);
      });
      snippet.innerHTML = html;
    }

    card.appendChild(title);
    card.appendChild(meta);
    if (item.excerpt) {
      card.appendChild(snippet);
    }
    dom.resultsList.appendChild(card);
  });
}

function filtersActive() {
  if (isSectionFilterActive()) return true;
  return Boolean(
    state.filters.query ||
    state.filters.characters.size ||
    state.filters.events.size ||
    state.filters.chapterMin ||
    state.filters.chapterMax
  );
}

function isSectionFilterActive() {
  const defaultSections = state.defaultSections.size;
  const selectedSections = state.filters.sections.size;
  return Boolean(selectedSections && selectedSections !== defaultSections);
}

function isQueryActive() {
  return Boolean(state.filters.query);
}

async function applyFilters() {
  const active = filtersActive();
  if (!active) {
    state.matchIds = new Set(state.items.map((item) => item.id));
    dom.resultsPanel.classList.add("hidden");
    dom.matchCount.textContent = "0";
    updateNavVisibility(false);
    return;
  }

  const params = new URLSearchParams();
  if (state.filters.query) params.set("q", state.filters.query);
  state.filters.sections.forEach((section) => params.append("section", section));
  state.filters.characters.forEach((c) => params.append("characters", c));
  state.filters.events.forEach((e) => params.append("events", e));
  if (state.filters.chapterMin) params.set("chapter_min", state.filters.chapterMin);
  if (state.filters.chapterMax) params.set("chapter_max", state.filters.chapterMax);

  const res = await fetch(`/api/items?${params.toString()}`);
  const data = await res.json();

  state.matchIds = new Set(data.items.map((item) => item.id));
  dom.matchCount.textContent = data.count;
  dom.resultsPanel.classList.remove("hidden");
  renderResults(data.items);
  updateNavVisibility(true);
}

async function selectItem(id) {
  if (!id) return;
  state.selectedId = id;
  const res = await fetch(`/api/item/${id}`);
  if (!res.ok) return;
  const data = await res.json();
  state.currentItem = data;

  dom.contentTitle.textContent = data.title;
  dom.contentBody.innerHTML = data.body_html || "";
  if (data.section === "正文" || data.section === "草稿") {
    collapseInfoUpdates(dom.contentBody);
  }
  dom.contentPath.textContent = `${data.section} / ${data.label}`;

  renderSummary(data);
  renderMeta(data);
  updateCurrentInfo(data);
  updateReadButton();
  updatePager(data);

  state.navItemMap.forEach((el, itemId) => {
    el.classList.toggle("is-selected", itemId === id);
  });
}

function collapseInfoUpdates(container) {
  if (!container) return;
  const heading = Array.from(container.querySelectorAll("h1, h2, h3, h4, h5, h6"))
    .find((node) => node.textContent.trim() === "信息更新");
  if (!heading) return;

  const details = document.createElement("details");
  details.className = "info-collapse";
  const summary = document.createElement("summary");
  summary.textContent = heading.textContent.trim();
  details.appendChild(summary);

  let node = heading.nextSibling;
  while (node) {
    const next = node.nextSibling;
    details.appendChild(node);
    node = next;
  }
  heading.replaceWith(details);
}

function renderSummary(data) {
  dom.contentSummary.innerHTML = "";
  const chips = [];

  if (data.chapter_number) {
    chips.push({ label: `第${data.chapter_number}章`, type: "chapter" });
  }
  if (data.timeline) {
    chips.push({ label: data.timeline, type: "timeline" });
  }
  if (data.word_count) {
    chips.push({ label: `${data.word_count} 字`, type: "word" });
  }
  (data.characters || []).forEach((name) => {
    chips.push({ label: name, type: "character", value: name });
  });
  (data.events || []).forEach((name) => {
    chips.push({ label: name, type: "event", value: name });
  });

  chips.forEach((chipData) => {
    const chip = document.createElement("button");
    chip.type = "button";
    chip.className = "chip";
    chip.textContent = chipData.label;
    if (chipData.type === "character") {
      chip.addEventListener("click", () => {
        state.filters.characters.add(chipData.value);
        applyFilters();
        syncFilterChips();
      });
    }
    if (chipData.type === "event") {
      chip.addEventListener("click", () => {
        state.filters.events.add(chipData.value);
        applyFilters();
        syncFilterChips();
      });
    }
    dom.contentSummary.appendChild(chip);
  });
}

function renderMeta(data) {
  dom.metaPanel.classList.add("hidden");
  dom.metaToggle.textContent = "展开元信息";

  const meta = data.meta || {};
  const preferred = [
    "chapter_number",
    "title",
    "name",
    "word_count",
    "timeline",
    "characters",
    "events",
    "scenes",
    "emotion_curve",
    "draft_date",
    "finalize_date",
    "revision_notes",
  ];
  const keys = Object.keys(meta);
  const ordered = [...preferred.filter((k) => keys.includes(k)), ...keys.filter((k) => !preferred.includes(k))];

  if (!ordered.length) {
    dom.metaPanel.innerHTML = "<p class=\"placeholder\">暂无元信息</p>";
    return;
  }

  function createMetaToken(text) {
    const key = normalizeKey(text);
    const linkedId = state.linkMap.get(key);
    if (linkedId) {
      const button = document.createElement("button");
      button.type = "button";
      button.className = "meta-link";
      button.textContent = text;
      button.addEventListener("click", () => selectItem(linkedId));
      return button;
    }
    const span = document.createElement("span");
    span.className = "meta-plain";
    span.textContent = text;
    return span;
  }

  const table = document.createElement("table");
  table.className = "meta-table";
  ordered.forEach((key) => {
    const row = document.createElement("tr");
    const cellKey = document.createElement("td");
    cellKey.textContent = key;
    const cellValue = document.createElement("td");
    const value = meta[key];
    if (Array.isArray(value)) {
      const list = document.createElement("div");
      list.className = "meta-list";
      value.forEach((item) => {
        list.appendChild(createMetaToken(item));
      });
      cellValue.appendChild(list);
    } else {
      const text = value === null || value === undefined ? "" : String(value);
      if (!text) {
        cellValue.textContent = "";
      } else {
        cellValue.appendChild(createMetaToken(text));
      }
    }
    row.appendChild(cellKey);
    row.appendChild(cellValue);
    table.appendChild(row);
  });

  dom.metaPanel.innerHTML = "";
  dom.metaPanel.appendChild(table);
}

function updateCurrentInfo(data) {
  if (!dom.currentInfo) return;
  dom.currentInfo.innerHTML = `
    <div>位置: ${escapeHtml(data.path || "")}</div>
    <div>分区: ${escapeHtml(data.section || "")}</div>
    <div>类型: ${escapeHtml(data.type || "")}</div>
  `;
}

function updateReadButton() {
  if (!state.currentItem) return;
  dom.markRead.disabled = false;
  const isRead = state.readSet.has(state.currentItem.id);
  dom.markRead.textContent = isRead ? "已读 ✔" : "标记已读";
}

function toggleRead() {
  if (!state.currentItem) return;
  const id = state.currentItem.id;
  if (state.readSet.has(id)) {
    state.readSet.delete(id);
  } else {
    state.readSet.add(id);
  }
  saveReadSet();
  updateReadButton();
  updateProgress();
}

function updatePager(data) {
  if (!data || data.type !== "chapter") {
    dom.prevChapter.disabled = true;
    dom.nextChapter.disabled = true;
    return;
  }
  const sectionItems = state.items
    .filter((item) => item.type === "chapter" && item.section === data.section)
    .sort((a, b) => (a.chapter_number || 0) - (b.chapter_number || 0));
  const index = sectionItems.findIndex((item) => item.id === data.id);
  const prev = sectionItems[index - 1];
  const next = sectionItems[index + 1];
  dom.prevChapter.disabled = !prev;
  dom.nextChapter.disabled = !next;
  dom.prevChapter.dataset.id = prev ? prev.id : "";
  dom.nextChapter.dataset.id = next ? next.id : "";
}

function syncFilterChips() {
  document.querySelectorAll("#character-filters .chip").forEach((chip) => {
    setChipActive(chip, state.filters.characters.has(chip.textContent));
  });
  document.querySelectorAll("#event-filters .chip").forEach((chip) => {
    setChipActive(chip, state.filters.events.has(chip.textContent));
  });
}

function resetFilters() {
  state.filters.query = "";
  state.filters.sections = new Set(state.defaultSections);
  state.filters.characters = new Set();
  state.filters.events = new Set();
  state.filters.chapterMin = "";
  state.filters.chapterMax = "";
  dom.searchInput.value = "";
  dom.chapterMin.value = "";
  dom.chapterMax.value = "";
  buildFilterChips(Array.from(state.defaultSections), dom.sectionFilters, state.filters.sections, false);
  syncFilterChips();
  applyFilters();
}

const applyFiltersDebounced = debounce(applyFilters, 200);

async function loadIndex() {
  const res = await fetch("/api/index");
  const data = await res.json();
  state.items = data.items || [];
  state.itemsById = new Map(state.items.map((item) => [item.id, item]));
  state.defaultSections = new Set(data.sections || []);

  if (!state.filters.sections.size) {
    state.filters.sections = new Set(state.defaultSections);
  }

  buildFilterChips(Array.from(state.defaultSections), dom.sectionFilters, state.filters.sections, false);
  buildFilterChips(data.filters.characters || [], dom.characterFilters, state.filters.characters);
  buildFilterChips(data.filters.events || [], dom.eventFilters, state.filters.events);

  buildLinkMap(state.items);
  buildNavTree(state.items);
  updateTopStats(data.totals);
  updateProgress();
  applyFilters();
}

function attachEvents() {
  dom.searchInput.addEventListener("input", (event) => {
    state.filters.query = event.target.value.trim();
    applyFiltersDebounced();
  });

  dom.chapterMin.addEventListener("input", (event) => {
    state.filters.chapterMin = event.target.value;
    applyFiltersDebounced();
  });

  dom.chapterMax.addEventListener("input", (event) => {
    state.filters.chapterMax = event.target.value;
    applyFiltersDebounced();
  });

  dom.clearFilters.addEventListener("click", resetFilters);

  dom.metaToggle.addEventListener("click", () => {
    dom.metaPanel.classList.toggle("hidden");
    const hidden = dom.metaPanel.classList.contains("hidden");
    dom.metaToggle.textContent = hidden ? "展开元信息" : "收起元信息";
  });

  dom.markRead.addEventListener("click", toggleRead);

  dom.prevChapter.addEventListener("click", () => {
    const id = dom.prevChapter.dataset.id;
    if (id) selectItem(id);
  });

  dom.nextChapter.addEventListener("click", () => {
    const id = dom.nextChapter.dataset.id;
    if (id) selectItem(id);
  });

  dom.refreshIndex.addEventListener("click", async () => {
    await fetch("/api/refresh", { method: "POST" });
    await loadIndex();
  });

  if (dom.resetProgress) {
    dom.resetProgress.addEventListener("click", () => {
      state.readSet = new Set();
      saveReadSet();
      updateReadButton();
      updateProgress();
    });
  }
}

loadReadSet();
attachEvents();
loadIndex();
