import React from 'react';
import _ from 'lodash';
import { Portal } from 'react-portal';
import ClassNames from 'classnames';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import ScrollBar from 'react-custom-scrollbars';
import Button from '../../inputs/button';
import Divider from '../../dataDisplay/divider';
import domUtils from '../../utils/domUtils';
import Header from '../../dataDisplay/header';
import Icon from '../../dataDisplay/icon';
import withStyles from '../../styles/withStyles';

const DEFAULT_CONTAINER_PADDING_TOP = 27;
const colorEnums = [ 'dark-blue', 'grey', 'white' ];

class CloseButton extends React.Component {
    constructor() {
        super();
        this._onCloseClick = this._onCloseClick.bind(this);
    }

    render() {
        const { closeButton, inverse, onClose } = this.props;

        if (!onClose) {
            return false;
        }

        let closeButtonJSX;

        if (_.isObject(closeButton)) {
            closeButtonJSX = React.cloneElement(closeButton, {
                className: 'drawer-close-button',
            });
        } else {
            closeButtonJSX = (
                <Button
                    className="drawer-close-button"
                    color={inverse ? 'transparent' : 'alternate'}
                    onClick={this._onCloseClick}
                    icon
                    style={{ margin: 0 }}
                >
                    <Icon inverse type={_.isString(closeButton) ? closeButton : 'times'} />
                </Button>
            );
        }

        return (
            <div className="drawer-close-button-container">
                {closeButtonJSX}
            </div>
        );
    }

    _onCloseClick() {
        this.props.onClose();
    }
}

CloseButton.propTypes = {
    closeButton: PropTypes.oneOfType([
        PropTypes.shape({}),
        PropTypes.string,
    ]),
    inverse: PropTypes.bool,
    onClose: PropTypes.func,
};

class DrawerDeprecatedWing extends React.Component {
    render() {
        const { children, className, color, width, style } = this.props;
        const rootClasses = ClassNames('drawer-wing', className, {
            'color-dark-blue': color === 'dark-blue',
            'color-grey': color === 'grey',
        });
        const defaultStyle = {
            width: width || null,
        };

        return (
            <div
                className={rootClasses}
                style={Object.assign({}, defaultStyle, style)}
            >
                {children}
            </div>
        );
    }

    componentDidMount() {
        const { onOpenToggle, width } = this.props;
        const newWidth = width ? `${width}px` : '100%';

        onOpenToggle(newWidth);
    }
}

DrawerDeprecatedWing.propTypes = {
    className: PropTypes.string,
    color: PropTypes.oneOf(colorEnums),
    onOpenToggle: PropTypes.func,
    style: PropTypes.shape({}),
    width: PropTypes.number,
};

class DrawerDeprecatedHeader extends React.Component {
    constructor() {
        super();

        this._drawerContainerInnerPaddingTop = DEFAULT_CONTAINER_PADDING_TOP;
    }

    render() {
        const { children, closeButton, inverse, onClose, title, titleTruncate } = this.props;
        const titleClass = ClassNames('title', {
            'drawer-title-truncate': titleTruncate,
        });

        return (
            <header className="drawer-header" ref={ref => this._drawerHeaderRef = ref}>
                {_.isObject(title) ? (
                    title
                ) : title ? (
                    <Header as="h2" className={titleClass} title={title}>
                        {title}
                    </Header>
                ) : null}

                <CloseButton closeButton={closeButton} inverse={inverse} onClose={onClose} />

                {children ? (
                    <div className="drawer-header-children">{children}</div>
                ) : (
                    <Divider />
                )}
            </header>
        );
    }

    componentDidMount() {
        this._onHeaderUpdate();
    }

    _onHeaderUpdate(node) {
        const headerEl = ReactDOM.findDOMNode(this);
        if (headerEl) {
            const paddingTop = headerEl.offsetHeight;
            const drawerInnerContainer = headerEl.closest('.drawer-container-inner');
            if (drawerInnerContainer && paddingTop !== this._drawerContainerInnerPaddingTop) {
                this._drawerContainerInnerPaddingTop = `${paddingTop + DEFAULT_CONTAINER_PADDING_TOP}px`;
                drawerInnerContainer.style.paddingTop = this._drawerContainerInnerPaddingTop;
            }
        }
    }
}

DrawerDeprecatedHeader.propTypes = {
    closeButton: PropTypes.oneOfType([
        PropTypes.shape({}),
        PropTypes.string,
    ]),
    inverse: PropTypes.bool,
    onClose: PropTypes.func,
    title: PropTypes.oneOfType([
        PropTypes.shape({}),
        PropTypes.string,
    ]),
    titleTruncate: PropTypes.bool,
};

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

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

        this.state = {
            isOpen: props.isOpen,
            isScrolled: false,
            transformValue: props.position === 'right' ?
                'translate(100%, 0)' :
                'translate(-100%, 0)',
            wing: null,
        };

        this._onClickOutside = this._onClickOutside.bind(this);
        this._onCloseAnimationComplete = this._onCloseAnimationComplete.bind(this);
        this._onCloseWingAnimationComplete = this._onCloseWingAnimationComplete.bind(this);
        this.onOpen = this.onOpen.bind(this);
        this.onOpenAnimationComplete = this.onOpenAnimationComplete.bind(this);
        this.onOpenWingToggle = this.onOpenWingToggle.bind(this);
        this._onClose = this._onClose.bind(this);
        this._onScrollStart = this._onScrollStart.bind(this);
        this._onScrollStop = this._onScrollStop.bind(this);

        this._drawerContainer = null;
        this._useComponentWillUnmount = false;
    }

    componentDidUpdate(prevProps) {
        // Open wing if there wasn't a previous wing open.
        if (!!this.props.wing && !!!prevProps.wing) {
            this.setState({ wing: this.props.wing });
        }

        // Open new wing and close old wing.
        if (!!this.props.wing && !!prevProps.wing && this.props.wing !== prevProps.wing) {
            this.setState({ wing: this.props.wing });
        }

        // Close wing if there is no other wing to open.
        if (!!!this.props.wing && !!prevProps.wing) {
            this._onCloseWingToggle(false);
        }

        if (this.props.isOpen && prevProps.maxWidth !== this.props.maxWidth) {
            const { maxWidth } = this.props;

            if (_.isNumber(maxWidth)) {
                this._drawerContainer.style.maxWidth = `${maxWidth}px`;
            } else if (_.isString(maxWidth)) {
                this._drawerContainer.style.maxWidth = maxWidth;
            } else {
                this._drawerContainer.style.maxWidth = '768px';
            }
        }

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

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

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

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

    componentWillUnmount() {
        const { isOpen } = this.state;

        if (isOpen && this._useComponentWillUnmount) { // We only want to clean up classes here if the closing animation never happens and the drawer isOpen.
            if (document.body.classList.contains('drawer-deprecated-open')) {
                document.body.classList.remove('drawer-deprecated-open');
            }

            if (document.body.classList.contains('drawer-dimmers')) {
                document.body.classList.remove('drawer-dimmers');
            }

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

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

    _onBeforeClose() {
        const animationEvent = this._transitionProps(this._drawerContainer);

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

        if (this._drawerContainer) {
            this._drawerContainer.classList.add('drawer-animate-out');
            this._drawerContainer.style.transform = this.props.position === 'right' ?
                'translate(100%, 0)' :
                'translate(-100%, 0)';
            this._drawerContainer.addEventListener(animationEvent, this._onCloseAnimationComplete);
        }
    }

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

        this._onClose();
    }

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

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

        onClose(...arguments);
    }

    _onCloseAnimationComplete() {
        const { onCloseComplete, position, onClickOutside } = this.props;
        const animationEvent = this._transitionProps(this.drawerContainerRef);
        const body = document.body;
        const numberOfModalDrawers = document.querySelectorAll('.ui.drawer-is_modal').length;
        const numberOfModalDeprecatedDrawers = document.querySelectorAll('.ui.drawer.deprecated.drawer-is_modal').length;

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

        this.drawerContainerRef.removeEventListener(animationEvent, this._onCloseAnimationComplete);

        if (numberOfModalDrawers <= 2) {
            body.classList.remove('drawer-deprecated-open-layered');
        }

        if (numberOfModalDrawers <= 1 || numberOfModalDeprecatedDrawers <= 1) {
            const scrollPosition = parseInt(body.style.top, 10);

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

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

        this.setState({ transformValue: position === 'right' ?
            'translate(100%, 0)' :
            'translate(-100%, 0)' });

        if (_.isFunction(onCloseComplete)) {
            onCloseComplete(true);
        }

        this._useComponentWillUnmount = false;

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

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

        this._useComponentWillUnmount = true;

        const body = document.body;
        const nodePortal = ReactDOM.findDOMNode(this);
        const scrollPosition = window.pageYOffset;
        const numberOfModalDrawers = document.querySelectorAll('.ui.drawer-is_modal').length;

        this._drawerContainer = nodePortal.querySelector('.drawer-container');

        const drawerDimmer = nodePortal.querySelector('.drawer-dimmer-deprecated');
        const layeredOffset = 11;
        const animationEvent = this._transitionProps(this._drawerContainer);

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

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

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

        setTimeout(() => {
            if (!isModal || numberOfModalDrawers >= 2) {
                drawerDimmer.style.display = 'none';
            }

            if (numberOfModalDrawers >= 2) {
                newZIndex = newZIndex + numberOfModalDrawers;

                if (isModal) {
                    domUtils.addClassName(body, 'drawer-deprecated-open-layered');
                }

                nodePortal.style.zIndex = newZIndex;
                this._drawerContainer.style.boxShadow = `${position === 'right' ? '-' : ''}2px 0 7px 0 rgba(0, 0, 0, 0.17)`;
                this._drawerContainer.style.zIndex = newZIndex;
            } else {
                body.style.top = `-${scrollPosition}px`;

                if (isModal) {
                    domUtils.addClassName(body, 'drawer-deprecated-open');
                }

                this._drawerContainer.style.boxShadow = `${position === 'right' ? '-' : ''}12px 0 19px 0 rgba(0, 0, 0, .22)`;
                nodePortal.style.zIndex = newZIndex - 1;
                this._drawerContainer.style.zIndex = newZIndex + numberOfModalDrawers;
            }

            if (!_.isUndefined(maxWidth)) {
                this._drawerContainer.style.maxWidth = _.isNumber(maxWidth) ?
                    `${maxWidth}px` :
                    _.isString(maxWidth) ?
                        maxWidth :
                        '768px';
            } else {
                this._drawerContainer.style.maxWidth = 768 - (layeredOffset * (numberOfModalDrawers - 1)) + 'px';
            }

            this._drawerContainer.style.transform = 'translate(0, 0)';
        }, 30);
    }

    onOpenAnimationComplete() {
        const {
            isModal,
        } = this.props;
        const animationEvent = this._transitionProps(this.drawerContainerRef);
        this.drawerContainerRef.removeEventListener(animationEvent, this.onOpenAnimationComplete);

        const { onOpenComplete } = this.props;

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

        if (isModal) {
            document.body.classList.add('drawer-dimmers');
        }
    }

    onOpenWingToggle(width) {
        const { position } = this.props;

        this.setState({ transformValue: position === 'right' ? `translate(-${width}, 0)` : `translate(${width}, 0)` });
    }

    _onCloseWingToggle() {
        const transitionEvent = this._transitionProps(this._drawerContainer);

        this._drawerContainer.addEventListener(transitionEvent, this._onCloseWingAnimationComplete);
        this.setState({ transformValue: 'translate(0, 0)' });
    }

    _onCloseWingAnimationComplete() {
        const transitionEvent = this._transitionProps(this._drawerContainer);

        this._drawerContainer.removeEventListener(transitionEvent, this._onCloseWingAnimationComplete);
        this.setState({ wing: null });
    }

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

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

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

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

    _transitionProps(el) {
        let t;
        let transitions = {
            'transition': 'transitionend',
            'oTransition': 'oTransitionEnd',
            'MozTransition': 'transitionend',
            'WebkitTransition': 'webkitTransitionEnd',
        };

        for (t in transitions) {
            if (el?.style && el.style[t] !== undefined) {
                return transitions[t];
            }
        }
    }

    render() {
        const {
            children,
            classes,
            className,
            closeButton,
            color,
            header,
            inverse,
            isModal,
            scrollBar,
            title,
            titleTruncate,
            position,
        } = this.props;

        const { isOpen } = this.state;

        if (!isOpen) {
            return false;
        }

        const { transformValue, wing } = this.state;

        const rootClasses = ClassNames(
            'ui',
            'drawer',
            'deprecated',
            classes.root,
            className,
            {
                'drawer-is_modal': isModal,
            },
        );

        const containerInnerClasses = ClassNames('drawer-container', {
            'color-dark-blue': color === 'dark-blue',
            'drawer-container-inverse': inverse,
            'drawer-container-is-scrolled': this.state.isScrolled,
            'drawer-container-no-header': header === false,
            'left-position': position === 'left',
        });

        const wingClasses = ClassNames('drawer-wing-container', {
            'left-position': position === 'left',
        });

        let renderContent;

        if (_.isArray(children)) {
            console.error('Please wrap the Drawer\'s children in an enclosing tag');
            return false;
        }

        renderContent = React.Children.map(children, child => {
            if (_.isFunction(child.type)) {
                return React.cloneElement(child, {
                    closeButton: closeButton,
                    inverse: inverse,
                    onClose: this._onClose,
                    title: title,
                    titleTruncate: titleTruncate,
                });
            } else {
                return React.Children.map(child.props.children, secondaryChild => {
                    if (_.isFunction(secondaryChild.type) && secondaryChild.type.name === 'DrawerDeprecatedHeader') {
                        return (
                            <DrawerDeprecatedHeader
                                children={secondaryChild.props.children}
                                closeButton={closeButton}
                                inverse={inverse}
                                onClose={this._onClose}
                                title={title}
                                titleTruncate={titleTruncate}
                            />
                        );
                    } else {
                        return secondaryChild;
                    }
                });
            }
        });

        if (_.isUndefined(header)) {
            renderContent.unshift(
                <DrawerDeprecatedHeader
                    closeButton={closeButton}
                    inverse={inverse}
                    key={`drawer-header-${_.kebabCase(title)}`}
                    onClose={this._onClose}
                    title={title}
                    titleTruncate={titleTruncate}
                />,
            );
        }

        renderContent = [
            <div className="drawer-children" key={`drawer-children-${_.kebabCase(title)}`}>
                {renderContent}
            </div>,
        ];

        if (header === false) {
            renderContent.unshift(
                <CloseButton
                    closeButton={closeButton}
                    inverse={inverse}
                    key="drawer-close-button"
                    onClose={this._onClose}
                />,
            );
        }

        return (
            <Portal>
                <div className={rootClasses}>
                    <div
                        className={containerInnerClasses}
                        ref={el => this.drawerContainerRef = el}
                        style={{ transform: transformValue }}
                    >
                        {position === 'left' && wing ? (
                            <div className={wingClasses}>
                                <div>
                                    {React.cloneElement(wing, { onOpenToggle: this.onOpenWingToggle })}
                                </div>
                            </div>
                        ) : null}

                        {scrollBar === true || _.isUndefined(scrollBar) ? (
                            <ScrollBar
                                autoHide
                                onScrollStart={this._onScrollStart}
                                onScrollStop={this._onScrollStop}
                            >
                                <div
                                    className="drawer-container-inner"
                                    ref={el => this._drawerContainerInnerRef = el}
                                    style={{
                                        paddingBottom: '33px',
                                        paddingLeft: '22px',
                                        paddingRight: '22px',
                                        paddingTop: header === false ? '22px' : null,
                                    }}
                                >
                                    {renderContent}
                                </div>
                            </ScrollBar>
                        ) : (
                            <div
                                className="drawer-container-inner"
                                ref={el => this._drawerContainerInnerRef = el}
                                style={{
                                    paddingBottom: '33px',
                                    paddingLeft: '22px',
                                    paddingRight: '22px',
                                    paddingTop: header === false ? '22px' : null,
                                }}
                            >
                                {renderContent}
                            </div>
                        )}

                        {position === 'right' && wing ? (
                            <div className={wingClasses}>
                                <div>
                                    {React.cloneElement(wing, { onOpenToggle: this.onOpenWingToggle })}
                                </div>
                            </div>
                        ) : null}
                    </div>

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

DrawerDeprecated.Header = DrawerDeprecatedHeader;
DrawerDeprecated.Wing = DrawerDeprecatedWing;

DrawerDeprecated.propTypes = {
    /**
     * Override or extend the styles applied to DrawerDeprecated.
     */
    classes: PropTypes.shape({
        root: PropTypes.string,
    }),
    className: PropTypes.string,
    closeButton: PropTypes.oneOfType([
        PropTypes.shape({}),
        PropTypes.string,
    ]),
    color: PropTypes.oneOf(colorEnums),
    header: PropTypes.bool,
    inverse: PropTypes.bool,
    isOpen: PropTypes.bool.isRequired,
    isModal: PropTypes.bool,
    maxWidth: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string,
    ]),
    onClickOutside: PropTypes.bool,
    onClose: PropTypes.func,
    onCloseComplete: PropTypes.func,
    onOpenComplete: PropTypes.func,
    path: PropTypes.string,
    position: PropTypes.oneOf([ 'left', 'right' ]),
    scrollBar: PropTypes.bool,
    style: PropTypes.shape({}),
    theme: PropTypes.shape({
        zIndex: PropTypes.shape({
            drawer: PropTypes.number,
        }),
    }),
    title: PropTypes.oneOfType([
        PropTypes.shape({}),
        PropTypes.string,
    ]),
    titleTruncate: PropTypes.bool,
    wing: PropTypes.shape({}),
};

DrawerDeprecated.defaultProps = {
    isModal: true,
    isOpen: false,
    position: 'right',
    theme: null,
};

DrawerDeprecated.contextTypes = {
    router: PropTypes.shape({}),
};

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