<template>
  <view 
    class="virtual-list"
    :style="containerStyle"
  >
    <slot name="inner" />
    <scroll-view 
      class="virtual-list-scroll"
      :scroll-y="direction === 'vertical'"
      :scroll-x="direction === 'horizontal'"
      :scroll-left="scrollLeft"
      :scroll-top="scrollTop"
      :scroll-into-view="scrollIntoView"
      :scroll-with-animation="scrollWithAnimation"
      :show-scrollbar="showScrollbar"
      @scroll="handleScroll"
    >
      <slot name="prefix" />
      <!-- 占位容器，用于撑开滚动区域 -->
      <view 
        class="virtual-list-placeholder" 
        :style="placeholderStyle"
      ></view>
      
      <!-- 可见条目容器 -->
      <view 
        class="virtual-list-content" 
        :style="contentStyle"
      >
        <slot name="list" :visibleItems="visibleItems" className="virtual-list-item" :itemStyle="itemStyle">
          <view 
            v-for="(item, index) in visibleItems" 
            :key="item.key" 
            :style="itemStyle"
            class="virtual-list-item"
          >
            <slot name="item" :key="item.key" :item="item.item" :index="startIndex + index"></slot>
          </view>
        </slot>
      </view>

      <slot v-if="data.length === 0" name="empty" />
      <slot name="suffix" />
    </scroll-view>
  </view>
</template>

<script setup lang="ts" generic="T">
import { ref, onMounted, computed, nextTick, getCurrentInstance, type PropType } from 'vue';
import { useTheme } from '../theme/ThemeDefine';
import { waitTimeOut } from '@imengyu/imengyu-utils';

const props = defineProps({
  /**
   * 条目高度（垂直滚动）或宽度（水平滚动）px
   */
  itemSize: {
    type: Number,
    required: true
  },
  /**
   * 数据源
   */
  data: {
    type: Array as PropType<T[]>,
    required: true,
    default: () => []
  },
  /**
   * 数据项的唯一键名，用于vue循环优化
   */
  dataKey: {
    type: String,
    default: undefined
  },
  /**
   * 滚动方向
   */
  direction: {
    type: String,
    default: 'vertical',
    validator: (val: string) => ['vertical', 'horizontal'].includes(val)
  },
  scrollLeft: {
    type: Number,
    default: 0
  },
  scrollTop: {
    type: Number,
    default: 0
  },
  scrollIntoView: {
    type: String,
    default: ''
  },
  scrollWithAnimation: {
    type: Boolean,
    default: false
  },
  showScrollbar: {
    type: Boolean,
    default: false
  },
  /**
   * 缓冲区大小（可视区域外额外渲染的条目数量）
   * @default 5
   */
  bufferSize: {
    type: Number,
    default: 5
  },
  /**
   * 容器样式
   * @default { height: '100%', width: '100%' }
   */
  containerStyle: {
    type: Object,
    default: () => ({ height: '100%', width: '100%' })
  }
});

const emit = defineEmits([ 'scroll' ]);

const scrollPosition = ref(0);
const containerSize = ref(0);
const startIndex = ref(0);
const visibleCount = ref(0);

const theme = useTheme();

const instance = getCurrentInstance();

onMounted(() => {
  updateContainerSizeAndFlush();
});

let flushLock = false;

async function updateContainerSize() {
  return new Promise<number>((resolve) => {
    const query = uni.createSelectorQuery().in(instance);
    query.select('.virtual-list-scroll').boundingClientRect((data) => {
      if (data && !(data instanceof Array))
        containerSize.value = (props.direction === 'vertical' ? data.height : data.width) || 0;
      else
        containerSize.value = 0;
      resolve(containerSize.value);
    }).exec();  
  })
}
async function updateContainerSizeAndFlush() {
  flushLock = true;
  await nextTick();
  const size = await updateContainerSize();
  if (size === 0) {
    await waitTimeOut(200);
    await updateContainerSize();
  }
  flushLock = false;
  calculateVisibleItems();
}
function handleScroll(e: any) {
  scrollPosition.value = props.direction === 'vertical' 
    ? e.detail.scrollTop 
    : e.detail.scrollLeft;
  calculateVisibleItems();
  emit('scroll', e);

}
function calculateVisibleItems() {
  if (containerSize.value === 0) {
    if (!flushLock)
      updateContainerSizeAndFlush();
    return;
  }

  const itemSize = props.itemSize;
  
  // 计算起始索引（减去缓冲区）
  const newStartIndex = Math.max(0, 
    Math.floor(scrollPosition.value / itemSize) - props.bufferSize
  );
  
  // 计算可见条目数量（容器尺寸/条目尺寸 + 2倍缓冲区）
  const newVisibleCount = Math.ceil(containerSize.value / itemSize) + props.bufferSize * 2;
  
  startIndex.value = newStartIndex;
  visibleCount.value = newVisibleCount;
}

// 可见数据
const visibleItems = computed(() => {
  const endIndex = Math.min(
    props.data.length, 
    startIndex.value + visibleCount.value
  );
  return props.data.slice(startIndex.value, endIndex).map((item, index) => ({
    item,
    key: props.dataKey ? ((item as Record<string, any>)[props.dataKey] ?? index) : index,
  }));
});

// 占位容器样式（总滚动区域）
const placeholderStyle = computed(() => {
  const size = `${props.data.length * props.itemSize}px`;
  return props.direction === 'vertical' 
    ? { height: size, width: '100%' }
    : { width: size, height: '100%' };
});

// 内容容器偏移量
const contentStyle = computed(() => {
  const offset = `${startIndex.value * props.itemSize}px`;
  return props.direction === 'vertical'
    ? { transform: `translateY(${offset})`, width: '100%' }
    : { transform: `translateX(${offset})`, height: '100%' };
});

// 条目样式
const itemStyle = computed(() => {
  const size = `${props.itemSize}px`;
  return props.direction === 'vertical'
    ? { height: size, width: '100%' }
    : { width: size, height: '100%' };
});


defineExpose({
  updateContainerSizeAndFlush,
})
</script>

<style scoped>
.virtual-list {
  position: relative;
  overflow: hidden;
}
.virtual-list-scroll {
  position: relative;
  overflow: hidden;
  width: 100%;
  height: 100%;
}
.virtual-list-placeholder {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
}
.virtual-list-content {
  position: absolute;
  top: 0;
  left: 0;
}
.virtual-list-item {
  box-sizing: border-box;
}
</style>