/**
 * Infinite loading hook for blocks.
 * Accumulates blocks across multiple pages for virtual scrolling.
 */

import { useState, useCallback, useRef, useEffect } from 'react';
import { api } from '../lib/api';
import type { ExplorerBlock } from '../lib/types';

interface UseInfiniteBlocksResult {
  blocks: ExplorerBlock[];
  isLoading: boolean;
  isLoadingMore: boolean;
  error: Error | null;
  hasMore: boolean;
  loadMore: () => Promise<void>;
  reset: () => void;
  total: number;
}

interface UseInfiniteBlocksOptions {
  pageSize?: number;
  initialLoad?: boolean;
}

export function useInfiniteBlocks(
  options: UseInfiniteBlocksOptions = {}
): UseInfiniteBlocksResult {
  const { pageSize = 50, initialLoad = true } = options;

  const [blocks, setBlocks] = useState<ExplorerBlock[]>([]);
  const [isLoading, setIsLoading] = useState(initialLoad);
  const [isLoadingMore, setIsLoadingMore] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  const [hasMore, setHasMore] = useState(true);
  const [total, setTotal] = useState(0);

  // Track current page to avoid duplicate fetches
  const currentPageRef = useRef(0);
  const isFetchingRef = useRef(false);

  const fetchPage = useCallback(
    async (page: number, isInitial = false) => {
      if (isFetchingRef.current) return;
      isFetchingRef.current = true;

      try {
        if (isInitial) {
          setIsLoading(true);
        } else {
          setIsLoadingMore(true);
        }
        setError(null);

        const response = await api.getBlocks(page, pageSize);

        setBlocks((prev) => {
          // For initial load, replace blocks
          if (isInitial) return response.data;
          // For subsequent loads, append (avoiding duplicates)
          const existingHashes = new Set(prev.map((b) => b.hash));
          const newBlocks = response.data.filter(
            (b) => !existingHashes.has(b.hash)
          );
          return [...prev, ...newBlocks];
        });

        setTotal(response.total);
        setHasMore(response.hasNext);
        currentPageRef.current = page;
      } catch (e) {
        setError(e instanceof Error ? e : new Error(String(e)));
      } finally {
        setIsLoading(false);
        setIsLoadingMore(false);
        isFetchingRef.current = false;
      }
    },
    [pageSize]
  );

  // Initial load
  useEffect(() => {
    if (initialLoad) {
      fetchPage(1, true);
    }
  }, [fetchPage, initialLoad]);

  const loadMore = useCallback(async () => {
    if (isFetchingRef.current || !hasMore) return;
    const nextPage = currentPageRef.current + 1;
    await fetchPage(nextPage);
  }, [fetchPage, hasMore]);

  const reset = useCallback(() => {
    setBlocks([]);
    setHasMore(true);
    setTotal(0);
    currentPageRef.current = 0;
    fetchPage(1, true);
  }, [fetchPage]);

  return {
    blocks,
    isLoading,
    isLoadingMore,
    error,
    hasMore,
    loadMore,
    reset,
    total,
  };
}

export default useInfiniteBlocks;
