
import React, { useCallback, useContext, useEffect, useMemo, useReducer } from 'react';
import SimpleReactValidator from 'simple-react-validator';

const ValidatorContext = React.createContext(null as any);
const ValidatorOptions = React.createContext(null as any);

export function ValidatorOptionProvider({ value, children }) {
  const previousOptions = useContext(ValidatorOptions);
  
  const combinedOptions = useMemo(() => ({
    ...(previousOptions || {}),
    ...(value || {}),
    messages: {
      ...((previousOptions || {}).messages || {}),
      ...((value || {}).messages || {})
    },
    validators: {
      ...((previousOptions || {}).validators || {}),
      ...((value || {}).validators || {})
    }
  }), [value, previousOptions]);
  
  return <ValidatorOptions.Provider value={combinedOptions}>
    { children }
  </ValidatorOptions.Provider>;
}

export function useValidator() {
  return useContext(ValidatorContext)[0];
}

export function useValidate(name: string|null, value:any = null, type:any = null) {
  const validator = useValidator();

  useEffect(function() {
    if (validator && name) {
      return function() {
        delete validator.fields[name];
        delete validator.errorMessages[name];
      };
    }
  }, [ validator, name ]);

  return validator.message(name, value, type || []);
}

export function ValidatorProvider({ children }) {
  const options = useContext(ValidatorOptions);
  const [,forceUpdate] = useReducer(x => ++x, 0);
  const validator = useMemo(() => new SimpleReactValidator({ ...options, autoForceUpdate: { forceUpdate }}), [options, forceUpdate]);

  const state = useMemo(() => ([validator, validator.messagesShown]), [validator, validator.messagesShown]);

  const handleSubmit = useCallback(function(e: Event) {
    if (!validator.allValid()) {
      e.preventDefault();
      e.stopPropagation();
      
      if (!validator.messagesShown) {
        validator.showMessages();
        forceUpdate();
      }
      return ;
    }

    if (validator.messagesShown) {
      validator.hideMessages();
      forceUpdate();
    }

    children.props.onSubmit && children.props.onSubmit(e);
  }, [validator, children.props.onSubmit, forceUpdate]);

  return <ValidatorContext.Provider value={state}>
    { React.cloneElement(children, { onSubmit: handleSubmit }) }
  </ValidatorContext.Provider>;
}
