import { Anchor, Image, Text } from "@mantine/core";
import { Fragment, ReactNode } from "react";

// Minimal markdown renderer for chat/desc embeds (issue 0128).
// Recognises:
//   ![alt](url)  -> <Image>
//   [name](url)  -> <Anchor>name</Anchor>
// Everything else is plain text with preserved whitespace.

interface ImgToken { kind: "img"; alt: string; url: string }
interface LinkToken { kind: "link"; label: string; url: string }
interface TextToken { kind: "text"; value: string }
type Token = ImgToken | LinkToken | TextToken;

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

// Allow only safe URL schemes. Reject javascript:, data:text/html, vbscript:, etc.
// Accepts: absolute http(s), protocol-relative //, and same-origin paths (/...).
function safeURL(url: string): string | null {
  const u = url.trim();
  if (u.startsWith("/")) return u;
  if (/^https?:\/\//i.test(u)) return u;
  return null;
}

// data: scheme is allowed only when the MIME prefix is image/.
function safeImageURL(url: string): string | null {
  const safe = safeURL(url);
  if (safe) return safe;
  const u = url.trim();
  if (/^data:image\/[a-z0-9.+-]+(;[a-z0-9-]+=[^,]+)*;base64,/i.test(u)) return u;
  return null;
}

function tokenize(input: string): Token[] {
  const out: Token[] = [];
  let last = 0;
  let m: RegExpExecArray | null;
  TOKEN_RE.lastIndex = 0;
  while ((m = TOKEN_RE.exec(input)) !== null) {
    if (m.index > last) {
      out.push({ kind: "text", value: input.slice(last, m.index) });
    }
    if (m[1]) {
      const url = safeImageURL(m[3]);
      if (url) {
        out.push({ kind: "img", alt: m[2] || "", url });
      } else {
        out.push({ kind: "text", value: m[0] });
      }
    } else if (m[4]) {
      const url = safeURL(m[6]);
      if (url) {
        out.push({ kind: "link", label: m[5], url });
      } else {
        out.push({ kind: "text", value: m[0] });
      }
    }
    last = TOKEN_RE.lastIndex;
  }
  if (last < input.length) {
    out.push({ kind: "text", value: input.slice(last) });
  }
  return out;
}

interface Props {
  text: string;
  size?: string;
}

export function MessageBody({ text, size = "sm" }: Props): ReactNode {
  const tokens = tokenize(text);
  if (tokens.length === 0) {
    return null;
  }

  const inlineNodes: ReactNode[] = [];
  const blocks: ReactNode[] = [];
  let key = 0;

  const flushInline = () => {
    if (inlineNodes.length === 0) return;
    blocks.push(
      <Text
        key={`t-${key++}`}
        size={size}
        style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}
      >
        {inlineNodes.map((n, i) => (
          <Fragment key={i}>{n}</Fragment>
        ))}
      </Text>
    );
    inlineNodes.length = 0;
  };

  for (const t of tokens) {
    if (t.kind === "img") {
      flushInline();
      blocks.push(
        <Anchor key={`i-${key++}`} href={t.url} target="_blank" rel="noopener noreferrer">
          <Image
            src={t.url}
            alt={t.alt}
            maw={220}
            mah={220}
            fit="contain"
            radius="sm"
          />
        </Anchor>
      );
    } else if (t.kind === "link") {
      inlineNodes.push(
        <Anchor key={`l-${key++}`} href={t.url} target="_blank" rel="noopener noreferrer">
          {t.label}
        </Anchor>
      );
    } else {
      inlineNodes.push(t.value);
    }
  }
  flushInline();

  return <>{blocks}</>;
}
