export function escapeHtml(input: string): string {
  return input
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#39;");
}

export function parseInline(input: string): string {
  let value = escapeHtml(input);

  // Render tag token groups like [[work, vibe]] as visual chips in preview.
  value = value.replace(/\[\[([^[\]]+)\]\]/g, (match, rawGroup: string) => {
    const tags = rawGroup
      .split(",")
      .map((tag) => tag.trim())
      .filter(Boolean);

    if (!tags.length) return match;

    const chips = tags
      .map((tag) => `<span class="markdown-tag-chip">${tag}</span>`)
      .join("");
    return `<span class="markdown-tag-list">${chips}</span>`;
  });

  // Render hashtag-style tags (#Work) as chips in preview.
  value = value.replace(
    /(^|\s)#([A-Za-z0-9][A-Za-z0-9_-]*)\b/g,
    (_, leading: string, tag: string) =>
      `${leading}<span class="markdown-tag-chip">#${tag}</span>`,
  );

  value = value.replace(/`([^`]+)`/g, "<code>$1</code>");
  value = value.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
  value = value.replace(/\+\+([^+]+)\+\+/g, "<u>$1</u>");
  value = value.replace(/\*([^*]+)\*/g, "<em>$1</em>");
  value = value.replace(
    /\[([^\]]+)\]\((journal:[^\s)]+)\)/g,
    '<a href="$2">$1</a>',
  );
  value = value.replace(
    /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g,
    '<a href="$2" target="_blank" rel="noreferrer">$1</a>',
  );
  return value;
}

export function renderMarkdown(markdown: string): string {
  const lines = markdown.replace(/\r\n/g, "\n").split("\n");
  const output: string[] = [];
  let i = 0;
  let inCode = false;
  let codeLines: string[] = [];

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

    if (trimmed.startsWith("```")) {
      if (inCode) {
        output.push(
          `<pre><code>${escapeHtml(codeLines.join("\n"))}</code></pre>`,
        );
        codeLines = [];
        inCode = false;
      } else {
        inCode = true;
      }
      i += 1;
      continue;
    }

    if (inCode) {
      codeLines.push(line);
      i += 1;
      continue;
    }

    if (!trimmed) {
      i += 1;
      continue;
    }

    const heading = trimmed.match(/^(#{1,6})\s+(.*)$/);
    if (heading) {
      const level = heading[1].length;
      output.push(`<h${level}>${parseInline(heading[2])}</h${level}>`);
      i += 1;
      continue;
    }

    if (/^[-*+]\s+/.test(trimmed)) {
      const items: string[] = [];
      while (i < lines.length && /^[-*+]\s+/.test(lines[i].trim())) {
        items.push(
          `<li>${parseInline(lines[i].trim().replace(/^[-*+]\s+/, ""))}</li>`,
        );
        i += 1;
      }
      output.push(`<ul>${items.join("")}</ul>`);
      continue;
    }

    if (/^\d+\.\s+/.test(trimmed)) {
      const items: string[] = [];
      while (i < lines.length && /^\d+\.\s+/.test(lines[i].trim())) {
        items.push(
          `<li>${parseInline(lines[i].trim().replace(/^\d+\.\s+/, ""))}</li>`,
        );
        i += 1;
      }
      output.push(`<ol>${items.join("")}</ol>`);
      continue;
    }

    if (/^>\s+/.test(trimmed)) {
      output.push(
        `<blockquote>${parseInline(trimmed.replace(/^>\s+/, ""))}</blockquote>`,
      );
      i += 1;
      continue;
    }

    if (/^(-{3,}|\*{3,})$/.test(trimmed)) {
      output.push("<hr />");
      i += 1;
      continue;
    }

    const paragraph: string[] = [];
    while (i < lines.length && lines[i].trim()) {
      paragraph.push(lines[i].trim());
      i += 1;
    }
    output.push(`<p>${parseInline(paragraph.join(" "))}</p>`);
  }

  if (inCode) {
    output.push(`<pre><code>${escapeHtml(codeLines.join("\n"))}</code></pre>`);
  }

  return output.join("");
}

export function extractEditorTitle(markdown: string, fallback: string): string {
  const firstLine =
    markdown.replace(/\r\n/g, "\n").split("\n")[0]?.trim() ?? "";
  const headingMatch = firstLine.match(/^#\s+(.+)$/);
  return headingMatch ? headingMatch[1] : fallback;
}
