// src/hooks/useInfiniteQuery.ts
import { useState, useCallback, useRef, useEffect } from 'react';
import { logger } from '../services/logger.client';
import { notifyError } from '../services/notificationService';

/**
 * The expected shape of a paginated API response.
 * The `items` array holds the data for the current page.
 * The `nextCursor` is an identifier (like an offset or page number) for the next set of data.
 */
export interface PaginatedResponse<T> {
  items: T[];
  nextCursor?: number | string | null;
}

/**
 * The type for the API function passed to the hook.
 * It must accept a cursor/page parameter and return a `PaginatedResponse`.
 */
type ApiFunction = (cursor?: number | string | null) => Promise<Response>;

interface UseInfiniteQueryOptions {
  initialCursor?: number | string | null;
}

/**
 * A custom hook for fetching and managing paginated data that accumulates over time.
 * Ideal for "infinite scroll" or "load more" UI patterns.
 *
 * @template T The type of the individual items being fetched.
 * @param apiFunction The API client function to execute for each page.
 * @param options Configuration options for the query.
 * @returns An object with state and methods for managing the infinite query.
 */
export function useInfiniteQuery<T>(
  apiFunction: ApiFunction,
  options: UseInfiniteQueryOptions = {}
) {
  const { initialCursor = 0 } = options;

  const [data, setData] = useState<T[]>([]);
  const [error, setError] = useState<Error | null>(null);
  const [isLoading, setIsLoading] = useState<boolean>(true); // For the very first fetch
  const [isFetchingNextPage, setIsFetchingNextPage] = useState<boolean>(false); // For subsequent fetches
  const [isRefetching, setIsRefetching] = useState<boolean>(false);
  const [hasNextPage, setHasNextPage] = useState<boolean>(true);

  // Use a ref to store the cursor for the next page.
  const nextCursorRef = useRef<number | string | null | undefined>(initialCursor);

  const fetchPage = useCallback(async (cursor?: number | string | null) => {
    // Determine which loading state to set
    const isInitialLoad = cursor === initialCursor && data.length === 0;
    if (isInitialLoad) {
      setIsLoading(true);
      setIsRefetching(false);
    } else {
      setIsFetchingNextPage(true);
    }
    setError(null);

    try {
      const response = await apiFunction(cursor);

      if (!response.ok) {
        let errorMessage = `Request failed with status ${response.status}: ${response.statusText}`;
        try {
          const errorData = await response.json();
          if (Array.isArray(errorData.issues) && errorData.issues.length > 0) {
            errorMessage = errorData.issues
              .map((issue: { path?: string[], message: string }) => `${issue.path?.join('.') || 'Error'}: ${issue.message}`)
              .join('; ');
          } else if (errorData.message) {
            errorMessage = errorData.message;
          }
        } catch { /* Ignore JSON parsing errors */ }
        throw new Error(errorMessage);
      }

      const page: PaginatedResponse<T> = await response.json();

      // Append new items to the existing data
      setData(prevData => (cursor === initialCursor ? page.items : [...prevData, ...page.items]));

      // Update cursor and hasNextPage status
      nextCursorRef.current = page.nextCursor;
      setHasNextPage(page.nextCursor != null);

    } catch (e) {
      const err = e instanceof Error ? e : new Error('An unknown error occurred.');
      logger.error('API call failed in useInfiniteQuery hook', { error: err.message, functionName: apiFunction.name });
      setError(err);
      notifyError(err.message);
    } finally {
      setIsLoading(false);
      setIsFetchingNextPage(false);
      setIsRefetching(false);
    }
  }, [apiFunction, initialCursor]);

  // Fetch the initial page on mount
  useEffect(() => {
    fetchPage(initialCursor);
  }, [fetchPage, initialCursor]);

  // Function to be called by the UI to fetch the next page
  const fetchNextPage = useCallback(() => {
    if (hasNextPage && !isFetchingNextPage) {
      fetchPage(nextCursorRef.current);
    }
  }, [fetchPage, hasNextPage, isFetchingNextPage]);

  // Function to be called by the UI to refetch the entire query from the beginning.
  const refetch = useCallback(() => {
    setIsRefetching(true);
    setData([]);
    fetchPage(initialCursor);
  }, [fetchPage, initialCursor]);

  return { data, error, isLoading, isFetchingNextPage, isRefetching, hasNextPage, fetchNextPage, refetch };
}