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

const { theme } = useThemeMode()

/**
 * YouTube 영상 ID를 추출한다.
 * @param {string} value - 임베드 URL
 * @returns {string} YouTube 영상 ID
 */
const getYouTubeId = (value) => {
  try {
    const parsedUrl = new URL(value)

    if (parsedUrl.hostname.includes('youtu.be')) {
      return parsedUrl.pathname.replace('/', '')
    }

    if (parsedUrl.hostname.includes('youtube.com')) {
      return parsedUrl.searchParams.get('v') || parsedUrl.pathname.split('/').pop() || ''
    }
  } catch {
    return ''
  }

  return ''
}

/**
 * Twitter/X 게시물 ID를 추출한다.
 * @param {string} value - 트윗 URL
 * @returns {string} 상태 ID
 */
const getTweetId = (value) => {
  try {
    const trimmed = value.trim()
    const parsedUrl = new URL(trimmed)
    const host = parsedUrl.hostname.replace(/^www\./, '')

    if (!['twitter.com', 'x.com', 'mobile.twitter.com'].includes(host)) {
      return ''
    }

    const parts = parsedUrl.pathname.split('/').filter(Boolean)
    const statusIdx = parts.indexOf('status')

    if (statusIdx >= 0 && parts[statusIdx + 1]) {
      return parts[statusIdx + 1].split(/[?#]/)[0] || ''
    }
  } catch {
    return ''
  }

  return ''
}

/**
 * Mastodon 공개 게시물 URL인지 확인하고 embed URL을 반환한다.
 * @param {string} value - Mastodon 게시물 URL
 * @returns {string} Mastodon embed URL
 */
const getMastodonEmbedUrl = (value) => {
  try {
    const parsedUrl = new URL(value.trim())
    const path = parsedUrl.pathname.replace(/\/$/, '')
    const isKnownNonMastodonHost = [
      'twitter.com',
      'x.com',
      'mobile.twitter.com',
      'youtube.com',
      'www.youtube.com',
      'youtu.be'
    ].includes(parsedUrl.hostname.replace(/^www\./, ''))

    if (
      isKnownNonMastodonHost ||
      !['http:', 'https:'].includes(parsedUrl.protocol)
    ) {
      return ''
    }

    if (/^\/@[^/]+\/\d+$/.test(path) || /^\/users\/[^/]+\/statuses\/\d+$/.test(path)) {
      return `${parsedUrl.origin}${path}/embed`
    }
  } catch {
    return ''
  }

  return ''
}

const youtubeId = computed(() => getYouTubeId(props.url))
const youtubeEmbedUrl = computed(() => youtubeId.value ? `https://www.youtube.com/embed/${youtubeId.value}` : '')
const tweetId = computed(() => getTweetId(props.url))
const mastodonEmbedUrl = computed(() => getMastodonEmbedUrl(props.url))
const mastodonIframeRef = ref(null)
const mastodonEmbedHeight = ref(640)
const mastodonEmbedId = ref(0)

/**
 * 외부 링크로 열어도 되는 URL인지 확인한다.
 * @param {string} value - 검사할 URL
 * @returns {boolean} 허용 여부
 */
const isSafeExternalUrl = (value) => {
  try {
    const parsedUrl = new URL(value)
    return ['http:', 'https:'].includes(parsedUrl.protocol)
  } catch {
    return false
  }
}

const safeExternalUrl = computed(() => isSafeExternalUrl(props.url) ? props.url : '')

/**
 * Twitter 공식 embed iframe 주소
 * @returns {string}
 */
const tweetEmbedUrl = computed(() => {
  if (!tweetId.value) {
    return ''
  }

  const twitterTheme = theme.value === 'dark' ? 'dark' : 'light'

  return `https://platform.twitter.com/embed/Tweet.html?id=${encodeURIComponent(tweetId.value)}&theme=${twitterTheme}&dnt=true`
})

/**
 * Mastodon embed iframe에 실제 콘텐츠 높이 계산을 요청한다.
 * @returns {void}
 */
const requestMastodonEmbedHeight = () => {
  if (!mastodonIframeRef.value?.contentWindow || !mastodonEmbedId.value) {
    return
  }

  mastodonIframeRef.value.contentWindow.postMessage({
    type: 'setHeight',
    id: mastodonEmbedId.value
  }, '*')
}

/**
 * Mastodon embed 높이 응답을 반영한다.
 * @param {MessageEvent} event - iframe 메시지 이벤트
 * @returns {void}
 */
const handleMastodonEmbedMessage = (event) => {
  const data = event.data || {}

  if (
    !mastodonIframeRef.value ||
    event.source !== mastodonIframeRef.value.contentWindow ||
    typeof data !== 'object' ||
    data.type !== 'setHeight' ||
    data.id !== mastodonEmbedId.value ||
    typeof data.height !== 'number'
  ) {
    return
  }

  try {
    const expectedOrigin = new URL(mastodonEmbedUrl.value).origin

    if (event.origin !== expectedOrigin) {
      return
    }
  } catch {
    return
  }

  mastodonEmbedHeight.value = Math.max(320, Math.ceil(data.height))
}

onMounted(() => {
  mastodonEmbedId.value = Math.floor(Math.random() * 1000000000) + 1
  window.addEventListener('message', handleMastodonEmbedMessage)
})

onBeforeUnmount(() => {
  window.removeEventListener('message', handleMastodonEmbedMessage)
})

watch(mastodonEmbedUrl, () => {
  mastodonEmbedHeight.value = 640
  requestMastodonEmbedHeight()
})
</script>

<template>
  <div
    class="prose-embed prose-embed-card my-8 overflow-hidden rounded-[10px]"
    :class="tweetEmbedUrl ? 'mx-auto max-w-[550px]' : mastodonEmbedUrl ? 'mx-auto max-w-[560px] border border-[var(--site-line)] bg-[var(--site-panel)]' : 'border border-[var(--site-line)] bg-[var(--site-panel)]'"
  >
    <iframe
      v-if="youtubeEmbedUrl"
      class="prose-embed__frame aspect-video w-full"
      :src="youtubeEmbedUrl"
      title="Embedded video"
      loading="lazy"
      allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
      allowfullscreen
    />
    <iframe
      v-else-if="tweetEmbedUrl"
      :key="tweetEmbedUrl"
      class="prose-embed__tweet block min-h-[560px] w-full border-0 sm:min-h-[620px]"
      :src="tweetEmbedUrl"
      title="Embedded post"
      loading="lazy"
    />
    <iframe
      v-else-if="mastodonEmbedUrl"
      :key="mastodonEmbedUrl"
      ref="mastodonIframeRef"
      class="prose-embed__mastodon block w-full border-0"
      :src="mastodonEmbedUrl"
      :height="mastodonEmbedHeight"
      title="Embedded Mastodon post"
      allow="fullscreen"
      loading="lazy"
      sandbox="allow-scripts allow-same-origin allow-popups"
      scrolling="no"
      @load="requestMastodonEmbedHeight"
    />
    <a
      v-else-if="safeExternalUrl"
      class="prose-embed__link block p-5 text-sm font-semibold text-[var(--site-text)] hover:opacity-70"
      :href="safeExternalUrl"
      target="_blank"
      rel="noreferrer"
    >
      {{ url }}
    </a>
    <p v-else class="prose-embed__invalid p-5 text-sm font-semibold text-[var(--site-muted)]">
      지원하지 않는 임베드 URL입니다.
    </p>
  </div>
</template>
