<template>
  <div class="virtual-scroll-wrapper">
    <!-- 顶部渐变遮罩（提示有更多内容在上面） -->
    <div 
      class="scroll-fade-top"
      :class="{ visible: canScrollUp }"
    />
    
    <!-- 滚动容器 -->
    <div
      ref="containerRef"
      class="virtual-scroll-container"
      @scroll="handleScroll"
    >
      <!-- 占位容器，用于撑开滚动条 -->
      <div
        class="virtual-scroll-spacer"
        :style="{ height: `${totalHeight}px` }"
      >
        <!-- 可见内容容器，使用transform定位 -->
        <div
          class="virtual-scroll-content"
          :style="{ transform: `translateY(${offsetY}px)` }"
        >
          <slot
            name="default"
            :visible-items="visibleItems"
          />
        </div>
      </div>
    </div>
    
    <!-- 底部渐变遮罩（提示有更多内容在下面） -->
    <div 
      class="scroll-fade-bottom"
      :class="{ visible: canScrollDown }"
    />
    
    <!-- 滚动提示（仅在有更多内容时显示） -->
    <div 
      v-if="canScrollDown && scrollTop < 50"
      class="scroll-hint"
    >
      <div class="scroll-hint-icon">↓</div>
      <div class="scroll-hint-text">向下滚动查看更多Token</div>
    </div>
  </div>
</template>

<script setup>
/**
 * 虚拟滚动列表组件
 * 100并发优化：只渲染可见区域的元素，大幅减少DOM数量和内存占用
 */
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'

// Props
const props = defineProps({
  // 所有数据项
  items: {
    type: Array,
    required: true,
    default: () => []
  },
  // 每个项目的高度（px）
  itemHeight: {
    type: Number,
    default: 200
  },
  // 缓冲区大小（上下各渲染几个额外项目）
  // 🚀 v3.13.5.5: 从2进一步减小到1，极致减少DOM（700token重度优化）
  buffer: {
    type: Number,
    default: 1
  }
})

// Emits
const emit = defineEmits(['scroll'])

// 容器引用
const containerRef = ref(null)

// 滚动位置
const scrollTop = ref(0)

// 容器高度（可见区域高度）
const containerHeight = ref(0)

// Grid布局优化：计算每行的列数
const columnsPerRow = ref(1)

// 监听容器宽度变化，重新计算列数
const updateColumnsPerRow = () => {
  if (!containerRef.value) return
  
  const width = containerRef.value.clientWidth
  // 根据媒体查询断点计算列数
  if (width >= 2400) {
    columnsPerRow.value = 7
  } else if (width >= 2000) {
    columnsPerRow.value = 6
  } else if (width >= 1600) {
    columnsPerRow.value = 5
  } else if (width >= 1280) {
    columnsPerRow.value = 4
  } else if (width >= 1024) {
    columnsPerRow.value = 3
  } else if (width >= 768) {
    columnsPerRow.value = 2
  } else {
    columnsPerRow.value = 1
  }
}

// 总行数
const totalRows = computed(() => {
  return Math.ceil(props.items.length / columnsPerRow.value)
})

// 总高度（基于行数计算）
const totalHeight = computed(() => {
  return totalRows.value * props.itemHeight
})

// 可见区域起始行（基于行的虚拟滚动）
const startRow = computed(() => {
  const row = Math.floor(scrollTop.value / props.itemHeight)
  return Math.max(0, row - props.buffer)
})

// 可见区域结束行
const endRow = computed(() => {
  const visibleRows = Math.ceil(containerHeight.value / props.itemHeight)
  const row = startRow.value + visibleRows + props.buffer * 2
  return Math.min(totalRows.value, row)
})

// 可见区域起始索引（基于行转换为项目索引）
const startIndex = computed(() => {
  return startRow.value * columnsPerRow.value
})

// 可见区域结束索引
const endIndex = computed(() => {
  return Math.min(props.items.length, endRow.value * columnsPerRow.value)
})

// 可见项目
const visibleItems = computed(() => {
  return props.items.slice(startIndex.value, endIndex.value).map((item, index) => ({
    ...item,
    _index: startIndex.value + index
  }))
})

// 偏移量（按行计算）
const offsetY = computed(() => {
  return startRow.value * props.itemHeight
})

// 🚀 性能优化：使用requestAnimationFrame优化滚动处理
let scrollRAF = null
const handleScroll = (event) => {
  // 取消上一帧的更新
  if (scrollRAF) {
    cancelAnimationFrame(scrollRAF)
  }
  
  // 使用RAF批量更新滚动位置
  scrollRAF = requestAnimationFrame(() => {
    scrollTop.value = event.target.scrollTop
    // 触发scroll事件，传递给父组件
    emit('scroll', event)
    scrollRAF = null
  })
}

// 判断是否可以向上滚动
const canScrollUp = computed(() => {
  return scrollTop.value > 10 // 滚动超过10px就显示顶部遮罩
})

// 判断是否可以向下滚动
const canScrollDown = computed(() => {
  const maxScroll = totalHeight.value - containerHeight.value
  return scrollTop.value < maxScroll - 10 // 距离底部超过10px就显示底部遮罩
})

// ResizeObserver 用于监听容器大小变化
let resizeObserver = null

onMounted(() => {
  if (containerRef.value) {
    // 初始化容器高度
    containerHeight.value = containerRef.value.clientHeight
    
    // 初始化列数
    updateColumnsPerRow()
    
    // 监听容器大小变化
    resizeObserver = new ResizeObserver((entries) => {
      for (const entry of entries) {
        containerHeight.value = entry.contentRect.height
        // 容器宽度变化时重新计算列数
        updateColumnsPerRow()
      }
    })
    resizeObserver.observe(containerRef.value)
  }
})

onUnmounted(() => {
  if (resizeObserver) {
    resizeObserver.disconnect()
  }
  // 🚀 性能优化：清理RAF
  if (scrollRAF) {
    cancelAnimationFrame(scrollRAF)
  }
})

// 🚀 性能优化：减少watch触发频率，只在数量大幅变化时重置
watch(() => props.items.length, (newLen, oldLen) => {
  // 如果items数量变化很大（超过50个），可能是新的批次，重置滚动
  if (Math.abs(newLen - oldLen) > 50 && containerRef.value) {
    containerRef.value.scrollTop = 0
    scrollTop.value = 0
  }
})

// 滚动到指定索引的item，显示在视口上3/4位置（优化后的自动跟随效果）
const scrollToIndex = (index, behavior = 'smooth') => {
  if (!containerRef.value) return
  
  // 计算该索引所在的行
  const row = Math.floor(index / columnsPerRow.value)
  
  // 🎯 优化：将目标项显示在视口的上3/4位置
  // 这样可以：
  // 1. 上方显示大量已完成的卡片（完整的历史记录）
  // 2. 正在执行的卡片在较下方的醒目位置
  // 3. 下方保留少量空间显示即将执行的卡片
  const itemTop = row * props.itemHeight
  const viewportThreeQuarters = (containerHeight.value * 3) / 4  // 视口的3/4位置
  const targetScrollTop = Math.max(0, itemTop - viewportThreeQuarters)
  
  containerRef.value.scrollTo({
    top: targetScrollTop,
    behavior
  })
}

// 暴露方法给父组件
defineExpose({
  scrollToIndex,
  containerRef
})
</script>

<style lang="scss" scoped>
.virtual-scroll-wrapper {
  position: relative;
  height: 100%;
  width: 100%;
}

.virtual-scroll-container {
  height: 100%;
  overflow-y: auto; /* 允许垂直滚动 */
  overflow-x: hidden; /* 禁止横向滚动 */
  position: relative;
  
  // 更粗、更明显的滚动条（100并发优化：清晰提示可滚动）
  &::-webkit-scrollbar {
    width: 12px; // 从8px增加到12px，更容易看到
  }
  
  &::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.08); // 稍微加深轨道颜色
    border-radius: 6px;
    margin: 4px 0; // 上下留白
  }
  
  &::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.3); // 加深滑块颜色
    border-radius: 6px;
    border: 2px solid transparent;
    background-clip: padding-box; // 创建内边距效果
    
    &:hover {
      background: rgba(0, 0, 0, 0.5); // 悬停时更深
    }
    
    &:active {
      background: rgba(0, 0, 0, 0.6); // 拖动时最深
    }
  }
}

.virtual-scroll-spacer {
  position: relative;
  width: 100%;
}

.virtual-scroll-content {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  will-change: transform;
}

// 顶部渐变遮罩（提示有更多内容在上面）
.scroll-fade-top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 40px;
  background: linear-gradient(to bottom, 
    rgba(255, 255, 255, 0.95) 0%, 
    rgba(255, 255, 255, 0.7) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  pointer-events: none;
  z-index: 10;
  opacity: 0;
  transition: opacity 0.3s ease;
  
  &.visible {
    opacity: 1;
  }
}

// 底部渐变遮罩（提示有更多内容在下面）
.scroll-fade-bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 60px;
  background: linear-gradient(to top, 
    rgba(255, 255, 255, 0.95) 0%, 
    rgba(255, 255, 255, 0.7) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  pointer-events: none;
  z-index: 10;
  opacity: 0;
  transition: opacity 0.3s ease;
  
  &.visible {
    opacity: 1;
  }
}

// 滚动提示（仅在开始时显示）
.scroll-hint {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  animation: bounce 2s infinite;
  pointer-events: none;
  
  .scroll-hint-icon {
    width: 40px;
    height: 40px;
    background: rgba(103, 126, 234, 0.9);
    color: white;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    font-weight: bold;
    box-shadow: 0 4px 12px rgba(103, 126, 234, 0.4);
  }
  
  .scroll-hint-text {
    background: rgba(0, 0, 0, 0.85);
    color: white;
    padding: 8px 16px;
    border-radius: 20px;
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  }
}

// 弹跳动画
@keyframes bounce {
  0%, 20%, 50%, 80%, 100% {
    transform: translateX(-50%) translateY(0);
  }
  40% {
    transform: translateX(-50%) translateY(-10px);
  }
  60% {
    transform: translateX(-50%) translateY(-5px);
  }
}

// 深色模式支持
@media (prefers-color-scheme: dark) {
  .scroll-fade-top {
    background: linear-gradient(to bottom, 
      rgba(24, 24, 28, 0.95) 0%, 
      rgba(24, 24, 28, 0.7) 50%,
      rgba(24, 24, 28, 0) 100%
    );
  }
  
  .scroll-fade-bottom {
    background: linear-gradient(to top, 
      rgba(24, 24, 28, 0.95) 0%, 
      rgba(24, 24, 28, 0.7) 50%,
      rgba(24, 24, 28, 0) 100%
    );
  }
}
</style>

