import cls from 'classnames';
import React, {useEffect, useRef, useState, CSSProperties, ReactNode, useLayoutEffect} from 'react';
import {fromEvent as rxFromEvent, Subscription, timer, race} from 'rxjs';
import {tap, switchMap} from 'rxjs/operators';
import {clamp, renderForeground} from '@core0/utils';
import cn from './tooltip.module.styl';

interface TooltipProps {
    enabled?: boolean;
    duration?: number;
    type?: 'normal' | 'error';
    style?: CSSProperties;
    className?: string;
    children?: ReactNode;
}

/**
 * Spinner effect used for resources loading
 */
export const Tooltip = (props: TooltipProps) => {
    const {
        enabled = true,
        duration = 10_000,
        type = 'normal',
        className,
        style,
        children
    } = props;

    const tooltipTrapRef = useRef<HTMLDivElement>(null);
    const tooltipElmRef = useRef<HTMLDivElement>(null);
    const [isTooltipActive, setIsTooltipActive] = useState<boolean>(false);

    /*
     ****************************************
     * Effect Hooks
     ****************************************
     */
    useLayoutEffect(() => {
        const anchorElm = tooltipTrapRef.current?.parentElement;
        if (!enabled || !anchorElm) return;

        const sub = subscribeTooltipShowAndDismiss(anchorElm);
        return () => {
            sub.unsubscribe();
        }
    }, [enabled, duration]);

    /*
     ****************************************
     * Event Handlers
     ****************************************
     */
    function subscribeTooltipShowAndDismiss(anchorElm: HTMLElement): Subscription {
        return rxFromEvent(anchorElm, 'mouseenter')
            .pipe(
                tap(() => setIsTooltipActive(true)),
                tap(() => {
                    requestAnimationFrame(() => {
                        const tooltipElm = tooltipElmRef.current;
                        if (!tooltipElm) return;

                        const {
                            top: anchorElmTop,
                            left: anchorElmLeft,
                            width: anchorElmWidth
                        } = anchorElm.getBoundingClientRect();
                        const {width: tooltipElmWidth, height: tooltipElmHeight} = tooltipElm.getBoundingClientRect();
                        const gapBetweenAnchorElm = 5;
                        const tooltipElmTop = anchorElmTop - tooltipElmHeight - gapBetweenAnchorElm;
                        const tooltipElmLeft = anchorElmLeft - (tooltipElmWidth - anchorElmWidth) / 2;
                        const viewportWidth = window.innerWidth ?? document.documentElement.offsetWidth;
                        const viewportHeight = window.innerHeight ?? document.documentElement.offsetHeight;
                        const clampedTooltipElmTop = clamp(0, tooltipElmTop, viewportHeight - tooltipElmHeight);
                        const clampedTooltipElmLeft = clamp(0, tooltipElmLeft, viewportWidth - tooltipElmWidth);
                        tooltipElm.style.top = `${clampedTooltipElmTop}px`;
                        tooltipElm.style.left = `${clampedTooltipElmLeft}px`;
                        tooltipElm.style.opacity = '1';
                    });
                }),
                switchMap(() => race(rxFromEvent(anchorElm, 'mouseleave'), timer(duration)))
            )
            .subscribe(() => setIsTooltipActive(false));
    }

    return (
        <>
            <div ref={tooltipTrapRef} className="tooltip-trap"/>
            {isTooltipActive &&
                renderForeground(
                    <div
                        ref={tooltipElmRef}
                        className={cls(cn.tooltip, className, {[cn.error]: type === 'error'})}
                        style={style}
                    >
                        {children}
                    </div>
                )}
        </>
    );
};
