import {
  FlashList,
  FlashListProps,
  MasonryFlashList,
  MasonryFlashListProps,
} from "@shopify/flash-list";
import React, { useEffect, useRef } from "react";
import { FlatList, RefreshControl } from "react-native";
import { HFlatList } from "react-native-head-tab-view";
import Animated from "react-native-reanimated";
import {
  IPropsSwipeListView,
  SwipeListView,
} from "react-native-swipe-list-view";
import { Platform_iOS, useColors, vs } from "../commons";
import { Spinner, View } from "../components";
import { LoadingParams, LoadingType } from "../types";

export type ListProps = FlashListProps<any> &
  Partial<IPropsSwipeListView<any>> &
  MasonryFlashListProps<any> & {
    refFlatList?: any;
    status?: LoadingType;

    reanimated?: boolean;
    useFlatList?: boolean;
    useSwipeList?: boolean;
    useMasonryList?: boolean;
    hideOverflow?: boolean;
    showIndicator?: boolean;

    listWidth?: number;
    listHeight?: number;
    hViewIndex?: number;

    onGetting?: (type: LoadingType, params: LoadingParams) => void;
    renderSkeleton?: (item: any, index: number) => any;
  };

export const DEFAULT_LOADING_PARAMS: LoadingParams = { page: 1, size: 10 };
const LOADING_DATA = new Array(Platform_iOS ? 6 : 4).fill(0);

const AFlatList = Animated.createAnimatedComponent(FlatList);
const AHFlatList = Animated.createAnimatedComponent(HFlatList);
const AFlashList = Animated.createAnimatedComponent(FlashList);
const AMasonryList = Animated.createAnimatedComponent(MasonryFlashList);
const ASwipeListView = Animated.createAnimatedComponent(SwipeListView);

export const List = (props: ListProps) => {
  const {
    refFlatList,
    data,
    status,

    reanimated,
    horizontal,
    useFlatList,
    useSwipeList,
    useMasonryList,
    hideOverflow,
    showIndicator,

    listWidth,
    listHeight,
    hViewIndex,
    numColumns = 1,

    style,
    onGetting,
    onEndReached,
    renderSkeleton,
    ListEmptyComponent,
    ListFooterComponent,
    ItemSeparatorComponent,
  } = props;

  const dataLength = data?.length || 0;
  const _isNormal = !status || status === "normal";
  const _isLoading = status === "loading";
  const _isRefreshing = status === "refreshing";
  const _isLoadingMore = status === "loadingmore";
  const enableGrid = !horizontal && numColumns > 1;
  const enableRefresh = !horizontal && !!status;

  const colors = useColors();
  const isEndOfListRef = useRef(true);
  const paramsRef = useRef(DEFAULT_LOADING_PARAMS);

  const _keyExtractor = (item: any, index: number) => {
    return String(item.id || item.Id || item.ID || index);
  };

  const _onRefresh = () => {
    if (!_isNormal) return;
    paramsRef.current = DEFAULT_LOADING_PARAMS;
    onGetting?.("refreshing", paramsRef.current);
  };

  const _onEndReached = () => {
    onEndReached?.();
    if (
      _isNormal &&
      !isEndOfListRef.current &&
      dataLength >= DEFAULT_LOADING_PARAMS.size
    ) {
      paramsRef.current = {
        page: paramsRef.current.page + DEFAULT_LOADING_PARAMS.page,
        size: paramsRef.current.size + DEFAULT_LOADING_PARAMS.size,
      };
      onGetting?.("loadingmore", paramsRef.current);
      isEndOfListRef.current = true;
    }
  };

  const renderRefresh = () => {
    if (!enableRefresh) return undefined;

    return (
      <RefreshControl
        refreshing={_isRefreshing}
        colors={[colors.colorApp]}
        tintColor={colors.colorApp}
        titleColor={colors.colorApp}
        onRefresh={_onRefresh}
      />
    );
  };

  const renderSpinner = () => {
    return (
      <Spinner
        mv={horizontal ? undefined : vs(14)}
        mh={horizontal ? vs(14) : undefined}
        height={horizontal ? listHeight : undefined}
      />
    );
  };

  const renderLoadingItem = (item: any, index: number) => {
    return (
      <>
        {!!index &&
          (typeof ItemSeparatorComponent === "function"
            ? // @ts-ignore
              ItemSeparatorComponent()
            : ItemSeparatorComponent)}

        <View flexRow={enableGrid}>
          {enableGrid
            ? new Array(numColumns).fill(0).map(renderSkeleton!)
            : renderSkeleton!(item, index)}
        </View>
      </>
    );
  };

  const renderLoading = () => {
    if (renderSkeleton) return <>{LOADING_DATA.map(renderLoadingItem)}</>;

    return (
      <View
        contentCenter
        width={horizontal ? listWidth || vs(100) : "100%"}
        height={horizontal ? listHeight : listHeight || vs(100)}
      >
        {renderSpinner()}
      </View>
    );
  };

  useEffect(() => {
    onGetting?.("loading", paramsRef.current);
  }, []);

  useEffect(() => {
    isEndOfListRef.current = false;
  }, [dataLength]);

  const _ListEmptyComponent = _isLoading ? renderLoading() : ListEmptyComponent;

  const _ListFooterComponent = _isLoadingMore
    ? renderSpinner()
    : ListFooterComponent;

  const MyList =
    typeof hViewIndex === "number"
      ? reanimated
        ? AHFlatList
        : HFlatList
      : useSwipeList
      ? reanimated
        ? ASwipeListView
        : SwipeListView
      : useFlatList || (horizontal && !dataLength)
      ? reanimated
        ? AFlatList
        : FlatList
      : useMasonryList
      ? reanimated
        ? AMasonryList
        : MasonryFlashList
      : reanimated
      ? AFlashList
      : FlashList;

  return (
    <View flex1={!horizontal} style={[style]}>
      <MyList
        {...props}
        ref={refFlatList}
        index={hViewIndex}
        bounces={!hideOverflow}
        refreshing={enableRefresh}
        overScrollMode={"never"}
        onEndReachedThreshold={0.5}
        style={undefined}
        keyboardShouldPersistTaps={"handled"}
        showsVerticalScrollIndicator={!!showIndicator}
        showsHorizontalScrollIndicator={!!showIndicator}
        keyExtractor={_keyExtractor}
        onEndReached={_onEndReached}
        refreshControl={
          typeof hViewIndex === "number" ? undefined : renderRefresh()
        }
        ListEmptyComponent={_ListEmptyComponent}
        ListFooterComponent={_ListFooterComponent}
        {...(enableRefresh
          ? {
              isRefreshing: _isRefreshing,
              onStartRefresh: _onRefresh,
              renderRefreshControl: renderSpinner,
            }
          : {})}
      />
    </View>
  );
};
