function escapeHtml(value = '') {
  return String(value)
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&#39;')
}

function escapeAttribute(value = '') {
  return escapeHtml(value).replaceAll('`', '&#96;')
}

function parseMarkdownImage(block = '') {
  const match = String(block).trim().match(/^!\[([^\]]*)\]\(((?:https?:\/\/|\/)[^) \t]+)(?:\s+"([^"]*)")?\)$/)

  if (!match) {
    return null
  }

  const [, alt = '', src = '', title = ''] = match

  if (!src) {
    return null
  }

  return {
    alt: alt.trim(),
    src: src.trim(),
    title: title.trim(),
  }
}

function applyInlineMarkdown(value = '') {
  let output = escapeHtml(value)

  output = output.replace(
    /!\[([^\]]*)\]\(((?:https?:\/\/|\/)[^) \t]+)(?:\s+"([^"]*)")?\)/g,
    (_, alt, src, title = '') => {
      const titleAttr = title ? ` title="${escapeAttribute(title)}"` : ''
      return `<img class="public-rich-image" src="${escapeAttribute(src)}" alt="${escapeAttribute(alt)}" loading="lazy" decoding="async"${titleAttr} />`
    },
  )
  output = output.replace(/`([^`]+)`/g, '<code>$1</code>')
  output = output.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
  output = output.replace(/__([^_]+)__/g, '<strong>$1</strong>')
  output = output.replace(/(^|[\\s(])\*([^*]+)\*(?=$|[\\s).,!?:;])/g, '$1<em>$2</em>')
  output = output.replace(/(^|[\\s(])_([^_]+)_(?=$|[\\s).,!?:;])/g, '$1<em>$2</em>')
  output = output.replace(
    /\[([^\]]+)\]\(((?:https?:\/\/|\/)[^) \t]+)\)/g,
    (_, label, href) => `<a href="${escapeAttribute(href)}" target="_blank" rel="noopener noreferrer">${label}</a>`,
  )

  return output
}

function renderStandaloneImage(block = '') {
  const image = parseMarkdownImage(block)

  if (!image) {
    return ''
  }

  const titleAttr = image.title ? ` title="${escapeAttribute(image.title)}"` : ''
  const caption = image.title ? `<figcaption>${escapeHtml(image.title)}</figcaption>` : ''

  return `<figure class="public-rich-figure"><img class="public-rich-image" src="${escapeAttribute(image.src)}" alt="${escapeAttribute(image.alt)}" loading="lazy" decoding="async"${titleAttr} />${caption}</figure>`
}

function renderParagraph(block = '') {
  const lines = block
    .split('\n')
    .map((line) => line.trim())
    .filter(Boolean)

  if (!lines.length) {
    return ''
  }

  return `<p>${lines.map((line) => applyInlineMarkdown(line)).join('<br />')}</p>`
}

function renderList(block = '') {
  const lines = block
    .split('\n')
    .map((line) => line.trim())
    .filter(Boolean)

  if (!lines.length) {
    return ''
  }

  const ordered = lines.every((line) => /^\d+\.\s+/.test(line))
  const tag = ordered ? 'ol' : 'ul'
  const items = lines
    .map((line) => line.replace(/^([-*+]|\d+\.)\s+/, ''))
    .map((line) => `<li>${applyInlineMarkdown(line)}</li>`)
    .join('')

  return `<${tag}>${items}</${tag}>`
}

function renderBlockquote(block = '') {
  const lines = block
    .split('\n')
    .map((line) => line.replace(/^>\s?/, '').trim())
    .filter(Boolean)

  if (!lines.length) {
    return ''
  }

  return `<blockquote><p>${lines.map((line) => applyInlineMarkdown(line)).join('<br />')}</p></blockquote>`
}

function renderHeading(block = '') {
  const match = block.trim().match(/^(#{1,6})\s+(.+)$/)

  if (!match) {
    return ''
  }

  const [, hashes, text] = match
  const level = Math.min(hashes.length, 6)
  return `<h${level}>${applyInlineMarkdown(text.trim())}</h${level}>`
}

function isListBlock(block = '') {
  const lines = block
    .split('\n')
    .map((line) => line.trim())
    .filter(Boolean)

  return lines.length > 0 && lines.every((line) => /^([-*+]|\d+\.)\s+/.test(line))
}

function isMarkdownRichText(value = '') {
  return /^#{1,6}\s+/m.test(value)
    || /^>\s?/m.test(value)
    || /^([-*+]|\d+\.)\s+/m.test(value)
    || /!\[([^\]]*)\]\(((?:https?:\/\/|\/)[^) \t]+)(?:\s+"([^"]*)")?\)/.test(value)
    || /\*\*[^*]+\*\*/.test(value)
    || /__[^_]+__/.test(value)
    || /\[[^\]]+\]\(((?:https?:\/\/|\/)[^) \t]+)\)/.test(value)
}

export function isHtmlRichText(value = '') {
  return /<\/?[a-z][\s\S]*>/i.test(String(value || ''))
}

export function renderPublicRichText(value = '') {
  const source = String(value || '').replace(/\r\n?/g, '\n').trim()

  if (!source) {
    return ''
  }

  if (isHtmlRichText(source)) {
    return source
  }

  const blocks = source
    .split(/\n{2,}/)
    .map((block) => block.trim())
    .filter(Boolean)

  if (!blocks.length) {
    return ''
  }

  if (!isMarkdownRichText(source)) {
    return blocks.map((block) => renderParagraph(block)).filter(Boolean).join('\n')
  }

  return blocks.map((block) => {
    if (parseMarkdownImage(block)) {
      return renderStandaloneImage(block)
    }

    if (/^(#{1,6})\s+/.test(block)) {
      return renderHeading(block)
    }

    if (isListBlock(block)) {
      return renderList(block)
    }

    if (/^>\s?/m.test(block)) {
      return renderBlockquote(block)
    }

    return renderParagraph(block)
  }).filter(Boolean).join('\n')
}
