/**
 * 极简、零依赖、先转义后渲染的 Markdown→HTML。仅覆盖聊天场景常用语法：
 * 标题 / 粗斜体 / 行内代码 / 围栏代码块 / 有序无序列表 / 引用 / 链接 / 段落与换行。
 * 安全策略：所有文本先 HTML 转义，仅注入我们自己生成的标签；链接仅允许 http(s)/相对/锚点。
 * 如需完整 CommonMark，可后续替换为 markdown-it（届时记得保留 XSS 防护）。
 */

function escapeHtml(s: string): string {
  return s
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
}

function safeUrl(url: string): string | null {
  const u = url.trim()
  if (/^https?:\/\//i.test(u) || u.startsWith('/') || u.startsWith('#')) return u
  return null
}

/** 行内格式：行内代码先占位保护，再处理链接/粗体/斜体，最后还原代码。入参须已 HTML 转义。 */
function inline(escaped: string): string {
  const codes: string[] = []
  let s = escaped.replace(/`([^`]+)`/g, (_m, c: string) => {
    codes.push(`<code class="mmd-code">${c}</code>`)
    return `\u0001${codes.length - 1}\u0001`
  })

  s = s.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_m, text: string, url: string) => {
    const safe = safeUrl(url)
    if (!safe) return `[${text}](${url})`
    return `<a class="mmd-link" href="${safe}" target="_blank" rel="noopener noreferrer">${text}</a>`
  })

  s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
  s = s.replace(/__([^_]+)__/g, '<strong>$1</strong>')
  s = s.replace(/(^|[^*])\*([^*\s][^*]*)\*/g, '$1<em>$2</em>')
  s = s.replace(/(^|[^_])_([^_\s][^_]*)_/g, '$1<em>$2</em>')

  s = s.replace(/\u0001(\d+)\u0001/g, (_m, i: string) => codes[Number(i)] ?? '')
  return s
}

export function renderMarkdown(src: string): string {
  if (!src) return ''
  const text = src.replace(/\r\n/g, '\n').replace(/\r/g, '\n')

  const blocks: string[] = []
  const withTokens = text.replace(/```([^\n]*)\n([\s\S]*?)```/g, (_m, lang: string, code: string) => {
    const cls = (lang || '').trim()
    const body = escapeHtml(code.replace(/\n$/, ''))
    const clsAttr = cls ? ` class="language-${escapeHtml(cls)}"` : ''
    blocks.push(`<pre class="mmd-pre"><code${clsAttr}>${body}</code></pre>`)
    return `\u0000${blocks.length - 1}\u0000`
  })

  const lines = withTokens.split('\n')
  const out: string[] = []
  let para: string[] = []
  let i = 0

  const flushPara = (): void => {
    if (para.length) {
      out.push(`<p>${inline(escapeHtml(para.join(' ')))}</p>`)
      para = []
    }
  }

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

    const codeToken = line.match(/^\u0000(\d+)\u0000\s*$/)
    if (codeToken) {
      flushPara()
      out.push(blocks[Number(codeToken[1])] ?? '')
      i++
      continue
    }

    if (/^\s*$/.test(line)) {
      flushPara()
      i++
      continue
    }

    const h = line.match(/^(#{1,6})\s+(.*)$/)
    if (h) {
      flushPara()
      const lvl = h[1].length
      const tag = lvl <= 2 ? 'h3' : lvl === 3 ? 'h4' : 'h5'
      out.push(`<${tag} class="mmd-h">${inline(escapeHtml(h[2]))}</${tag}>`)
      i++
      continue
    }

    if (/^\s*>\s?/.test(line)) {
      flushPara()
      const items: string[] = []
      while (i < lines.length && /^\s*>\s?/.test(lines[i])) {
        items.push(lines[i].replace(/^\s*>\s?/, ''))
        i++
      }
      out.push(`<blockquote class="mmd-quote">${inline(escapeHtml(items.join(' ')))}</blockquote>`)
      continue
    }

    if (/^\s*[-*+]\s+/.test(line)) {
      flushPara()
      const items: string[] = []
      while (i < lines.length && /^\s*[-*+]\s+/.test(lines[i])) {
        items.push(lines[i].replace(/^\s*[-*+]\s+/, ''))
        i++
      }
      out.push(`<ul class="mmd-ul">${items.map((it) => `<li>${inline(escapeHtml(it))}</li>`).join('')}</ul>`)
      continue
    }

    if (/^\s*\d+\.\s+/.test(line)) {
      flushPara()
      const items: string[] = []
      while (i < lines.length && /^\s*\d+\.\s+/.test(lines[i])) {
        items.push(lines[i].replace(/^\s*\d+\.\s+/, ''))
        i++
      }
      out.push(`<ol class="mmd-ol">${items.map((it) => `<li>${inline(escapeHtml(it))}</li>`).join('')}</ol>`)
      continue
    }

    para.push(line)
    i++
  }
  flushPara()

  return out.join('\n')
}
