<script setup lang="ts">
/**
 * 单张 PPT 幻灯片渲染（16:9，分步揭示，图文/视频自动排版）
 */
import { computed, ref, watch } from 'vue'
import type { PptSlide, RenderUnit } from '../../utils/articlePptSlides'
import {
  getSlideRenderUnits,
  isStandaloneMediaBlock,
  resolveImageUrl,
} from '../../utils/articlePptSlides'
import type { SlideDirection } from '../../composables/usePptPresentation'
import '../../styles/pptPreview.css'

interface Props {
  slide: PptSlide
  compact?: boolean
  /** 分步揭示：0=仅标题居中；Infinity=显示全部（缩略图） */
  visibleStep?: number
  slideDirection?: SlideDirection
  templateVars?: Record<string, string>
  revealTransition?: string
}

const props = withDefaults(defineProps<Props>(), {
  compact: false,
  visibleStep: Number.POSITIVE_INFINITY,
  slideDirection: 'none',
  templateVars: () => ({}),
  revealTransition: 'ppt-reveal',
})

type ImageLoadState = 'initial' | 'noReferrer' | 'failed'

const imageLoadStates = ref<Map<string, ImageLoadState>>(new Map())
const videoRefs = ref<Map<number, HTMLVideoElement>>(new Map())

const showFull = computed(() => !Number.isFinite(props.visibleStep))

const titleMetaLines = computed(() => {
  if (props.slide.kind !== 'title') return []
  const lines = [
    props.slide.date,
    props.slide.categoryName || '未分类',
    props.slide.userName ? `作者：${props.slide.userName}` : '',
    props.slide.tagNames.length ? `标签：${props.slide.tagNames.join(' · ')}` : '',
  ]
  return lines.filter(Boolean) as string[]
})

const allRenderUnits = computed((): RenderUnit[] => {
  if (props.slide.kind !== 'section') return []
  return getSlideRenderUnits(props.slide)
})

const visibleUnits = computed(() => {
  if (showFull.value) return allRenderUnits.value
  if (props.visibleStep <= 0) return []
  return allRenderUnits.value.slice(0, props.visibleStep)
})

const isTitleFocus = computed(() => {
  if (props.slide.kind === 'title') return props.visibleStep === 0
  return !showFull.value && props.visibleStep === 0
})

const showTitleMeta = computed(() => {
  if (props.slide.kind !== 'title') return false
  return showFull.value || props.visibleStep >= 1
})

const setVideoRef = (idx: number, el: HTMLVideoElement | null) => {
  if (el) videoRefs.value.set(idx, el)
  else videoRefs.value.delete(idx)
}

watch(
  () => props.visibleStep,
  () => {
    videoRefs.value.forEach((video) => {
      if (!video.paused) video.pause()
    })
  }
)

const getImageLoadState = (src: string): ImageLoadState =>
  imageLoadStates.value.get(src) ?? 'initial'

const useNoReferrerPolicy = (src: string) => getImageLoadState(src) === 'initial'

const isImageFailed = (src: string) => getImageLoadState(src) === 'failed'

const imageKey = (src: string) => `${src}:${getImageLoadState(src)}`

const onImageError = (src: string) => {
  const next = new Map(imageLoadStates.value)
  const state = next.get(src) ?? 'initial'
  if (state === 'initial') {
    next.set(src, 'noReferrer')
  } else {
    next.set(src, 'failed')
  }
  imageLoadStates.value = next
}

const isHeroMedia = (unit: RenderUnit) => {
  if (props.compact || unit.kind !== 'single') return false
  return isStandaloneMediaBlock(unit.block)
}

const isUnitVisible = (unitIdx: number) => showFull.value || unitIdx < props.visibleStep

const sectionTitleParts = computed(() => {
  if (props.slide.kind !== 'section') return { parent: '', title: '' }
  const parts = props.slide.title.split(' · ')
  if (parts.length >= 2) {
    return {
      parent: parts.slice(0, -1).join(' · '),
      title: parts[parts.length - 1] ?? props.slide.title,
    }
  }
  return { parent: '', title: props.slide.title }
})

const stopMediaEvent = (e: Event) => {
  e.stopPropagation()
}
</script>

<template>
  <div class="ppt-preview-root" :style="templateVars">
    <!-- 封面 -->
    <div
      v-if="slide.kind === 'title'"
      class="ppt-slide-canvas ppt-slide-canvas--title"
      :class="{ 'ppt-slide-canvas--focus-title': isTitleFocus }"
    >
      <h1 class="ppt-slide-title-main">{{ slide.title }}</h1>
      <Transition :name="revealTransition">
        <div v-if="showTitleMeta && titleMetaLines.length" class="ppt-slide-title-meta">
          <div v-for="(line, idx) in titleMetaLines" :key="idx">{{ line }}</div>
        </div>
      </Transition>
    </div>

    <!-- 章节页 -->
    <div
      v-else
      class="ppt-slide-canvas"
      :class="{
        'ppt-slide-canvas--focus-title': isTitleFocus,
        'ppt-slide-canvas--has-content': !isTitleFocus,
      }"
    >
      <header
        class="ppt-slide-section-header"
        :class="{ 'ppt-slide-section-header--top': !isTitleFocus }"
      >
        <p v-if="sectionTitleParts.parent" class="ppt-slide-section-parent">
          {{ sectionTitleParts.parent }}
        </p>
        <h2 class="ppt-slide-section-title">{{ sectionTitleParts.title }}</h2>
        <Transition :name="revealTransition">
          <div v-if="!isTitleFocus" class="ppt-slide-section-accent"></div>
        </Transition>
      </header>

      <div v-if="!isTitleFocus" class="ppt-slide-body">
        <template v-for="(unit, idx) in visibleUnits" :key="idx">
          <Transition :name="revealTransition">
            <div
              v-if="isUnitVisible(idx)"
              class="ppt-block-reveal"
              :class="{ 'ppt-block-reveal--hero': isHeroMedia(unit) }"
            >
              <template v-if="unit.kind === 'single'">
                <h1 v-if="unit.block.type === 'h1'" class="ppt-block-h1">{{ unit.block.text }}</h1>

                <h2 v-else-if="unit.block.type === 'h2'" class="ppt-block-h2">{{ unit.block.text }}</h2>

                <h3 v-else-if="unit.block.type === 'h3'" class="ppt-block-h3">{{ unit.block.text }}</h3>

                <p v-else-if="unit.block.type === 'paragraph'" class="ppt-block-paragraph">
                  {{ unit.block.text }}
                </p>

                <div
                  v-else-if="unit.block.type === 'image'"
                  class="ppt-block-image-wrap"
                  :class="{ 'ppt-block-image-wrap--hero': isHeroMedia(unit) }"
                  @click.stop
                  @pointerdown.stop
                >
                  <img
                    v-if="!isImageFailed(unit.block.src)"
                    :key="imageKey(unit.block.src)"
                    :src="resolveImageUrl(unit.block.src)"
                    :alt="unit.block.alt"
                    class="ppt-block-image"
                    :referrerpolicy="useNoReferrerPolicy(unit.block.src) ? 'no-referrer' : undefined"
                    loading="eager"
                    @error="onImageError(unit.block.src)"
                    @click.stop
                    @pointerdown.stop
                  />
                  <div v-else class="ppt-block-image-fallback">[图片: {{ unit.block.alt }}]</div>
                </div>

                <div
                  v-else-if="unit.block.type === 'video'"
                  class="ppt-block-video-wrap"
                  :class="{ 'ppt-block-video-wrap--hero': isHeroMedia(unit) }"
                  @click.stop
                  @pointerdown.stop
                  @dblclick.stop
                >
                  <video
                    :ref="(el) => setVideoRef(idx, el as HTMLVideoElement | null)"
                    controls
                    playsinline
                    webkit-playsinline
                    preload="metadata"
                    class="ppt-block-video"
                    :src="resolveImageUrl(unit.block.src)"
                    :poster="unit.block.poster ? resolveImageUrl(unit.block.poster) : undefined"
                    @click.stop="stopMediaEvent"
                    @pointerdown.stop="stopMediaEvent"
                    @dblclick.stop="stopMediaEvent"
                  >
                    <source
                      v-if="unit.block.mimeType"
                      :src="resolveImageUrl(unit.block.src)"
                      :type="unit.block.mimeType"
                    />
                  </video>
                </div>

                <div v-else-if="unit.block.type === 'code'" class="ppt-block-code">
                  <div class="ppt-block-code-label">{{ unit.block.lang?.toUpperCase() || 'CODE' }}</div>
                  <pre class="ppt-block-code-content">{{ unit.block.text }}</pre>
                </div>

                <ul
                  v-else-if="unit.block.type === 'list'"
                  class="ppt-block-list"
                  :class="{ 'ppt-block-list--ordered': unit.block.ordered }"
                >
                  <li v-for="(item, itemIdx) in unit.block.items" :key="itemIdx">{{ item }}</li>
                </ul>

                <p v-else-if="unit.block.type === 'placeholder'" class="ppt-block-placeholder">
                  {{ unit.block.text }}
                </p>
              </template>
            </div>
          </Transition>
        </template>
      </div>
    </div>
  </div>
</template>
