import {
  IOnboardingAction,
  IOnboardingContext,
  IOnboardingState,
  OnboardingActionType,
  OnboardingContext
} from './Provider';
import { ITipDescriptor } from './types';
import _ from 'lodash';
import { useCallback, useContext, useEffect } from 'react';
import * as yup from 'yup';

function useOnboardingContext(): IOnboardingContext {
  const context = useContext(OnboardingContext);
  if (context === undefined) {
    throw new Error(
      '[Onboarding] useOnboardingContext: context undefined. Please provide a valid OnboardingContext using OnboardingProvider'
    );
  }
  return context;
}

function useOnboardingState(): IOnboardingState {
  const context = useOnboardingContext();
  return context.state;
}

function useOnboardingDispatch(): React.Dispatch<IOnboardingAction> {
  const context = useOnboardingContext();
  return context.dispatch;
}

function useOnboardingTip(config: ITipDescriptor): {
  tip: ITipDescriptor & { open: boolean };
  dismiss: () => void;
} {
  yup.object({ id: yup.string().required(), step: yup.number().required() }).validateSync(config);

  const { id, step, completeOnboarding } = config;
  const { show, tips, currentStep } = useOnboardingState();
  const tip = _.find(tips, { id: id });
  const { dismissed } = tip ?? {};
  const dispatch = useOnboardingDispatch();
  const dismiss = useCallback(() => {
    dispatch({
      type: OnboardingActionType.DISMISS_TIP,
      payload: id
    });
  }, [dispatch, id]);

  useEffect(() => {
    if (_.isNil(tip) || step !== tip?.step) {
      dispatch({
        type: OnboardingActionType.REGISTER_TIP,
        payload: {
          id: id,
          step: step,
          completeOnboarding: completeOnboarding
        }
      });
    }
  }, [id, step, completeOnboarding, tip, dispatch]);

  useEffect(() => {
    return () => {
      dispatch({ type: OnboardingActionType.UNREGISTER_TIP, payload: id });
    };
  }, [id, dispatch]);

  return {
    tip: _.chain(tip ?? config)
      .clone()
      .extend({ open: show && !_.isNil(tip) && currentStep === step && !dismissed })
      .value(),
    dismiss: dismiss
  };
}

function useRestartOnboarding(): () => void {
  const dispatch = useOnboardingDispatch();
  return useCallback(() => {
    dispatch({ type: OnboardingActionType.RESTART });
  }, [dispatch]);
}

export { useOnboardingTip, useRestartOnboarding };
