import { useMemo } from "preact/hooks";
import DOMPurify from "dompurify";
import { marked, Renderer } from "marked";
import { cn } from "@/lib/utils";

type MarkdownMode = "default" | "citationTokens";

type Props = {
  markdown: string;
  className?: string;
  mode?: MarkdownMode;
  resolveCitationIndex?: (href: string) => number | undefined;
  openLinksInNewTab?: boolean;
};

function replaceMarkdownLinksWithCitationTokens(markdown: string, resolveCitationIndex?: (href: string) => number | undefined) {
  if (!resolveCitationIndex) return markdown;
  return markdown.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, (_full, _label, href) => {
    const index = resolveCitationIndex(href);
    if (!index) return "";
    return `<span class="md-cite-token">[${index}]</span>`;
  });
}

const markdownRenderer = new Renderer();
const renderTable = markdownRenderer.table.bind(markdownRenderer);

markdownRenderer.table = (token) => {
  return `<div class="md-table-scroll">${renderTable(token)}</div>`;
};

function setNewTabLinkAttributes(currentNode: Element) {
  if (currentNode.tagName !== "A") return;
  currentNode.setAttribute("target", "_blank");
  currentNode.setAttribute("rel", "noopener noreferrer");
}

function renderMarkdown(markdown: string, openLinksInNewTab: boolean) {
  const rawHtml = marked.parse(markdown, { gfm: true, breaks: true, renderer: markdownRenderer }) as string;
  if (!openLinksInNewTab) return DOMPurify.sanitize(rawHtml, { ADD_ATTR: ["class", "target", "rel"] });

  DOMPurify.addHook("afterSanitizeAttributes", setNewTabLinkAttributes);
  try {
    return DOMPurify.sanitize(rawHtml, { ADD_ATTR: ["class", "target", "rel"] });
  } finally {
    DOMPurify.removeHook("afterSanitizeAttributes", setNewTabLinkAttributes);
  }
}

export function MarkdownContent({ markdown, className, mode = "default", resolveCitationIndex, openLinksInNewTab = false }: Props) {
  const html = useMemo(() => {
    const prepared =
      mode === "citationTokens" ? replaceMarkdownLinksWithCitationTokens(markdown, resolveCitationIndex) : markdown;
    return renderMarkdown(prepared, openLinksInNewTab);
  }, [markdown, mode, openLinksInNewTab, resolveCitationIndex]);

  return <div className={cn("md-content", className)} dangerouslySetInnerHTML={{ __html: html }} />;
}
