// Lightweight, safe Markdown renderer for chat bubbles.
// Supports the formatting agents commonly return: bold/italic text, inline code,
// links, bullets/numbered lists, and line breaks. It intentionally renders React
// nodes instead of using dangerouslySetInnerHTML so model output stays escaped.

function chatMdParseInline(text, keyPrefix) {
  const nodes = [];
  const pattern = /(\[[^\]]+\]\([^\s)]+\)|`[^`]+`|\*\*[^*]+\*\*|__[^_]+__|\*[^*]+\*|_[^_]+_)/g;
  let last = 0;
  let match;

  while ((match = pattern.exec(text)) !== null) {
    if (match.index > last) nodes.push(text.slice(last, match.index));
    const token = match[0];
    const key = `${keyPrefix}-${nodes.length}`;

    if (token[0] === "[" && token.includes("](")) {
      const close = token.indexOf("](");
      const label = token.slice(1, close);
      const href = token.slice(close + 2, -1);
      if (/^https?:\/\//i.test(href) || /^mailto:/i.test(href)) {
        nodes.push(<a key={key} href={href} target="_blank" rel="noreferrer">{label}</a>);
      } else {
        nodes.push(label);
      }
    } else if (token[0] === "`") {
      nodes.push(<code key={key}>{token.slice(1, -1)}</code>);
    } else if (token.startsWith("**") || token.startsWith("__")) {
      nodes.push(<strong key={key}>{token.slice(2, -2)}</strong>);
    } else {
      nodes.push(<em key={key}>{token.slice(1, -1)}</em>);
    }
    last = pattern.lastIndex;
  }

  if (last < text.length) nodes.push(text.slice(last));
  return nodes;
}

function chatMdFlushParagraph(blocks, paragraph, keyBase) {
  if (!paragraph.length) return;
  const lines = paragraph.splice(0, paragraph.length);
  blocks.push(
    <p key={`${keyBase}-p-${blocks.length}`}>
      {lines.map((line, idx) => (
        <React.Fragment key={`${keyBase}-p-${blocks.length}-${idx}`}>
          {idx > 0 ? <br /> : null}
          {chatMdParseInline(line, `${keyBase}-p-${blocks.length}-${idx}`)}
        </React.Fragment>
      ))}
    </p>
  );
}

function chatMdFlushList(blocks, list, keyBase) {
  if (!list.items.length) return;
  const Tag = list.type === "ol" ? "ol" : "ul";
  const items = list.items.splice(0, list.items.length);
  blocks.push(
    <Tag key={`${keyBase}-${list.type}-${blocks.length}`}>
      {items.map((item, idx) => (
        <li key={`${keyBase}-${list.type}-${blocks.length}-${idx}`}>
          {chatMdParseInline(item, `${keyBase}-${list.type}-${blocks.length}-${idx}`)}
        </li>
      ))}
    </Tag>
  );
}

function renderChatMarkdown(text) {
  const raw = String(text || "");
  const lines = raw.replace(/\r\n?/g, "\n").split("\n");
  const blocks = [];
  const paragraph = [];
  const list = { type: null, items: [] };
  const keyBase = `chat-md-${raw.length}`;

  lines.forEach((line) => {
    const bullet = line.match(/^\s*[-*•]\s+(.+)$/);
    const numbered = line.match(/^\s*\d+[.)]\s+(.+)$/);

    if (!line.trim()) {
      chatMdFlushParagraph(blocks, paragraph, keyBase);
      chatMdFlushList(blocks, list, keyBase);
      return;
    }

    if (bullet || numbered) {
      chatMdFlushParagraph(blocks, paragraph, keyBase);
      const type = numbered ? "ol" : "ul";
      if (list.type && list.type !== type) chatMdFlushList(blocks, list, keyBase);
      list.type = type;
      list.items.push((bullet || numbered)[1]);
      return;
    }

    chatMdFlushList(blocks, list, keyBase);
    paragraph.push(line);
  });

  chatMdFlushParagraph(blocks, paragraph, keyBase);
  chatMdFlushList(blocks, list, keyBase);

  return blocks.length ? <div className="chat-md">{blocks}</div> : null;
}

window.renderChatMarkdown = renderChatMarkdown;
