<script setup>
import {ref, computed, watch, nextTick, getCurrentInstance, onUnmounted} from 'vue'

/* -------------------- 组件接口定义 -------------------- */
const currentPage = defineModel({type: [Number, String], default: 1})

const props = defineProps({
  id: {type: String, required: true},
  title: {type: String, default: ''},
  data: {type: Array, default: () => [1]},
  pageSize: {type: [Number, String], default: 1},
  showDot: {type: Boolean, default: true},
  showPagination: {type: Boolean, default: true},
  showHover: {type: Boolean, default: true},
  autoPlay: {type: Boolean, default: false},
  transitionDuration: {type: Number, default: 300},
  sourceWidth: {type: Number, default: 276},
  sourceHeight: {type: Number, default: 355},
  sourceGap: {type: Number, default: 20},
})

const emit = defineEmits(['page-change', 'item-click', 'item-hover', 'title-click'])

/* -------------------- 响应式变量 -------------------- */
const instance = getCurrentInstance()
const $fontSize = instance.appContext.config.globalProperties.$fontSize
const trackRef = ref(null)
let autoPlayTimer = null

/* -------------------- 计算属性 -------------------- */
const itemWidth = computed(() => $fontSize(props.sourceWidth))
const itemHeight = computed(() => $fontSize(props.sourceHeight))
const itemGap = computed(() => $fontSize(props.sourceGap))

const itemTotalWidth = computed(() => itemWidth.value + itemGap.value)
const viewPortWidth = computed(() => props.pageSize * itemWidth.value + (props.pageSize - 1) * itemGap.value + 'px')
const trackWidth = computed(() => props.data.length * itemTotalWidth.value - itemGap.value + 'px')
const totalPages = computed(() => props.data.length - props.pageSize + 1)

/* -------------------- 方法 -------------------- */
/**
 * 标题点击事件
 */
const clickTitle = () => {
  emit('title-click')
}

/**
 * 执行平移动画
 * @param {number} page - 目标页码
 */
const translate = (page) => {
  if (!trackRef.value) return
  trackRef.value.style.transition = `transform ${props.transitionDuration}ms ease`
  trackRef.value.style.transform = `translateX(-${itemTotalWidth.value * (page - 1)}px)`
}

/**
 * 切换到指定页码
 * @param {number} page - 目标页码
 */
const goToPage = (page) => {
  currentPage.value = Number(page)
}

/**
 * 切换到下一页，循环播放
 */
const nextPage = () => {
  const nextPage = currentPage.value < totalPages.value ? currentPage.value + 1 : 1
  goToPage(nextPage)
}

/**
 * 切换到上一页，循环播放
 */
const prevPage = () => {
  const prevPage = currentPage.value > 1 ? currentPage.value - 1 : totalPages.value
  goToPage(prevPage)
}

/**
 * 处理项目点击事件
 * @param {Object} item - 项目数据
 * @param {number} index - 项目索引
 */
const handleItemClick = (item, index) => {
  emit('item-click', item, index)
}

/**
 * 处理项目悬停事件
 * @param {Object} item - 项目数据
 * @param {number} index - 项目索引
 */
const handleItemHover = (item, index) => {
  clearInterval(autoPlayTimer)
  autoPlayTimer = null
  emit('item-hover', item, index)
}

/**
 * 处理项目鼠标移出事件
 * @param {Object} item - 项目数据
 * @param {number} index - 项目索引
 */
const handleItemHoverLeave = () => {
  startAutoPlay()
}

/**
 * 启动自动播放
 */
const startAutoPlay = () => {
  if (!props.autoPlay) return
  stopAutoPlay()
  autoPlayTimer = setInterval(nextPage, 3000)
}

/**
 * 停止自动播放
 */
const stopAutoPlay = () => {
  if (autoPlayTimer) {
    clearInterval(autoPlayTimer)
    autoPlayTimer = null
  }
}

/* -------------------- 监听器 -------------------- */
// 监听页码变化，执行平移动画
watch(
  currentPage,
  (newVal, oldVal) => {
    if (newVal !== oldVal) {
      nextTick(() => translate(newVal))
    }
  },
  {immediate: true}
)

// 监听自动播放设置变化
watch(
  () => props.autoPlay,
  (autoPlay) => {
    autoPlay ? startAutoPlay() : stopAutoPlay()
  },
  {immediate: true}
)

// 监听数据变化，重置到第一页
watch(
  () => props.data.length,
  (newLen, oldLen) => {
    if (newLen !== oldLen && currentPage.value !== 1) {
      currentPage.value = 1
    }
  }
)

// 组件卸载时清理定时器
onUnmounted(stopAutoPlay)
</script>

<template>
  <section :id="id" class="generic-carousel">
    <!-- 标题 -->
    <slot name="title">
      <h2 v-if="title" class="carousel-title" @click="clickTitle">{{ title }}</h2>
    </slot>
    <!-- <h2 v-if="title" class="carousel-title">{{ title }}</h2> -->

    <!-- 分页指示器 -->
    <div v-if="showDot" class="carousel-pagination">
      <button
        v-for="page in totalPages"
        :key="page"
        :class="['pagination-dot', {'is-active': page === currentPage}]"
        @click="goToPage(page)"
        :aria-label="`切换到第${page}页`"
      />
    </div>

    <!-- 导航按钮 -->
    <div v-if="showPagination && totalPages > 1" class="carousel-navigation flex j-s">
      <button class="nav-btn prev-btn" @click="prevPage" aria-label="上一页">&lt;</button>
      <button class="nav-btn next-btn" @click="nextPage" aria-label="下一页">&gt;</button>
    </div>

    <!-- 轮播内容区域 -->
    <div class="carousel-content">
      <div class="carousel-viewport" :style="{width: viewPortWidth}">
        <div
          ref="trackRef"
          class="carousel-track"
          :class="{'carousel-track-hover': showHover}"
          :style="{width: trackWidth}"
        >
          <div
            v-for="(item, index) in data"
            :key="index"
            class="carousel-item"
            :class="{'carousel-item-hover': showHover}"
            :style="{
              width: itemWidth + 'px',
              height: itemHeight + 'px',
              marginRight: index < data.length - 1 ? itemGap + 'px' : '0',
            }"
            @click="handleItemClick(item, index)"
            @mouseenter="handleItemHover(item, index)"
            @mouseleave="handleItemHoverLeave(item, index)"
          >
            <slot :item="item" :index="index" :isActive="currentPage === Math.ceil((index + 1) / pageSize)" />
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<style lang="scss" scoped>
.generic-carousel {
  padding: 32px 0;
  text-align: center;
  position: relative;

  .carousel-title {
    font-family: 'PingFang SC';
    font-weight: 600;
    font-size: 24px;
    color: #333333;
    &:hover {
      cursor: pointer;
      color: #0389ff;
    }
  }

  .carousel-pagination {
    width: 90px;
    height: 5px;
    margin: 20px auto 30px;
    display: flex;

    .pagination-dot {
      flex: 1;
      height: 100%;
      background: #d9d9d9;
      border: none;
      cursor: pointer;
      transition: background 0.3s ease;

      &.is-active {
        background: #0389ff;
      }
    }
  }

  .carousel-navigation {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    padding: 0 50px;
    z-index: 10;
    pointer-events: none;

    .nav-btn {
      pointer-events: auto;
      width: 40px;
      height: 40px;
      border: none;
      border-radius: 50%;
      background: rgba(255, 255, 255, 0.9);
      color: #333;
      font-size: 20px;
      cursor: pointer;
      transition: all 0.3s ease;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);

      &:hover {
        background: #0389ff;
        color: white;
      }
    }
  }
}

.carousel-content {
  width: 100%;
  overflow: hidden;
}

.carousel-viewport {
  overflow: hidden;
  position: relative;
  left: 50%;
  transform: translateX(-50%);
}

.carousel-track {
  display: flex;
  transition: transform 0.3s ease;
  will-change: transform;
  padding-top: 20px;

  &-hover {
    padding-top: 50px;
  }
}

.carousel-item {
  flex-shrink: 0;
  transition: all 0.3s ease;
  border-radius: 8px;
  overflow: hidden;

  &-hover {
    cursor: pointer;

    &:hover {
      transform: translateY(-50px);
      box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
    }
  }

  .item-title {
    margin-bottom: 5px;
  }
}
</style>
