<script setup lang="ts">
import { usePreferredReducedMotion } from '@vueuse/core'
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import type { Memory } from '../api/client'
import WashiTape from './WashiTape.vue'

const props = defineProps<{
  memory: Memory
  index: number
  /** 插画页用：缩小照片区、隐藏正文摘要，避免与长文抢版面 */
  compact?: boolean
}>()

const root = ref<HTMLElement | null>(null)
const visible = ref(false)
const loadedSet = ref<Record<number, boolean>>({})
const activeIndex = ref(0)
const reduceMotion = usePreferredReducedMotion()

let observer: IntersectionObserver | null = null
let slideTimer: ReturnType<typeof setInterval> | undefined

const rotateClass = computed(() =>
  props.index % 2 === 0 ? '-rotate-1 sm:rotate-1' : 'rotate-1 sm:-rotate-1',
)

/** 主图 + 附加图；兼容旧数据仅有 image */
const imageUrls = computed(() => {
  const m = props.memory
  if (m.images?.length) return m.images
  if (m.image) return [m.image]
  return []
})

const hasMultiple = computed(() => imageUrls.value.length > 1)

const excerpt = computed(() => {
  const line =
    props.memory.main_text?.split('\n').find((l) => l.trim())?.trim() ?? ''
  const plain = line.replace(/\*\*/g, '').replace(/[#>`]/g, '')
  if (!plain) return ''
  return plain.length > 140 ? `${plain.slice(0, 140)}…` : plain
})

function onImgLoad(i: number) {
  loadedSet.value = { ...loadedSet.value, [i]: true }
}

function stackStyle(i: number) {
  const urls = imageUrls.value
  const n = urls.length
  if (n <= 1) {
    return {
      zIndex: 2,
      transform: 'translate(0,0) scale(1) rotate(0deg)',
      opacity: 1,
    }
  }
  const flat = reduceMotion.value === 'reduce'
  const rel = (i - activeIndex.value + n) % n
  const z = 20 - rel
  if (flat) {
    return {
      zIndex: z,
      transform: 'translate(0,0) scale(1)',
      opacity: rel === 0 ? 1 : 0,
    }
  }
  const off = rel * 6
  const scale = 1 - rel * 0.028
  const rot = rel * 0.9 - (n - 1) * 0.35
  const opacity = rel === 0 ? 1 : Math.max(0.32, 0.78 - rel * 0.12)
  return {
    zIndex: z,
    transform: `translate(${off}px, ${off * 0.65}px) scale(${scale}) rotate(${rot}deg)`,
    opacity,
  }
}

function startCarousel() {
  if (slideTimer) clearInterval(slideTimer)
  slideTimer = undefined
  if (reduceMotion.value === 'reduce') return
  if (!hasMultiple.value || !visible.value) return
  slideTimer = setInterval(() => {
    const n = imageUrls.value.length
    if (n <= 1) return
    activeIndex.value = (activeIndex.value + 1) % n
  }, 3000)
}

watch(
  () => props.memory.id,
  () => {
    activeIndex.value = 0
    loadedSet.value = {}
    startCarousel()
  },
)

watch([hasMultiple, visible], () => {
  startCarousel()
})

watch(imageUrls, () => {
  activeIndex.value = 0
  startCarousel()
})

watch(reduceMotion, () => {
  activeIndex.value = 0
  startCarousel()
})

onMounted(() => {
  observer = new IntersectionObserver(
    (entries) => {
      for (const e of entries) {
        if (e.isIntersecting) {
          visible.value = true
          observer?.disconnect()
          observer = null
          startCarousel()
        }
      }
    },
    { root: null, threshold: 0.12 },
  )
  if (root.value) observer.observe(root.value)
})

onBeforeUnmount(() => {
  observer?.disconnect()
  if (slideTimer) clearInterval(slideTimer)
})
</script>

<template>
  <article
    ref="root"
    class="relative mx-auto transition-all duration-700 ease-out"
    :class="[
      compact ? 'max-w-[min(100%,260px)]' : 'max-w-[min(100%,320px)]',
      visible
        ? `translate-y-0 opacity-100 ${rotateClass}`
        : 'translate-y-6 opacity-0 rotate-0',
    ]"
  >
    <WashiTape
      class="-left-2 top-3"
      :tone="index % 3 === 0 ? 'pink' : index % 3 === 1 ? 'mint' : 'cream'"
      rotate="-rotate-12"
      width-class="w-20"
    />
    <div
      class="nepal-deckle-frame relative bg-white shadow-[0_14px_44px_-10px_rgba(30,53,80,0.28)] ring-1 ring-lokta-deep/10"
      :class="compact ? 'p-2.5 pb-9' : 'p-3 pb-16'"
    >
      <div
        class="pointer-events-none absolute inset-2 nepal-fiber opacity-[0.18] mix-blend-multiply"
        aria-hidden="true"
      />
      <div
        class="relative overflow-visible rounded-sm bg-paper-deep"
        :class="
          compact
            ? 'mx-auto h-[min(40vw,188px)] w-[min(32vw,150px)] sm:h-[210px] sm:w-[168px]'
            : 'aspect-[4/5]'
        "
      >
        <template v-if="imageUrls.length">
          <div
            v-if="!loadedSet[activeIndex] && imageUrls[activeIndex]"
            class="absolute inset-0 z-[5] animate-pulse bg-gradient-to-br from-paper-deep to-paper-shadow"
          />
          <div
            v-for="(url, i) in imageUrls"
            :key="`${url}-${i}`"
            class="absolute inset-0 overflow-hidden rounded-sm shadow-[2px_3px_0_rgba(30,53,80,0.12)] ring-1 ring-black/5 transition-[transform,opacity] duration-500 ease-out"
            :style="stackStyle(i)"
          >
            <img
              :src="url"
              :alt="`${memory.event_title} · ${i + 1}/${imageUrls.length}`"
              loading="lazy"
              decoding="async"
              class="h-full w-full object-cover transition-opacity duration-500"
              :class="loadedSet[i] ? 'opacity-100' : 'opacity-0'"
              @load="onImgLoad(i)"
            />
          </div>
          <div
            v-if="hasMultiple"
            class="pointer-events-none absolute bottom-2 right-2 z-[25] rounded-full bg-black/45 px-2 py-0.5 font-mono text-[10px] text-white tabular-nums"
            aria-hidden="true"
          >
            {{ activeIndex + 1 }} / {{ imageUrls.length }}
          </div>
        </template>
        <div
          v-else
          class="flex h-full items-center justify-center text-ink-muted text-sm"
          :class="compact ? 'min-h-[120px]' : 'min-h-[200px]'"
        >
          待上传照片
        </div>
      </div>
      <p
        class="font-hand leading-tight text-ink px-1 text-center"
        :class="compact ? 'mt-2 text-lg sm:text-xl' : 'mt-3 text-2xl sm:text-3xl'"
      >
        {{ memory.event_title }}
      </p>
      <p
        v-if="excerpt && !compact"
        class="mt-3 px-1 text-center text-sm leading-relaxed text-ink-muted"
      >
        {{ excerpt }}
      </p>
    </div>
  </article>
</template>
