import React, {
  useEffect, Fragment, useState, useRef,
} from 'react';
import Formsy from 'formsy-react';
import PropTypes from 'prop-types';
import Sticky from 'react-sticky-el';
import { Page } from '../../core/layout/page/Page';
import { PageContent } from '../../core/layout/page/PageContent';
import { PageFooter } from '../../core/layout/page/PageFooter';
import { Icon } from '../../core/atoms/icon/Icon';
import { PageLoader } from '../../core/layout/page/PageLoader';
import { FormMultiStepsHeader } from './FormMultiStepsHeader';
import { FormMultiStepsFooter } from './FormMultiStepsFooter';
import useLayoutFocusMode from '../../core/helpers/useLayoutFocusMode';

const propTypes = {
  /**
   * Badge content for the header component
   */
  badge: PropTypes.node,
  /**
   * Children must be <FormMultiStepsItem> components
   */
  children: PropTypes.node,
  /**
   * Icon name for the header component
   */
  icon: PropTypes.string,
  isContentLoading: PropTypes.bool,
  /**
   * Toggle icon color for the header component
   */
  isIconColored: PropTypes.bool,
  isFormLoading: PropTypes.bool,
  /**
   * Add to pass from Tabs view to Wizard view
   */
  isWizard: PropTypes.bool,
  /**
   * Display message in the footer when isFormLoading is true
   */
  loadingMessage: PropTypes.node,
  onCancel: PropTypes.func,
  /**
   * onChange(values, { invalidateField })
   */
  onChange: PropTypes.func,
  /**
   * onContinue(values, { invalidateField })
   */
  onContinue: PropTypes.func,
  /**
   * onSubmit(values, { invalidateField })
   */
  onSubmit: PropTypes.func,
  submitLabel: PropTypes.node,
  /**
   * Title content for the header component
   */
  title: PropTypes.node,
};

const defaultProps = {
  badge: null,
  children: '',
  icon: null,
  isContentLoading: false,
  isIconColored: false,
  isFormLoading: false,
  isWizard: false,
  loadingMessage: '',
  onCancel: () => window.history.back(),
  onChange: () => {},
  onContinue: () => {},
  onSubmit: () => {},
  submitLabel: 'Save',
  title: '',
};

export const FormMultiSteps = ({
  badge,
  children,
  icon,
  isContentLoading,
  isIconColored,
  isFormLoading,
  isWizard,
  loadingMessage,
  onCancel,
  onChange,
  onContinue,
  onSubmit,
  submitLabel,
  title,
}) => {
  useLayoutFocusMode();
  const [currentPage, setCurrentPage] = useState(0);
  const [lastAvailablePage, setLastAvailablePage] = useState(0);

  const steps = (React.Children.toArray(children)).reduce((acc, cur) => {
    if (cur.type !== Fragment) {
      return [...acc, cur];
    }

    // If child is a Fragment, spread its children
    return [...acc, ...(React.Children.toArray(cur.props.children))];
  }, []);
  const isLastStep = currentPage === (steps.length - 1);

  const stepsArrayRef = useRef(steps.map(() => React.createRef()));
  const [stepsValidations, setStepsValidations] = useState(steps.map(() => true));
  const stepsValidationsRef = useRef();
  stepsValidationsRef.current = stepsValidations;

  const getFormValues = () => stepsArrayRef.current.reduce((obj, ref) => ({
    ...obj,
    ...(
      ref.current
        ? ref.current.getModel()
        : {}
    ),
  }), {});

  // Enable first validation of forms
  useEffect(() => {
    setTimeout(() => {
      stepsArrayRef.current.forEach((ref) => {
        if (ref && ref.current) {
          ref.current.validateForm();
        }
      });
    });
  }, [stepsArrayRef.current]);

  const scrollToTopOfPage = () => {
    const $scrollElement = document.querySelector('.sui-l-app-layout__page-scroll');

    if (!$scrollElement) {
      return;
    }

    $scrollElement.scrollTop = 0;
  };

  const goto = (page) => {
    if (isFormLoading) {
      return;
    }

    if (page < 0) {
      return;
    }

    if (page >= steps.length) {
      submitStep(); // eslint-disable-line no-use-before-define
      return;
    }

    scrollToTopOfPage();
    setCurrentPage(page);
    setLastAvailablePage(page > lastAvailablePage ? page : lastAvailablePage);
  };

  const invalidateStep = (page) => {
    setStepsValidations(stepsValidationsRef.current.map(
      (isStepValid, stepPage) => (stepPage === page ? false : isStepValid)
    ));
  };

  const validateStep = (page) => {
    setStepsValidations(stepsValidationsRef.current.map(
      (isStepValid, stepPage) => (stepPage === page ? true : isStepValid)
    ));
  };

  const invalidateField = (fieldName, message) => {
    stepsArrayRef.current.forEach((ref, page) => {
      if (!ref.current.getModel()[fieldName]) {
        return;
      }

      // Delay field validation for changeValue event
      setTimeout(() => {
        invalidateStep(page);
        ref.current.updateInputsWithError({
          [fieldName]: message,
        });
      });
    });
  };

  const onFormChange = (stepValues) => {
    onChange(
      // Form values
      {
        ...getFormValues(),
        ...stepValues,
      },

      // Form actions
      {
        invalidateField,
      },
    );
  };

  const submitForm = () => {
    if (isFormLoading) {
      return;
    }

    // Submit all steps Formsy forms
    stepsArrayRef.current.forEach((ref) => {
      if (ref.current) {
        ref.current.submit();
      }
    });

    // If one step is not valid
    if (stepsValidationsRef.current.some((value) => value === false)) {
      return;
    }

    onSubmit(
      getFormValues(),

      // Form actions
      {
        invalidateField,
      },
    );
  };

  const submitStep = () => {
    if (isFormLoading) {
      return;
    }

    // Submit current step Formsy form
    if (stepsArrayRef.current[currentPage].current) {
      stepsArrayRef.current[currentPage].current.submit();
    }

    // Not working :(
    if (!stepsValidationsRef.current[currentPage] && currentPage >= lastAvailablePage) {
      return;
    }

    if (!isLastStep) {
      onContinue(
        getFormValues(),
        // Form actions
        {
          invalidateField,
        },
      );
      goto(currentPage + 1);
      return;
    }

    submitForm();
  };

  return (
    <Page
      size="md"
    >
      <PageContent>
        {(title || isWizard) && (
          <FormMultiStepsHeader
            icon={icon}
            isIconColored={isIconColored}
            title={title}
            badge={badge}
          >
            {isWizard && (
              <button
                type="button"
                className="sui-a-button as--min-width-lg"
                onClick={onCancel}
              >
                Cancel
              </button>
            )}
          </FormMultiStepsHeader>
        )}

        { !isWizard && steps && steps.length > 1 && (
          <Sticky
            className="sui-h-mb-lg"
            scrollElement=".sui-l-app-layout__page-scroll"
            style={{
              background: 'white',
              zIndex: 2,
            }}
          >
            <div className="sui-m-tabs as--lg as--fill as--auto@md">
              <div className="sui-m-tabs__wrapper">
                {steps.map((Step, stepPage) => (
                  <button
                    key={stepPage} // eslint-disable-line react/no-array-index-key
                    type="button"
                    onClick={() => { goto(stepPage); }}
                    className={`sui-m-tabs__item ${currentPage === stepPage ? 'as--active' : ''}`}
                  >
                    {Step.props.name || '-'}
                    {!stepsValidationsRef.current[stepPage] && (
                      <small className="sui-h-text-danger sui-h-ml-sm">
                        <Icon name="fa-warning" />
                      </small>
                    )}
                  </button>
                ))}
              </div>
            </div>
          </Sticky>
        )}

        {
          isContentLoading
            ? (
              <PageLoader isLoading />
            )
            : steps.map((Step, stepPage) => (
              <Formsy
                key={stepPage} // eslint-disable-line react/no-array-index-key
                style={{
                  display: stepPage !== currentPage ? 'none' : false,
                  pointerEvents: isFormLoading ? 'none' : false,
                }}
                ref={stepsArrayRef.current[stepPage]}
                onChange={onFormChange}
                onValid={() => validateStep(stepPage)}
                onInvalid={() => invalidateStep(stepPage)}
              >
                {isWizard && Step.props && !!Step.props.name && (
                  <h3>
                    {Step.props.name || '-'}
                  </h3>
                )}
                {Step}
              </Formsy>
            ))
        }
      </PageContent>
      <PageFooter>
        <FormMultiStepsFooter
          isWizard={isWizard}
          isFormLoading={isFormLoading}
          loadingMessage={loadingMessage}
          submitLabel={submitLabel}
          stepsValidations={stepsValidationsRef.current}
          page={currentPage}
          lastAvailablePage={lastAvailablePage}
          setPage={goto}
          submitStep={submitStep}
          submitForm={submitForm}
          onCancel={onCancel}
        />
      </PageFooter>
    </Page>
  );
};

FormMultiSteps.propTypes = propTypes;
FormMultiSteps.defaultProps = defaultProps;
