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

const activeLightboxImages = ref([])
const activeLightboxIndex = ref(0)

/**
 * 마크다운 블록을 생성
 * @param {string} type - 블록 타입
 * @param {string|Array<string>} text - 블록 텍스트
 * @param {number|null} level - 제목 레벨
 * @param {string} id - 블록 ID
 * @param {Object} options - 추가 블록 옵션
 * @returns {Object} 블록
 */
const createBlock = (type = 'paragraph', text = '', level = null, id = '', options = {}) => ({
  id,
  type,
  text,
  level,
  url: options.url || '',
  alt: options.alt || '',
  title: options.title || '',
  width: options.width || 'regular',
  images: options.images || []
})

/**
 * 이미지 마크다운 행을 이미지 데이터로 변환
 * @param {string} line - 마크다운 행
 * @returns {Object|null} 이미지 데이터
 */
const parseImageLine = (line) => {
  const match = line.trim().match(/^!\[([^\]]*)\]\(([^)]+)\)(?:\{width=(regular|wide|full)\})?$/)

  if (!match) {
    return null
  }

  return {
    alt: match[1],
    url: match[2],
    width: match[3] || 'regular'
  }
}

/**
 * 닫힘 표식까지의 행 목록을 반환
 * @param {Array<string>} lines - 전체 마크다운 행
 * @param {number} startIndex - 본문 시작 인덱스
 * @returns {{contentLines: Array<string>, nextIndex: number}} 블록 본문과 다음 인덱스
 */
const collectFencedLines = (lines, startIndex) => {
  const contentLines = []
  let index = startIndex

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

  return {
    contentLines,
    nextIndex: index + 1
  }
}

/**
 * 마크다운 문자열을 렌더링용 블록 목록으로 변환
 * @param {string} markdown - 마크다운 문자열
 * @returns {Array<Object>} 블록 목록
 */
const parseMarkdownBlocks = (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 === ':::gallery') {
      const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
      const images = []

      contentLines.forEach((contentLine) => {
        const image = parseImageLine(contentLine)
        if (image) {
          images.push(image)
        }
      })

      blocks.push(createBlock('gallery', '', null, `block-${blocks.length}`, { images }))
      index = nextIndex
      continue
    }

    if (trimmedLine === ':::callout') {
      const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
      blocks.push(createBlock('callout', contentLines.join('\n'), null, `block-${blocks.length}`))
      index = nextIndex
      continue
    }

    if (trimmedLine.startsWith(':::toggle')) {
      const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
      const title = trimmedLine.replace(/^:::toggle\s*/, '').trim()
      blocks.push(createBlock('toggle', contentLines.join('\n'), null, `block-${blocks.length}`, { title }))
      index = nextIndex
      continue
    }

    if (trimmedLine === ':::embed') {
      const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
      blocks.push(createBlock('embed', '', null, `block-${blocks.length}`, { url: contentLines.join('\n').trim() }))
      index = nextIndex
      continue
    }

    const image = parseImageLine(trimmedLine)

    if (image) {
      blocks.push(createBlock('image', '', null, `block-${blocks.length}`, image))
      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(createBlock('code', codeLines.join('\n'), null, `block-${blocks.length}`))
      index += 1
      continue
    }

    if (trimmedLine === '---') {
      blocks.push(createBlock('divider', '', null, `block-${blocks.length}`))
      index += 1
      continue
    }

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

    if (headingMatch) {
      blocks.push(createBlock('heading', headingMatch[2], headingMatch[1].length, `block-${blocks.length}`))
      index += 1
      continue
    }

    if (trimmedLine.startsWith('> ')) {
      blocks.push(createBlock('quote', trimmedLine.replace(/^>\s?/, ''), null, `block-${blocks.length}`))
      index += 1
      continue
    }

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

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

      blocks.push(createBlock('list', items, null, `block-${blocks.length}`))
      continue
    }

    blocks.push(createBlock('paragraph', trimmedLine, null, `block-${blocks.length}`))
    index += 1
  }

  return blocks
}

const blocks = computed(() => parseMarkdownBlocks(props.content))
const activeLightboxImage = computed(() => activeLightboxImages.value[activeLightboxIndex.value])

/**
 * 라이트박스를 연다
 * @param {Array<Object>} images - 이미지 목록
 * @param {number} index - 시작 인덱스
 * @returns {void}
 */
const openLightbox = (images, index) => {
  activeLightboxImages.value = images
  activeLightboxIndex.value = index
}

/**
 * 라이트박스를 닫는다
 * @returns {void}
 */
const closeLightbox = () => {
  activeLightboxImages.value = []
  activeLightboxIndex.value = 0
}

/**
 * 라이트박스 이전 이미지로 이동
 * @returns {void}
 */
const showPreviousImage = () => {
  activeLightboxIndex.value = activeLightboxIndex.value === 0
    ? activeLightboxImages.value.length - 1
    : activeLightboxIndex.value - 1
}

/**
 * 라이트박스 다음 이미지로 이동
 * @returns {void}
 */
const showNextImage = () => {
  activeLightboxIndex.value = (activeLightboxIndex.value + 1) % activeLightboxImages.value.length
}
</script>

<template>
  <div class="content-markdown-renderer">
    <template v-for="block in blocks" :key="block.id">
      <ProseHeading v-if="block.type === 'heading'" :level="block.level">
        {{ block.text }}
      </ProseHeading>
      <ProseBlockquote v-else-if="block.type === 'quote'">
        {{ block.text }}
      </ProseBlockquote>
      <ProseList v-else-if="block.type === 'list'">
        <li v-for="(item, itemIndex) in block.text" :key="`${block.id}-${itemIndex}`">
          {{ item }}
        </li>
      </ProseList>
      <ProseImage v-else-if="block.type === 'image'" :src="block.url" :alt="block.alt" :variant="block.width">
        {{ block.alt }}
      </ProseImage>
      <ProseCallout v-else-if="block.type === 'callout'">
        {{ block.text }}
      </ProseCallout>
      <ProseToggle v-else-if="block.type === 'toggle'" :title="block.title || '더 보기'">
        {{ block.text }}
      </ProseToggle>
      <ProseEmbed v-else-if="block.type === 'embed'" :url="block.url" />
      <div v-else-if="block.type === 'gallery'" class="content-markdown-renderer__gallery my-8 grid grid-cols-2 gap-2 md:grid-cols-3">
        <button
          v-for="(image, imageIndex) in block.images"
          :key="`${block.id}-${image.url}`"
          class="content-markdown-renderer__gallery-button overflow-hidden rounded bg-surface"
          type="button"
          @click="openLightbox(block.images, imageIndex)"
        >
          <img class="content-markdown-renderer__gallery-image aspect-[4/3] w-full object-cover transition-transform hover:scale-[1.02]" :src="image.url" :alt="image.alt">
        </button>
      </div>
      <pre
        v-else-if="block.type === 'code'"
        class="content-markdown-renderer__code my-6 overflow-x-auto rounded bg-[#15171a] px-4 py-3 text-sm leading-6 text-white"
      ><code>{{ block.text }}</code></pre>
      <hr v-else-if="block.type === 'divider'" class="content-markdown-renderer__divider my-10 border-line">
      <p v-else class="content-markdown-renderer__paragraph my-5 leading-8">
        {{ block.text }}
      </p>
    </template>

    <div
      v-if="activeLightboxImage"
      class="content-markdown-renderer__lightbox fixed inset-0 z-50 grid place-items-center bg-black/90 px-5 py-8"
      role="dialog"
      aria-modal="true"
      @click.self="closeLightbox"
    >
      <button class="content-markdown-renderer__lightbox-close absolute right-5 top-5 rounded bg-white px-3 py-2 text-sm font-semibold text-ink" type="button" @click="closeLightbox">
        닫기
      </button>
      <button
        v-if="activeLightboxImages.length > 1"
        class="content-markdown-renderer__lightbox-prev absolute left-5 top-1/2 rounded bg-white px-3 py-2 text-sm font-semibold text-ink"
        type="button"
        @click="showPreviousImage"
      >
        이전
      </button>
      <img class="content-markdown-renderer__lightbox-image max-h-[84vh] max-w-[92vw] object-contain" :src="activeLightboxImage.url" :alt="activeLightboxImage.alt">
      <button
        v-if="activeLightboxImages.length > 1"
        class="content-markdown-renderer__lightbox-next absolute right-5 top-1/2 rounded bg-white px-3 py-2 text-sm font-semibold text-ink"
        type="button"
        @click="showNextImage"
      >
        다음
      </button>
    </div>
  </div>
</template>
