/**
 * Минимальный markdown→HTML renderer без внешних зависимостей.
 * Поддерживает: # / ## / ### headings, **bold**, `code`, ссылки [text](url),
 * списки -/* / 1., чекбоксы [ ]/[x], абзацы.
 * Не поддерживает: tables, blockquotes, code fences (для нашего docs хватает).
 */
function escapeHtml(s: string): string {
  return s
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

/**
 * Allow only safe URL schemes in rendered links.
 * Blocks javascript:, data:, vbscript:, file:, etc.
 */
function safeUrl(url: string): string {
  const trimmed = url.trim();
  if (
    trimmed.startsWith("https://") ||
    trimmed.startsWith("http://") ||
    trimmed.startsWith("/") ||
    trimmed.startsWith("#") ||
    trimmed.startsWith("mailto:")
  ) {
    return trimmed;
  }
  return "#";
}

function inlineMd(line: string): string {
  // escape first, then re-introduce safe markup
  let html = escapeHtml(line);
  // code spans `...`
  html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
  // bold **...**
  html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
  // links [text](url) — URL is validated before being placed in href.
  // Note: at this point `html` is already HTML-escaped, so we must unescape
  // the captured URL before passing through safeUrl, then re-escape for href.
  html = html.replace(
    /\[([^\]]+)\]\(([^)]+)\)/g,
    (_, text: string, escapedUrl: string) => {
      const rawUrl = escapedUrl
        .replace(/&amp;/g, "&")
        .replace(/&lt;/g, "<")
        .replace(/&gt;/g, ">")
        .replace(/&quot;/g, '"')
        .replace(/&#39;/g, "'");
      const href = escapeHtml(safeUrl(rawUrl));
      return `<a href="${href}" target="_blank" rel="noopener noreferrer">${text}</a>`;
    },
  );
  // checkboxes
  html = html.replace(
    /\[ \]/g,
    '<input type="checkbox" disabled style="margin-right:6px;"/>',
  );
  html = html.replace(
    /\[x\]/gi,
    '<input type="checkbox" checked disabled style="margin-right:6px;"/>',
  );
  return html;
}

export function renderMarkdown(md: string): string {
  const lines = md.split("\n");
  const out: string[] = [];
  let inList = false;

  for (const rawLine of lines) {
    const line = rawLine.replace(/\r$/, "");

    if (line.trim() === "") {
      if (inList) {
        out.push("</ul>");
        inList = false;
      }
      continue;
    }

    // Headings
    if (line.startsWith("### ")) {
      if (inList) {
        out.push("</ul>");
        inList = false;
      }
      out.push(`<h3>${inlineMd(line.slice(4))}</h3>`);
      continue;
    }
    if (line.startsWith("## ")) {
      if (inList) {
        out.push("</ul>");
        inList = false;
      }
      out.push(`<h2>${inlineMd(line.slice(3))}</h2>`);
      continue;
    }
    if (line.startsWith("# ")) {
      if (inList) {
        out.push("</ul>");
        inList = false;
      }
      out.push(`<h1>${inlineMd(line.slice(2))}</h1>`);
      continue;
    }

    // List items (- or * at indent 0 or 2)
    const listMatch = line.match(/^(\s*)[-*]\s+(.*)$/);
    if (listMatch) {
      if (!inList) {
        out.push("<ul>");
        inList = true;
      }
      out.push(`<li>${inlineMd(listMatch[2])}</li>`);
      continue;
    }

    // Plain paragraph
    if (inList) {
      out.push("</ul>");
      inList = false;
    }
    out.push(`<p>${inlineMd(line)}</p>`);
  }

  if (inList) out.push("</ul>");
  return out.join("\n");
}
