import { LegendList } from "@legendapp/list";
import { type ComponentType, type ReactElement, useRef } from "react";
import { type Breakpoint, HR, useBreakpointMap } from "~/primitives";
import { useSetError } from "~/providers/error-provider";
import { type QueryIdentifier, useInfiniteFetch } from "~/query";
import { ErrorView } from "../ui/errors";

export type Layout = {
	numColumns: Breakpoint<number>;
	size: Breakpoint<number>;
	gap: Breakpoint<number>;
	layout: "grid" | "horizontal" | "vertical";
};

export const InfiniteFetch = <Data, Props, _, Kind extends number | string>({
	query,
	placeholderCount = 2,
	incremental = false,
	Render,
	Loader,
	layout,
	Empty,
	divider,
	Header,
	headerProps,
	getItemType,
	getItemSize,
	fetchMore = true,
	nested = false,
	...props
}: {
	query: QueryIdentifier<Data>;
	placeholderCount?: number;
	layout: Layout;
	horizontal?: boolean;
	Render: (props: { item: Data; index: number }) => ReactElement | null;
	Loader: (props: { index: number }) => ReactElement | null;
	Empty?: JSX.Element;
	incremental?: boolean;
	divider?: true | ComponentType;
	Header?: ComponentType<Props & { children: JSX.Element }> | ReactElement;
	headerProps?: Props;
	getItemType?: (item: Data | null, index: number) => Kind;
	getItemSize?: (kind: Kind) => number;
	fetchMore?: boolean;
	nested?: boolean;
}): JSX.Element | null => {
	const { numColumns, size, gap } = useBreakpointMap(layout);
	const [setOffline, clearOffline] = useSetError("offline");
	const oldItems = useRef<Data[] | undefined>(undefined);
	let {
		items,
		isPaused,
		error,
		fetchNextPage,
		isFetching,
		refetch,
		isRefetching,
	} = useInfiniteFetch(query);
	if (incremental && items) oldItems.current = items;

	if (!query.infinite)
		console.warn("A non infinite query was passed to an InfiniteFetch.");

	if (isPaused) setOffline();
	else clearOffline();

	if (error) return <ErrorView error={error} />;

	if (incremental) items ??= oldItems.current;
	const count = items
		? numColumns - (items.length % numColumns)
		: placeholderCount;
	const placeholders = [...Array(count === 0 ? numColumns : count)].fill(null);
	const data =
		isFetching || !items ? [...(items || []), ...placeholders] : items;

	return (
		<LegendList
			data={data}
			recycleItems
			renderItem={({ item, index }) =>
				item ? <Render index={index} item={item} /> : <Loader index={index} />
			}
			keyExtractor={(item: any, index) => (item ? item.id : index)}
			estimatedItemSize={size}
			horizontal={layout.layout === "horizontal"}
			numColumns={layout.layout === "horizontal" ? 1 : numColumns}
			onEndReached={fetchMore ? () => fetchNextPage() : undefined}
			onEndReachedThreshold={0.5}
			onRefresh={layout.layout !== "horizontal" ? refetch : undefined}
			refreshing={isRefetching}
			ListHeaderComponent={Header}
			ItemSeparatorComponent={
				divider === true ? HR : (divider as any) || undefined
			}
			ListEmptyComponent={Empty}
			contentContainerStyle={{ gap, marginHorizontal: gap }}
			{...props}
		/>
	);
};
