import React from 'react';
import PropTypes from 'prop-types';
import {Confirm} from './popups/Confirm';
import {DangerousHTML} from './DangerousHTML';

export class Wizard extends React.Component {
    static confirmWizardRestore = 'TRANSLATE:CONFIRM_WIZARD_RESTORE';

    static childContextTypes = {
        wizard: PropTypes.object
    };

    constructor(props, context) {
        super(props, context);

        // XXX: startSnapshotRecording
        this.snapshotRecording = true;

        this.state = {
            showSnapshotRestoreDialog: !!this.getSnapshot(),
            currentStepIndex: 0,
            maxVisitedStepIndex: 0,
            errors: {}
        };

        this.setCurrentStepIndex = index => {
            if (index >= this.getSteps().length) {
                throw new RangeError(
                    'Current wizard step index ' +
                        this.state.currentStepIndex +
                        ' is out of ranges - step not found'
                );
            }

            this.setState({
                currentStepIndex: index,
                maxVisitedStepIndex: Math.max(this.state.maxVisitedStepIndex, index)
            });
        };

        this.resetMaxVisitedStepIndex = index => {
            this.setState({maxVisitedStepIndex: Number(index) || 0});
        };

        this.goNext = () => {
            let nextStepIndex = this.getEnabledSiblingStepIndex(1);

            if (nextStepIndex !== null) {
                this.validateCurrentStep(() => {
                    this.setCurrentStepIndex(nextStepIndex);
                }, nextStepIndex);
            } else {
                // eslint-disable-next-line
                console.error('Wizard has not following enabled steps - can not go next');
            }
        };

        this.goBack = () => {
            let prevStepIndex = this.getEnabledSiblingStepIndex(-1);

            if (prevStepIndex !== null) {
                this.validateCurrentStep(() => {
                    this.setCurrentStepIndex(prevStepIndex);
                }, prevStepIndex);
            } else {
                // eslint-disable-next-line
                console.error('Wizard has not previous enabled steps - can not go back');
            }
        };

        // XXX: inconsistency return - null (see getStepIndexById -1)
        this.getEnabledSiblingStepIndex = direction => {
            let targetStep = null;
            let targetStepIndex = this.state.currentStepIndex + direction;
            let steps = this.getSteps();

            while (!targetStep) {
                targetStep = steps[targetStepIndex];
                if (targetStep.props.disabled) {
                    targetStepIndex += direction;
                    targetStep = null;

                    if (targetStepIndex > steps.length - 1 || targetStepIndex < 0) {
                        targetStepIndex = null;
                        break;
                    }
                }
            }

            return targetStepIndex;
        };

        this.goToStep = id => {
            // XXX: RangeError within render
            let stepIndex = this.getStepIndexById(id);

            if (stepIndex === -1) {
                throw new RangeError('Wizard step [' + id + '] is not exists');
            }

            this.validateCurrentStep(() => {
                this.setCurrentStepIndex(stepIndex);
            }, stepIndex);
        };

        this.isFirstStep = id => {
            return this.getStepIndexById(id) === 0;
        };

        this.isLastStep = id => {
            let stepIndex = this.getStepIndexById(id);
            let lastStepIndex = this.getSteps().length - 1;

            return stepIndex >= 0 && stepIndex === lastStepIndex;
        };

        this.getCurrentStepIndex = () => {
            return this.state.currentStepIndex;
        };

        this.getMaxVisitedStepIndex = () => {
            return this.state.maxVisitedStepIndex;
        };

        this.stepOnLeaveHandlers = {};
        this.addStepOnLeaveHandler = (id, func) => {
            this.stepOnLeaveHandlers[id] = func;
        };
    }

    getChildContext() {
        return {wizard: this};
    }

    componentDidUpdate() {
        if (!this.state.showSnapshotRestoreDialog) {
            this.saveSnapshot();
        }
    }

    /**
     * @abstract
     */
    getSnapshotKey() {
        return null;
    }

    /**
     * @abstract
     */
    getSnapshotValue() {
        return null;
    }

    /**
     * @abstract
     */
    restoreSnapshot() {}

    saveSnapshot() {
        let key = this.getSnapshotKey();
        let value = this.getSnapshotValue();

        if (key && value && this.snapshotRecording) {
            if (window.localStorage) {
                window.localStorage.setItem(key, JSON.stringify(value));
            }
        }
    }

    getSnapshot() {
        let key = this.getSnapshotKey();
        return key && window.localStorage
            ? window.localStorage.getItem(this.getSnapshotKey())
            : null;
    }

    getRestoreConfirmationText() {
        return <DangerousHTML>{Wizard.confirmWizardRestore}</DangerousHTML>;
    }

    readSnapshot() {
        return JSON.parse(this.getSnapshot());
    }

    resetSnapshot(stopRecording = true) {
        let key = this.getSnapshotKey();

        if (stopRecording) {
            this.stopSnapshotRecording();
        }

        return key && window.localStorage
            ? window.localStorage.removeItem(this.getSnapshotKey())
            : null;
    }

    startSnapshotRecording() {
        this.snapshotRecording = true;
    }

    stopSnapshotRecording() {
        this.snapshotRecording = false;
    }

    getSteps() {
        return [];
    }

    getStepIndexById(id) {
        return this.getSteps().findIndex(step => step.props.id === id);
    }

    validateCurrentStep(onSuccess, targetStepIndex) {
        let currentStep = this.getSteps()[this.state.currentStepIndex];
        let isValidationNeeded = targetStepIndex > this.state.currentStepIndex;

        let successHandler = () => {
            let stepOnLeaveHandler = this.stepOnLeaveHandlers[currentStep.props.id];

            if (stepOnLeaveHandler) {
                stepOnLeaveHandler().then(onSuccess.bind(this));
            } else {
                onSuccess.call(this);
            }
        };

        if (currentStep.props.validation && isValidationNeeded) {
            this.setState({errors: {}});

            currentStep.props
                .validation(this.state)
                .then(successHandler)
                .catch(errors => {
                    this.setState({errors: errors});
                });
        } else {
            successHandler();
        }
    }

    render() {
        if (this.state.showSnapshotRestoreDialog) {
            return (
                <Confirm
                    closeButton={false}
                    noAutoClose
                    submitAction={e => {
                        let snapshot = this.readSnapshot();
                        this.restoreSnapshot(snapshot);
                        this.setState({showSnapshotRestoreDialog: false});
                    }}
                    cancelAction={e => this.setState({showSnapshotRestoreDialog: false})}>
                    {this.getRestoreConfirmationText()}
                </Confirm>
            );
        } else {
            return this.getSteps()[this.state.currentStepIndex];
        }
    }
}
