import { CSSProperties, forwardRef, memo, useCallback, useRef } from 'react';

import { VariableSizeList as ReactWindowList, ListChildComponentProps, ListOnScrollProps } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';

import { IVariableSizedListProps, ListRef } from './type';

/**
 * Web `List` — thin wrapper around `react-window`'s `VariableSizeList`
 * + `react-window-infinite-loader` for lazy loading.
 *
 * NOTE: animation hooks on the web side are intentionally not wired up yet;
 * see the TODO in the original yagami implementation.
 */
const List = memo(
  forwardRef<ListRef, IVariableSizedListProps<unknown, { data: unknown[] }>>(
    (
      {
        loadMoreItems,
        data,
        minimumBatchSize,
        threshold,
        itemCount,
        itemSize,
        itemData,
        RenderItem,
        height,
        width,
        style,
        outerElementType,
        overScanCount,
        onScroll,
      },
      // react-window's ref is a VariableSizeList instance; the public ListRef
      // is only partially satisfied (scrollToEnd isn't exposed by react-window
      // directly). Consumers that need it typically wrap and call scrollTo.

      _forwardedRef,
    ) => {
      const listRef = useRef<ReactWindowList<{ data: unknown[] }>>(null);

      const isRowLoaded = (index: number) => {
        return index < data.length;
      };

      // TODO: add web-based implementation for row enter / exit animations.
      const RenderListItem = useCallback(({ data: listData, index }: ListChildComponentProps<{ data: unknown[] }>) => {
        return <RenderItem item={listData.data[index]} index={index} itemData={itemData} isVisible={false} />;
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, []);

      return (
        // nicely with React 18's children-as-function pattern here.
        <InfiniteLoader
          isItemLoaded={isRowLoaded}
          itemCount={itemCount ?? 0}
          loadMoreItems={loadMoreItems}
          minimumBatchSize={minimumBatchSize}
          threshold={threshold}
        >
          {({ onItemsRendered }) => {
            return (
              // data generic which we relax here for wrapper ergonomics.
              <ReactWindowList
                height={height ?? 0}
                width={width ?? 0}
                itemCount={itemCount ?? 0}
                itemSize={itemSize ?? (() => 0)}
                itemData={itemData}
                ref={listRef}
                style={style as CSSProperties}
                // outerElementType at runtime.
                outerElementType={outerElementType}
                overscanCount={overScanCount}
                useIsScrolling
                onItemsRendered={(params) => {
                  onItemsRendered(params);
                }}
                onScroll={(_e: ListOnScrollProps) => {
                  onScroll?.({
                    nativeEvent: {
                      contentOffset: {
                        x: 0,
                        y: 0,
                      },
                      layoutMeasurement: {
                        height: 0,
                        width: 0,
                      },
                      contentSize: {
                        height: 0,
                        width: 0,
                      },
                    },
                  });
                }}
              >
                {RenderListItem}
              </ReactWindowList>
            );
          }}
        </InfiniteLoader>
      );
    },
  ),
) as unknown as <T, U extends { data: T[] }>(
  props: IVariableSizedListProps<T, U> & { ref?: React.Ref<ListRef> },
) => React.JSX.Element;

(List as unknown as { displayName: string }).displayName = 'List';

export default List;
