import React, {forwardRef, useCallback, useEffect, useState} from 'react'
import PropTypes from 'prop-types'
import hoistNonReactStatics from 'hoist-non-react-statics'
import Button from 'react-uikit/button'
import Form from 'react-uikit/form'
import Layout from 'react-uikit/layout'
import ProgressBar from 'react-uikit/progress-bar'
import {noop} from 'common-fe/utils'
import Debug from 'common-fe/utils/debug'
import {PAYMENT_CHECKOUT} from '../../portfolio-onboarding-form/steps'

import styles from './styles.scss'
import BEMModule from 'utils/bem'

const bem = new BEMModule(styles)

/**
 * A multi-step form component. This component behaves differently in that there
 * is no `onSubmit`. Instead, there is a `willNext` prop which is called prior
 * to proceeding to the next step / completing the final step. It is up to the
 * implementer to define the validations and handling for each page.
 */
const MultiStepForm = forwardRef(
    (
        {
            className,
            data,
            error,
            formName,
            validate,
            steps,
            willNext,
            onChange,
            onComplete,
            onError,
            onUpdate,
        },
        ref
    ) => {
        const [canGoNext, setCanGoNext] = useState(false)
        const [currStepIndex, setCurrStepIndex] = useState(0)
        const canGoBack = currStepIndex !== 0
        const isLastStep = currStepIndex === steps.length - 1
        const step = steps[currStepIndex]

        // Change event handler for the Stripe form. We want to watch for when it is complete
        // enabling the Pay button
        const handlePaymentChange = ({isComplete}) => {
            setCanGoNext(isComplete)
        }

        const nextStep = useCallback(() => {
            const nextStepIndex = Math.min(steps.length - 1, currStepIndex + 1)

            willNext(currStepIndex, nextStepIndex)
                .then(() => {
                    if (isLastStep) {
                        return onComplete?.()
                    }

                    return setCurrStepIndex(nextStepIndex)
                })
                .catch((error) =>
                    Debug.log(
                        `[MultiStepForm] Failed to proceed due to error: ${
                            error.message || error
                        }`
                    )
                )
        }, [currStepIndex, isLastStep, steps])

        const prevStep = useCallback(() => {
            const prevStep = Math.max(0, currStepIndex - 1)

            if (prevStep === currStepIndex) {
                return
            }

            setCurrStepIndex(prevStep)
        }, [currStepIndex])

        // Determines whether a user can attempt to proceed to the next step of
        // portfolio creation. This property toggles the disabled state of the
        // 'Next' / 'Finish' button of the MultiStepForm.
        useEffect(() => {
            if (
                (!Array.isArray(step.inputs) || !step.inputs.length) &&
                step !== PAYMENT_CHECKOUT
            ) {
                setCanGoNext(true)
                return
            }

            // Check to see if every input meets the minimum requirements to proceed.
            // NOTE: This assumes that the inputs are strings – can customize this
            // later on if needed.
            const meetsRequirements = step.inputs?.every(
                ({name, isRequired}) => {
                    const value = data[name]
                    return Boolean(!isRequired || value?.length || value?.value)
                }
            )

            setCanGoNext(meetsRequirements)
        }, [data, step])

        const {headerText, render: Page} = steps[currStepIndex]
        const classes = bem.classNames('c-multi-step-form', className)
        const headerClasses = bem.classNames('c-multi-step-form__header')
        const bodyClasses = bem.classNames('c-multi-step-form__body')
        const progressClasses = bem.classNames('c-multi-step-form__progress')
        const progressLabelClasses = bem.classNames(
            'c-multi-step-form__progress-label'
        )
        const progressButtonClasses = bem.classNames(
            'c-multi-step-form__progress-button',
            {disabled: !canGoBack}
        )
        const buttonClasses = bem.classNames('c-multi-step-form__button')

        const currentStepDisplay = currStepIndex + 1
        const lastStepDisplay = steps.length
        const buttonText = step.buttonText || (isLastStep && 'Finish') || 'Next'

        return (
            <Form
                className={classes}
                data={data}
                error={error}
                name={formName}
                submitOnEnter={false}
                validate={validate}
                validateOnUpdate
                onChange={onChange}
                onError={onError}
                onUpdate={onUpdate}
            >
                <header className={headerClasses}>{headerText}</header>

                <section className={bodyClasses}>
                    <Page
                        data={data}
                        error={error}
                        innerRef={ref}
                        onChange={handlePaymentChange}
                    />

                    <Layout.Flex
                        className={progressClasses}
                        direction="column"
                        justify="center"
                    >
                        <Layout.Flex>
                            <button
                                className={progressButtonClasses}
                                type="button"
                                onClick={prevStep}
                            >
                                Back
                            </button>

                            <label className={progressLabelClasses}>
                                {`${currentStepDisplay} of ${lastStepDisplay}`}
                            </label>
                        </Layout.Flex>

                        <ProgressBar
                            showLegend={false}
                            size="x-small"
                            value={currentStepDisplay}
                            max={lastStepDisplay}
                        />
                    </Layout.Flex>

                    <Button
                        className={buttonClasses}
                        disabled={!canGoNext}
                        form="payment"
                        variant="primary"
                        size="small"
                        text={buttonText}
                        type="button"
                        onClick={nextStep}
                    />
                </section>
            </Form>
        )
    }
)

MultiStepForm.propTypes = {
    canGoNext: PropTypes.bool,
    className: PropTypes.string,
    data: PropTypes.object,
    error: PropTypes.object,
    formName: PropTypes.string,
    steps: PropTypes.arrayOf(
        PropTypes.shape({
            buttonText: PropTypes.string,
            headerText: PropTypes.string,
            inputs: PropTypes.arrayOf(
                PropTypes.shape({
                    name: PropTypes.string,
                    isRequired: PropTypes.bool,
                })
            ),
            render: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
        })
    ),
    validate: PropTypes.func,
    validateOnUpdate: PropTypes.bool,
    willNext: PropTypes.func,
    onChange: PropTypes.func,
    onComplete: PropTypes.func,
    onError: PropTypes.func,
    onUpdate: PropTypes.func,
}

MultiStepForm.defaultProps = {
    steps: [],
    validate: noop,
    willNext: Promise.resolve,
}

export default hoistNonReactStatics(React.memo(MultiStepForm), MultiStepForm)
