/* Comment bodies: a markdown-lite renderer for the ONLY formatting the
 * standard's comments use — attachment images, links, bold, inline code.
 * Dependency-free and XSS-free because it never touches innerHTML.
 *
 * The render rule (Interaction Standard addendum): an image is loaded ONLY
 * from the app's own relative attachment URLs (`imagePrefix`, default
 * "/api/"), never from an external URL in user text — a comment must never
 * make the reader's browser fetch a tracking pixel. Links accept https and
 * the same relative shape (a PDF attachment is a link). Anything else
 * renders as the text it is. */

import { Fragment, type ReactNode } from "react";
import { openLightbox } from "./Lightbox";

const PATTERN =
  /!\[([^\]]*)\]\(([^)\s]+)\)|\[([^\]]+)\]\(([^)\s]+)\)|\*\*([^*]+)\*\*|`([^`]+)`/g;

/** The app's own relative URL, and nothing that could escape it. */
export function isOwnUrl(url: string, prefix = "/api/"): boolean {
  return url.startsWith(prefix) && !url.startsWith("//") && !url.includes("..");
}

export function renderBody(text: string, imagePrefix = "/api/"): ReactNode[] {
  const out: ReactNode[] = [];
  let last = 0;
  let key = 0;
  for (const m of text.matchAll(PATTERN)) {
    const at = m.index!;
    if (at > last) out.push(text.slice(last, at));
    if (m[2] !== undefined) {
      out.push(isOwnUrl(m[2], imagePrefix) ? (
        <button key={key++} type="button" className="cu-md-img-link"
                onClick={(e) => { e.stopPropagation(); openLightbox(m[2], m[1]); }}
                aria-label={`Open ${m[1] || "the image"} full size`}>
          <img src={m[2]} alt={m[1] || "attachment"} className="cu-md-img" loading="lazy" />
        </button>
      ) : m[0]);
    } else if (m[3] && m[4]) {
      const ok = /^https:\/\//.test(m[4]) || isOwnUrl(m[4], imagePrefix);
      out.push(ok ? (
        <a key={key++} href={m[4]} target="_blank" rel="noreferrer">{m[3]}</a>
      ) : m[0]);
    } else if (m[5]) {
      out.push(<strong key={key++}>{m[5]}</strong>);
    } else if (m[6]) {
      out.push(<code key={key++}>{m[6]}</code>);
    }
    last = at + m[0].length;
  }
  if (last < text.length) out.push(text.slice(last));
  return out.map((n, i) => <Fragment key={`f${i}`}>{n}</Fragment>);
}

export function Body({ text, imagePrefix }: { text: string; imagePrefix?: string }) {
  return <>{renderBody(text, imagePrefix)}</>;
}
