import { marked } from "https://esm.sh/marked@15";
import markedKatex from "https://esm.sh/marked-katex-extension@5?deps=marked@15,katex@0.16";
import hljs from "https://esm.sh/highlight.js@11";
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";

marked.use(
  markedKatex({
    throwOnError: false,
    nonStandard: true
  })
);
// breaks: true — 段内单换行渲染为 <br>，与 Obsidian 等编辑器的换行习惯一致（CommonMark 默认会把单换行当成空格）
marked.setOptions({ gfm: true, breaks: true });

function decodeHtmlEntities(str) {
  return String(str)
    .replace(/&nbsp;/g, "\u00a0")
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/&#(\d+);/g, (_, n) => {
      const code = Number(n);
      return code >= 0 && code <= 0x10ffff ? String.fromCharCode(code) : "";
    })
    .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => {
      const code = parseInt(h, 16);
      return code >= 0 && code <= 0x10ffff ? String.fromCharCode(code) : "";
    });
}

function escapeHtml(s) {
  return String(s)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

function mermaidFencedToDiv(html) {
  return html.replace(
    /<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/gi,
    (_, inner) => {
      const raw = decodeHtmlEntities(inner);
      return `<div class="mermaid">${escapeHtml(raw)}</div>`;
    }
  );
}

function decodeMdFromB64(b64) {
  return decodeURIComponent(escape(atob(b64)));
}

async function renderOne(el) {
  const b64 = el.getAttribute("data-b64-md");
  if (!b64) return;
  let md;
  try {
    md = decodeMdFromB64(b64);
  } catch {
    el.innerHTML = "<p>正文解码失败。</p>";
    el.classList.remove("markdown-pending");
    return;
  }
  el.removeAttribute("data-b64-md");
  let html = marked.parse(md, { async: false });
  if (typeof html !== "string") html = "";
  html = mermaidFencedToDiv(html);
  el.innerHTML = html;
  el.classList.remove("markdown-pending");
  el.querySelectorAll("pre code").forEach((block) => {
    try {
      hljs.highlightElement(block);
    } catch {
      /* ignore unknown languages */
    }
  });
  const mNodes = el.querySelectorAll(".mermaid");
  if (mNodes.length) {
    mermaid.initialize({ startOnLoad: false, theme: "neutral" });
    await mermaid.run({ nodes: mNodes });
  }
}

function run() {
  document.querySelectorAll(".markdown-body[data-b64-md]").forEach((el) => {
    renderOne(el).catch(() => {
      el.innerHTML = "<p>渲染失败。</p>";
      el.classList.remove("markdown-pending");
    });
  });
}

if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", run);
else run();
