import * as React from 'react';

import {Portal} from './Portal';
import {withOverlayStackSynchronyzer} from '../overlay_stack/synchronizer';
import {joinClassNames} from '../../../../utils/joinClassNames';
import {filterProps} from '../../../../utils/filterProps';

import {
    DefaultProps,
    IProps,
    FullProps,
    FullPropsWithDefault
} from './Overlay.types';

import * as theme from './overlay.m.scss';

class UnwrappedBasicOverlay extends React.PureComponent<FullProps> {
    static defaultProps: DefaultProps = {
        shouldCloseOnClickOutside: true,
        shouldCloseOnEsc: true,
        hasBackdrop: false
    };

    static excludingProps: Array<keyof FullProps> = [
        'isLast',
        'isLastBackdrop',
        'isContainsInUpperOverlays',
        'isOpen',
        'hasBackdrop',
        'shouldCloseOnClickOutside',
        'shouldCloseOnEsc',
        'styles',
        'onClose'
    ];

    private overlayElement = React.createRef<HTMLDivElement>();

    private handleOutsideClick: EventListener = (e) => {
        const {isOpen, hasBackdrop, onClose, shouldCloseOnClickOutside, isContainsInUpperOverlays} = this
            .props as FullPropsWithDefault;

        const eventTarget = e.target as Element;

        if (
            shouldCloseOnClickOutside &&
            isOpen &&
            !hasBackdrop &&
            onClose &&
            this.overlayElement.current &&
            !this.overlayElement.current.contains(eventTarget) &&
            !isContainsInUpperOverlays(eventTarget)
        ) {
            onClose(e);
        }
    };

    private handleOverlayClick: React.MouseEventHandler<HTMLDivElement> = (e) => {
        const {isOpen, onClick, hasBackdrop, onClose, shouldCloseOnClickOutside} = this.props as FullPropsWithDefault;
        const eventTarget = e.target as Element;

        if (
            shouldCloseOnClickOutside &&
            onClose &&
            isOpen &&
            hasBackdrop &&
            eventTarget === this.overlayElement.current
        ) {
            onClose(e);
        }

        if (onClick) {
            onClick(e);
        }
    };

    private handleKeyUp: EventListener = (e: KeyboardEvent) => {
        const {isOpen, onClose, shouldCloseOnEsc, isLast} = this.props as FullPropsWithDefault;

        if (
            isLast &&
            shouldCloseOnEsc &&
            onClose &&
            isOpen &&
            e.key === 'Escape'
        ) {
            onClose(e);
        }
    };

    override componentDidMount () {
        document.body.addEventListener('click', this.handleOutsideClick);
        document.body.addEventListener('keyup', this.handleKeyUp);
    }

    override componentWillUnmount () {
        document.body.removeEventListener('click', this.handleOutsideClick);
        document.body.removeEventListener('keyup', this.handleKeyUp);
    }

    private renderBackdrop () {
        const {hasBackdrop, styles} = this.props;
        const style = styles && styles.backdrop;

        if (hasBackdrop) {
            return <div style={style} className={theme.backdrop} />;
        }

        return null;
    }


    override render () {
        if (!this.props.isOpen) {
            return null;
        }

        const {styles, hasBackdrop, isLastBackdrop, isLast} = this.props as FullPropsWithDefault;
        const style = styles && styles.overlay;

        const className = joinClassNames(
            theme.overlay,
            [theme.overlayHasBackdrop, hasBackdrop],
            [theme.overlayIsLast, isLast],
            [theme.overlayIsLastBackdrop, isLastBackdrop],
        );

        const {children, ...htmlProps} = {
            ...filterProps<IProps, React.HTMLAttributes<HTMLDivElement>>(
                this.props,
                UnwrappedBasicOverlay.excludingProps,
            ),
            ref: this.overlayElement,
            style,
            className,
            onClick: this.handleOverlayClick
        };

        return (
            <Portal>
                <div {...htmlProps}>
                    {this.renderBackdrop()}
                    {children}
                </div>
            </Portal>
        );
    }
}

export const Overlay = withOverlayStackSynchronyzer(UnwrappedBasicOverlay);
