import { OnboardingModes } from './enums';
import { onboardingModesSchema, onboardingTopicSchema } from './schemas';
import { useLocalStorage } from '@/hooks';
import _ from 'lodash';
import { useDataProvider, useGetIdentity } from 'ra-core';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useMutation, useQuery, useQueryClient } from 'react-query';
import * as yup from 'yup';

type IOnboardingDataProvider = {
  onboardingRequired: boolean;
  acknowledgeOnboarding: () => Promise<void>;
};

function useUserProfileDataProvider(
  topic: string
): (IOnboardingDataProvider | object) & { loading: boolean; error: boolean } {
  const queryClient = useQueryClient();
  const [onboardingRequired, setOnboardingRequired] = useState(false);
  const [acknowledgeOnboarding, setAcknowledgeOnboarding] = useState(() => () => Promise.resolve());
  const identity = useGetIdentity();
  const user = identity?.data?.email;
  const dataProvider = useDataProvider();
  const fetch = useCallback(() => {
    if (!user) {
      return Promise.reject();
    } else {
      return dataProvider.get('onboarding').then((response: any) => {
        return response?.data?.value;
      });
    }
  }, [dataProvider, user]);
  const { data, isLoading, isError } = useQuery({
    queryKey: ['GET', 'onboarding', user],
    queryFn: fetch,
    staleTime: 24 * 60 * 60 * 1000,
    retry: user !== undefined
  });
  const [post, setPost] = useState(() => () => Promise.resolve());
  const mutation = useMutation({ mutationFn: post });
  const mutateAsync = mutation.mutateAsync;

  useEffect(() => {
    function post() {
      const newValue = _.chain(data ?? {})
        .clone()
        .extend({ [topic]: Date.now() })
        .value();
      return dataProvider.post('onboarding', newValue).then(() => {
        queryClient.invalidateQueries(['GET', 'onboarding', user]);
      });
    }
    setPost(() => post);
  }, [dataProvider, data, topic, queryClient, user]);

  useEffect(() => {
    setOnboardingRequired(_.isNil((data ?? {})[topic]));
  }, [data, topic, setOnboardingRequired]);

  useEffect(() => {
    setAcknowledgeOnboarding(() => () => {
      return mutateAsync();
    });
  }, [mutateAsync]);

  return {
    loading: isLoading,
    error: isError,
    onboardingRequired: onboardingRequired,
    acknowledgeOnboarding: acknowledgeOnboarding
  };
}

function useLocalStorageDataProvider(topic: string): IOnboardingDataProvider {
  const [localStorageValue, setLocalStorageValue] = useLocalStorage('onboarding', {}) as [
    { [key: string]: number },
    (value: (oldValue: { [key: string]: number }) => { [key: string]: number }) => { [key: string]: number }
  ];
  const [onboardingRequired, setOnboardingRequired] = useState(false);
  const [acknowledgeOnboarding, setAcknowledgeOnboarding] = useState(() => () => Promise.resolve());

  useEffect(() => {
    setOnboardingRequired(_.isNil((localStorageValue ?? {})[topic]));
  }, [localStorageValue, topic, setOnboardingRequired]);

  useEffect(() => {
    setAcknowledgeOnboarding(() => () => {
      setLocalStorageValue((oldValue) => _.extend(oldValue, { [topic]: Date.now() }));
      return Promise.resolve();
    });
  }, [topic, setLocalStorageValue, setAcknowledgeOnboarding]);

  return {
    onboardingRequired: onboardingRequired,
    acknowledgeOnboarding: acknowledgeOnboarding
  };
}

function useFakeLocalStorageDataProvider(): IOnboardingDataProvider {
  const setterRef = useRef(() => Promise.resolve());

  return {
    onboardingRequired: false,
    acknowledgeOnboarding: setterRef.current
  };
}

function useOnboardingDataProvider(mode: OnboardingModes, topic: string): IOnboardingDataProvider {
  yup.object({ mode: onboardingModesSchema, topic: onboardingTopicSchema }).validateSync({ mode: mode, topic: topic });
  const userProfileDataProvider = useUserProfileDataProvider(topic);
  const localStorageDataProvider = useLocalStorageDataProvider(topic);
  const fakeLocalStorageDataProvider = useFakeLocalStorageDataProvider();

  switch (mode) {
    case OnboardingModes.USER:
      if (userProfileDataProvider.loading) {
        return fakeLocalStorageDataProvider;
      } else if (userProfileDataProvider.error) {
        return localStorageDataProvider;
      } else {
        return _.pick(userProfileDataProvider, [
          'onboardingRequired',
          'acknowledgeOnboarding'
        ]) as IOnboardingDataProvider;
      }
    case OnboardingModes.BROWSER:
      return localStorageDataProvider;
    default:
      return fakeLocalStorageDataProvider;
  }
}

export { useOnboardingDataProvider };
