<!-- SPEC: PJ2026-010405 云端控制台. -->
<!-- Responsibility: 提供按容器宽度分列、按虚拟行窗口化的通用响应式网格。 -->

<script setup lang="ts" generic="T extends object">
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import VirtualList from "./VirtualList.vue";
import {
  calculateVirtualGridColumnCount,
  calculateVirtualGridRowWindowLimit,
  chunkVirtualGridItems,
  type VirtualGridRow,
} from "./virtual-grid";

const props = withDefaults(defineProps<{
  items: T[];
  itemKey: (item: T) => string;
  rowHeight: number;
  height?: number | string;
  minimumColumnWidth?: number;
  maximumColumns?: number;
  gap?: number;
  overscan?: number;
  windowLimit?: number;
  ariaLabel?: string;
}>(), {
  height: 420,
  minimumColumnWidth: 360,
  maximumColumns: 3,
  gap: 10,
  overscan: 3,
  ariaLabel: "响应式虚拟网格",
});

const root = ref<HTMLElement | null>(null);
const width = ref(0);
let resizeObserver: ResizeObserver | null = null;
const columns = computed(() => calculateVirtualGridColumnCount(width.value, props.minimumColumnWidth, props.maximumColumns, props.gap));
const rows = computed(() => chunkVirtualGridItems(props.items, columns.value, props.itemKey));
const rowWindowLimit = computed(() => calculateVirtualGridRowWindowLimit(props.windowLimit, columns.value));
const rootStyle = computed(() => ({ height: typeof props.height === "number" ? `${props.height}px` : props.height }));

function rowKey(row: VirtualGridRow<T>): string {
  return row.key;
}

function readWidth(): void {
  const nextWidth = root.value?.clientWidth ?? 0;
  if (nextWidth > 0 && nextWidth !== width.value) width.value = nextWidth;
}

onMounted(() => {
  readWidth();
  if (!root.value) return;
  if (typeof ResizeObserver !== "undefined") {
    resizeObserver = new ResizeObserver(readWidth);
    resizeObserver.observe(root.value);
  } else if (typeof window !== "undefined") {
    window.addEventListener("resize", readWidth, { passive: true });
  }
});

onBeforeUnmount(() => {
  resizeObserver?.disconnect();
  resizeObserver = null;
  if (typeof window !== "undefined") window.removeEventListener("resize", readWidth);
});
</script>

<template>
  <div ref="root" class="console-virtual-grid" :style="rootStyle" :data-columns="columns">
    <VirtualList
      :items="rows"
      :item-key="rowKey"
      :item-height="rowHeight + gap"
      height="100%"
      :overscan="overscan"
      :window-limit="rowWindowLimit"
      root-role="list"
      item-role="presentation"
      :aria-label="ariaLabel"
    >
      <template #default="{ item: row }">
        <div
          class="console-virtual-grid-row"
          role="presentation"
          :style="{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, gap: `${gap}px`, height: `${rowHeight}px` }"
        >
          <div v-for="(item, localIndex) in row.items" :key="itemKey(item)" role="listitem" class="console-virtual-grid-cell">
            <slot :item="item" :index="row.startIndex + localIndex" :columns="columns" />
          </div>
        </div>
      </template>
    </VirtualList>
  </div>
</template>
