// rule: dangerous-html-sink
// file-path: src/components/chat/message/message-markdown-katex.tsx
// audit-verdict: pass
// weakness: react-bench-exact-callsite
// source: React Bench 0.9.7 exhaustive audit 9eafa62cae5455a9ad6d612f9b01f69ef7b2580966c346f9036af82ee9f960e4
import { cn } from "@/lib/utils";
import katex from "katex";
import { useMemo } from "react";
import { sanitizeMathTex } from "./message-markdown-math";
import "katex/dist/katex.min.css";

/**
 * Rendering the same formula on every streamed chunk is wasteful, so keep the generated
 * markup around. The cache is cleared wholesale once it grows past the limit.
 */
const RENDER_CACHE_LIMIT = 512;
const renderCache = new Map<string, string>();

function renderTex(tex: string, display: boolean) {
  const cacheKey = `${display ? "display" : "inline"} ${tex}`;
  const cached = renderCache.get(cacheKey);
  if (cached !== undefined) {
    return cached;
  }

  let html = "";
  try {
    // `throwOnError: false` keeps invalid formulas inline with KaTeX's own error styling
    // instead of breaking the whole message.
    html = katex.renderToString(sanitizeMathTex(tex), {
      displayMode: display,
      throwOnError: false,
      strict: false,
      trust: false,
      output: "htmlAndMathml",
    });
  } catch {
    // Only unexpected failures land here; the raw TeX is shown instead.
    html = "";
  }

  if (renderCache.size >= RENDER_CACHE_LIMIT) {
    renderCache.clear();
  }
  renderCache.set(cacheKey, html);

  return html;
}

interface MessageMarkdownKatexProps {
  tex: string;
  display?: boolean;
  className?: string;
}

/**
 * Render a TeX formula with KaTeX, falling back to the raw source when it cannot be
 * rendered at all.
 */
export function MessageMarkdownKatex({
  tex,
  display = false,
  className,
}: MessageMarkdownKatexProps) {
  const html = useMemo(() => renderTex(tex, display), [tex, display]);

  if (!html) {
    return (
      <span className={cn("font-mono text-[0.85em] whitespace-pre-wrap", className)}>{tex}</span>
    );
  }

  if (display) {
    return (
      <div
        className={cn("my-2 overflow-x-auto", className)}
        // KaTeX escapes its input and emits no raw HTML when `trust` is disabled.
        dangerouslySetInnerHTML={{ __html: html }}
      />
    );
  }

  return <span className={className} dangerouslySetInnerHTML={{ __html: html }} />;
}
