export type InlineNode =
  | { type: "text"; value: string }
  | { type: "code"; value: string }
  | { type: "link"; text: string; href: string }
  | { type: "img"; alt: string; src: string }
  | { type: "strong"; nodes: InlineNode[] }
  | { type: "em"; nodes: InlineNode[] };

export type Block =
  | { type: "h"; level: 1 | 2 | 3; inlines: InlineNode[] }
  | { type: "p"; inlines: InlineNode[] }
  | { type: "ul"; items: InlineNode[][] }
  | { type: "ol"; items: InlineNode[][] }
  | { type: "code"; lang?: string; value: string }
  | { type: "img"; alt: string; src: string }
  | { type: "quote"; lines: InlineNode[][] };

function parseInline(text: string): InlineNode[] {
  const nodes: InlineNode[] = [];
  let i = 0;

  const pushText = (value: string) => {
    if (!value) return;
    const last = nodes[nodes.length - 1];
    if (last?.type === "text") last.value += value;
    else nodes.push({ type: "text", value });
  };

  while (i < text.length) {
    const codeStart = text.indexOf("`", i);
    const linkStart = text.indexOf("[", i);
    const starStart = text.indexOf("*", i);
    const next = [codeStart, linkStart, starStart].filter((x) => x !== -1).sort((a, b) => a - b)[0];
    if (next === undefined) {
      pushText(text.slice(i));
      break;
    }

    if (next > i) pushText(text.slice(i, next));

    if (next === codeStart) {
      const end = text.indexOf("`", codeStart + 1);
      if (end === -1) {
        pushText(text.slice(codeStart));
        break;
      }
      nodes.push({ type: "code", value: text.slice(codeStart + 1, end) });
      i = end + 1;
      continue;
    }

    if (next === linkStart) {
      const close = text.indexOf("]", linkStart + 1);
      const openParen = close !== -1 ? text.indexOf("(", close + 1) : -1;
      const closeParen = openParen !== -1 ? text.indexOf(")", openParen + 1) : -1;
      if (close === -1 || openParen !== close + 1 || closeParen === -1) {
        pushText(text.slice(linkStart, linkStart + 1));
        i = linkStart + 1;
        continue;
      }
      const label = text.slice(linkStart + 1, close);
      const href = text.slice(openParen + 1, closeParen);
      const isImage = linkStart > 0 && text[linkStart - 1] === "!";
      if (isImage) {
        const last = nodes[nodes.length - 1];
        if (last?.type === "text" && last.value.endsWith("!")) {
          last.value = last.value.slice(0, -1);
          if (!last.value) nodes.pop();
        }
        nodes.push({ type: "img", alt: label, src: href });
      } else {
        nodes.push({ type: "link", text: label, href });
      }
      i = closeParen + 1;
      continue;
    }

    if (next === starStart) {
      const isStrong = text.startsWith("**", starStart);
      if (isStrong) {
        const end = text.indexOf("**", starStart + 2);
        if (end === -1) {
          pushText("**");
          i = starStart + 2;
          continue;
        }
        const inner = text.slice(starStart + 2, end);
        nodes.push({ type: "strong", nodes: parseInline(inner) });
        i = end + 2;
        continue;
      }

      const end = text.indexOf("*", starStart + 1);
      if (end === -1) {
        pushText("*");
        i = starStart + 1;
        continue;
      }
      const inner = text.slice(starStart + 1, end);
      nodes.push({ type: "em", nodes: parseInline(inner) });
      i = end + 1;
      continue;
    }
  }

  return nodes;
}

export function parseMarkdownToBlocks(content: string): Block[] {
  const lines = (content || "").replace(/\r\n/g, "\n").split("\n");
  const blocks: Block[] = [];
  let i = 0;

  const isBlank = (s: string) => s.trim().length === 0;
  const splitByImages = (text: string): Block[] => {
    const s = (text || "").trim();
    if (!s) return [];

    const re = /!\[([^\]]*)\]\(([^)]+)\)/g;
    const parts: Block[] = [];
    let last = 0;
    let m: RegExpExecArray | null;
    while ((m = re.exec(s))) {
      const before = s.slice(last, m.index).trim();
      if (before) parts.push({ type: "p", inlines: parseInline(before) });
      parts.push({ type: "img", alt: m[1] || "", src: m[2] || "" });
      last = m.index + m[0].length;
    }
    const after = s.slice(last).trim();
    if (after) parts.push({ type: "p", inlines: parseInline(after) });
    return parts.length ? parts : [{ type: "p", inlines: parseInline(s) }];
  };

  while (i < lines.length) {
    const line = lines[i];

    if (isBlank(line)) {
      i++;
      continue;
    }

    if (line.startsWith("```")) {
      const lang = line.slice(3).trim() || undefined;
      i++;
      const codeLines: string[] = [];
      while (i < lines.length && !lines[i].startsWith("```")) {
        codeLines.push(lines[i]);
        i++;
      }
      if (i < lines.length && lines[i].startsWith("```")) i++;
      blocks.push({ type: "code", lang, value: codeLines.join("\n") });
      continue;
    }

    const imgMatch = line.match(/^!\[(.*?)\]\((.*?)\)\s*$/);
    if (imgMatch) {
      blocks.push({ type: "img", alt: imgMatch[1] || "", src: imgMatch[2] || "" });
      i++;
      continue;
    }

    if (line.startsWith(">")) {
      const linesInQuote: InlineNode[][] = [];
      while (i < lines.length && lines[i].startsWith(">")) {
        const raw = lines[i].replace(/^>\s?/, "");
        if (raw.trim().length) linesInQuote.push(parseInline(raw.trim()));
        i++;
      }
      blocks.push({ type: "quote", lines: linesInQuote });
      continue;
    }

    const hMatch = line.match(/^(#{1,3})\s+(.*)$/);
    if (hMatch) {
      const level = hMatch[1].length as 1 | 2 | 3;
      blocks.push({ type: "h", level, inlines: parseInline(hMatch[2]) });
      i++;
      continue;
    }

    if (line.startsWith("- ")) {
      const items: InlineNode[][] = [];
      while (i < lines.length && lines[i].startsWith("- ")) {
        items.push(parseInline(lines[i].slice(2)));
        i++;
      }
      blocks.push({ type: "ul", items });
      continue;
    }

    if (/^\d+\.\s+/.test(line)) {
      const items: InlineNode[][] = [];
      while (i < lines.length && /^\d+\.\s+/.test(lines[i])) {
        items.push(parseInline(lines[i].replace(/^\d+\.\s+/, "")));
        i++;
      }
      blocks.push({ type: "ol", items });
      continue;
    }

    const para: string[] = [line.trim()];
    i++;
    while (i < lines.length && !isBlank(lines[i]) && !lines[i].startsWith("```")) {
      if (/^(#{1,3})\s+/.test(lines[i])) break;
      if (lines[i].startsWith("- ")) break;
      if (/^\d+\.\s+/.test(lines[i])) break;
      if (/^!\[.*?\]\(.*?\)\s*$/.test(lines[i])) break;
      if (lines[i].startsWith(">")) break;
      para.push(lines[i].trim());
      i++;
    }
    const paraText = para.join(" ");
    blocks.push(...splitByImages(paraText));
  }

  return blocks;
}
