"use client";

/**
 * Virtual Scroll 훅
 * 대용량 피벗 데이터의 가상 스크롤 처리
 */

import { useState, useEffect, useRef, useMemo, useCallback } from "react";

// ==================== 타입 ====================

export interface VirtualScrollOptions {
  itemCount: number; // 전체 아이템 수
  itemHeight: number; // 각 아이템 높이 (px)
  containerHeight: number; // 컨테이너 높이 (px)
  overscan?: number; // 버퍼 아이템 수 (기본: 5)
}

export interface VirtualScrollResult {
  // 현재 보여야 할 아이템 범위
  startIndex: number;
  endIndex: number;

  // 가상 스크롤 관련 값
  totalHeight: number; // 전체 높이
  offsetTop: number; // 상단 오프셋

  // 보여지는 아이템 목록
  visibleItems: number[];

  // 이벤트 핸들러
  onScroll: (scrollTop: number) => void;

  // 컨테이너 ref
  containerRef: React.RefObject<HTMLDivElement>;
}

// ==================== 훅 ====================

export function useVirtualScroll(options: VirtualScrollOptions): VirtualScrollResult {
  const {
    itemCount,
    itemHeight,
    containerHeight,
    overscan = 5,
  } = options;

  const containerRef = useRef<HTMLDivElement>(null);
  const [scrollTop, setScrollTop] = useState(0);

  // 보이는 아이템 수
  const visibleCount = Math.ceil(containerHeight / itemHeight);

  // 시작/끝 인덱스 계산 (음수 방지)
  const { startIndex, endIndex } = useMemo(() => {
    // itemCount가 0이면 빈 배열
    if (itemCount === 0) {
      return { startIndex: 0, endIndex: -1 };
    }
    const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
    const end = Math.min(
      itemCount - 1,
      Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan
    );
    return { startIndex: start, endIndex: Math.max(start, end) }; // end가 start보다 작지 않도록
  }, [scrollTop, itemHeight, containerHeight, itemCount, overscan]);

  // 전체 높이
  const totalHeight = itemCount * itemHeight;

  // 상단 오프셋
  const offsetTop = startIndex * itemHeight;

  // 보이는 아이템 인덱스 배열
  const visibleItems = useMemo(() => {
    const items: number[] = [];
    for (let i = startIndex; i <= endIndex; i++) {
      items.push(i);
    }
    return items;
  }, [startIndex, endIndex]);

  // 스크롤 핸들러
  const onScroll = useCallback((newScrollTop: number) => {
    setScrollTop(newScrollTop);
  }, []);

  // 스크롤 이벤트 리스너
  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    const handleScroll = () => {
      setScrollTop(container.scrollTop);
    };

    container.addEventListener("scroll", handleScroll, { passive: true });

    return () => {
      container.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return {
    startIndex,
    endIndex,
    totalHeight,
    offsetTop,
    visibleItems,
    onScroll,
    containerRef,
  };
}

// ==================== 열 가상 스크롤 ====================

export interface VirtualColumnScrollOptions {
  columnCount: number; // 전체 열 수
  columnWidth: number; // 각 열 너비 (px)
  containerWidth: number; // 컨테이너 너비 (px)
  overscan?: number;
}

export interface VirtualColumnScrollResult {
  startIndex: number;
  endIndex: number;
  totalWidth: number;
  offsetLeft: number;
  visibleColumns: number[];
  onScroll: (scrollLeft: number) => void;
}

export function useVirtualColumnScroll(
  options: VirtualColumnScrollOptions
): VirtualColumnScrollResult {
  const {
    columnCount,
    columnWidth,
    containerWidth,
    overscan = 3,
  } = options;

  const [scrollLeft, setScrollLeft] = useState(0);

  const { startIndex, endIndex } = useMemo(() => {
    const start = Math.max(0, Math.floor(scrollLeft / columnWidth) - overscan);
    const end = Math.min(
      columnCount - 1,
      Math.ceil((scrollLeft + containerWidth) / columnWidth) + overscan
    );
    return { startIndex: start, endIndex: end };
  }, [scrollLeft, columnWidth, containerWidth, columnCount, overscan]);

  const totalWidth = columnCount * columnWidth;
  const offsetLeft = startIndex * columnWidth;

  const visibleColumns = useMemo(() => {
    const cols: number[] = [];
    for (let i = startIndex; i <= endIndex; i++) {
      cols.push(i);
    }
    return cols;
  }, [startIndex, endIndex]);

  const onScroll = useCallback((newScrollLeft: number) => {
    setScrollLeft(newScrollLeft);
  }, []);

  return {
    startIndex,
    endIndex,
    totalWidth,
    offsetLeft,
    visibleColumns,
    onScroll,
  };
}

// ==================== 2D 가상 스크롤 (행 + 열) ====================

export interface Virtual2DScrollOptions {
  rowCount: number;
  columnCount: number;
  rowHeight: number;
  columnWidth: number;
  containerHeight: number;
  containerWidth: number;
  rowOverscan?: number;
  columnOverscan?: number;
}

export interface Virtual2DScrollResult {
  // 행 범위
  rowStartIndex: number;
  rowEndIndex: number;
  totalHeight: number;
  offsetTop: number;
  visibleRows: number[];

  // 열 범위
  columnStartIndex: number;
  columnEndIndex: number;
  totalWidth: number;
  offsetLeft: number;
  visibleColumns: number[];

  // 스크롤 핸들러
  onScroll: (scrollTop: number, scrollLeft: number) => void;

  // 컨테이너 ref
  containerRef: React.RefObject<HTMLDivElement>;
}

export function useVirtual2DScroll(
  options: Virtual2DScrollOptions
): Virtual2DScrollResult {
  const {
    rowCount,
    columnCount,
    rowHeight,
    columnWidth,
    containerHeight,
    containerWidth,
    rowOverscan = 5,
    columnOverscan = 3,
  } = options;

  const containerRef = useRef<HTMLDivElement>(null);
  const [scrollTop, setScrollTop] = useState(0);
  const [scrollLeft, setScrollLeft] = useState(0);

  // 행 계산
  const { rowStartIndex, rowEndIndex, visibleRows } = useMemo(() => {
    const start = Math.max(0, Math.floor(scrollTop / rowHeight) - rowOverscan);
    const end = Math.min(
      rowCount - 1,
      Math.ceil((scrollTop + containerHeight) / rowHeight) + rowOverscan
    );

    const rows: number[] = [];
    for (let i = start; i <= end; i++) {
      rows.push(i);
    }

    return {
      rowStartIndex: start,
      rowEndIndex: end,
      visibleRows: rows,
    };
  }, [scrollTop, rowHeight, containerHeight, rowCount, rowOverscan]);

  // 열 계산
  const { columnStartIndex, columnEndIndex, visibleColumns } = useMemo(() => {
    const start = Math.max(0, Math.floor(scrollLeft / columnWidth) - columnOverscan);
    const end = Math.min(
      columnCount - 1,
      Math.ceil((scrollLeft + containerWidth) / columnWidth) + columnOverscan
    );

    const cols: number[] = [];
    for (let i = start; i <= end; i++) {
      cols.push(i);
    }

    return {
      columnStartIndex: start,
      columnEndIndex: end,
      visibleColumns: cols,
    };
  }, [scrollLeft, columnWidth, containerWidth, columnCount, columnOverscan]);

  const totalHeight = rowCount * rowHeight;
  const totalWidth = columnCount * columnWidth;
  const offsetTop = rowStartIndex * rowHeight;
  const offsetLeft = columnStartIndex * columnWidth;

  const onScroll = useCallback((newScrollTop: number, newScrollLeft: number) => {
    setScrollTop(newScrollTop);
    setScrollLeft(newScrollLeft);
  }, []);

  // 스크롤 이벤트 리스너
  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    const handleScroll = () => {
      setScrollTop(container.scrollTop);
      setScrollLeft(container.scrollLeft);
    };

    container.addEventListener("scroll", handleScroll, { passive: true });

    return () => {
      container.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return {
    rowStartIndex,
    rowEndIndex,
    totalHeight,
    offsetTop,
    visibleRows,
    columnStartIndex,
    columnEndIndex,
    totalWidth,
    offsetLeft,
    visibleColumns,
    onScroll,
    containerRef,
  };
}

export default useVirtualScroll;

