// ─────────────────────────────────────────────────────────────────────────
// Meridian — lightweight Markdown → JSX renderer (NO deps, no innerHTML).
//
// Built for the research/analysis & call summaries, which are GitHub-flavoured
// markdown straight from the vault: H1–H6, **bold**/*italic*/`code`, ordered &
// unordered lists, blockquotes, fenced code, horizontal rules, clickable links,
// and — crucially — pipe TABLES (the Eulerpool/AAQS blocks). The stored files
// lead with a YAML frontmatter block which we strip.
//
// Lives in its own file (NOT lib.jsx) on purpose. Exposes `renderMarkdown(md)`
// (→ array of JSX blocks) and a styled `<MarkdownView text=… />` wrapper.
// ─────────────────────────────────────────────────────────────────────────

/* Strip a leading YAML frontmatter block ("---\n…\n---"). */
function stripFrontmatter(src) {
  if (!/^---\s*\r?\n/.test(src)) return src;
  const m = src.match(/^---\s*\r?\n[\s\S]*?\r?\n---\s*(\r?\n|$)/);
  return m ? src.slice(m[0].length) : src;
}

/* Split a markdown table row into trimmed cells (tolerates optional edge pipes). */
function splitTableRow(line) {
  return line.trim().replace(/^\|/, "").replace(/\|\s*$/, "").split("|").map((c) => c.trim());
}

function isTableSeparator(line) {
  return !!line && line.includes("|") && /^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/.test(line);
}

function isBlockStart(line, next) {
  if (!line || !line.trim()) return true;
  return (
    /^\s*(#{1,6})\s+/.test(line) ||
    /^```/.test(line.trim()) ||
    /^\s*>\s?/.test(line) ||
    /^\s*[-*+]\s+/.test(line) ||
    /^\s*\d+\.\s+/.test(line) ||
    /^\s*([-*_])\1{2,}\s*$/.test(line) ||
    (line.includes("|") && isTableSeparator(next))
  );
}

/* ── Inline: **bold** *italic* `code` ~~del~~ [text](url) + bare URLs ──────── */
const INLINE = [
  { re: /`([^`]+)`/,                       node: (m, kp) => React.createElement("code", { key: kp }, m[1]) },
  { re: /\[([^\]]+)\]\(([^)\s]+)\)/,        node: (m, kp) => React.createElement("a", { key: kp, href: m[2], target: "_blank", rel: "noopener noreferrer" }, parseInline(m[1], kp + "a")) },
  { re: /\*\*([^*]+)\*\*/,                  node: (m, kp) => React.createElement("strong", { key: kp }, parseInline(m[1], kp + "b")) },
  { re: /__([^_]+)__/,                      node: (m, kp) => React.createElement("strong", { key: kp }, parseInline(m[1], kp + "B")) },
  { re: /~~([^~]+)~~/,                      node: (m, kp) => React.createElement("del", { key: kp }, parseInline(m[1], kp + "d")) },
  { re: /(^|[\s(])\*([^*\n]+)\*(?=[\s).,;:!?]|$)/, node: (m, kp) => [m[1], React.createElement("em", { key: kp }, parseInline(m[2], kp + "i"))], lead: true },
  { re: /(^|[\s(])_([^_\n]+)_(?=[\s).,;:!?]|$)/,   node: (m, kp) => [m[1], React.createElement("em", { key: kp }, parseInline(m[2], kp + "I"))], lead: true },
  { re: /(https?:\/\/[^\s<>()]+)/,          node: (m, kp) => React.createElement("a", { key: kp, href: m[1], target: "_blank", rel: "noopener noreferrer" }, m[1]) },
];

function parseInline(text, keyPrefix) {
  if (text == null) return null;
  const out = [];
  let rest = String(text);
  let k = 0;
  while (rest) {
    let best = null;
    for (const p of INLINE) {
      const m = p.re.exec(rest);
      if (m && (!best || m.index < best.m.index)) best = { p, m };
    }
    if (!best) { out.push(rest); break; }
    const kp = keyPrefix + "-" + k++;
    // For "lead" patterns the match keeps a leading separator char in group 1.
    const before = rest.slice(0, best.m.index) + (best.p.lead ? "" : "");
    if (best.m.index > 0) out.push(before);
    const produced = best.p.node(best.m, kp);
    if (Array.isArray(produced)) produced.forEach((n) => out.push(n));
    else out.push(produced);
    rest = rest.slice(best.m.index + best.m[0].length);
  }
  return out;
}

/* ── Block parser → array of JSX elements ─────────────────────────────────── */
function renderMarkdown(src) {
  if (!src || typeof src !== "string") return null;
  const lines = stripFrontmatter(src).replace(/\r\n?/g, "\n").split("\n");
  const blocks = [];
  let i = 0, key = 0;
  const add = (el) => blocks.push(React.cloneElement(el, { key: "blk" + key++ }));

  while (i < lines.length) {
    const line = lines[i];
    if (!line.trim()) { i++; continue; }

    // Fenced code block
    if (/^```/.test(line.trim())) {
      i++;
      const buf = [];
      while (i < lines.length && !/^```/.test(lines[i].trim())) { buf.push(lines[i]); i++; }
      i++; // closing fence
      add(React.createElement("pre", { className: "md-pre" }, React.createElement("code", null, buf.join("\n"))));
      continue;
    }

    // Horizontal rule
    if (/^\s*([-*_])\1{2,}\s*$/.test(line)) { add(<hr />); i++; continue; }

    // Heading
    const hm = line.match(/^\s*(#{1,6})\s+(.*)$/);
    if (hm) {
      const tag = "h" + Math.min(hm[1].length, 6);
      add(React.createElement(tag, null, parseInline(hm[2].replace(/\s+#*\s*$/, "").trim(), "h" + i)));
      i++; continue;
    }

    // Pipe table (header row + separator row)
    if (line.includes("|") && isTableSeparator(lines[i + 1])) {
      const header = splitTableRow(line);
      const align = splitTableRow(lines[i + 1]).map((c) => {
        const t = c.trim();
        if (/^:-+:$/.test(t)) return "center";
        if (/-+:$/.test(t)) return "right";
        if (/^:-+/.test(t)) return "left";
        return null;
      });
      i += 2;
      const rows = [];
      while (i < lines.length && lines[i].trim() && lines[i].includes("|") && !isTableSeparator(lines[i])) {
        rows.push(splitTableRow(lines[i])); i++;
      }
      add(
        <div className="md-table-wrap">
          <table className="md-table">
            <thead>
              <tr>{header.map((c, ci) => <th key={ci} style={align[ci] ? { textAlign: align[ci] } : undefined}>{parseInline(c, "th" + ci)}</th>)}</tr>
            </thead>
            <tbody>
              {rows.map((r, ri) => (
                <tr key={ri}>{header.map((_, ci) => <td key={ci} style={align[ci] ? { textAlign: align[ci] } : undefined}>{parseInline(r[ci] != null ? r[ci] : "", "td" + ri + "_" + ci)}</td>)}</tr>
              ))}
            </tbody>
          </table>
        </div>,
      );
      continue;
    }

    // Blockquote (recurse on inner content)
    if (/^\s*>\s?/.test(line)) {
      const buf = [];
      while (i < lines.length && /^\s*>\s?/.test(lines[i])) { buf.push(lines[i].replace(/^\s*>\s?/, "")); i++; }
      add(<blockquote>{renderMarkdown(buf.join("\n"))}</blockquote>);
      continue;
    }

    // Unordered list
    if (/^\s*[-*+]\s+/.test(line)) {
      const items = [];
      while (i < lines.length && /^\s*[-*+]\s+/.test(lines[i])) {
        let content = lines[i].replace(/^\s*[-*+]\s+/, "");
        i++;
        // fold indented continuation lines into the same item
        while (i < lines.length && lines[i].trim() && /^\s{2,}\S/.test(lines[i]) && !/^\s*[-*+]\s+/.test(lines[i]) && !/^\s*\d+\.\s+/.test(lines[i])) {
          content += " " + lines[i].trim(); i++;
        }
        items.push(content);
      }
      add(<ul>{items.map((it, ii) => <li key={ii}>{parseInline(it, "uli" + i + "_" + ii)}</li>)}</ul>);
      continue;
    }

    // Ordered list
    if (/^\s*\d+\.\s+/.test(line)) {
      const items = [];
      while (i < lines.length && /^\s*\d+\.\s+/.test(lines[i])) {
        let content = lines[i].replace(/^\s*\d+\.\s+/, "");
        i++;
        while (i < lines.length && lines[i].trim() && /^\s{2,}\S/.test(lines[i]) && !/^\s*[-*+]\s+/.test(lines[i]) && !/^\s*\d+\.\s+/.test(lines[i])) {
          content += " " + lines[i].trim(); i++;
        }
        items.push(content);
      }
      add(<ol>{items.map((it, ii) => <li key={ii}>{parseInline(it, "oli" + i + "_" + ii)}</li>)}</ol>);
      continue;
    }

    // Paragraph — gather consecutive non-block lines
    const buf = [line]; i++;
    while (i < lines.length && lines[i].trim() && !isBlockStart(lines[i], lines[i + 1])) { buf.push(lines[i]); i++; }
    add(<p>{parseInline(buf.join(" "), "p" + i)}</p>);
  }
  return blocks;
}

/* Styled wrapper: comfortable line-height + reading max-width. Reuses the shared
   `.markdown-body` styles plus the table/code rules added in styles.css. */
function MarkdownView({ text, style, className }) {
  return (
    <div className={"markdown-body markdown-rich" + (className ? " " + className : "")} style={style}>
      {renderMarkdown(text)}
    </div>
  );
}

Object.assign(window, { renderMarkdown, MarkdownView });
