<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import type Lenis from 'lenis'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import { useUserStore } from '~/stores/user'

gsap.registerPlugin(ScrollTrigger)

const { t } = useI18n()
const introTitle = 'ArcticNewOne'
const introLetters = introTitle.split('')
const heroPinDistance = 1200
const isIntroActive = ref(true)
const userStore = useUserStore()
const demoUserName = computed(() => userStore.displayName)
const demoUserRole = computed(() => userStore.role)

if (!userStore.isLoggedIn) {
  userStore.loadDemoUser()
}

// 让首屏入场的锁滚动在 SSR/首帧就生效，避免等 onMounted 才隐藏滚动条。
useHead({
  htmlAttrs: {
    class: computed(() => (isIntroActive.value ? 'is-home-intro-active' : undefined)),
  },
})

// 背景图是装饰层，左右两列共用同一套模板，只通过 side 区分位置和速度。
const heroGalleryColumns = [
  {
    side: 'left',
    images: [
      'https://picsum.photos/id/1018/900/1200',
      'https://picsum.photos/id/1015/900/1200',
      'https://picsum.photos/id/1024/900/1200',
      'https://picsum.photos/id/1039/900/1200',
    ],
  },
  {
    side: 'right',
    images: [
      'https://picsum.photos/id/1043/900/1200',
      'https://picsum.photos/id/1050/900/1200',
      'https://picsum.photos/id/1067/900/1200',
      'https://picsum.photos/id/1074/900/1200',
    ],
  },
] as const
const pageRef = ref<HTMLElement | null>(null)
const loaderRef = ref<HTMLElement | null>(null)
const loaderBarRef = ref<HTMLElement | null>(null)
const introRef = ref<HTMLElement | null>(null)
const introTitleRef = ref<HTMLElement | null>(null)
const heroStageRef = ref<HTMLElement | null>(null)
const heroRef = ref<HTMLElement | null>(null)
const heroPanelRef = ref<HTMLElement | null>(null)
let heroAnimationContext: gsap.Context | null = null
let removeLoadListener: (() => void) | null = null
let restorePageScroll: (() => void) | null = null
let cleanupHeroScroll: (() => void) | null = null
let hasIntroStarted = false
let loaderTween: gsap.core.Tween | null = null
let introTimeline: gsap.core.Timeline | null = null
let introStartTimer: ReturnType<typeof window.setTimeout> | null = null

const setHomeIntroActive = (active: boolean) => {
  isIntroActive.value = active
  document.documentElement.classList.toggle('is-home-intro-active', active)
}

onMounted(() => {
  const pageElement = pageRef.value
  const loaderElement = loaderRef.value
  const loaderBarElement = loaderBarRef.value
  const introElement = introRef.value
  const introTitleElement = introTitleRef.value
  const heroStageElement = heroStageRef.value
  const heroElement = heroRef.value
  const heroPanelElement = heroPanelRef.value

  if (
    !pageElement
    || !loaderElement
    || !loaderBarElement
    || !introElement
    || !introTitleElement
    || !heroStageElement
    || !heroElement
    || !heroPanelElement
  ) {
    return
  }

  const introCharElements = gsap.utils.toArray('.hero-intro-char', introElement)
    const tmElement = introElement.querySelector('.hero-intro-tm')

    if (introCharElements.length === 0 || !tmElement) {
      return
    }

  const { $lenis } = useNuxtApp() as ReturnType<typeof useNuxtApp> & { $lenis?: Lenis }
  const topHeader = document.querySelector<HTMLElement>('.site-header-top')

  heroAnimationContext = gsap.context(() => {
    const pinEndScale = 0.65
    const finalScale = 0.18
    const getExitDistance = () => window.innerHeight

    const setupHeroScroll = () => {
      cleanupHeroScroll?.()

      gsap.set(heroPanelElement, {
        scale: 1,
        force3D: true,
        transformOrigin: 'center center',
      })

      let renderedScale = -1

      const setHeroPanelScale = (scale: number) => {
        if (Math.abs(renderedScale - scale) < 0.001) {
          return
        }

        renderedScale = scale
        gsap.set(heroPanelElement, { scale })
      }

      const updateHeroPanelScale = () => {
        const exitDistance = getExitDistance()
        const totalDistance = heroPinDistance + exitDistance
        // Lenis 存在时只读 Lenis 的平滑滚动值，避免向上滚时 window.scrollY 和 Lenis 值互相打架。
        const currentScroll = $lenis ? $lenis.scroll : window.scrollY
        const currentDistance = gsap.utils.clamp(0, totalDistance, currentScroll - heroStageElement.offsetTop)

        if (currentDistance <= heroPinDistance) {
          setHeroPanelScale(gsap.utils.interpolate(1, pinEndScale, currentDistance / heroPinDistance))
          return
        }

        setHeroPanelScale(
          gsap.utils.interpolate(
            pinEndScale,
            finalScale,
            (currentDistance - heroPinDistance) / exitDistance,
          ),
        )
      }

      // CSS sticky 负责固定；ticker 每帧读取当前滚动距离，避免依赖 scroll/ScrollTrigger 事件链。
      updateHeroPanelScale()
      gsap.ticker.add(updateHeroPanelScale)

      cleanupHeroScroll = () => {
        gsap.ticker.remove(updateHeroPanelScale)
      }
    }

    const playIntro = () => {
      if (hasIntroStarted) {
        return
      }

      hasIntroStarted = true
      removeLoadListener?.()
      removeLoadListener = null
      if (introStartTimer) {
        window.clearTimeout(introStartTimer)
        introStartTimer = null
      }
      loaderTween?.kill()
      loaderTween = null
      introTimeline?.kill()

      introTimeline = gsap.timeline({
        defaults: { ease: 'power3.out' },
        onComplete: () => {
          gsap.set(introElement, { display: 'none' })
          gsap.set(heroElement, { clearProps: 'transform' })
          restorePageScroll?.()
          restorePageScroll = null
          ScrollTrigger.refresh()
          introTimeline = null
        },
      })

      introTimeline
        .to(loaderBarElement, {
          scaleX: 1,
          duration: 0.25,
          ease: 'power2.out',
        })
        .to(loaderElement, {
          yPercent: -100,
          duration: 0.75,
          ease: 'power4.inOut',
        })
        .set(loaderElement, {
          display: 'none',
        })
        .set(introElement, {
          display: 'grid',
          yPercent: 0,
          autoAlpha: 1,
        })
        .set(introTitleElement, {
          y: 0,
          autoAlpha: 1,
        })
        .set(tmElement, {
          autoAlpha: 0,
        })
        .fromTo(introCharElements, {
          yPercent: 110,
          y: 0,
        }, {
          yPercent: 0,
          y: 0,
          duration: 0.75,
          stagger: 0.055,
        })
        .to(tmElement, {
          autoAlpha: 1,
          duration: 0.4,
          ease: 'power2.out',
        })
        .addLabel('introOut', '+=0.35')
        .to(introTitleElement, {
          autoAlpha: 0,
          duration: 1.05,
          ease: 'power4.inOut',
        }, 'introOut')
        .to(introElement, {
          yPercent: -100,
          duration: 1.05,
          ease: 'power4.inOut',
        }, 'introOut')
        .to(heroElement, {
          yPercent: 0,
          duration: 1.05,
          ease: 'power4.inOut',
        }, 'introOut')
      if (topHeader) {
        introTimeline.to(topHeader, {
          yPercent: 0,
          autoAlpha: 1,
          duration: 0.55,
          ease: 'power3.out',
        }, 'introOut+=0.45')
      }
    }

    const releasePageScroll = () => {
      setHomeIntroActive(false)
      $lenis?.start()
    }

    // 首屏入场期间只通过 html class 锁滚动，避免 class 和 inline overflow 两套状态互相覆盖。
    setHomeIntroActive(true)
    $lenis?.scrollTo(0, { immediate: true })
    $lenis?.stop()
    restorePageScroll = releasePageScroll

    // Loading 层先接管首屏；真正的品牌入场等 window.load 完成后再开始。
    gsap.set(loaderElement, { display: 'grid', yPercent: 0, autoAlpha: 1 })
    gsap.set(loaderBarElement, { scaleX: 0.18, transformOrigin: 'left center' })
    gsap.set(introElement, { display: 'grid', yPercent: 0, autoAlpha: 0 })
    gsap.set(introTitleElement, { y: 0, autoAlpha: 1 })
    // 强制清除 CSS 中的 translateY 像素值，避免被 GSAP 解析为固定 y 像素导致一直不可见
    gsap.set(introCharElements, { y: 0, yPercent: 110 })
    gsap.set(tmElement, { autoAlpha: 0 })
    gsap.set(heroElement, { yPercent: 100 })
    if (topHeader) {
      gsap.set(topHeader, { yPercent: -140, autoAlpha: 0 })
    }
    setupHeroScroll()

    loaderTween = gsap.to(loaderBarElement, {
      scaleX: 0.82,
      duration: 1.2,
      ease: 'power1.inOut',
      repeat: -1,
      yoyo: true,
    })

    const loaderStartedAt = window.performance.now()
    const minLoaderDuration = 1200
    const queueIntro = () => {
      const remainingTime = Math.max(0, minLoaderDuration - (window.performance.now() - loaderStartedAt))

      introStartTimer = window.setTimeout(() => {
        window.requestAnimationFrame(playIntro)
      }, remainingTime)
    }

    if (document.readyState === 'complete') {
      queueIntro()
      return
    }

    window.addEventListener('load', queueIntro, { once: true })
    removeLoadListener = () => window.removeEventListener('load', queueIntro)
  }, pageElement)
})

onBeforeUnmount(() => {
  setHomeIntroActive(false)
  hasIntroStarted = false
  if (introStartTimer) {
    window.clearTimeout(introStartTimer)
    introStartTimer = null
  }
  loaderTween?.kill()
  loaderTween = null
  introTimeline?.kill()
  introTimeline = null
  cleanupHeroScroll?.()
  cleanupHeroScroll = null
  removeLoadListener?.()
  removeLoadListener = null
  restorePageScroll?.()
  restorePageScroll = null
  heroAnimationContext?.revert()
  heroAnimationContext = null
})

</script>

<template>
  <main
    ref="pageRef"
    id="top"
    :data-demo-user="demoUserName"
    :data-demo-user-role="demoUserRole"
  >
    <div ref="loaderRef" class="page-loader" aria-hidden="true" v-once>
      <div class="page-loader-content">
        <p>Loading</p>
        <span class="page-loader-line">
          <span ref="loaderBarRef" class="page-loader-line-inner" />
        </span>
      </div>
    </div>

    <div ref="introRef" class="hero-intro" aria-hidden="true">
      <h1 ref="introTitleRef" class="hero-intro-title" :aria-label="introTitle" v-once>
        <span class="hero-intro-word">
          <span
            v-for="(letter, index) in introLetters"
            :key="`${letter}-${index}`"
            class="hero-intro-char-wrap"
          >
            <span class="hero-intro-char">{{ letter }}</span>
          </span>
          <span class="hero-intro-tm">TM</span>
        </span>
      </h1>
    </div>

    <section
      ref="heroStageRef"
      class="hero-stage"
      :style="{ '--hero-pin-distance': `${heroPinDistance}px` }"
    >
      <section ref="heroRef" class="hero-section" v-once>
        <div class="hero-backdrop" aria-hidden="true">
          <div
            v-for="column in heroGalleryColumns"
            :key="column.side"
            class="hero-gallery"
            :class="`hero-gallery-${column.side}`"
          >
            <!-- 复制两份图片组，配合 translateY(-50%) 做无缝循环。 -->
            <div
              class="hero-gallery-track"
              :class="{ 'hero-gallery-track-slow': column.side === 'right' }"
            >
              <div v-for="copy in 2" :key="`${column.side}-copy-${copy}`" class="hero-gallery-group">
                <img
                  v-for="image in column.images"
                  :key="`${column.side}-${copy}-${image}`"
                  class="hero-gallery-image"
                  :src="image"
                  alt=""
                  decoding="async"
                  loading="eager"
                />
              </div>
            </div>
          </div>
        </div>
        <div ref="heroPanelRef" class="hero-panel">
          <p>Hero Section</p>
        </div>
      </section>
    </section>
    <section class="next-section">
      <h2>Next Section</h2>
    </section>
    <section id="work" class="work-section" data-cursor-label="Read more">
      <p>{{ t('home.welcome') }}</p>
    </section>
  </main>
</template>

<style scoped>
.page-loader {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  color: #fff;
  background: #000;
  will-change: transform;
}

.page-loader-content {
  width: min(220px, calc(100vw - 48px));
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.page-loader-content p {
  margin: 0 0 14px;
}

.page-loader-line {
  display: block;
  width: 100%;
  height: 1px;
  overflow: hidden;
  background: rgb(255 255 255 / 20%);
}

.page-loader-line-inner {
  display: block;
  width: 100%;
  height: 100%;
  background: #fff;
  transform: scaleX(0);
  transform-origin: left center;
}

.hero-intro {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  overflow: hidden;
  color: #fff;
  background: #000;
  opacity: 0;
  visibility: hidden;
  will-change: transform;
}

.hero-intro-title {
  display: flex;
  margin: 0;
  font-size: clamp(36px, 6.2vw, 104px);
  font-weight: 700;
  line-height: 0.9;
  letter-spacing: -0.07em;
  white-space: nowrap;
}

.hero-intro-word {
  position: relative;
  display: inline-flex;
}

.hero-intro-char-wrap,
.hero-intro-char {
  display: inline-block;
}

.hero-intro-char-wrap {
  position: relative;
  overflow: hidden;
}

.hero-intro-char {
  will-change: transform;
}

.hero-intro-tm {
  position: absolute;
  left: calc(100% + 0.12em);
  top: 0.08em;
  font-size: 0.13em;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  opacity: 0;
  visibility: hidden;
}

.hero-stage {
  position: relative;
  height: calc(100svh + var(--hero-pin-distance));
  background: #000;
}

.hero-section {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  background: #111;
}

.hero-backdrop {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #000;
}

.hero-gallery {
  position: absolute;
  top: -40px;
  bottom: -40px;
  width: 49vw;
  overflow: hidden;
}

.hero-gallery-left {
  left: 0;
}

.hero-gallery-right {
  right: 0;
}

.hero-gallery-track {
  will-change: transform;
  animation: hero-gallery-scroll 28s linear infinite;
}

.hero-gallery-track-slow {
  animation-duration: 42s;
}

.hero-gallery-group {
  display: grid;
  gap: 36px;
  padding-bottom: 36px;
}

.hero-gallery-image {
  display: block;
  width: 100%;
  height: clamp(260px, 42vh, 430px);
  border-radius: 0;
  object-fit: cover;
}

@keyframes hero-gallery-scroll {
  to {
    transform: translateY(-50%);
  }
}

.hero-panel {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: #fff;
  font-size: 48px;
  will-change: transform;
}

.next-section,
.work-section {
  min-height: 100svh;
  display: grid;
  place-items: center;
}
</style>
