<template>
  <Comp @handle-prize-result="handlePrizeResult" />
  <el-button @click="testAnimation">测试动画</el-button>
  <Teleport to="body">
    <Transition name="result">
      <div
        v-if="showResult"
        class="fixed inset-0 z-[9999] bg-black/70 backdrop-blur flex flex-col items-center justify-center"
      >
        <template v-if="isThanksPrize">
          <div ref="blessingWrapRef" class="blessing-wrap">
            <div ref="blessingGlowRef" class="blessing-glow" aria-hidden="true" />

            <div ref="blessingFanSceneRef" class="blessing-fan-scene">
              <div ref="blessingTagSlotRef" class="blessing-tag-slot" aria-hidden="true" />

              <div ref="blessingFanRef" class="blessing-fan" aria-hidden="true">
                <div
                  v-for="(text, index) in fanStackTexts"
                  :key="`${text}-${index}`"
                  :ref="setFanItemRef"
                  class="blessing-fan-item"
                  :style="fanItemStyle(index, fanStackTexts.length)"
                  :data-angle="fanItemMotion(index, fanStackTexts.length).angle"
                  :data-x="fanItemMotion(index, fanStackTexts.length).xOffset"
                  :data-y="fanItemMotion(index, fanStackTexts.length).yOffset"
                  :data-scale="fanItemMotion(index, fanStackTexts.length).scale"
                >
                  <div class="blessing-fan-face">
                    <span class="blessing-fan-emoji">{{ fanEmoji(index) }}</span>
                    <div class="blessing-fan-text">
                      <span
                        v-for="(char, charIndex) in text.split('')"
                        :key="`${char}-${charIndex}`"
                        class="blessing-fan-char"
                      >
                        {{ char }}
                      </span>
                    </div>
                  </div>
                </div>
              </div>

              <div ref="blessingTagStageRef" class="blessing-tag-stage">
                <div ref="blessingTagRef" class="blessing-tag">
                  <div ref="blessingTagTopRef" class="blessing-tag-top">
                    <span class="blessing-tag-hole" />
                    <span class="blessing-tag-knot" />
                  </div>
                  <div class="blessing-tag-headline">LUCKY</div>
                  <div class="blessing-tag-divider" />
                  <div class="blessing-text" :class="{ 'is-long-text': blessingChars.length > 10 }">
                    <span
                      v-for="(char, index) in blessingChars"
                      :key="`${char}-${index}`"
                      :ref="setBlessingCharRef"
                    >
                      {{ char }}
                    </span>
                  </div>
                  <div ref="blessingSealRef" class="blessing-tag-seal">GOOD DAY</div>
                </div>
              </div>
            </div>
          </div>

          <div ref="blessingCaptionRef" class="blessing-caption">
            <p class="blessing-caption-title">今日签语</p>
            <p class="blessing-caption-subtitle">抽一支好运电子签，把今天过得亮一点</p>
          </div>

          <el-button class="mt-8" type="primary" @click="showResult = false">知道了</el-button>
        </template>

        <template v-else>
          <img :src="currentPrize?.prizeImageUrl" class="w-60 h-60 animate-pop" />
          <div class="mt-6 text-white text-3xl font-bold">
            恭喜获得 {{ currentPrize?.prizeName }}
          </div>
          <el-button class="mt-8" type="primary" @click="showResult = false">知道了</el-button>
        </template>
      </div>
    </Transition>
  </Teleport>
</template>

<script setup lang="ts">
import type { ComponentPublicInstance } from 'vue'
import gsap from 'gsap'

import Comp from './components/LuckyWheel.vue'
import type { LuckWheelResultDto } from '@/api'

const showResult = ref(false)
const currentPrize = ref<LuckWheelResultDto | null>(null)

const blessingWrapRef = ref<HTMLElement | null>(null)
const blessingGlowRef = ref<HTMLElement | null>(null)
const blessingFanSceneRef = ref<HTMLElement | null>(null)
const blessingFanRef = ref<HTMLElement | null>(null)
const blessingTagSlotRef = ref<HTMLElement | null>(null)
const blessingTagStageRef = ref<HTMLElement | null>(null)
const blessingTagRef = ref<HTMLElement | null>(null)
const blessingTagTopRef = ref<HTMLElement | null>(null)
const blessingSealRef = ref<HTMLElement | null>(null)
const blessingCaptionRef = ref<HTMLElement | null>(null)
const fanItemRefs = ref<HTMLElement[]>([])
const blessingCharRefs = ref<HTMLElement[]>([])

let blessingTimeline: gsap.core.Timeline | null = null
let floatTween: gsap.core.Tween | null = null

const blessingEmojiList = ['·ᴗ·', '^_^', '◕‿◕', '✦ᴗ✦', '˶ᵔ ᵕ ᵔ˶', '•‿•', '๑´ڡ`๑']

const isThanksPrize = computed(() => currentPrize.value?.prizeId == null)
const blessingChars = computed(() => {
  const text = (currentPrize.value?.blessingText || '').trim()
  return text ? text.split('') : ['好', '运', '常', '在']
})

const fanPreviewTexts = [
  '钱包鼓鼓 烦恼全无',
  '年年有鱼摸 岁岁越平安',
  '干啥啥都顺 吃嘛嘛都香',
  '狂吃不胖 熬夜不秃',
  '凡是发生皆有利于我',
  '内核强大 所向披靡',
  '逆风如解意 税后十个亿',
  '恶缘退散 好人靠近',
  '发发发 发量爆棚',
  '桃旺旺 钱财多多',
  '拒绝精神内耗 本人配享太庙',
]

const fanStackTexts = computed(() => {
  const texts = [...fanPreviewTexts]
  const selectedText = (currentPrize.value?.blessingText || '').trim()

  if (!isThanksPrize.value || !selectedText) {
    return texts
  }

  const selectedIndex = texts.indexOf(selectedText)
  if (selectedIndex === -1) {
    return texts
  }

  const [selectedItem] = texts.splice(selectedIndex, 1)
  const centerIndex = Math.floor(texts.length / 2)
  texts.splice(centerIndex, 0, selectedItem as string)

  return texts
})

const fanItemMotion = (index: number, total: number) => {
  const center = (total - 1) / 2
  const offset = index - center
  const spreadRatio = center === 0 ? 0 : offset / center
  const angle = spreadRatio * 30
  const xOffset = offset * 40
  const yOffset = Math.abs(offset) * 4
  const scale = 1 - Math.abs(spreadRatio) * 0.08

  return {
    angle,
    xOffset,
    yOffset,
    scale,
  }
}

const fanItemStyle = (index: number, total: number) => {
  const { angle, xOffset, yOffset, scale } = fanItemMotion(index, total)

  return {
    '--angle': `${angle}deg`,
    '--x-offset': `${xOffset}px`,
    '--y-offset': `${yOffset}px`,
    '--scale': `${scale}`,
  }
}

const fanEmoji = (index: number) => blessingEmojiList[index % blessingEmojiList.length]

const setFanItemRef = (el: Element | ComponentPublicInstance | null) => {
  if (el instanceof HTMLElement) {
    fanItemRefs.value.push(el)
  }
}

const setBlessingCharRef = (el: Element | ComponentPublicInstance | null) => {
  if (el instanceof HTMLElement) {
    blessingCharRefs.value.push(el)
  }
}

const clearBlessingAnimation = () => {
  blessingTimeline?.kill()
  blessingTimeline = null
  floatTween?.kill()
  floatTween = null
}

const startFloating = () => {
  if (!blessingTagRef.value) {
    return
  }

  floatTween?.kill()
  floatTween = gsap.to(blessingTagRef.value, {
    y: -10,
    rotation: 0.8,
    duration: 2.6,
    repeat: -1,
    yoyo: true,
    ease: 'sine.inOut',
  })
}

const playBlessingAnimation = () => {
  const wrap = blessingWrapRef.value
  const glow = blessingGlowRef.value
  const scene = blessingFanSceneRef.value
  const fan = blessingFanRef.value
  const slot = blessingTagSlotRef.value
  const tag = blessingTagRef.value
  const tagTop = blessingTagTopRef.value
  const seal = blessingSealRef.value
  const caption = blessingCaptionRef.value
  const fanItems = [...fanItemRefs.value]
  const chars = [...blessingCharRefs.value]

  if (
    !wrap ||
    !glow ||
    !scene ||
    !fan ||
    !slot ||
    !tag ||
    !tagTop ||
    !seal ||
    !caption ||
    !fanItems.length
  ) {
    return
  }

  clearBlessingAnimation()

  gsap.set(wrap, { transformPerspective: 1400 })
  gsap.set(scene, { y: 20, opacity: 0 })
  gsap.set(glow, { opacity: 0, scale: 0.78 })
  gsap.set(slot, { opacity: 0, scaleX: 0.72, transformOrigin: '50% 50%' })
  gsap.set(fan, { y: 34, opacity: 0 })
  gsap.set(blessingTagStageRef.value, {
    opacity: 0,
    y: 26,
    filter: 'blur(8px)',
    transformOrigin: '50% 100%',
  })
  gsap.set(fanItems, {
    opacity: 0,
    y: 78,
    rotate: (_index, target) => Number(target.getAttribute('data-angle') || 0) * 0.4,
    scale: (_index, target) => Number(target.getAttribute('data-scale') || 1) - 0.08,
    transformOrigin: '50% 100%',
  })
  gsap.set(tag, {
    opacity: 0,
    y: 356,
    scale: 0.94,
    rotation: 0,
    transformOrigin: '50% 100%',
    filter: 'blur(10px)',
  })
  gsap.set(tagTop, { opacity: 0.65 })
  gsap.set(chars, { opacity: 0, y: 10 })
  gsap.set(seal, { opacity: 0, y: 8 })
  gsap.set(caption, { opacity: 0, y: 20 })

  blessingTimeline = gsap.timeline({
    defaults: { ease: 'power2.out' },
    onComplete: () => {
      startFloating()
    },
  })

  blessingTimeline
    .to(scene, { opacity: 1, y: 0, duration: 0.45 })
    .to(glow, { opacity: 0.95, scale: 1, duration: 0.7, ease: 'sine.out' }, '<')
    .to(slot, { opacity: 1, scaleX: 1, duration: 0.52 }, '-=0.3')
    .to(fan, { opacity: 1, y: 0, duration: 0.4 }, '<')
    .to(
      fanItems,
      {
        opacity: 1,
        y: (_index, target) => Number(target.getAttribute('data-y') || 0),
        rotate: (_index, target) => Number(target.getAttribute('data-angle') || 0),
        scale: (_index, target) => Number(target.getAttribute('data-scale') || 1),
        duration: 0.9,
        ease: 'back.out(1.08)',
        stagger: {
          each: 0.055,
          from: 'center',
        },
      },
      '+=0.02',
    )
    .to(
      fanItems,
      {
        x: (_index, target) => Number(target.getAttribute('data-x') || 0) * 0.1,
        duration: 0.32,
        ease: 'power1.inOut',
        stagger: {
          each: 0.02,
          from: 'edges',
        },
        yoyo: true,
        repeat: 1,
      },
      '+=0.26',
    )
    .to(
      blessingTagStageRef.value,
      {
        opacity: 1,
        y: 0,
        filter: 'blur(0px)',
        duration: 0.48,
        ease: 'power2.out',
      },
      '+=0.02',
    )
    .to(
      tag,
      {
        y: 272,
        opacity: 0.5,
        scale: 0.97,
        filter: 'blur(6px)',
        duration: 0.42,
        ease: 'power2.out',
      },
      '<',
    )
    .to(
      tag,
      {
        y: 44,
        opacity: 1,
        scale: 1.01,
        filter: 'blur(1px)',
        duration: 1.08,
        ease: 'power3.out',
      },
      '+=0.1',
    )
    .to(
      tag,
      {
        y: 18,
        rotation: -1.4,
        scale: 1.015,
        filter: 'blur(0px)',
        duration: 0.3,
        ease: 'power2.out',
      },
      '-=0.18',
    )
    .to(
      tag,
      {
        y: 24,
        rotation: 0.4,
        scale: 1,
        filter: 'blur(0px)',
        duration: 0.32,
        ease: 'back.out(1.5)',
      },
      '+=0.02',
    )
    .to(tagTop, { opacity: 1, duration: 0.22 }, '<')
    .to(
      chars,
      {
        opacity: 1,
        y: 0,
        duration: 0.34,
        stagger: 0.05,
      },
      '-=0.08',
    )
    .to(
      seal,
      {
        opacity: 1,
        y: 0,
        duration: 0.28,
      },
      '-=0.12',
    )
    .to(
      caption,
      {
        opacity: 1,
        y: 0,
        duration: 0.42,
      },
      '-=0.02',
    )
}

watch(
  () => showResult.value,
  async (visible) => {
    if (!visible) {
      clearBlessingAnimation()
      return
    }

    if (!isThanksPrize.value) {
      clearBlessingAnimation()
      return
    }

    await nextTick()
    playBlessingAnimation()
  },
)

onBeforeUpdate(() => {
  fanItemRefs.value = []
  blessingCharRefs.value = []
})

onBeforeUnmount(() => {
  clearBlessingAnimation()
})

const handlePrizeResult = (prize: LuckWheelResultDto) => {
  currentPrize.value = prize
  showResult.value = true
}

const testAnimation = () => {
  showResult.value = true
  currentPrize.value = {
    prizeId: null,
    prizeImageUrl: '',
    prizeName: '谢谢参与',
    blessingText: '拒绝精神内耗 本人配享太庙',
    coinCost: 0,
    isWin: false,
  }
}
</script>

<style>
.result-enter-active {
  animation: resultFadeIn 0.42s ease;
}

.result-leave-active {
  animation: resultFadeOut 0.24s ease forwards;
}

@keyframes resultFadeIn {
  from {
    opacity: 0;
    transform: scale(1.03);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes resultFadeOut {
  from {
    opacity: 1;
    transform: scale(1);
  }
  to {
    opacity: 0;
    transform: scale(0.985);
  }
}

.animate-pop {
  animation: pop 0.8s ease-out;
}

@keyframes pop {
  0% {
    transform: scale(0.3);
    opacity: 0;
  }
  60% {
    transform: scale(1.1);
  }
  100% {
    transform: scale(1);
  }
}

.blessing-wrap {
  position: relative;
  width: min(92vw, 520px);
  min-height: min(84vh, 680px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  isolation: isolate;
}

.blessing-glow {
  position: absolute;
  left: 50%;
  bottom: 120px;
  width: 390px;
  height: 390px;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(255, 223, 119, 0.44) 0%,
    rgba(255, 204, 63, 0.14) 45%,
    rgba(255, 204, 63, 0) 76%
  );
  transform: translateX(-50%);
  filter: blur(18px);
  z-index: 0;
  pointer-events: none;
}

.blessing-fan-scene {
  position: relative;
  width: min(92vw, 520px);
  height: 510px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 1;
}

.blessing-fan {
  position: absolute;
  left: 50%;
  bottom: 28px;
  width: min(92vw, 520px);
  height: 408px;
  transform: translateX(-50%);
  z-index: 1;
}

/* .blessing-tag-slot {
  position: absolute;
  left: 50%;
  bottom: 56px;
  width: 138px;
  height: 100px;
  border-radius: 999px 999px 30px 30px;
  background:
    linear-gradient(180deg, rgba(107, 68, 0, 0.34), rgba(107, 68, 0, 0.06)),
    radial-gradient(circle at 50% 0, rgba(255, 233, 165, 0.42), rgba(255, 233, 165, 0) 74%);
  box-shadow:
    inset 0 10px 18px rgba(87, 51, 0, 0.18),
    0 10px 22px rgba(0, 0, 0, 0.16);
  transform: translateX(-50%);
  z-index: 2;
} */

.blessing-tag-stage {
  position: absolute;
  left: 50%;
  bottom: 28px;
  width: 188px;
  height: 550px;
  /* overflow: hidden; */
  transform: translateX(-50%);
  z-index: 2;
}

.blessing-fan-item {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 84px;
  height: 450px;
  transform-origin: center 100%;
  transform: translateX(calc(-50% + var(--x-offset))) translateY(var(--y-offset))
    rotate(var(--angle)) scale(var(--scale));
}

.blessing-fan-face {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 16px 16px 11px 11px;
  background:
    linear-gradient(
      180deg,
      rgba(255, 249, 204, 0.76) 0%,
      rgba(255, 236, 165, 0.16) 12%,
      transparent 18%
    ),
    linear-gradient(180deg, #ffdd68 0%, #f7c529 56%, #ebb116 100%);
  border: 1px solid rgba(123, 79, 0, 0.18);
  box-shadow:
    0 16px 28px rgba(0, 0, 0, 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.45),
    inset 0 -10px 24px rgba(162, 101, 0, 0.12);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px 0 18px;
  overflow: hidden;
}

.blessing-fan-face::before {
  content: '';
  position: absolute;
  inset: 9px;
  border-radius: 11px;
  border: 1px solid rgba(142, 92, 0, 0.14);
  pointer-events: none;
}

.blessing-fan-emoji {
  position: relative;
  z-index: 1;
  color: rgba(94, 53, 0, 0.88);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.5px;
}

.blessing-fan-text {
  position: relative;
  z-index: 1;
  margin-top: 20px;
  color: rgba(96, 53, 0, 0.68);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 1px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.blessing-fan-char {
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.18);
}

.blessing-tag {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 120px;
  min-height: 470px;
  padding: 20px 0 26px;
  border-radius: 18px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.36) 0%, rgba(255, 255, 255, 0) 14%),
    linear-gradient(180deg, #ffe783 0%, #ffc93f 48%, #f0b416 100%);
  border: 1px solid rgba(118, 74, 0, 0.18);
  box-shadow:
    0 24px 54px rgba(0, 0, 0, 0.24),
    0 12px 36px rgba(255, 205, 63, 0.24),
    inset 0 1px 0 rgba(255, 255, 255, 0.52),
    inset 0 -16px 24px rgba(171, 100, 0, 0.12);
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow: hidden;
  transform: translateX(-50%);
  z-index: 3;
}

.blessing-tag::before {
  content: '';
  position: absolute;
  inset: 10px 9px;
  border-radius: 14px;
  border: 1px solid rgba(125, 81, 0, 0.16);
  pointer-events: none;
}

.blessing-tag::after {
  content: '';
  position: absolute;
  left: 16px;
  right: 16px;
  top: 12px;
  height: 92px;
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.24), rgba(255, 255, 255, 0));
  filter: blur(4px);
  pointer-events: none;
}

.blessing-tag-top {
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.blessing-tag-hole {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: rgba(116, 72, 0, 0.28);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.14);
}

.blessing-tag-knot {
  width: 2px;
  height: 26px;
  margin-top: 4px;
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(125, 82, 0, 0.72), rgba(125, 82, 0, 0.2));
}

.blessing-tag-headline {
  margin-top: 6px;
  color: rgba(120, 69, 0, 0.56);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 3px;
}

.blessing-tag-divider {
  width: 56px;
  height: 1px;
  margin: 12px 0 18px;
  background: linear-gradient(
    90deg,
    rgba(133, 86, 0, 0),
    rgba(133, 86, 0, 0.46),
    rgba(133, 86, 0, 0)
  );
}

.blessing-text {
  color: #603500;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: 2px;
  line-height: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  flex: 1;
  padding: 0 10px;
}

.blessing-text.is-long-text {
  font-size: 23px;
  gap: 8px;
}

.blessing-text > span {
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.24);
}

.blessing-tag-seal {
  margin-top: 18px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(144, 41, 22, 0.1);
  color: rgba(123, 44, 28, 0.68);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 1.8px;
}

.blessing-caption {
  margin-top: 34px;
  text-align: center;
}

.blessing-caption-title {
  margin: 0;
  color: rgba(255, 255, 255, 0.96);
  font-size: 34px;
  font-weight: 700;
  letter-spacing: 3px;
}

.blessing-caption-subtitle {
  margin: 12px 0 0;
  color: rgba(255, 255, 255, 0.72);
  font-size: 15px;
  letter-spacing: 1px;
}

@media (max-width: 640px) {
  .blessing-wrap {
    width: min(94vw, 390px);
    min-height: 560px;
  }

  .blessing-fan-scene {
    width: min(94vw, 390px);
    height: 434px;
  }

  .blessing-fan {
    width: min(94vw, 390px);
    height: 350px;
    bottom: 24px;
  }

  .blessing-fan-item {
    width: 70px;
    height: 304px;
  }

  .blessing-tag-stage {
    width: 160px;
    height: 484px;
    bottom: 24px;
  }

  .blessing-tag {
    width: 118px;
    min-height: 410px;
  }

  .blessing-fan-text {
    font-size: 14px;
    gap: 6px;
  }

  .blessing-text {
    font-size: 23px;
    gap: 8px;
  }

  .blessing-text.is-long-text {
    font-size: 20px;
    gap: 7px;
  }

  .blessing-caption-title {
    font-size: 28px;
  }
}
</style>
