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

/**
 * Custom hook for infinite scroll functionality using Intersection Observer
 * @param {Function} fetchMore - Function to fetch more data
 * @param {Object} options - Configuration options
 * @returns {Object} - { loadMoreRef, isLoading, hasMore, error, loadMore }
 */
const useInfiniteScroll = (fetchMore, options = {}) => {
  const {
    threshold = 0.1, // Intersection Observer threshold
    rootMargin = "100px", // Root margin for early triggering
    enabled = true, // Whether infinite scroll is enabled
    isRestoringScroll = null, // Ref to check if scroll is being restored
  } = options;

  const [isLoading, setIsLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const [error, setError] = useState(null);
  const [page, setPage] = useState(1);

  const loadMoreRef = useRef(null);
  const observerRef = useRef(null);
  const isLoadingRef = useRef(false);
  const lastLoadTimeRef = useRef(0);
  const initialLoadRef = useRef(true);

  // Cleanup observer on unmount
  useEffect(() => {
    return () => {
      if (observerRef.current) {
        observerRef.current.disconnect();
      }
    };
  }, []);

  // Load more data function
  const loadMore = useCallback(async () => {
    if (isLoadingRef.current || !hasMore || !enabled) {
      return;
    }

    // Debounce: prevent rapid API calls (minimum 1 second between calls)
    const now = Date.now();
    if (now - lastLoadTimeRef.current < 1000) {
      console.log("[useInfiniteScroll] Debouncing load more request");
      return;
    }
    lastLoadTimeRef.current = now;

    isLoadingRef.current = true;
    setIsLoading(true);
    setError(null);

    try {
      console.log("[useInfiniteScroll] Loading more data, page:", page + 1);
      const result = await fetchMore(page + 1);

      // Check if we got data - be more explicit about empty results
      if (result && Array.isArray(result) && result.length > 0) {
        setPage((prevPage) => prevPage + 1);
        console.log("[useInfiniteScroll] Loaded", result.length, "more items");
      } else {
        // No more data available - explicitly set hasMore to false
        setHasMore(false);
        console.log(
          "[useInfiniteScroll] No more data available - result:",
          result
        );
      }
    } catch (err) {
      console.error("[useInfiniteScroll] Error loading more data:", err);
      setError(err);
      // On error, also stop infinite scroll to prevent endless retries
      setHasMore(false);
    } finally {
      setIsLoading(false);
      isLoadingRef.current = false;
    }
  }, [fetchMore, page, hasMore, enabled]);

  // Setup Intersection Observer
  useEffect(() => {
    if (!enabled || !hasMore) {
      return;
    }

    const observer = new IntersectionObserver(
      (entries) => {
        const [entry] = entries;
        if (entry.isIntersecting && !isLoadingRef.current && hasMore) {
          // Skip if scroll is being restored to prevent double API calls
          if (isRestoringScroll?.current) {
            console.log(
              "[useInfiniteScroll] Skipping load - scroll is being restored"
            );
            return;
          }
          // Prevent triggering on initial load - but only for a very short time
          if (initialLoadRef.current) {
            console.log(
              "[useInfiniteScroll] Initial load detected, setting timer to enable and load"
            );
            // Allow intersection to work after a short delay and trigger load
            setTimeout(() => {
              initialLoadRef.current = false;
              // console.log(
              //   "[useInfiniteScroll] Timer completed, checking if we should load..."
              // );

              // Check if the trigger element is still intersecting and we should load
              if (loadMoreRef.current && !isLoadingRef.current && hasMore) {
                // Skip if scroll is being restored to prevent double API calls
                if (isRestoringScroll?.current) {
                  console.log(
                    "[useInfiniteScroll] Timer: Skipping load - scroll is being restored"
                  );
                  return;
                }
                const container = loadMoreRef.current.closest(
                  ".table-chat-messages"
                );
                if (container) {
                  const scrollTop = container.scrollTop;
                  const scrollHeight = container.scrollHeight;

                  // Load more if we're within a reasonable threshold from top (recommended: 10-15% or 300-500px)
                  const threshold = Math.min(500, scrollHeight * 0.15); // 15% of scroll height or max 500px
                  const isNearTop = scrollTop < threshold;

                  if (isNearTop) {
                    console.log(
                      "[useInfiniteScroll] Timer: Near top detected, loading more..."
                    );
                    loadMore();
                  } else {
                    console.log(
                      "[useInfiniteScroll] Timer: Not near top, skipping load",
                      {
                        scrollTop,
                        scrollHeight,
                        isNearTop,
                      }
                    );
                  }
                } else {
                  // Fallback: if container not found, still try to load
                  console.log(
                    "[useInfiniteScroll] Timer: Container not found, loading more anyway..."
                  );
                  loadMore();
                }
              } else {
                console.log(
                  "[useInfiniteScroll] Timer: Conditions not met for loading",
                  {
                    hasLoadMoreRef: !!loadMoreRef.current,
                    isLoading: isLoadingRef.current,
                    hasMore,
                  }
                );
              }
            }, 500);
            return;
          }

          // More lenient check for scroll position - allow loading when near top
          const container = entry.target.closest(".table-chat-messages");
          if (container) {
            const scrollTop = container.scrollTop;
            const scrollHeight = container.scrollHeight;
            const clientHeight = container.clientHeight;

            // Load more if we're within a reasonable threshold from top (recommended: 10-15% or 300-500px)
            const threshold = Math.min(500, scrollHeight * 0.15); // 15% of scroll height or max 500px
            const isNearTop = scrollTop < threshold;

            if (isNearTop) {
              console.log(
                "[useInfiniteScroll] Near top detected, loading more...",
                { scrollTop, scrollHeight, clientHeight, isNearTop }
              );
              loadMore();
            } else {
              console.log(
                "[useInfiniteScroll] Intersection detected but not near top, ignoring...",
                { scrollTop, scrollHeight, clientHeight, isNearTop }
              );
            }
          } else {
            // Fallback: if container not found, still try to load
            console.log(
              "[useInfiniteScroll] Container not found, loading more anyway..."
            );
            loadMore();
          }
        }
      },
      {
        threshold: 0.1,
        rootMargin: "200px 0px 0px 0px", // Only trigger when element is 200px from top
      }
    );

    observerRef.current = observer;

    // Observe the load more element
    if (loadMoreRef.current) {
      observer.observe(loadMoreRef.current);
    }

    return () => {
      if (observerRef.current) {
        observerRef.current.disconnect();
      }
    };
  }, [loadMore, threshold, enabled, hasMore]);

  // Reset function to reset the infinite scroll state
  const reset = useCallback(() => {
    setPage(1);
    setHasMore(true);
    setError(null);
    setIsLoading(false);
    isLoadingRef.current = false;
    lastLoadTimeRef.current = 0; // Reset debounce timer
    initialLoadRef.current = true; // Reset initial load flag
  }, []);

  // Update hasMore state externally
  const updateHasMore = useCallback((newHasMore) => {
    setHasMore(newHasMore);
  }, []);

  // Update current page externally (for manual page loads outside the hook)
  const updateCurrentPage = useCallback(
    (newPage) => {
      setPage(newPage);
    },
    [page]
  );

  return {
    loadMoreRef,
    isLoading,
    hasMore,
    error,
    loadMore,
    reset,
    updateHasMore,
    updateCurrentPage,
    currentPage: page,
  };
};

export default useInfiniteScroll;
