<script setup>
const props = defineProps({
  content: {
    type: String,
    default: ''
  }
})

/**
 * HTML 특수 문자 이스케이프
 * @param {string} value - 원본 문자열
 * @returns {string} 이스케이프된 문자열
 */
const escapeHtml = (value) => value
  .replace(/&/g, '&amp;')
  .replace(/</g, '&lt;')
  .replace(/>/g, '&gt;')
  .replace(/"/g, '&quot;')
  .replace(/'/g, '&#039;')

/**
 * 인라인 마크다운 변환
 * @param {string} value - 원본 문자열
 * @returns {string} 변환된 HTML
 */
const parseInlineMarkdown = (value) => escapeHtml(value)
  .replace(/`([^`]+)`/g, '<code class="admin-markdown-preview__inline-code">$1</code>')
  .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
  .replace(/\*([^*]+)\*/g, '<em>$1</em>')

/**
 * 목록 HTML 변환
 * @param {Array<string>} items - 목록 아이템
 * @param {'ul' | 'ol'} tagName - 목록 태그명
 * @returns {string} 목록 HTML
 */
const renderList = (items, tagName) => {
  const listItems = items
    .map((item) => `<li>${parseInlineMarkdown(item)}</li>`)
    .join('')

  return `<${tagName}>${listItems}</${tagName}>`
}

/**
 * 기본 마크다운을 미리보기 HTML로 변환
 * @param {string} markdown - 마크다운 문자열
 * @returns {string} 미리보기 HTML
 */
const renderMarkdown = (markdown) => {
  const lines = markdown.split('\n')
  const blocks = []
  let index = 0

  while (index < lines.length) {
    const line = lines[index]
    const trimmedLine = line.trim()

    if (!trimmedLine) {
      index += 1
      continue
    }

    if (trimmedLine.startsWith('```')) {
      const codeLines = []
      index += 1

      while (index < lines.length && !lines[index].trim().startsWith('```')) {
        codeLines.push(lines[index])
        index += 1
      }

      blocks.push(`<pre><code>${escapeHtml(codeLines.join('\n'))}</code></pre>`)
      index += 1
      continue
    }

    const headingMatch = trimmedLine.match(/^(#{1,3})\s+(.+)$/)

    if (headingMatch) {
      const level = headingMatch[1].length
      blocks.push(`<h${level}>${parseInlineMarkdown(headingMatch[2])}</h${level}>`)
      index += 1
      continue
    }

    if (trimmedLine.startsWith('> ')) {
      const quotes = []

      while (index < lines.length && lines[index].trim().startsWith('> ')) {
        quotes.push(lines[index].trim().replace(/^>\s?/, ''))
        index += 1
      }

      blocks.push(`<blockquote>${quotes.map((quote) => `<p>${parseInlineMarkdown(quote)}</p>`).join('')}</blockquote>`)
      continue
    }

    if (/^- /.test(trimmedLine)) {
      const items = []

      while (index < lines.length && /^- /.test(lines[index].trim())) {
        items.push(lines[index].trim().replace(/^- /, ''))
        index += 1
      }

      blocks.push(renderList(items, 'ul'))
      continue
    }

    if (/^\d+\. /.test(trimmedLine)) {
      const items = []

      while (index < lines.length && /^\d+\. /.test(lines[index].trim())) {
        items.push(lines[index].trim().replace(/^\d+\. /, ''))
        index += 1
      }

      blocks.push(renderList(items, 'ol'))
      continue
    }

    const paragraphs = []

    while (
      index < lines.length &&
      lines[index].trim() &&
      !lines[index].trim().startsWith('```') &&
      !lines[index].trim().match(/^(#{1,3})\s+(.+)$/) &&
      !lines[index].trim().startsWith('> ') &&
      !/^- /.test(lines[index].trim()) &&
      !/^\d+\. /.test(lines[index].trim())
    ) {
      paragraphs.push(lines[index].trim())
      index += 1
    }

    blocks.push(`<p>${parseInlineMarkdown(paragraphs.join(' '))}</p>`)
  }

  return blocks.join('')
}

const previewHtml = computed(() => renderMarkdown(props.content))
</script>

<template>
  <article
    class="admin-markdown-preview post-prose min-h-[28rem] rounded border border-line bg-white px-5 py-4 text-sm leading-7"
    v-html="previewHtml"
  />
</template>
