import { Portal } from 'react-portal';
import {
    isNumber,
    isString,
    isUndefined,
    kebabCase,
} from 'lodash';
import ClassNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import ScrollBar from 'react-custom-scrollbars';
import domUtils from '../../utils/domUtils';
import ModalHeader from './modalDeprecatedHeader';
import withStyles from '../../styles/withStyles';

const propTypes = {
    autoHeight: PropTypes.bool,
    /**
     * Add a `data-testid` attribute to the ModalDeprecated's child container element.
     * Used for DOM Testing.
     * See https://testing-library.com/docs/queries/bytestid/.
     */
    childContainerDataTestId: PropTypes.string,
    /**
     * Override or extend the styles applied to Modal.
     */
    classes: PropTypes.shape({
        root: PropTypes.string,
    }),
    className: PropTypes.string,
    closeButton: PropTypes.oneOfType([
        PropTypes.bool,
        PropTypes.shape({}),
        PropTypes.string,
    ]),
    /**
     * Add a `data-testid` attribute to the ModalDeprecated's Close Button.
     * Used for DOM Testing.
     * See https://testing-library.com/docs/queries/bytestid/.
     */
    closeButtonDataTestId: PropTypes.string,
    /**
     * Add a `data-testid` attribute to the ModalDeprecated's root element.
     * Used for DOM Testing.
     * See https://testing-library.com/docs/queries/bytestid/.
     */
    dataTestId: PropTypes.string,
    fluidContent: PropTypes.bool,
    header: PropTypes.bool,
    headerStyle: PropTypes.shape({}),
    height: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string,
    ]),
    inverse: PropTypes.bool,
    isOpen: PropTypes.bool.isRequired,
    maxHeight: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string,
    ]),
    maxWidth: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string,
    ]),
    minHeight: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string,
    ]),
    minWidth: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string,
    ]),
    onClickOutside: PropTypes.bool,
    onClose: PropTypes.func.isRequired,
    style: PropTypes.shape({}),
    theme: PropTypes.shape({
        zIndex: PropTypes.shape({
            modal: PropTypes.number,
        }),
    }),
    title: PropTypes.string,
    titleTruncate: PropTypes.bool,
    width: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string,
    ]),
};

const defaultProps = {
    childContainerDataTestId: undefined,
    classes: null,
    closeButtonDataTestId: undefined,
    dataTestId: undefined,
    headerStyle: undefined,
    theme: null,
};

const styles = (theme) => ({
    root: {
        alignItems: 'center',
        backfaceVisibility: 'hidden',
        display: 'flex',
        height: '100%',
        justifyContent: 'center',
        left: 0,
        minWidth: 320,
        position: 'fixed',
        top: 0,
        width: '100%',
        zIndex: theme.zIndex.modal,
    },
});

class ModalDeprecated extends React.Component {
    constructor(props) {
        super(props);

        this._defaultDimensions = {
            autoHeightMax: null,
            height: '100%',
            maxHeight: '100%',
            maxWidth: 'none',
            minHeight: 'auto',
            minWidth: '320px',
            width: '100%',
        };

        this.state = {
            height: this._defaultDimensions.height,
            isOpen: props.isOpen,
            isScrolled: false,
            maxHeight: this._defaultDimensions.maxHeight,
            maxWidth: this._defaultDimensions.maxWidth,
            minHeight: this._defaultDimensions.minHeight,
            minWidth: this._defaultDimensions.minWidth,
            width: this._defaultDimensions.width,
        };

        this._mounted = false;
        this.modalContainer = null;

        this._onBeforeClose = this._onBeforeClose.bind(this);
        this._onClickOutside = this._onClickOutside.bind(this);
        this._onCloseAnimationComplete = this._onCloseAnimationComplete.bind(this);
        this._onClose = this._onClose.bind(this);
        this.onOpen = this.onOpen.bind(this);
        this.onOpenAnimationComplete = this.onOpenAnimationComplete.bind(this);
        this._onResize = this._onResize.bind(this);
        this._onScrollStart = this._onScrollStart.bind(this);
        this._onScrollStop = this._onScrollStop.bind(this);
    }

    componentDidMount() {
        const { isOpen } = this.props;
        this._mounted = true;

        window.addEventListener('resize', this._onResize);

        this._onResize();

        if (isOpen) {
            this.setState({
                isOpen,
            }, () => {
                this.onOpen();
            });
        }
    }

    componentDidUpdate(prevProps) {
        const {
            isOpen,
        } = this.props;

        if (!prevProps.isOpen && isOpen) {
            this.setState({
                isOpen,
            }, () => {
                this.onOpen();
            });
        }

        if (prevProps.isOpen && !isOpen) {
            this._onBeforeClose();
        }
    }


    componentWillUnmount() {
        this._mounted = false;

        window.removeEventListener('resize', this._onResize);

        if (document.body.classList.contains('modal-open')) {
            document.body.classList.remove('modal-open');
        }

        if (document.body.classList.contains('modal-dimmer')) {
            document.body.classList.remove('modal-dimmer');
        }

        if (document.body.classList.contains('modal-open-layered')) {
            document.body.classList.remove('modal-open-layered');
        }

        if (document.body.classList.contains('modal-animate-out')) {
            document.body.classList.remove('modal-animate-out');
        }
    }

    animationProps(el) {
        let a;
        const animations = {
            animation: 'animationend',
            OAnimation: 'oAnimationEnd',
            MozAnimation: 'animationend',
            WebkitAnimation: 'webkitAnimationEnd',
        };

        for (a in animations) {
            if (el.style[a] !== undefined) {
                return animations[a];
            }
        }
    }

    _onBeforeClose() {
        const nodePortal = ReactDOM.findDOMNode(this);
        const modalContainer = nodePortal.querySelector('.modal-container');
        const animationEvent = this.animationProps(modalContainer);

        document.body.classList.add('modal-animate-out');
        nodePortal.classList.add('modal-animate-out');

        this.modalContainer.addEventListener(animationEvent, this._onCloseAnimationComplete);
    }

    _onClickOutside(event) {
        if (this.modalContainerRef.contains(event.target) || !this.props.onClickOutside) {
            return;
        }

        this._onClose();
    }

    _onClose() {
        const { onClickOutside, onClose } = this.props;

        if (onClickOutside) {
            document.removeEventListener('click', this._onClickOutside);
        }

        onClose();
    }

    _onCloseAnimationComplete() {
        const { body } = document;
        const animationEvent = this.animationProps(this.modalContainerRef);
        this.modalContainerRef.removeEventListener(animationEvent, this._onCloseAnimationComplete);

        const element = document.body;
        const modalLength = document.querySelectorAll('.ui.modal').length;

        if (modalLength <= 2) {
            body.classList.remove('modal-open-layered');
        }

        if (modalLength <= 1) {
            const scrollPosition = parseInt(element.style.top, 10);

            body.classList.remove('modal-open');
            window.scroll(0, Math.abs(scrollPosition));
            body.style.top = null;
        }

        body.classList.remove('modal-animate-out');

        this.setState({
            isOpen: false,
        });
    }

    onOpen() {
        const {
            autoHeight,
            onClickOutside,
            maxWidth,
            theme,
        } = this.props;

        const { body } = document;
        const nodePortal = ReactDOM.findDOMNode(this);
        const scrollPosition = window.pageYOffset;
        const modalLength = document.querySelectorAll('.ui.modal').length;
        this.modalContainer = nodePortal.querySelector('.modal-container');
        const modalDimmer = nodePortal.querySelector('.modal-dimmer');
        const animationEvent = this.animationProps(this.modalContainer);

        let newZIndex = theme.zIndex.modal + 2; // adding 2 accounts for the frist .modal and .modal-dimmers- z-indexes

        this.modalContainer.addEventListener(animationEvent, this.onOpenAnimationComplete);

        if (onClickOutside) {
            document.addEventListener('click', this._onClickOutside);
        }

        if (domUtils.hasClassName(body, 'modal-open')) {
            newZIndex += modalLength;
            domUtils.addClassName(body, 'modal-open-layered');

            nodePortal.style.zIndex = newZIndex;
            this.modalContainer.style.zIndex = newZIndex;
            modalDimmer.style.display = 'none';
        } else {
            body.style.top = `-${scrollPosition}px`;
            domUtils.addClassName(body, 'modal-open');
            nodePortal.style.zIndex = newZIndex - 1;
            this.modalContainer.style.zIndex = newZIndex + modalLength;
        }

        if (!isUndefined(maxWidth)) {
            this.modalContainer.style.maxWidth = isNumber(maxWidth) ?
                `${maxWidth}px` :
                isString(maxWidth) ?
                    maxWidth :
                    null;
        } else {
            this.modalContainer.style.maxWidth = '768px';
        }

        if (autoHeight) {
            const modalPaddingBottom = parseInt(getComputedStyle(nodePortal).paddingBottom);
            const modalPaddingTop = parseInt(getComputedStyle(nodePortal).paddingTop);
            const modalHeight = nodePortal.offsetHeight;
            const newAutoHeightMax = modalHeight - modalPaddingBottom - modalPaddingTop;

            this.setState({ autoHeightMax: newAutoHeightMax });
        }
    }

    onOpenAnimationComplete() {
        const animationEvent = this.animationProps(this.modalContainerRef);
        this.modalContainer.removeEventListener(animationEvent, this.onOpenAnimationComplete);

        const { onOpenComplete } = this.props;

        if (typeof onOpenComplete === 'function') {
            onOpenComplete();
        }
    }

    _onResize() {
        if (this._mounted) {
            let dimensions = {};

            if (window.matchMedia('(min-width: 768px)').matches === true) {
                dimensions = {
                    height: this.props.height || '500px',
                    maxHeight: this.props.maxHeight || this._defaultDimensions.maxHeight,
                    maxWidth: this.props.maxWidth || this._defaultDimensions.maxWidth,
                    minHeight: this.props.minHeight || '305px',
                    minWidth: this.props.minWidth || this._defaultDimensions.minWidth,
                    width: this.props.width || '640px',
                };
            } else {
                dimensions = {
                    height: this._defaultDimensions.height,
                    maxHeight: this._defaultDimensions.maxHeight,
                    maxWidth: this._defaultDimensions.maxWidth,
                    minHeight: this._defaultDimensions.minHeight,
                    minWidth: this._defaultDimensions.minWidth,
                    width: this._defaultDimensions.width,
                };
            }

            this.setState(dimensions);
        }
    }

    _onScrollStart() {
        const scrollContainerPos = this.modalContainerInner.parentNode.scrollTop;

        this.setState({ isScrolled: scrollContainerPos > 0 });
    }

    _onScrollStop() {
        const scrollContainerPos = this.modalContainerInner.parentNode.scrollTop;

        this.setState({ isScrolled: scrollContainerPos > 0 });
    }

    render() {
        const {
            autoHeight,
            children,
            childContainerDataTestId,
            classes,
            className,
            closeButton,
            closeButtonDataTestId,
            dataTestId,
            fluidContent,
            headerStyle,
            inverse,
            title,
            titleTruncate,
            style,
        } = this.props;

        const {
            autoHeightMax,
            height,
            isOpen,
            isScrolled,
            maxHeight,
            maxWidth,
            minHeight,
            minWidth,
            width,
        } = this.state;

        if (!isOpen) {
            return false;
        }

        const rootClasses = ClassNames(
            'ui',
            'modal',
            classes.root,
            className,
        );

        const containerInnerClasses = ClassNames('modal-container', {
            'modal-container-inverse': inverse,
            'modal-container-is-scrolled': isScrolled,
        });

        const containerInnerStyles = {
            height,
            maxHeight,
            maxWidth,
            minHeight,
            minWidth,
            width,
            ...style,
        };

        const containerInnerScrollStyles = {
            ...(fluidContent && {
                height: '100%',
            }),
            ...((!title && !closeButton) && {
                paddingTop: 33,
            }),

        };

        return (
            <Portal>
                <div className={rootClasses} data-testid={dataTestId}>
                    <div
                        className={containerInnerClasses}
                        ref={(ref) => this.modalContainerRef = ref}
                        style={containerInnerStyles}
                    >
                        <ScrollBar
                            autoHeight={autoHeight}
                            autoHeightMax={autoHeightMax}
                            autoHide
                            onScrollStart={this._onScrollStart}
                            onScrollStop={this._onScrollStop}
                        >
                            <div
                                className="modal-container-inner"
                                ref={(el) => this.modalContainerInner = el}
                                style={containerInnerScrollStyles}
                            >
                                {(title || closeButton) && (
                                    <ModalHeader
                                        closeButton={closeButton}
                                        closeButtonDataTestId={closeButtonDataTestId}
                                        inverse={inverse}
                                        key={`modal-header-${kebabCase(title)}`}
                                        onClose={this._onClose}
                                        title={title}
                                        titleTruncate={titleTruncate}
                                        style={headerStyle}
                                    />
                                )}
                                <div
                                    className="modal-children"
                                    data-testid={childContainerDataTestId}
                                    key={`modal-children-${kebabCase(title)}`}
                                >
                                    {children}
                                </div>
                            </div>
                        </ScrollBar>
                    </div>

                    <div className="modal-dimmer" />
                </div>
            </Portal>
        );
    }
}

ModalDeprecated.propTypes = propTypes;
ModalDeprecated.defaultProps = defaultProps;

export default withStyles(styles, { withTheme: true })(ModalDeprecated);
