import { useVirtualizer } from "@tanstack/react-virtual";
import clsx from "clsx";
import * as React from "react";
import { SuffixContext, useSuffixContext } from "../../contexts";
import { useComposedRefs } from "../../utils/mergeRefs";
import * as styles from "./styles.module.css";

interface Props {
  className: string;
  data: any[];
  type?: string;
  __length?: number;
  children?: (value: any, index: number) => React.ReactElement;
}

export function List({
  className,
  data,
  type: _type,
  __length,
  children,
  ...props
}: Props) {
  const suffix = useSuffixContext();
  return children && Array.isArray(data) && data.length ? (
    <ul className={clsx(styles.list, className)} {...props}>
      {data.map((value, idx) => {
        const itemKey = `${value?.id ?? idx}`;
        const key = suffix ? `${itemKey}-${suffix}` : itemKey;
        return (
          <SuffixContext.Provider value={key} key={key}>
            <li className={styles.item}>
              {children ? children(value, idx) : null}
            </li>
          </SuffixContext.Provider>
        );
      })}
    </ul>
  ) : null;
}

export function Repeater({ data, children }: Props) {
  const suffix = useSuffixContext();
  return children && Array.isArray(data) && data.length ? (
    <>
      {data.map((value, idx) => {
        const itemKey = `${value?.id ?? idx}`;
        const key = suffix ? `${itemKey}-${suffix}` : itemKey;
        return (
          <SuffixContext.Provider value={key} key={key}>
            {children ? children(value, idx) : null}
          </SuffixContext.Provider>
        );
      })}
    </>
  ) : null;
}

export function VirtualList({
  ref,
  className,
  data,
  type: _type,
  __length,
  children,
  estimatedSize = 35,
  overscan = 5,
  hasNextPage = false,
  isFetchingNextPage = false,
  fetchNextPage,
  ...props
}: Props & {
  ref?: React.Ref<HTMLDivElement>;
  estimatedSize?: number;
  overscan?: number;
  hasNextPage?: boolean;
  isFetchingNextPage?: boolean;
  fetchNextPage?: () => void;
}) {
  const parentRef = React.useRef<HTMLDivElement>(null);
  const suffix = useSuffixContext();
  const items = Array.isArray(data) ? data : [];

  // The virtualizer
  const rowVirtualizer = useVirtualizer({
    count: hasNextPage ? items.length + 1 : items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => estimatedSize,
    overscan: overscan,
  });

  React.useEffect(() => {
    const lastItem = rowVirtualizer.getVirtualItems().at(-1);
    if (!lastItem) return;
    if (
      lastItem.index === items.length - 1 &&
      hasNextPage &&
      fetchNextPage &&
      !isFetchingNextPage
    ) {
      fetchNextPage();
    }
  }, [
    hasNextPage,
    fetchNextPage,
    items.length,
    isFetchingNextPage,
    rowVirtualizer.getVirtualItems(),
  ]);

  const allRefs = useComposedRefs(parentRef, ref);

  return children && items.length ? (
    <div ref={allRefs} className={clsx(styles.list, className)} {...props}>
      <ul
        style={{
          height: `${rowVirtualizer.getTotalSize()}px`,
          width: "100%",
          position: "relative",
        }}
      >
        {rowVirtualizer.getVirtualItems().map((virtualItem) => {
          const isLoaderRow = virtualItem.index > items.length - 1;
          const value = items[virtualItem.index];
          const itemKey = `${value?.id ?? virtualItem.index}`;
          const key = suffix ? `${itemKey}-${suffix}` : itemKey;
          return (
            <SuffixContext.Provider value={key} key={virtualItem.index}>
              <li
                className="__list-item"
                style={{
                  position: "absolute",
                  top: 0,
                  left: 0,
                  width: "100%",
                  height: `${virtualItem.size}px`,
                  transform: `translateY(${virtualItem.start}px)`,
                }}
              >
                {isLoaderRow ? (
                  <span className="__list-item-loader">
                    {hasNextPage ? "Loading more..." : "No more items"}
                  </span>
                ) : children ? (
                  children(value, virtualItem.index)
                ) : null}
              </li>
            </SuffixContext.Provider>
          );
        })}
      </ul>
    </div>
  ) : null;
}
