<template>
  <view class="simple-vertical-swiper" :style="{ height: height + 'rpx' }">
    <view
      class="swiper-wrapper"
      @touchstart.stop="onTouchStart"
      @touchmove.stop.prevent="onTouchMove"
      @touchend.stop="onTouchEnd"
    >
      <view
        v-for="(item, index) in displayItems"
        :key="index"
        class="swiper-slide"
        :class="{ 'swiper-slide-active': index === virtualCurrentIndex }"
        :style="getSlideStyle(index)"
      >
        <!-- 公告类型 -->
        <view v-if="type === 'notice'" class="notice-item">
          <text class="notice-icon">📢</text>
          <text class="notice-text">{{ item.content || item }}</text>
        </view>

        <!-- 商品类型 -->
        <view v-else-if="type === 'product'" class="product-item">
          <image
            class="product-image"
            :src="item.image"
            mode="aspectFill"
          ></image>
          <view class="product-info">
            <text class="product-name">{{ item.name }}</text>
            <text class="product-price">¥{{ item.price }}</text>
          </view>
        </view>

        <!-- 内容类型 -->
        <view v-else-if="type === 'content'" class="content-item">
          <text class="content-title">{{ item.title }}</text>
          <text class="content-desc">{{ item.description }}</text>
        </view>

        <!-- 文本类型 -->
        <view v-else-if="type === 'text'" class="text-item">
          <text class="text-content">{{ item.text || item }}</text>
        </view>

        <!-- 自定义插槽 -->
        <slot v-else :item="item" :index="getOriginalIndex(index)">
          <view class="default-item">
            <text>{{ item.text || item.content || item }}</text>
          </view>
        </slot>
      </view>
    </view>

    <!-- 指示器 -->
    <view v-if="showIndicators && items.length > 1" class="indicators">
      <view
        v-for="(_item, originalItemIndex) in items"
        :key="originalItemIndex"
        class="indicator"
        :class="{ active: originalItemIndex === currentIndex }"
      ></view>
    </view>
  </view>
</template>

<script>
export default {
  name: 'SimpleVerticalSwiper',
  props: {
    // 轮播数据
    items: {
      type: Array,
      default: () => [],
    },
    // 数据类型：notice(公告), product(商品), content(内容), text(文本), custom(自定义)
    type: {
      type: String,
      default: 'text',
    },
    // 组件高度
    height: {
      type: Number,
      default: 60,
    },
    // 是否自动轮播
    autoplay: {
      type: Boolean,
      default: true,
    },
    // 自动轮播间隔时间(ms)
    interval: {
      type: Number,
      default: 3000,
    },
    // 动画持续时间(ms)
    duration: {
      type: Number,
      default: 300,
    },
    // 是否显示指示器
    showIndicators: {
      type: Boolean,
      default: false,
    },
    // 是否循环轮播
    circular: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      currentIndex: 0, // Index in original items array
      virtualCurrentIndex: 0, // Index in displayItems array
      timer: null,
      isTransitioning: false,
      isJumping: false, // Flag for instant jumps, to disable CSS transition
      touchStartY: 0,
      touchStartTime: 0,
      isTouching: false,
    }
  },
  computed: {
    originalItemsLength() {
      return this.items.length
    },
    displayItems() {
      if (!this.items || this.originalItemsLength === 0) {
        return []
      }
      if (!this.circular) {
        return this.items
      }

      if (this.originalItemsLength === 1) {
        return [this.items[0], this.items[0], this.items[0]]
      }

      const lastItem = this.items[this.originalItemsLength - 1]
      const firstItem = this.items[0]
      return [lastItem, ...this.items, firstItem]
    },
  },
  watch: {
    items: {
      handler() {
        this.initSwiperState()
      },
      immediate: true,
    },
    autoplay(newVal) {
      if (this.originalItemsLength === 0) return
      if (newVal) {
        this.startAutoplay()
      } else {
        this.clearTimer()
      }
    },
    circular() {
      this.initSwiperState()
    },
    virtualCurrentIndex() {
      // This watcher can be used for debugging or complex state reactions if needed
    },
  },
  mounted() {
    // Initialization is handled by immediate watch on items
  },
  beforeDestroy() {
    this.clearTimer()
  },
  // uniapp 生命周期
  onReady() {
    this.initSwiperState()
  },
  onUnload() {
    this.clearTimer()
  },
  methods: {
    getOriginalIndex(displayIndex) {
      if (this.originalItemsLength === 0) return 0
      if (!this.circular) {
        return displayIndex
      }
      if (this.originalItemsLength === 1) {
        return 0
      }
      if (displayIndex === 0) return this.originalItemsLength - 1
      if (displayIndex === this.displayItems.length - 1) return 0
      if (displayIndex > 0 && displayIndex <= this.originalItemsLength) {
        return displayIndex - 1
      }
      return 0 // Fallback, should ideally not be reached with correct logic
    },
    initSwiperState() {
      this.clearTimer()
      if (this.originalItemsLength === 0) {
        this.currentIndex = 0
        this.virtualCurrentIndex = 0
        if (this.autoplay) this.startAutoplay() // Try to start autoplay even if no items initially
        return
      }

      if (this.circular) {
        if (this.originalItemsLength === 1) {
          this.virtualCurrentIndex = 1
          this.currentIndex = 0
        } else {
          this.virtualCurrentIndex = 1
          this.currentIndex = 0
        }
      } else {
        this.virtualCurrentIndex = 0
        this.currentIndex = 0
      }

      // Ensure DOM is updated if items change, then start autoplay
      this.$nextTick(() => {
        if (this.autoplay) {
          this.startAutoplay()
        }
      })
    },
    getSlideStyle(indexInDisplayItems) {
      const offset = indexInDisplayItems - this.virtualCurrentIndex
      const translateY = offset * this.height

      let opacity = 0.6
      let zIndex = 1

      if (offset === 0) {
        // Active slide
        opacity = 1
        zIndex = 10
      } else if (Math.abs(offset) === 1) {
        // Adjacent slides
        opacity = 0.3
        zIndex = 5
      } else {
        // Far away slides
        opacity = 0
        zIndex = 0
      }

      const transition =
        (this.isTransitioning || this.isJumping) &&
        !this.isTouching &&
        !(this.isJumping && !this.isTransitioning) // Allow transition if only isTransitioning is true
          ? this.isJumping
            ? 'none'
            : `all ${this.duration}ms cubic-bezier(0.25, 0.46, 0.45, 0.94)`
          : 'none'

      return {
        transform: `translateY(${translateY}px)`,
        opacity: opacity,
        zIndex: zIndex,
        transition: transition,
      }
    },
    startAutoplay() {
      this.clearTimer()
      // Condition for autoplay: must have items, and if not circular, must have more than 1 item.
      const canAutoplay =
        this.originalItemsLength > 0 &&
        (this.circular || this.originalItemsLength > 1)
      if (this.autoplay && canAutoplay) {
        this.timer = setInterval(() => {
          this.next()
        }, this.interval)
      }
    },
    clearTimer() {
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
    },
    updateCurrentIndex(currentVirtualIdx, isMidJump = false) {
      const oldOriginalIndex = this.currentIndex
      let newOriginalIndex = 0

      if (this.originalItemsLength === 0) {
        this.currentIndex = 0
        return
      }

      if (!this.circular) {
        newOriginalIndex = currentVirtualIdx
      } else if (this.originalItemsLength === 1) {
        newOriginalIndex = 0
      } else {
        if (currentVirtualIdx === 0) {
          newOriginalIndex = this.originalItemsLength - 1
        } else if (currentVirtualIdx === this.displayItems.length - 1) {
          newOriginalIndex = 0
        } else {
          newOriginalIndex = currentVirtualIdx - 1
        }
      }

      this.currentIndex = newOriginalIndex
      // Emit change only if it's not a mid-jump correction that results in the same original index visually
      // Or if the original index actually changed.
      if (oldOriginalIndex !== newOriginalIndex || !isMidJump) {
        this.$emit('change', {
          current: this.currentIndex,
          currentItem: this.items[this.currentIndex],
        })
      }
    },
    moveTo(targetVirtualIndex, direction) {
      // direction can be 'next' or 'prev', used to emit correct original index during transition
      if (this.isTransitioning && !this.isJumping) return
      if (this.originalItemsLength === 0) return

      this.isTransitioning = true
      this.virtualCurrentIndex = targetVirtualIndex

      // Emit change based on where we are going (even if it's a clone)
      this.updateCurrentIndex(targetVirtualIndex)

      setTimeout(() => {
        this.isTransitioning = false
        if (this.circular) {
          this.checkAndCorrectLoopBoundary()
        }
      }, this.duration)
    },
    checkAndCorrectLoopBoundary() {
      if (this.originalItemsLength <= 1 && this.circular) {
        // Special handling for 1 item circular
        // displayItems = [item, item, item], virtualCurrentIndex can be 0, 1, 2
        if (this.virtualCurrentIndex !== 1) {
          this.isJumping = true
          this.virtualCurrentIndex = 1 // Jump to the middle "real" one
          this.updateCurrentIndex(1, true)
          this.$nextTick(() => {
            this.isJumping = false
          })
        }
        return
      }
      if (this.originalItemsLength < 2 || !this.circular) return // Only for 2+ items circular

      const firstRealItemVirtualIdx = 1
      const lastRealItemVirtualIdx = this.originalItemsLength

      let jumpToVirtualIndex = -1

      if (this.virtualCurrentIndex === 0) {
        jumpToVirtualIndex = lastRealItemVirtualIdx
      } else if (this.virtualCurrentIndex === this.displayItems.length - 1) {
        jumpToVirtualIndex = firstRealItemVirtualIdx
      }

      if (jumpToVirtualIndex !== -1) {
        this.isJumping = true
        this.virtualCurrentIndex = jumpToVirtualIndex
        this.updateCurrentIndex(jumpToVirtualIndex, true) // Update original index reflecting the jump destination
        this.$nextTick(() => {
          this.isJumping = false
        })
      }
    },
    next() {
      if (this.originalItemsLength === 0) return
      if (
        !this.circular &&
        this.virtualCurrentIndex >= this.originalItemsLength - 1
      )
        return

      let targetVirtualIndex = this.virtualCurrentIndex + 1
      this.moveTo(targetVirtualIndex, 'next')
    },
    prev() {
      if (this.originalItemsLength === 0) return
      if (!this.circular && this.virtualCurrentIndex <= 0) return

      let targetVirtualIndex = this.virtualCurrentIndex - 1
      this.moveTo(targetVirtualIndex, 'prev')
    },
    goToSlide(originalIndex) {
      if (originalIndex < 0 || originalIndex >= this.originalItemsLength) return
      // if (originalIndex === this.currentIndex && !this.isTransitioning && !this.isJumping) return;

      let targetVirtualIndex
      if (!this.circular) {
        targetVirtualIndex = originalIndex
      } else {
        if (this.originalItemsLength === 1) targetVirtualIndex = 1
        else targetVirtualIndex = originalIndex + 1
      }
      this.clearTimer()
      this.moveTo(targetVirtualIndex)
      if (this.autoplay) {
        setTimeout(() => this.startAutoplay(), this.duration + 100)
      }
    },
    onTouchStart(e) {
      if (this.originalItemsLength === 0) return
      if (!this.circular && this.originalItemsLength <= 1) return // No swipe for single non-circular item

      this.clearTimer()
      this.touchStartY = e.touches[0].clientY
      this.touchStartTime = Date.now()
      this.isTouching = true
      this.isJumping = false
    },
    onTouchMove(e) {
      if (!this.isTouching) return
    },
    onTouchEnd(e) {
      if (!this.isTouching || this.originalItemsLength === 0) {
        this.isTouching = false // Ensure isTouching is reset
        return
      }
      this.isTouching = false

      const currentY = e.changedTouches[0].clientY
      const deltaY = currentY - this.touchStartY
      const deltaTime = Date.now() - this.touchStartTime

      const threshold = this.height * 0.2
      const velocityThreshold = 0.3
      const velocity = deltaTime > 0 ? Math.abs(deltaY) / deltaTime : 0

      if (Math.abs(deltaY) > threshold || velocity > velocityThreshold) {
        if (deltaY < 0) {
          this.next()
        } else if (deltaY > 0) {
          this.prev()
        }
      } else {
        // No swipe, or too small. Potentially restart autoplay if it was interrupted.
        if (this.autoplay) {
          this.startAutoplay()
        }
      }
      // Autoplay restart is handled within next/prev through moveTo or if no swipe occurs
      // If a swipe occurs, next/prev call moveTo, which stops autoplay via clearTimer. Then this onTouchEnd may restart.
      // If no swipe, this onTouchEnd directly restarts.
      // Consider a more unified autoplay restart logic after touch interaction concludes.
      // For now, let's consolidate: if autoplay was on, restart it after a delay.
      if (this.autoplay) {
        setTimeout(() => {
          if (!this.timer) this.startAutoplay() // Restart only if not already restarted by a quick succession
        }, this.duration + 200) // Delay after potential transition
      }
    },
  },
}
</script>

<style lang="scss" scoped>
.simple-vertical-swiper {
  position: relative;
  overflow: hidden;
  // 防止触摸时选中文本
  user-select: none;
  -webkit-user-select: none;
  // App端需要的设置
  -webkit-overflow-scrolling: touch;

  .swiper-wrapper {
    position: relative;
    width: 100%;
    height: 100%;
    // uniapp App端触摸控制
    touch-action: pan-y; /* More specific touch action */
    // 防止触摸时的默认行为
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
    // App端滚动穿透防护
    overflow: hidden;
  }

  .swiper-slide {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    // 防止子元素被选中
    user-select: none;
    -webkit-user-select: none;

    &.swiper-slide-active {
      opacity: 1 !important;
      z-index: 10 !important;
    }
  }

  // 公告样式
  .notice-item {
    display: flex;
    align-items: center;
    padding: 0 15px;
    width: calc(100% - 30px);
    height: 80%;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border-radius: 6px;

    .notice-icon {
      font-size: 16px;
      margin-right: 8px;
      color: #fff;
    }

    .notice-text {
      color: #fff;
      font-size: 14px;
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }

  // 商品样式
  .product-item {
    display: flex;
    align-items: center;
    padding: 10px 15px;
    width: calc(100% - 30px);
    height: calc(100% - 20px);
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);

    .product-image {
      width: 60px;
      height: 60px;
      border-radius: 6px;
      margin-right: 12px;
      background-color: #f0f0f0;
    }

    .product-info {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: center;

      .product-name {
        font-size: 14px;
        color: #333;
        margin-bottom: 4px;
        font-weight: 500;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .product-price {
        font-size: 16px;
        color: #ff6b6b;
        font-weight: 600;
      }
    }
  }

  // 内容样式
  .content-item {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 15px;
    width: calc(100% - 30px);
    height: calc(100% - 30px);
    text-align: center;
    background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
    border-radius: 8px;

    .content-title {
      font-size: 16px;
      color: #fff;
      font-weight: 600;
      margin-bottom: 6px;
    }

    .content-desc {
      font-size: 12px;
      color: rgba(255, 255, 255, 0.9);
      line-height: 1.3;
    }
  }

  // 文本样式
  .text-item {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 15px;
    width: calc(100% - 30px);
    height: 80%;
    background: linear-gradient(135deg, #fd79a8 0%, #e84393 100%);
    border-radius: 6px;

    .text-content {
      color: #fff;
      font-size: 14px;
      font-weight: 500;
      text-align: center;
    }
  }

  // 默认样式
  .default-item {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 15px;
    width: calc(100% - 30px);
    height: 80%;
    background-color: #f8f9fa;
    border-radius: 6px;
    border: 1px solid #e9ecef;

    text {
      color: #333;
      font-size: 14px;
      text-align: center;
    }
  }

  // 指示器
  .indicators {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    gap: 3px;
    z-index: 20;

    .indicator {
      width: 3px;
      height: 12px;
      background-color: rgba(255, 255, 255, 0.4);
      border-radius: 2px;
      transition: all 0.3s ease;

      &.active {
        background-color: rgba(255, 255, 255, 0.9);
        height: 16px;
      }
    }
  }
}
</style>
