import { ArrowUpIcon, LoaderIcon } from "lucide-react";
import { observer } from "mobx-react-lite";
import { useCallback, useEffect, useRef, useState } from "react";
import { matchPath } from "react-router-dom";
import PullToRefresh from "react-simple-pull-to-refresh";
import { Button } from "@/components/ui/button";
import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import { Routes } from "@/router";
import { memoStore, userStore, viewStore } from "@/store";
import { State } from "@/types/proto/api/v1/common_pb";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import Empty from "../Empty";
import type { MemoRenderContext } from "../MasonryView";
import MasonryView from "../MasonryView";
import MemoEditor from "../MemoEditor";
import MemoFilters from "../MemoFilters";
import MemoSkeleton from "../MemoSkeleton";

interface Props {
  renderer: (memo: Memo, context?: MemoRenderContext) => JSX.Element;
  listSort?: (list: Memo[]) => Memo[];
  state?: State;
  orderBy?: string;
  filter?: string;
  pageSize?: number;
  showCreator?: boolean;
}

const PagedMemoList = observer((props: Props) => {
  const t = useTranslate();
  const { md } = useResponsiveWidth();

  // Simplified state management - separate state variables for clarity
  const [isRequesting, setIsRequesting] = useState(true);
  const [nextPageToken, setNextPageToken] = useState("");

  // Ref to manage auto-fetch timeout to prevent memory leaks
  const autoFetchTimeoutRef = useRef<number | null>(null);
  // Ref to track if initial fetch has been triggered to prevent duplicates
  const initialFetchTriggeredRef = useRef(false);

  // Apply custom sorting if provided, otherwise use store memos directly
  const sortedMemoList = props.listSort ? props.listSort(memoStore.state.memos) : memoStore.state.memos;

  // Show memo editor only on the root route
  const showMemoEditor = Boolean(matchPath(Routes.ROOT, window.location.pathname));

  // Fetch more memos with pagination support
  const fetchMoreMemos = useCallback(
    async (pageToken: string) => {
      setIsRequesting(true);

      try {
        const response = await memoStore.fetchMemos({
          state: props.state || State.NORMAL,
          orderBy: props.orderBy || "display_time desc",
          filter: props.filter,
          pageSize: props.pageSize || DEFAULT_LIST_MEMOS_PAGE_SIZE,
          pageToken,
        });

        setNextPageToken(response?.nextPageToken || "");

        // Batch-fetch creators in parallel to avoid individual fetches in MemoView
        // This significantly improves perceived performance by pre-populating the cache
        if (response?.memos && props.showCreator) {
          const uniqueCreators = Array.from(new Set(response.memos.map((memo) => memo.creator)));
          await Promise.allSettled(uniqueCreators.map((creator) => userStore.getOrFetchUserByName(creator)));
        }
      } finally {
        setIsRequesting(false);
      }
    },
    [props.state, props.orderBy, props.filter, props.pageSize, props.showCreator],
  );

  // Helper function to check if page has enough content to be scrollable
  const isPageScrollable = useCallback(() => {
    const documentHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
    return documentHeight > window.innerHeight + 100; // 100px buffer for safe measure
  }, []);

  // Auto-fetch more content if page isn't scrollable and more data is available
  const checkAndFetchIfNeeded = useCallback(async () => {
    // Clear any pending auto-fetch timeout
    if (autoFetchTimeoutRef.current) {
      clearTimeout(autoFetchTimeoutRef.current);
    }

    // Wait for DOM to update before checking scrollability
    await new Promise((resolve) => setTimeout(resolve, 200));

    // Only fetch if: page isn't scrollable, we have more data, not currently loading, and have memos
    const shouldFetch = !isPageScrollable() && nextPageToken && !isRequesting && sortedMemoList.length > 0;

    if (shouldFetch) {
      await fetchMoreMemos(nextPageToken);

      // Schedule another check with delay to prevent rapid successive calls
      autoFetchTimeoutRef.current = window.setTimeout(() => {
        checkAndFetchIfNeeded();
      }, 500);
    }
  }, [nextPageToken, isRequesting, sortedMemoList.length, isPageScrollable, fetchMoreMemos]);

  // Refresh the entire memo list from the beginning
  const refreshList = useCallback(async () => {
    memoStore.state.updateStateId();
    setNextPageToken("");
    await fetchMoreMemos("");
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [fetchMoreMemos]);

  // Track previous props to detect changes
  const propsKey = `${props.state}-${props.orderBy}-${props.filter}-${props.pageSize}`;
  const prevPropsKeyRef = useRef<string>();

  // Initial load and reload when props change
  useEffect(() => {
    const propsChanged = prevPropsKeyRef.current !== undefined && prevPropsKeyRef.current !== propsKey;
    prevPropsKeyRef.current = propsKey;

    // Skip first render if we haven't marked it yet
    if (!initialFetchTriggeredRef.current) {
      initialFetchTriggeredRef.current = true;
      refreshList();
      return;
    }
    // For subsequent changes, refresh if props actually changed
    if (propsChanged) {
      refreshList();
    }
  }, [refreshList, propsKey]);

  // Auto-fetch more content when list changes and page isn't full
  useEffect(() => {
    if (!isRequesting && sortedMemoList.length > 0) {
      checkAndFetchIfNeeded();
    }
  }, [sortedMemoList.length, isRequesting, checkAndFetchIfNeeded]);

  // Cleanup timeout on component unmount
  useEffect(() => {
    return () => {
      if (autoFetchTimeoutRef.current) {
        clearTimeout(autoFetchTimeoutRef.current);
      }
    };
  }, []);

  // Infinite scroll: fetch more when user scrolls near bottom
  useEffect(() => {
    if (!nextPageToken) return;

    const handleScroll = () => {
      const nearBottom = window.innerHeight + window.scrollY >= document.body.offsetHeight - 300;
      if (nearBottom && !isRequesting) {
        fetchMoreMemos(nextPageToken);
      }
    };

    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, [nextPageToken, isRequesting, fetchMoreMemos]);

  const children = (
    <div className="flex flex-col justify-start items-start w-full max-w-full">
      {/* Show skeleton loader during initial load */}
      {isRequesting && sortedMemoList.length === 0 ? (
        <div className="w-full flex flex-col justify-start items-center">
          <MemoSkeleton showCreator={props.showCreator} count={4} />
        </div>
      ) : (
        <>
          <MasonryView
            memoList={sortedMemoList}
            renderer={props.renderer}
            prefixElement={
              <>
                {showMemoEditor ? <MemoEditor className="mb-2" cacheKey="home-memo-editor" /> : undefined}
                <MemoFilters />
              </>
            }
            listMode={viewStore.state.layout === "LIST"}
          />

          {/* Loading indicator for pagination */}
          {isRequesting && (
            <div className="w-full flex flex-row justify-center items-center my-4">
              <LoaderIcon className="animate-spin text-muted-foreground" />
            </div>
          )}

          {/* Empty state or back-to-top button */}
          {!isRequesting && (
            <>
              {!nextPageToken && sortedMemoList.length === 0 ? (
                <div className="w-full mt-12 mb-8 flex flex-col justify-center items-center italic">
                  <Empty />
                  <p className="mt-2 text-muted-foreground">{t("message.no-data")}</p>
                </div>
              ) : (
                <div className="w-full opacity-70 flex flex-row justify-center items-center my-4">
                  <BackToTop />
                </div>
              )}
            </>
          )}
        </>
      )}
    </div>
  );

  if (md) {
    return children;
  }

  return (
    <PullToRefresh
      onRefresh={() => refreshList()}
      pullingContent={
        <div className="w-full flex flex-row justify-center items-center my-4">
          <LoaderIcon className="opacity-60" />
        </div>
      }
      refreshingContent={
        <div className="w-full flex flex-row justify-center items-center my-4">
          <LoaderIcon className="animate-spin" />
        </div>
      }
    >
      {children}
    </PullToRefresh>
  );
});

const BackToTop = () => {
  const t = useTranslate();
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    const handleScroll = () => {
      const shouldShow = window.scrollY > 400;
      setIsVisible(shouldShow);
    };

    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);

  const scrollToTop = () => {
    window.scrollTo({
      top: 0,
      behavior: "smooth",
    });
  };

  // Don't render if not visible
  if (!isVisible) {
    return null;
  }

  return (
    <Button variant="ghost" onClick={scrollToTop}>
      {t("router.back-to-top")}
      <ArrowUpIcon className="ml-1 w-4 h-auto" />
    </Button>
  );
};

export default PagedMemoList;
