import {
    debounce,
    get,
    isFunction,
} from 'lodash';
import ClassNames from 'classnames';
import PropTypes from 'prop-types';
import React, { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import domUtils from '../../utils/domUtils';
import {
    BEM_BLOCK_NAME,
    OPTION_INNER_CLASS_NAME,
    OPTIONS_THEME_LIGHT,
} from './dropdownMenuConstants';
import makeStyles from '../../styles/makeStyles';

const propTypes = {
    children: PropTypes.node.isRequired,
    className: PropTypes.string,
    id: PropTypes.string,
    isOpen: PropTypes.bool.isRequired,
    getParentContainer: PropTypes.func,
    onToggleOpen: PropTypes.func.isRequired,
    /**
     * Options list theme
     */
    optionsTheme: PropTypes.oneOf([
        'dark',
        'light',
    ]),
    style: PropTypes.shape({}),
    /**
     * Indicates whether or not the Dropdown Menu can be focused.
     */
    tabIndex: PropTypes.number,
};

const defaultProps = {
    optionsTheme: 'dark',
    className: undefined,
    id: undefined,
    style: undefined,
    getParentContainer: undefined,
    tabIndex: -1,
};
const useStyles = makeStyles((theme) => {
    const backgroundColorInverse = get(theme, 'palette.grey[500]');
    const backgroundColorPrimary = get(theme, 'palette.background.primary');
    const borderColorPrimary = get(theme, 'palette.border.primary');
    const borderRadiusMain = get(theme, 'shape.borderRadius');
    const colorHighlight = get(theme, 'palette.cyan[500]');
    const colorPrimary = get(theme, 'palette.grey[600]');
    const colorStatic = get(theme, 'palette.text.secondary');
    const fontWeightMedium = get(theme, 'typography.fontWeightMedium');
    const textColorConstrast = get(theme, 'palette.text.contrastText');
    const textColorPrimary = get(theme, 'palette.text.primary');
    const pxToRem = get(theme, 'typography.pxToRem', (px) => px);

    return {
        root: {
            backgroundColor: ({ optionsTheme }) => (optionsTheme === OPTIONS_THEME_LIGHT ?
                backgroundColorPrimary :
                backgroundColorInverse
            ),
            borderRadius: borderRadiusMain,
            border: ({ optionsTheme }) => (optionsTheme === OPTIONS_THEME_LIGHT ?
                `1px solid ${borderColorPrimary}` :
                0
            ),
            boxShadow: '0 4px 4px 0 rgba(0, 0, 0, 0.43)',
            boxSizing: 'border-box',
            cursor: 'default',
            margin: '5px 0',
            padding: 0,
            position: 'absolute',
            textAlign: 'left',
            transform: 'opacity 300ms ease-in',
            zIndex: '1000',
            '& .dropdown_menu': {
                '&--option': {
                    color: ({ optionsTheme }) => (optionsTheme === OPTIONS_THEME_LIGHT ?
                        textColorPrimary :
                        textColorConstrast
                    ),
                    fontSize: pxToRem(14),
                    fontWeight: fontWeightMedium,
                    outline: 'none',
                    padding: `${pxToRem(4)} 0`,
                    textAlign: 'left',
                    '&:first-child': {
                        paddingTop: pxToRem(11),
                    },
                    '&:last-child': {
                        paddingBottom: pxToRem(11),
                    },
                    '&_inner': {
                        alignItems: 'center',
                        cursor: 'pointer',
                        display: 'flex',
                        justifyContent: 'flex-start',
                        padding: `${pxToRem(7)} ${pxToRem(22)}`,
                        transition: 'background-color 150ms ease-out, color 150ms ease-out',
                        whiteSpace: 'nowrap',
                        '&:hover': {
                            backgroundColor: ({ optionsTheme }) => (
                                optionsTheme === OPTIONS_THEME_LIGHT ?
                                    colorHighlight :
                                    backgroundColorPrimary
                            ),
                            color: ({ optionsTheme }) => (optionsTheme === OPTIONS_THEME_LIGHT ?
                                textColorConstrast :
                                textColorPrimary
                            ),
                            '& .ui.icon-color-primary.icon-inverse': {
                                '& .icon-use-path': {
                                    fill: colorPrimary,
                                },
                            },
                        },
                    },
                    '&-disable': {
                        color: `${colorStatic} !important`,
                        cursor: 'default',
                        pointerEvents: 'none',
                    },
                },
            },
        },
        dropdownMenuClosed: {
            opacity: 0,
            visibility: 'hidden',
        },
        dropdownMenuOpen: {
            opacity: 1,
            visibility: 'visible',
        },
    };
});
function DropdownMenu(props) {
    const {
        children,
        className,
        getParentContainer,
        id,
        isOpen,
        optionsTheme,
        onToggleOpen,
        style,
        tabIndex,
    } = props;

    const dropdownMenuRef = useRef(null);

    const [menuPositionStyle, setMenuPositionStyle] = useState({
        bottom: null,
        left: 0,
        right: null,
        top: '100%',
    });

    const classes = useStyles({
        ...props,
        optionsTheme, // just to avoid the ESLint issue 'PropType is defined but prop is never used'
    });

    function onDropdownMenuReposition() {
        let parentContainer = get(dropdownMenuRef, 'current.parentElement');

        if (isFunction(getParentContainer)) {
            // eslint-disable-next-line react/no-find-dom-node
            parentContainer = ReactDOM.findDOMNode(getParentContainer());
        }

        const dropdownMenuObj = domUtils.isInViewport(dropdownMenuRef.current, parentContainer);

        const {
            isInTop,
            isInRight,
            isInBottom,
            topBias,
            bottomBias,
        } = dropdownMenuObj;

        const menuXPosition = isInRight ? 'left' : 'right';
        let menuYPosition = topBias < bottomBias ? 'top' : 'bottom';

        if (isInBottom || bottomBias < 0) {
            menuYPosition = 'top';
        } else if (isInTop || topBias < 0) {
            menuYPosition = 'bottom';
        }

        const newMenuPositionStyle = {
            bottom: menuYPosition === 'bottom' ? '100%' : null,
            left: menuXPosition === 'left' ? 0 : null,
            right: menuXPosition === 'right' ? 0 : null,
            top: menuYPosition === 'top' ? '100%' : null,
        };

        setMenuPositionStyle((prevState) => ({
            ...prevState,
            ...newMenuPositionStyle,
        }));
    }

    const debounceDropdownMenuReposition = debounce(() => onDropdownMenuReposition(), 100);

    useEffect(() => {
        function onClickOutside(event) {
            const parentContainer = dropdownMenuRef.current;
            const containsTarget = parentContainer.contains(event.target);

            if (
                !containsTarget ||
                (containsTarget && event.target.className === OPTION_INNER_CLASS_NAME)
            ) {
                onToggleOpen(event);
            }

            return null;
        }

        if (isOpen) {
            window.addEventListener('resize', debounceDropdownMenuReposition);
            window.addEventListener('scroll', debounceDropdownMenuReposition);
            document.addEventListener('click', onClickOutside);
        }

        onDropdownMenuReposition();

        return () => {
            if (isOpen) {
                window.removeEventListener('resize', debounceDropdownMenuReposition);
                window.removeEventListener('scroll', debounceDropdownMenuReposition);
                document.removeEventListener('click', onClickOutside);
            }
        };
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [isOpen]);

    const rootClasses = ClassNames(
        'ui',
        BEM_BLOCK_NAME,
        className,
        classes.root,
        {
            [classes.dropdownMenuClosed]: !isOpen,
            [classes.dropdownMenuOpen]: isOpen,
            [`${BEM_BLOCK_NAME}-closed`]: !isOpen,
            [`${BEM_BLOCK_NAME}-opened`]: isOpen,
        },
    );

    return (
        <div
            className={rootClasses}
            id={id}
            ref={dropdownMenuRef}
            style={{
                ...style,
                ...menuPositionStyle,
            }}
            tabIndex={tabIndex}
        >
            {children}
        </div>
    );
}

DropdownMenu.propTypes = propTypes;
DropdownMenu.defaultProps = defaultProps;

export default DropdownMenu;
