import React, { useContext, useEffect, createContext, useMemo, useState, useCallback } from "react";
import { RampCatalog } from "./types";
import { Loadable } from "../../types";

import api from "./api";

const initialState: Loadable<RampCatalog> = {
  isLoading: false,
  value: null,
  error: null,
};

export type RampCatalogDataAPI = {
  fetchRampCatalog: () => Promise<RampCatalog>;
};

type RampCatalogContextType = {
  state: Loadable<RampCatalog>;
  updateCatalog: () => Promise<void>;
};

export const rampCatalogContext = createContext<RampCatalogContextType>({
  state: initialState,
  updateCatalog: () => Promise.resolve(),
});

type RampCatalogProviderProps = {
  children: React.ReactNode;
  updateFrequency: number;
  provider?: string; // "production" - only for backwards compatibility
};

export function useRampCatalogContext() {
  const context = useContext(rampCatalogContext);

  if (context === undefined) {
    throw new Error("useRampCatalog must be used within a RampCatalogProvider");
  }
  return context.state;
}

export function RampCatalogProvider({
  children,
  updateFrequency,
}: RampCatalogProviderProps): React.JSX.Element {
  const [state, setState] = useState<Loadable<RampCatalog>>(initialState);

  const updateCatalog = useCallback(async () => {
    setState(currentState => ({
      ...currentState,
      isLoading: true,
      error: null,
    }));

    const result = await api.fetchRampCatalog().then(
      (value): { value: RampCatalog; fetchError: null } => ({ value, fetchError: null }),
      (e): { value: null; fetchError: unknown } => ({ value: null, fetchError: e }),
    );

    if (result.fetchError !== null) {
      setState(currentState => ({
        ...currentState,
        isLoading: false,
        error: result.fetchError,
      }));
    } else {
      setState(() => ({
        isLoading: false,
        value: result.value,
        error: null,
      }));
    }
  }, []);

  const value: RampCatalogContextType = useMemo(
    () => ({
      state,
      updateCatalog,
    }),
    [state, updateCatalog],
  );

  useEffect(() => {
    const interval = setInterval(() => {
      updateCatalog();
    }, updateFrequency);
    updateCatalog();
    return () => {
      clearInterval(interval);
    };
  }, [updateFrequency, updateCatalog]);

  return <rampCatalogContext.Provider value={value}>{children}</rampCatalogContext.Provider>;
}
