<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import type { ListItem } from '@/types'

interface Props {
  data: ListItem[]
  height?: string | number
  frozen?: boolean
  disableHover?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  height: '100%',
  frozen: false,
  disableHover: false
})

const emit = defineEmits<{
  select: [item: ListItem]
  click: [item: ListItem]
  contextmenu: [data: { item: ListItem }]
  showToast: [message: string]
}>()

// ===== 列表配置常量 =====
/** 列表项基础高度（像素） */
const ITEM_HEIGHT = 50
/** 列表项内边距总和（像素） */
const ITEM_PADDING = 16
/** 上下缓冲区域的项目数量 */
const BUFFER_COUNT = 10
/** 滚动防抖时间（毫秒） */
const SCROLL_DEBOUNCE_TIME = 16
/** 虚拟滚动阈值，少于此数量则直接渲染全部 */
const VIRTUALIZATION_THRESHOLD = 500

// 计算列表项实际总高度
const totalItemHeight = computed(() => 
  ITEM_HEIGHT + ITEM_PADDING
)

// 响应式状态管理
const containerHeight = ref<number>(0)
const startIndex = ref<number>(0)
const selectedIndex = ref<number>(0)
const isKeyboardNavigating = ref<boolean>(false)
let keyboardTimer: number | null = null

// DOM 引用
const containerRef = ref<HTMLElement | null>(null)
const listRef = ref<HTMLElement | null>(null)

// 计算可视区域能显示的列表项数量
const visibleCount = computed(() => 
  Math.floor(containerHeight.value / totalItemHeight.value)
)

/**
 * 是否需要虚拟滚动
 * 当数据量小于阈值时直接渲染全部
 */
const needVirtualization = computed<boolean>(() => 
  props.data.length > VIRTUALIZATION_THRESHOLD
)

// 计算当前需要渲染的数据
const visibleData = computed(() => {
  // 数据量小于阈值时，直接返回全部数据
  if (!needVirtualization.value) {
    return props.data.map((item, index) => ({
      ...item,
      index
    }))
  }
  
  // 否则使用虚拟滚动
  const visibleStart = Math.max(0, startIndex.value - BUFFER_COUNT)
  const visibleEnd = Math.min(
    props.data.length,
    startIndex.value + visibleCount.value + BUFFER_COUNT
  )
  
  return props.data.slice(visibleStart, visibleEnd).map((item, index) => ({
    ...item,
    index: visibleStart + index
  }))
})

// 计算列表偏移量
const offsetY = computed(() => 
  needVirtualization.value
    ? Math.max(0, (startIndex.value - BUFFER_COUNT) * totalItemHeight.value)
    : 0
)

// 计算虚拟列表总高度
const phantomHeight = computed(() => 
  needVirtualization.value
    ? props.data.length * totalItemHeight.value
    : 'auto'
)

// 选择处理
const handleSelect = (item: ListItem): void => {
  if (props.frozen) return
  selectedIndex.value = item.index ?? 0
  emit('select', item)
}

/**
 * 处理列表项点击
 * @param item 点击的列表项
 */
const handleClick = (item: ListItem): void => {
  if (props.frozen) return
  handleSelect(item)
  emit('click', item)
}

/**
 * 更新容器高度
 * 从父元素或窗口获取实际高度
 */
function updateContainerHeight(): void {
  if (containerRef.value) {
    const parentHeight = containerRef.value.parentElement?.clientHeight
    containerHeight.value = parentHeight || window.innerHeight
  }
}

/** ResizeObserver 实例，用于监听容器尺寸变化 */
let resizeObserver: ResizeObserver | null = null

/**
 * 防抖函数
 * 在指定延迟后执行函数，如果在延迟期间再次调用则重置延迟
 * @param fn 需要防抖的函数
 * @param delay 延迟时间（毫秒）
 */
function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): (...args: Parameters<T>) => void {
  let timer: number | null = null
  return function (this: any, ...args: Parameters<T>) {
    if (timer) clearTimeout(timer)
    timer = window.setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}

/**
 * 处理滚动事件（已防抖）
 * 根据滚动位置更新可视区的起始索引
 */
const handleScroll = debounce(() => {
  // 不需要虚拟滚动时直接返回
  if (!needVirtualization.value) return
  
  if (!containerRef.value) return
  const scrollTop = containerRef.value.scrollTop
  const newStartIndex = Math.floor(scrollTop / totalItemHeight.value)
  
  if (newStartIndex !== startIndex.value) {
    startIndex.value = newStartIndex
  }
}, SCROLL_DEBOUNCE_TIME)

/** 鼠标悬停禁用状态 */
const disableHover = ref<boolean>(false)
/** 鼠标悬停禁用定时器 */
let hoverTimer: number | null = null

/**
 * 处理鼠标移入事件
 * 在非键盘导航状态下更新选中项
 * @param index 目标项的索引
 */
function handleMouseEnter(index: number): void {
  if (props.frozen || disableHover.value || props.disableHover) return
  if (!isKeyboardNavigating.value) {
    selectedIndex.value = index
    emit('select', { ...props.data[index], index })
    ensureSelectedItemVisible()
  }
}

/**
 * 记录到达列表边界的时间
 * 用于实现边界循环导航的延迟判断
 */
const lastReachBoundaryTime = ref<{ top: number; bottom: number }>({
  top: 0,
  bottom: 0
})

/**
 * 处理键盘事件
 * 实现键盘导航、边界循环和项目选择功能
 * @param e 键盘事件对象
 */
function handleKeyDown(e: KeyboardEvent): void {
  if (props.frozen) return

  if (e.key === 'Enter') {
    if (selectedIndex.value >= 0 && selectedIndex.value < props.data.length) {
      const item = props.data[selectedIndex.value]
      emit('click', { ...item, index: selectedIndex.value })
    }
  } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
    e.preventDefault()
    
    const isFirstItem = selectedIndex.value === 0
    const isLastItem = selectedIndex.value === props.data.length - 1
    const now = Date.now()
    
    if (e.key === 'ArrowUp' && isFirstItem) {
      const timeSinceLastTop = now - lastReachBoundaryTime.value.top
      if (timeSinceLastTop < 5000) {
        disableHover.value = true
        selectedIndex.value = props.data.length - 1
        emit('select', { ...props.data[selectedIndex.value], index: selectedIndex.value })
        ensureSelectedItemVisible()
        if (hoverTimer) clearTimeout(hoverTimer)
        hoverTimer = window.setTimeout(() => {
          disableHover.value = false
        }, 1000)
      } else {
        lastReachBoundaryTime.value.top = now
        emit('showToast', '已经到列表顶端，再次点击将回到列表底端')
      }
      return
    }
    
    if (e.key === 'ArrowDown' && isLastItem) {
      const timeSinceLastBottom = now - lastReachBoundaryTime.value.bottom
      if (timeSinceLastBottom < 5000) {
        disableHover.value = true
        selectedIndex.value = 0
        emit('select', { ...props.data[selectedIndex.value], index: selectedIndex.value })
        ensureSelectedItemVisible()
        if (hoverTimer) clearTimeout(hoverTimer)
        hoverTimer = window.setTimeout(() => {
          disableHover.value = false
        }, 1000)
      } else {
        lastReachBoundaryTime.value.bottom = now
        emit('showToast', '已经到列表底端，再次点击将回到列表顶端')
      }
      return
    }
    
    isKeyboardNavigating.value = true
    if (keyboardTimer) clearTimeout(keyboardTimer)
    
    if (e.key === 'ArrowUp') {
      const nextIndex = Math.max(0, selectedIndex.value - 1)
      selectedIndex.value = nextIndex
      emit('select', { ...props.data[nextIndex], index: nextIndex })
      
      const itemTop = nextIndex * totalItemHeight.value
      if (itemTop < (containerRef.value?.scrollTop ?? 0) && containerRef.value) {
        containerRef.value.scrollTop = itemTop
      }
    } else {
      const nextIndex = Math.min(props.data.length - 1, selectedIndex.value + 1)
      selectedIndex.value = nextIndex
      emit('select', { ...props.data[nextIndex], index: nextIndex })
      
      const itemBottom = (nextIndex + 1) * totalItemHeight.value
      const scrollBottom = (containerRef.value?.scrollTop ?? 0) + containerHeight.value
      if (itemBottom > scrollBottom && containerRef.value) {
        containerRef.value.scrollTop = itemBottom - containerHeight.value
      }
    }
    
    keyboardTimer = window.setTimeout(() => {
      isKeyboardNavigating.value = false
    }, 1000)
  }
}

/**
 * 保持选中项在可视区域内
 * 如果选中项不可见，则滚动到合适位置
 */
function ensureSelectedItemVisible(): void {
  if (!containerRef.value) return
  
  const containerTop = containerRef.value.scrollTop
  const containerHeight = containerRef.value.clientHeight
  
  const itemTop = selectedIndex.value * totalItemHeight.value
  const itemBottom = (selectedIndex.value + 1) * totalItemHeight.value
  const scrollBottom = containerTop + containerHeight
  
  if (itemTop < containerTop) {
    containerRef.value.scrollTop = itemTop
  } else if (itemBottom > scrollBottom) {
    containerRef.value.scrollTop = itemBottom - containerHeight
  }
}

/**
 * 处理右键点击事件
 * @param e 鼠标事件对象
 * @param item 目标列表项
 */
const handleContextMenu = (e: MouseEvent, item: ListItem): void => {
  if (props.frozen) return
  e.preventDefault()
  emit('contextmenu', { item })
}

// ===== 生命周期钩子 =====
onMounted(() => {
  if (containerRef.value) {
    // 添加滚动事件监听
    containerRef.value.addEventListener('scroll', handleScroll)
    
    // 创建并启用 ResizeObserver
    resizeObserver = new ResizeObserver(() => {
      updateContainerHeight()
    })
    
    if (containerRef.value.parentElement) {
      resizeObserver.observe(containerRef.value.parentElement)
    }
  }
  
  // 初始化容器高度
  updateContainerHeight()
  // 添加全局事件监听
  window.addEventListener('resize', updateContainerHeight)
  window.addEventListener('keydown', handleKeyDown)
})

onBeforeUnmount(() => {
  // 清理所有事件监听和定时器
  if (containerRef.value) {
    containerRef.value.removeEventListener('scroll', handleScroll)
  }
  
  if (resizeObserver) {
    resizeObserver.disconnect()
  }
  
  window.removeEventListener('resize', updateContainerHeight)
  window.removeEventListener('keydown', handleKeyDown)
  if (keyboardTimer) clearTimeout(keyboardTimer)
  if (hoverTimer) clearTimeout(hoverTimer)
})
</script>

<template>
  <!-- 虚拟列表容器 -->
  <div 
    ref="containerRef" 
    class="virtual-list-container"
    :class="{ 'list-frozen': frozen }"
    :style="{ height: '100%' }"
    tabindex="0"
  >
    <!-- 虚拟高度占位元素 -->
    <div 
      ref="listRef"
      class="virtual-list-phantom"
      :style="{ height: needVirtualization ? `${phantomHeight}px` : 'auto' }"
    >
      <!-- 实际渲染的列表内容 -->
      <div 
        class="virtual-list"
        :style="needVirtualization ? { transform: `translateY(${offsetY}px)` } : {}"
      >
        <!-- 列表项 -->
        <div
          v-for="item in visibleData"
          :key="item.index"
          class="list-item"
          :class="{ 'selected': selectedIndex === item.index }"
          :style="{ height: `${ITEM_HEIGHT}px` }"
          @mouseenter="handleMouseEnter(item.index)"
          @click="handleClick(item)"
          @contextmenu="handleContextMenu($event, item)"
        >
          <slot name="item" :item="item">
            <!-- 默认列表项样式 -->
            <div class="default-item">
              <!-- 项目图标 -->
              <div class="item-icon">
                <svg viewBox="0 0 24 24" width="16" height="16">
                  <circle cx="12" cy="12" r="10" fill="#1a1a1a" />
                </svg>
              </div>
              <!-- 项目内容 -->
              <div class="item-content">
                <!-- 标题行：名称和标签 -->
                <div class="item-header">
                  <div class="item-name">{{ item.name }}</div>
                  <!-- 标签列表 -->
                  <div class="item-tags" v-if="item.tags?.length">
                    <span 
                      v-for="tag in item.tags" 
                      :key="tag.id"
                      class="item-tag"
                      :style="{ backgroundColor: `${tag.color}15`, color: tag.color }"
                    >
                      {{ tag.name }}
                    </span>
                  </div>
                </div>
                <!-- 路径信息 -->
                <div class="item-info">
                  <div class="item-path">{{ item.path }}</div>
                </div>
              </div>
            </div>
          </slot>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
/* 虚拟列表容器：可滚动区域 */
.virtual-list-container {
  overflow-y: auto;
  border: 1px solid #ccc;
  position: relative;
  background-color: #f4f4f4;
  width: 100%;
  height: 100%;
}

/* 虚拟高度容器：用于保持滚动条比例 */
.virtual-list-phantom {
  position: relative;
  width: 100%;
}

/* 实际列表内容容器：通过 transform 实现位置偏移 */
.virtual-list {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}

/* 列表项基础样式 */
.list-item {
  padding: 8px 16px;
  display: flex;
  align-items: center;
  transition: background-color 0.2s ease, opacity 0.2s ease;
  cursor: pointer;
  background-color: white;
}

/* 选中状态样式 */
.list-item.selected {
  background-color: rgba(64, 169, 255, 0.15);
}

/* 列表项布局容器 */
.default-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
}

/* 图标容器 */
.item-icon {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 内容区域布局 */
.item-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* 标题行布局 */
.item-header {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* 项目名称样式 */
.item-name {
  font-size: 14px;
  color: #1a1a1a;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;
  min-width: 0;
}

/* 信息行布局 */
.item-info {
  display: flex;
  gap: 4px;
}

/* 路径文本样式 */
.item-path {
  font-size: 12px;
  color: #666;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
}

/* 标签容器样式 */
.item-tags {
  display: flex;
  gap: 6px;
  flex-wrap: nowrap;
  flex-shrink: 0;
  overflow: hidden;
}

/* 单个标签样式 */
.item-tag {
  font-size: 11px;
  padding: 2px 6px;
  border-radius: 4px;
  font-weight: 500;
  white-space: nowrap;
  flex-shrink: 0;
}

/* 列表冻结状态样式 */
.list-frozen {
  pointer-events: none;  /* 禁用鼠标事件 */
  opacity: 0.7;         /* 降低透明度 */
  user-select: none;    /* 禁用文本选择 */
}
</style> 