import { useRef, useEffect, FunctionComponent, FormEvent } from 'react';
import React, { ReactNode } from 'react';
/**
 * Checks HTML5's `checkValidity` to add a custom style and scroll
 * behavior to invalid inputs
 * @param formInput The input to check
 * @param invalidClassName The class to apply to the input element
 * @param feedbackClassName The class to apply to the message displayed
 */
const validateInput = (formInput: HTMLInputElement, invalidClassName: string, feedbackClassName: string): void => {
  const inputValid = formInput.checkValidity();
  let errorMessage: string;
  if (!inputValid) {
    formInput.classList.add(invalidClassName);
    errorMessage = formInput.validationMessage;
    // formInput.scrollIntoView({ behavior: 'smooth', block: 'end' });
  } else {
    formInput.classList.remove(invalidClassName);
    errorMessage = '';
  }

  if (!formInput.parentNode) {
    return;
  }

  const errorLabel = formInput.parentNode.querySelector(feedbackClassName);
  if (errorLabel && formInput.nodeName.toLowerCase() !== 'button') {
    errorLabel.textContent = errorMessage;
  }
};

type FormProps = {
  invalidClassName?: string;
  feedbackClassName?: string;
  width?: string;
  onSubmit: (e: FormEvent<HTMLFormElement>) => unknown;
  className?: string;
  children?: ReactNode;
};
  
const Form: FunctionComponent<FormProps> = props => {
  const formElement = useRef<HTMLFormElement>(null);
  const invalidClassName = props.invalidClassName || 'g-invalid-input';
  const feedbackClassName = props.feedbackClassName || 'g-invalid-feedback';

  useEffect(() => {
    if (formElement.current === null) {
      return;
    }
    const inputs = formElement.current.querySelectorAll('input,textarea,select');

    const focusLostEvent = (e: Event): void => {
      const formInput = e.target as HTMLInputElement;
      validateInput(formInput, invalidClassName, feedbackClassName);
    };

    for (const i of inputs) {
      i.addEventListener('focusout', e => focusLostEvent(e));
    }

    return (): void => {
      for (const i of inputs) {
        i.removeEventListener('focusout', e => focusLostEvent(e));
      }
    };
  }, [feedbackClassName, invalidClassName]);

  const validate = (): boolean => {
    if (formElement.current === null) {
      return false;
    }

    const valid = formElement.current.checkValidity();
    const inputs = formElement.current.querySelectorAll('input,textarea,select');

    for (const formInput of inputs) {
      const inputElement = formInput as HTMLInputElement;
      validateInput(inputElement, invalidClassName, feedbackClassName);
    }

    return valid;
  };

  const onSubmit = (e: FormEvent<HTMLFormElement>): void => {
    e.preventDefault();
    if (validate()) {
      props.onSubmit(e);
    }
  };

  return (
    <form className={props.className} method='post' onSubmit={onSubmit} noValidate ref={formElement}>
      {props.children}
      <style jsx>{`
        form {
          width: ${props.width ? props.width : 'auto'};
        }
      `}</style>
    </form>
  );
};

export default Form;
