import * as React from "react";
import * as R from "ramda";

type SequentialLoadingContextType = {
  isLoaded: (index: number) => boolean;
  onLoadFinished: (index: number) => (any) => void;
  allLoaded: boolean;
  isReadyToLoad: (index: number) => boolean;
} | null;

type SequentialLoadingProviderTypes = {
  children: JSX.Element;
  data: Array<any>;
};

export const SequentialLoadingContext =
  React.createContext<SequentialLoadingContextType>(null);

export const SequentialLoadingProvider = ({
  children,
  data,
}: SequentialLoadingProviderTypes) => {
  const initialData = React.useMemo(() => {
    const arr = new Array(data.length).fill(false);

    return R.set(R.lensIndex(0), true)(arr);
  }, []);

  const [componentsState, dispatch] = React.useReducer(
    (state, { data, type }) => {
      switch (type) {
        case "set":
          return R.set(R.lensIndex(data), true)(state);
        case "reset":
          return data;
        default:
          return state;
      }
    },
    initialData
  );

  const componentsStateRef = React.useRef(componentsState);

  React.useLayoutEffect(() => {
    componentsStateRef.current = componentsState;
  }, [componentsState]);

  const onLoadFinished = React.useCallback(
    (index) => () => {
      dispatch({ data: index, type: "set" });
    },
    []
  );

  const isLoaded = React.useCallback((index) => {
    return componentsStateRef.current[index];
  }, []);

  const isReadyToLoad = React.useCallback(
    (index) => R.compose(R.all(R.equals(true)), R.take(index))(componentsState),
    [componentsState]
  );

  const allLoaded = R.all(R.equals(true), componentsState);

  return (
    <SequentialLoadingContext.Provider
      value={{ onLoadFinished, isLoaded, allLoaded, isReadyToLoad }}
    >
      {children}
    </SequentialLoadingContext.Provider>
  );
};

export const useSequentialLoader = () =>
  React.useContext(SequentialLoadingContext);
