<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';

const { t } = useI18n();
import defaultBannerImg from '../assets/images/banner.webp';

export interface BannerItem {
  id?: string;
  linkType?: string | null;
  linkTarget?: string | null;
  translation?: { title?: string; body?: string; imageUrl?: string };
}

const props = defineProps<{
  banners: BannerItem[];
  /** 未配置跳转链接时的默认路由 */
  fallbackTo?: string;
}>();

const router = useRouter();
const active = ref(0);
const timer = ref<ReturnType<typeof setInterval> | null>(null);
const touchStartX = ref(0);
const touchDeltaX = ref(0);
const FALLBACK_IMG = '/uploads/banners/welcome.svg';

function imageUrl(banner: BannerItem) {
  return banner.translation?.imageUrl || defaultBannerImg || FALLBACK_IMG;
}

function onImgError(e: Event) {
  const img = e.target as HTMLImageElement;
  if (img.dataset.fallbackApplied) return;
  img.dataset.fallbackApplied = '1';
  img.src = defaultBannerImg || FALLBACK_IMG;
}

function title(banner: BannerItem) {
  return banner.translation?.title || t('home.banner_fallback');
}

function goTo(index: number) {
  if (!props.banners.length) return;
  active.value = (index + props.banners.length) % props.banners.length;
}

function next() {
  goTo(active.value + 1);
}

function prev() {
  goTo(active.value - 1);
}

function onBannerClick(banner: BannerItem) {
  if (banner.id) {
    void router.push(`/announcements/${banner.id}`);
    return;
  }
  if (props.fallbackTo) {
    void router.push(props.fallbackTo);
  }
}

function startAutoPlay() {
  stopAutoPlay();
  if (props.banners.length <= 1) return;
  timer.value = setInterval(next, 4500);
}

function stopAutoPlay() {
  if (timer.value) {
    clearInterval(timer.value);
    timer.value = null;
  }
}

function onTouchStart(e: TouchEvent) {
  touchStartX.value = e.touches[0].clientX;
  touchDeltaX.value = 0;
  stopAutoPlay();
}

function onTouchMove(e: TouchEvent) {
  touchDeltaX.value = e.touches[0].clientX - touchStartX.value;
}

function onTouchEnd() {
  if (touchDeltaX.value > 50) prev();
  else if (touchDeltaX.value < -50) next();
  touchDeltaX.value = 0;
  startAutoPlay();
}

watch(
  () => props.banners.length,
  () => {
    active.value = 0;
    startAutoPlay();
  },
);

onMounted(startAutoPlay);
onUnmounted(stopAutoPlay);
</script>

<template>
  <div
    v-if="banners.length"
    class="carousel"
    @mouseenter="stopAutoPlay"
    @mouseleave="startAutoPlay"
    @touchstart.passive="onTouchStart"
    @touchmove.passive="onTouchMove"
    @touchend="onTouchEnd"
  >
    <div class="media">
      <div class="viewport">
        <div class="track" :style="{ transform: `translateX(-${active * 100}%)` }">
          <div
            v-for="(banner, i) in banners"
            :key="banner.id ?? i"
            class="slide"
            @click="onBannerClick(banner)"
          >
            <template v-if="imageUrl(banner)">
              <img
                :src="imageUrl(banner)"
                alt=""
                class="slide-backdrop"
                aria-hidden="true"
                :loading="i === 0 ? 'eager' : 'lazy'"
                @error="onImgError"
              />
              <img
                :src="imageUrl(banner)"
                :alt="title(banner)"
                class="slide-img"
                :loading="i === 0 ? 'eager' : 'lazy'"
                @error="onImgError"
              />
            </template>
            <div v-else class="slide-fallback">{{ title(banner) }}</div>
          </div>
        </div>
      </div>

      <button v-if="banners.length > 1" class="nav prev" type="button" :aria-label="t('home.banner_prev')" @click.stop="prev">‹</button>
      <button v-if="banners.length > 1" class="nav next" type="button" :aria-label="t('home.banner_next')" @click.stop="next">›</button>

      <div v-if="banners.length > 1" class="dots">
        <button
          v-for="(_, i) in banners"
          :key="i"
          type="button"
          class="dot"
          :class="{ active: i === active }"
          :aria-label="t('home.banner_slide', { n: i + 1 })"
          @click.stop="goTo(i)"
        />
      </div>
    </div>
  </div>
</template>

<style scoped>
.carousel {
  position: relative;
  margin: -12px -16px 14px;
  overflow: hidden;
}

.media {
  position: relative;
  overflow: hidden;
  background: #05080d;
}

.viewport {
  overflow: hidden;
  width: 100%;
  height: clamp(148px, 41.86vw, 180px);
}

.track {
  display: flex;
  height: 100%;
  transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}

.slide {
  position: relative;
  flex: 0 0 100%;
  min-width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background:
    linear-gradient(135deg, rgba(7, 18, 31, 0.94), rgba(6, 8, 12, 0.98)),
    #070d15;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.slide::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(90deg, rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0.03) 48%, rgba(0, 0, 0, 0.18));
  box-shadow: inset 0 -1px 0 rgba(212, 175, 55, 0.16);
  pointer-events: none;
}

.slide-backdrop {
  position: absolute;
  inset: -18px;
  width: calc(100% + 36px);
  height: calc(100% + 36px);
  object-fit: cover;
  filter: blur(16px);
  opacity: 0.42;
  transform: scale(1.04);
  pointer-events: none;
}

.slide-img {
  position: relative;
  z-index: 2;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
}

.slide-fallback {
  position: relative;
  z-index: 2;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, rgba(212, 175, 55, 0.25), var(--secondary));
  font-size: 22px;
  font-weight: 800;
  color: var(--primary-light);
}

.media .nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: var(--primary-light);
  border: 1px solid var(--border);
  padding-bottom: 5px;
  font-size: 22px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}

.nav.prev { left: 8px; }
.nav.next { right: 8px; }

.media .dots {
  position: absolute;
  bottom: 10px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  gap: 8px;
  z-index: 2;
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.35);
  border: 2px solid transparent;
  padding: 0;
}

.dot.active {
  width: 22px;
  border-radius: 6px;
  background: var(--gradient-gold);
  border-color: var(--primary-light);
  box-shadow: none;
}
</style>
