import React, {Component} from 'react';
import PropTypes from 'prop-types';
import RenderInBody from '../RenderInBody';
import isFunction from 'lodash.isfunction';
import './style.scss';
import WizardActions from './WizardActions';
import WizardInfo from './WizardInfo';
import Icon from '../Icon';
import WizardStepList from './Model/WizardStepList';

class Wizard extends Component {
    static getCoords(elementId) {
        const element = document.getElementById(elementId);
        if (!element) {
            return null;
        }
        const coordinates = element.getBoundingClientRect();

        return {
            top: coordinates.top + (coordinates.height / 2) + window.pageYOffset,
            left: (coordinates.left + coordinates.width),
        };
    }

    getStepsCount() {
        return this.state.stepList.stepsCount;
    }

    static getSubStep(step) {
        return step.substeps.find((subStep) => {
            const coords = Wizard.getCoords(subStep.id);
            if (coords === null) {
                if (ENV === 'development') {
                    console.error(`Wizard can't find ${subStep.id} id`);
                }
                return false;
            }
            return coords.left !== 0;
        });
    }

    static setStepFocused(elementId) {
        document.getElementById(elementId).focus();
    }

    static scrollTo(elementId) {
        window.scrollTo({
            behavior: 'smooth',
            left: 0,
            top: Wizard.getCoords(elementId).top - 220,
        });
    }

    constructor(props) {
        super(props);
        this.state = {
            showWizard: props.show,
            currentStepNumber: 0,
            enableWizard: window.screen.width > 768,
            wizardId: props.wizardId,
            rules: props.rules,
            context: props.context,
        };

        this.state.stepList = new WizardStepList(this.state.rules, this.state.context);

        this.recalculatePosition = this.recalculatePosition.bind(this);
        this.setCurrentStep = this.setCurrentStep.bind(this);
        this.nextStepDefaultCallback = this.nextStepDefaultCallback.bind(this);
        this.previousStepDefaultCallback = this.previousStepDefaultCallback.bind(this);
        this.timeout = 0;
        this.componentIsMounted = false;
    }

    componentWillMount() {
        this.componentIsMounted = true;
    }

    componentDidMount() {
        window.addEventListener('click', this.recalculatePosition);
        window.addEventListener('resize', this.recalculatePosition);

        if (this.state.showWizard) {
            this.setCurrentStep(this.state.currentStepNumber);
        }
    }

    componentWillReceiveProps(props) {
        if (props.show !== this.state.showWizard) {
            this.toggleWizard();
        }

        const stepList = new WizardStepList(props.rules, props.context);

        this.setState({
            animation: '',
            wizardId: props.wizardId,
            context: props.context,
            rules: props.rules,
            stepList,
        });

        if (props.wizardId !== this.state.wizardId) {
            this.setCurrentStep(0);
        } else {
            this.setCurrentStep(this.state.currentStepNumber);
        }
    }

    componentWillUnmount() {
        this.componentIsMounted = false;
        window.removeEventListener('click', this.recalculatePosition);
        window.removeEventListener('resize', this.recalculatePosition);
    }

    getStep(stepNumber) {
        return this.state.stepList.getStep(stepNumber);
    }

    setCurrentStep(stepNumber) {
        if (!this.state.enableWizard || !this.getStepsCount() || !this.state.showWizard || !this.componentIsMounted) {
            return null;
        }

        const currentStepNumber = (this.state.stepList.stepsCount - 1) >= stepNumber ? stepNumber : this.state.stepList.stepsCount;

        const curentStep = this.getStep(currentStepNumber);

        this.setState({
            currentStepNumber,
            animation: 'pulse',
        });

        this.state.stepList.isStepCompleted(curentStep);
    }

    recalculatePosition(event) {
        if (this.state.showWizard) {
            if (event.type === 'resize') {
                if (this.timeout) {
                    clearTimeout(this.timeout);
                }
                this.timeout = setTimeout(() => {
                    this.setCurrentStep(this.state.currentStepNumber);
                    this.timeout = 0;
                },
                500);
            } else if (event.target.tagName === 'BUTTON') {
                setTimeout(() => this.setCurrentStep(this.state.currentStepNumber), 100);
            }
        }
    }

    getCurrentSubstep() {
        const curentStep = this.getStep(this.state.currentStepNumber);
        const currentSubstep = Wizard.getSubStep(curentStep);

        return currentSubstep;
    }

    nextStepDefaultCallback() {
        const nextStepNumber = this.state.stepList.getNextStepNumber(this.state.currentStepNumber);
        this.setCurrentStep(nextStepNumber);

        const nextStep = this.getStep(nextStepNumber);
        const nextStepSubstep = Wizard.getSubStep(nextStep);

        Wizard.setStepFocused(nextStepSubstep.id);
        Wizard.scrollTo(nextStepSubstep.id);
    }

    previousStepDefaultCallback() {
        const prevStepNumber = this.state.stepList.getPrevStepNumber(this.state.currentStepNumber);
        this.setCurrentStep(prevStepNumber);

        const prevStep = this.getStep(prevStepNumber);
        const prevStepSubstep = Wizard.getSubStep(prevStep);

        Wizard.setStepFocused(prevStepSubstep.id);
        Wizard.scrollTo(prevStepSubstep.id);
    }

    toggleWizard() {
        this.setState({
            showWizard: !this.state.showWizard,
        }, () => {
            if (this.state.showWizard) {
                this.setCurrentStep(this.state.currentStepNumber);
                const currentSubstep = this.getCurrentSubstep();

                if (currentSubstep) {
                    Wizard.setStepFocused(currentSubstep.id);
                    Wizard.scrollTo(currentSubstep.id);
                }
            }
            this.props.wizardStateCallback(this.state.showWizard);
        });
    }

    render() {
        if (!this.state.enableWizard || !this.getStepsCount()) {
            return null;
        }

        const minimizedWizard = (
            <RenderInBody>
                <button
                    className="Wizard__show-button"
                    onClick={() => this.toggleWizard()}
                >
                    <Icon
                        name="assistance"
                        color="white"
                        classNames="Wizard__show-button-icon"
                    />
                    Wizard
                </button>
            </RenderInBody>
        );

        if (!this.state.showWizard) {
            return minimizedWizard;
        }
        const stepList = this.state.stepList;
        const stepsCount = this.getStepsCount();
        const currentStep = this.getStep(this.state.currentStepNumber);
        const currentSubstep = Wizard.getSubStep(currentStep);

        if (!currentSubstep) {
            return minimizedWizard;
        }

        const tips = currentSubstep.tips ? <div className="Wizard__tips">{currentSubstep.tips}</div> : '';
        const position = Wizard.getCoords(currentSubstep.id);
        const id = `wizard-${this.state.wizardId}`;
        let nextStepCallback = this.nextStepDefaultCallback;

        if (isFunction(currentSubstep.nextStepCallback)) {
            nextStepCallback = currentSubstep.nextStepCallback;
        }

        let compositeNextStepCallback = nextStepCallback;

        if (isFunction(currentSubstep.completeStepCallback)) {
            compositeNextStepCallback = () => {
                nextStepCallback();
                currentSubstep.completeStepCallback();
            };
        }

        this.props.areAllStepsCompletedCallback(stepList.isWizardWasCompleted());

        return (
            <RenderInBody>
                <div
                    className="Wizard"
                    style={position}
                    id={id}
                >
                    <div className="Wizard__content">
                        <button
                            className="Wizard__close"
                            onClick={() => this.toggleWizard()}
                        >
                            <Icon
                                name="close"
                                size="small"
                                color="gray"
                            />
                        </button>

                        <WizardInfo
                            show={stepList.stepsCount !== (this.state.currentStepNumber + 1)}
                            steps={stepList.steps}
                            currentStepNumber={this.state.currentStepNumber + 1}
                            stepsCount={stepsCount}
                        />

                        <div className="Wizard__title">
                            {currentSubstep.title}
                        </div>

                        <div
                            className="Wizard__description"
                            dangerouslySetInnerHTML={{__html: currentSubstep.description}}
                        />

                        {tips}

                        <WizardActions
                            show
                            currentSubstep={currentSubstep}
                            isShowBackButton={this.state.currentStepNumber !== 0}
                            isShowNextButton={!currentSubstep.hideNextButton}
                            backStepCallback={() => this.setCurrentStep(stepList.getPrevStepNumber(this.state.currentStepNumber))}
                            nextStepCallback={compositeNextStepCallback}
                            disableNextStepButton={!currentStep.isNextStepButtonActive(currentSubstep)}
                        />
                    </div>
                    <div className={`Wizard__pin ${this.state.animation}`} />
                </div>
            </RenderInBody>
        );
    }
}

Wizard.propTypes = {
    rules: PropTypes.array.isRequired,
    context: PropTypes.any.isRequired,
    show: PropTypes.bool.isRequired,
    wizardId: PropTypes.string,
    wizardStateCallback: PropTypes.func,
    areAllStepsCompletedCallback: PropTypes.func,
};

Wizard.defaultProps = {
    wizardId: '',
    areAllStepsCompletedCallback: (value) => value,
    wizardStateCallback: (value) => value,
};

export default Wizard;
