import React, { useState, useRef, useContext } from 'react';
import PropTypes from 'prop-types';
import ReCAPTCHA from 'react-google-recaptcha';
import { FaArrowRight } from '@react-icons/all-files/fa/FaArrowRight';
import { translate } from 'gatsby-core-theme/src/helpers/getters';
import { Context } from 'gatsby-core-theme/src/context/MainProvider';
import { contactUsForm } from '../../../constants/forms';
import Button from '~atoms/button/button';
import styles from './form.module.scss';

const FormComponent = ({
  formOptions = Object.values(contactUsForm)[0] || {},
  successMessage = 'Form has sent successfully',
  failMessage = 'Form has not sent, Got some error',
  validationMessage = 'Fill all the required fields.',
  submitUrl = '',
  hasButton = true,
  showButtonIcon = true,
  buttonLabel = 'Submit',
  disabled = true,
  showLabels = true,
  buttonSize = 'm',
  buttonType = 'primary',
}) => {
  const { translations } = useContext(Context) || {};
  const recaptchaRef = useRef();
  const formRef = useRef();
  const [state, setState] = useState({
    loading: false,
    success: false,
    failed: false,
    isValid: true,
    isDisabled: disabled,
  });
  const values =
    formOptions.fields?.map((field) => ({
      [field.id]: field.value ? field.value : '',
    })) || [];
  // add recaptcha if its added in form options
  if (formOptions.hasReCAPTCHA) values.push({ gRecaptchaResponse: '' });
  const [elements, setElements] = useState(Object.assign({}, ...values));

  const handleChange = (name, value) => {
    setState({ ...state, [name]: value });
    setElements({ ...elements, [name]: value });
  };

  async function postData(url = '', data = {}) {
    return fetch(url, {
      method: 'POST',
      cache: 'no-cache',
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
      },
      body: JSON.stringify(Object.fromEntries(data)),
    });
  }

  const handleSubmit = (e) => {
    e.preventDefault();

    // check if all required fields are empty
    const inValid = Object.values(elements).some((value) => value === '');

    if (inValid) {
      setState({ ...state, isValid: false, success: false });
      return;
    }

    // form is valid
    const form = e.target;
    setState({ ...state, loading: true, success: false, failed: false, isDisabled: true });
    postData(submitUrl, new FormData(form))
      .then((response) => {
        // on success
        if (response.ok) {
          setState({
            ...state,
            success: true,
            loading: false,
            isValid: true,
            isDisabled: true,
          });
        } else {
          // on error
          setState({
            ...state,
            failed: true,
            loading: false,
            isValid: true,
          });
        }
      })
      .catch((err) => {
        // on error
        setState({
          ...state,
          failed: true,
          loading: false,
          isValid: true,
        });
      });
    recaptchaRef?.current?.reset();
  };

  function recaptchaOnChange(value) {
    // eslint-disable-next-line no-unused-expressions
    if (value) {
      setState({ ...state, isDisabled: false });
      setElements({ ...elements, gRecaptchaResponse: value });
    }
  }

  const getField = (field) => {
    const { type, id, translationKey, label, placeholder, ...props } = field;

    switch (type) {
      case 'textarea':
        return (
          <div className={styles.textareaGroup || ''}>
            <textarea
              name={id}
              id={id}
              placeholder={translate(translations, placeholder?.translationKey, placeholder?.label)}
              {...props}
              value={elements[id]}
              onChange={(e) => handleChange(e.target.name, e.target.value)}
              className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
            />
            {field.maxLength && <span>{`${elements[id].length}/${field.maxLength}`}</span>}
          </div>
        );
      case 'checkbox':
        return (
          <div className={styles.checkboxGroup || ''}>
            {field.options.map((option) => (
              <div key={option.id}>
                <input
                  id={option.id}
                  name={option.id}
                  type={type}
                  checked={elements[option.id] === 'true'}
                  onChange={(e) => handleChange(e.target.name, e.target.checked.toString())}
                />
                <label
                  htmlFor={option.id}
                  className={`${
                    (!state.isValid && !elements[option.id].length && styles.invalid) || ''
                  }`}
                >
                  {translate(translations, option.translationKey, option.label)}
                </label>
              </div>
            ))}
          </div>
        );
      case 'radio':
        return (
          <div className={styles.radioGroup || ''}>
            {field.options.map((option) => (
              <div key={option.id}>
                <input
                  id={option.id}
                  name={id}
                  type={type}
                  checked={elements[id] === option.label}
                  onChange={(e) => handleChange(e.target.name, option.label)}
                />
                <label
                  key={option.id}
                  htmlFor={option.id}
                  className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
                >
                  {translate(translations, option.translationKey, option.label)}
                </label>
              </div>
            ))}
          </div>
        );
      case 'select':
        return (
          <select
            id={id}
            aria-label={id}
            name={id}
            defaultValue={elements[id]}
            onChange={(e) => handleChange(e.target.name, e.target.value)}
            className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
          >
            {field.showDefault && (
              <option value="">
                {translate(translations, field.defaultTranslationKey, field.defaultLabel)}
              </option>
            )}
            {field.options.map((option) => (
              <option key={option.id} value={option.label}>
                {translate(translations, option.translationKey, option.label)}
              </option>
            ))}
          </select>
        );
      case 'range':
        return (
          <div className={styles.radioGroup || ''}>
            <input
              name={id}
              id={id}
              type={type}
              aria-describedby={id}
              {...props}
              value={elements[id]}
              onChange={(e) => handleChange(e.target.name, e.target.value)}
            />
            <div className={styles.rangeValues || ''}>
              <span>{field.min}</span>
              <span
                className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
              >
                {elements[id] || 'Selected Value'}
              </span>
              <span>{field.max}</span>
            </div>
          </div>
        );
      default:
        return (
          <input
            name={id}
            id={id}
            type={type}
            aria-describedby={id}
            placeholder={translate(translations, placeholder?.translationKey, placeholder?.label)}
            {...props}
            value={elements[id]}
            onChange={(e) => handleChange(e.target.name, e.target.value)}
            className={`${(!state.isValid && elements[id] === '' && styles.invalid) || ''}`}
          />
        );
    }
  };

  // form not specified on constant
  if (!Object.keys(formOptions).length) return;

  return (
    <div className={`${styles.formComponent || ''} `}>
      {formOptions?.title && (
        <h2>
          {translate(translations, formOptions.title?.translationKey, formOptions.title?.label)}
        </h2>
      )}
      {formOptions?.description && (
        <p>
          {translate(
            translations,
            formOptions.description?.translationKey,
            formOptions.description?.label
          )}
        </p>
      )}
      {formOptions?.fields && (
        <form
          ref={formRef}
          onSubmit={(e) => handleSubmit(e)}
          action={submitUrl}
          className={formOptions.twoCol ? styles.twoCol || '' : styles.singleCol || ''}
          target="_self"
        >
          {formOptions.fields?.map((field) => {
            const { id, translationKey, label, validations } = field;

            return (
              <div
                key={id}
                className={`${styles.formGroup || ''} ${styles[id] ? styles[id] : ''} ${
                  (!field.twoCol && styles.fullWidth) || ''
                }`}
              >
                {showLabels && label && (
                  <label htmlFor={id}>{translate(translations, translationKey, label)}</label>
                )}
                {elements && getField(field)}
                {validations && !state.isValid && elements[id] === '' && (
                  <span className={styles.alertWarningMessage || ''}>
                    {validations?.icon}
                    {translate(translations, validations?.translationKey, validations?.label)}
                  </span>
                )}
              </div>
            );
          })}
          {formOptions.hasReCAPTCHA && (
            <div
              className={`${styles.recaptcha || ''} ${
                (!state.isValid && elements.gRecaptchaResponse === '' && styles.invalid) || ''
              }`}
            >
              <ReCAPTCHA
                ref={recaptchaRef}
                sitekey={`${process.env.RECAPTCHA_SITE_KEY}`}
                // eslint-disable-next-line react/jsx-no-bind
                onChange={recaptchaOnChange}
              />
              {!state.isValid &&
                elements.gRecaptchaResponse === '' &&
                formOptions?.reCaptcha?.validations && (
                  <span className={styles.alertWarningMessage || ''}>
                    {formOptions?.reCaptcha?.validations?.icon}
                    {translate(
                      translations,
                      formOptions?.reCaptcha?.validations?.translationKey,
                      formOptions?.reCaptcha?.validations.label
                    )}
                  </span>
                )}
            </div>
          )}
          {hasButton && (
            <button className={styles.formButton || ''} type="submit">
              <Button
                buttonSize={buttonSize}
                buttonType={buttonType}
                isInternalLink={false}
                btnText={state.loading ? 'sending...' : buttonLabel}
                gtmClass="form-gtm btn-cta"
                icon={
                  showButtonIcon ? (
                    <FaArrowRight fontSize={20} title="Right-pointing Arrow Icon" />
                  ) : null
                }
              />
            </button>
          )}
          {(state.success || state.failed || !state.isValid) && (
            <div className={styles.formAlerts || ''}>
              {state.success && <div className={styles.alertSuccess || ''}>{successMessage}</div>}
              {state.failed && <div className={styles.alertDanger || ''}>{failMessage}</div>}
              {!state.isValid && (
                <div className={styles.alertWarning || ''}>{validationMessage}</div>
              )}
            </div>
          )}
        </form>
      )}
    </div>
  );
};

FormComponent.propTypes = {
  formOptions: PropTypes.shape({}),
  successMessage: PropTypes.string,
  failMessage: PropTypes.string,
  validationMessage: PropTypes.string,
  submitUrl: PropTypes.string.isRequired,
  hasButton: PropTypes.bool,
  buttonLabel: PropTypes.string,
  disabled: PropTypes.bool,
  showLabels: PropTypes.bool,
  showButtonIcon: PropTypes.bool,
  buttonType: PropTypes.string,
  buttonSize: PropTypes.string,
};
export default FormComponent;
