import { marked } from "marked";
import markedKatex from "marked-katex-extension";
import hljs from "highlight.js";

marked.use(
  markedKatex({
    throwOnError: false,
    nonStandard: true
  })
);
marked.setOptions({ gfm: true, breaks: true });

function decodeHtmlEntities(str: string): string {
  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: string): string {
  return String(s)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

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

/** 将 Markdown 渲染进元素（含 KaTeX、高亮、Mermaid） */
export async function renderMarkdownInto(
  el: HTMLElement,
  markdown: string
): Promise<void> {
  let html = marked.parse(markdown, { async: false }) as string;
  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 as HTMLElement);
    } catch {
      /* ignore */
    }
  });
  const mNodes = el.querySelectorAll(".mermaid");
  if (mNodes.length) {
    const mermaid = (await import("mermaid")).default;
    mermaid.initialize({ startOnLoad: false, theme: "neutral" });
    await mermaid.run({ nodes: [...mNodes] as HTMLElement[] });
  }
}
