const roundByDPR = (value) => {
    const dpr = window.devicePixelRatio || 1;
    return Math.round(value * dpr) / dpr;
};
const attributeToOption = (attribute) => {
    attribute = attribute.replace('tooltip', '');
    return attribute.charAt(0).toLowerCase() + attribute.slice(1);
};
const isTouchDevice = () => 'ontouchstart' in window || navigator.maxTouchPoints > 0;
export const createTooltip = ($el, content, options) => {
    options = {
        animation: [
            [{ opacity: 0 }, { opacity: 1 }],
            [{ opacity: 1 }, { opacity: 0 }],
        ],
        appendTo: document.body,
        arrow: true,
        delay: [0, 0],
        duration: [0, 0],
        easing: ['linear', 'linear'],
        hideOnClick: true, // Возможные значения: true, 'all', 'toggle'
        interactive: true,
        offset: [0, 8],
        placement: 'top',
        shiftPadding: [8, 0],
        theme: 'light',
        trigger: 'mouseenter',
        virtualReference: undefined,
        zIndex: '',
        ...options,
    };
    for (const [key, value] of Object.entries($el.dataset)) {
        if (key.startsWith('tooltip')) {
            let parsedValue = value;
            try {
                parsedValue = JSON.parse(value);
            } catch {} // eslint-disable-line no-empty
            options[attributeToOption(key)] = parsedValue;
        }
    }
    let showTimeout;
    let hideTimeout;
    let rafId;
    const listeners = [];
    $el._tooltip = {
        options,
        isVisible: false,
        $tooltip: undefined,
        $container: undefined,
        $arrow: undefined,
        $interactive: undefined,
        autoUpdateCleanup: () => {},
        setContent(updatedContent) {
            if (updatedContent !== undefined) {
                content = updatedContent;
            }
            if ($el._tooltip.$container) {
                if (content instanceof HTMLElement) {
                    $el._tooltip.$container.innerHTML = '';
                    $el._tooltip.$container.append(content);
                } else {
                    $el._tooltip.$container.innerHTML = content;
                }
            }
        },
        async updateOptions(updatedOptions = {}) {
            for (const [name, value] of Object.entries(updatedOptions)) {
                options[name] = value;
            }
            if (updatedOptions.arrow !== undefined && $el._tooltip.$tooltip) {
                if (options.arrow) {
                    if (!$el._tooltip.$arrow) {
                        $el._tooltip.$tooltip.insertAdjacentHTML(
                            'afterbegin',
                            `
                                
                                
                            `,
                        );
                        $el._tooltip.$arrow = $el._tooltip.$tooltip.querySelector('.tooltip__arrow');
                    }
                } else {
                    $el._tooltip.$arrow?.remove();
                    $el._tooltip.$arrow = undefined;
                }
            }
            if (updatedOptions.interactive !== undefined) {
                for (const { el, event, listener } of listeners) {
                    el.removeEventListener(event, listener);
                }
                listeners.length = 0;
                registerListeners();
                if ($el._tooltip.$tooltip) {
                    if (options.interactive) {
                        if (!$el._tooltip.$interactive) {
                            $el._tooltip.$tooltip.insertAdjacentHTML(
                                'afterbegin',
                                `
                                    
                                    
                                `,
                            );
                            $el._tooltip.$interactive =
                                $el._tooltip.$tooltip.querySelector('.tooltip__interactive-helper');
                        }
                    } else {
                        $el._tooltip.$interactive?.remove();
                        $el._tooltip.$interactive = undefined;
                    }
                }
            }
            if (updatedOptions.theme !== undefined && $el._tooltip.$tooltip) {
                const classIndex = [...$el._tooltip.$tooltip.classList].findIndex((className) =>
                    className.startsWith('tooltip_theme_'),
                );
                if (classIndex === -1) {
                    $el._tooltip.$tooltip.classList.add(`tooltip_theme_${options.theme}`);
                } else {
                    $el._tooltip.$tooltip.classList.replace(
                        $el._tooltip.$tooltip.classList[classIndex],
                        `tooltip_theme_${options.theme}`,
                    );
                }
            }
            if (updatedOptions.zIndex !== undefined && $el._tooltip.$tooltip) {
                Object.assign($el._tooltip.$tooltip.style, { zIndex: options.zIndex });
            }
            if ($el._tooltip.$tooltip) {
                await $el._tooltip.updatePosition();
            }
        },
        destroy() {
            if (!$el._tooltip) {
                return;
            }
            $el._tooltip.$tooltip?.remove();
            // Вызываем autoUpdateCleanup только если всплывающая подсказка была видна (иначе вызывать её не имеет смысла)
            if ($el._tooltip.isVisible) {
                $el._tooltip.autoUpdateCleanup();
            }
            for (const { el, event, listener } of listeners) {
                el.removeEventListener(event, listener);
            }
            listeners.length = 0;
            delete $el._tooltip;
        },
    };
    if (options.onCreate) {
        options.onCreate($el._tooltip);
    }
    $el._tooltip.show = async ({ immediately } = {}) => {
        clearTimeout(hideTimeout);
        if (!$el._tooltip.$tooltip) {
            const { computePosition, offset, flip, shift, arrow } = await import('@floating-ui/dom');
            $el._tooltip.$tooltip = document.createElement('div');
            $el._tooltip.$tooltip._reference = $el;
            $el._tooltip.$tooltip.classList.add('tooltip', `tooltip_theme_${options.theme}`);
            Object.assign($el._tooltip.$tooltip.style, { zIndex: options.zIndex });
            $el._tooltip.$tooltip.innerHTML = `
                
            `;
            $el._tooltip.$container = $el._tooltip.$tooltip.querySelector('.tooltip__container');
            $el._tooltip.$arrow = $el._tooltip.$tooltip.querySelector('.tooltip__arrow');
            $el._tooltip.$interactive = $el._tooltip.$tooltip.querySelector('.tooltip__interactive-helper');
            $el._tooltip.setContent();
            $el._tooltip.updatePosition = () => {
                cancelAnimationFrame(rafId);
                rafId = requestAnimationFrame(async () => {
                    const { x, y, placement, middlewareData } = await computePosition(
                        options.virtualReference ?? $el,
                        $el._tooltip.$tooltip,
                        {
                            placement: options.placement,
                            middleware: [
                                offset({ mainAxis: options.offset[1], crossAxis: options.offset[0] }),
                                flip(),
                                shift({
                                    padding: {
                                        top: options.shiftPadding[1],
                                        right: options.shiftPadding[0],
                                        bottom: options.shiftPadding[1],
                                        left: options.shiftPadding[0],
                                    },
                                }),
                                arrow({ element: $el._tooltip.$arrow }),
                            ],
                        },
                    );
                    Object.assign($el._tooltip.$tooltip.style, {
                        transform: `translate(${roundByDPR(x)}px, ${roundByDPR(y)}px)`,
                    });
                    const side = placement.split('-')[0];
                    const staticSide = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }[side];
                    $el._tooltip.$tooltip.classList.remove(
                        'tooltip_side_top',
                        'tooltip_side_bottom',
                        'tooltip_side_left',
                        'tooltip_side_right',
                    );
                    $el._tooltip.$tooltip.classList.add(`tooltip_side_${side}`);
                    if ($el._tooltip.$interactive) {
                        Object.assign($el._tooltip.$interactive.style, {
                            width: side === 'left' || side === 'right' ? `${options.offset[1]}px` : '100%',
                            height: side === 'top' || side === 'bottom' ? `${options.offset[1]}px` : '100%',
                            [staticSide]: `-${options.offset[1]}px`,
                        });
                    }
                    if ($el._tooltip.$arrow && middlewareData.arrow) {
                        const { x, y } = middlewareData.arrow;
                        Object.assign($el._tooltip.$arrow.style, {
                            left: x === undefined ? '' : `${x}px`,
                            top: y === undefined ? '' : `${y}px`,
                            [staticSide]: `-${$el._tooltip.$arrow.offsetWidth / 2}px`,
                        });
                    }
                });
            };
            if (options.onMount) {
                options.onMount($el._tooltip);
            }
        }
        const { autoUpdate } = await import('@floating-ui/dom');
        showTimeout = setTimeout(
            async () => {
                if (!$el._tooltip.isVisible) {
                    (options.appendTo === 'parent' ? $el.parentElement : options.appendTo).append(
                        $el._tooltip.$tooltip,
                    );
                    $el._tooltip.isVisible = true;
                    $el._tooltip.autoUpdateCleanup = autoUpdate(
                        $el,
                        $el._tooltip.$tooltip,
                        $el._tooltip.updatePosition,
                    );
                    if (
                        options.hideOnClick &&
                        (options.trigger.includes('click') || options.trigger.includes('manual'))
                    ) {
                        document.body.addEventListener('click', $el._tooltip.hideOnClickListener);
                        listeners.push({
                            el: document.body,
                            event: 'click',
                            listener: $el._tooltip.hideOnClickListener,
                        });
                    }
                    if (options.onShow) {
                        options.onShow($el._tooltip);
                    }
                    try {
                        await $el._tooltip.$tooltip.querySelector('.tooltip__root').animate(options.animation[0], {
                            duration: immediately ? 0 : options.duration[0],
                            easing: options.easing[0],
                        }).finished;
                    } catch {} // eslint-disable-line no-empty
                    if (options.onShown) {
                        options.onShown($el._tooltip);
                    }
                }
            },
            immediately ? 0 : options.delay[0],
        );
    };
    $el._tooltip.hide = ({ immediately } = {}) => {
        clearTimeout(showTimeout);
        hideTimeout = setTimeout(
            async () => {
                if ($el._tooltip.isVisible) {
                    if (options.onHide) {
                        options.onHide($el._tooltip);
                    }
                    try {
                        await $el._tooltip.$tooltip.querySelector('.tooltip__root').animate(options.animation[1], {
                            duration: immediately ? 0 : options.duration[1],
                            easing: options.easing[1],
                        }).finished;
                    } catch {} // eslint-disable-line no-empty
                    if ($el._tooltip.$tooltip) {
                        $el._tooltip.$tooltip.remove();
                    }
                    $el._tooltip.isVisible = false;
                    $el._tooltip.autoUpdateCleanup();
                    if (options.onHidden) {
                        options.onHidden($el._tooltip);
                    }
                }
            },
            immediately ? 0 : options.delay[1],
        );
    };
    $el._tooltip.hideOnClickListener = ({ target }) => {
        if (!$el._tooltip.isVisible) {
            return;
        }
        if (options.hideOnClick === 'all') {
            $el._tooltip.hide();
            document.body.removeEventListener('click', $el._tooltip.hideOnClickListener);
        } else if (options.hideOnClick === 'toggle') {
            if ($el.contains(target)) {
                $el._tooltip.hide();
                document.body.removeEventListener('click', $el._tooltip.hideOnClickListener);
            }
        } else {
            // options.hideOnClick === true
            if ($el.contains(target) || !$el._tooltip.$tooltip.contains(target)) {
                $el._tooltip.hide();
                document.body.removeEventListener('click', $el._tooltip.hideOnClickListener);
            }
        }
    };
    $el._tooltip.clickListener = () => {
        if (!$el._tooltip.isVisible) {
            $el._tooltip.show();
        }
    };
    $el._tooltip.mouseEnterListener = () => {
        $el._tooltip.show();
    };
    $el._tooltip.mouseLeaveListener = ({ relatedTarget }) => {
        if (options.interactive && relatedTarget && $el._tooltip.$tooltip?.contains(relatedTarget)) {
            $el._tooltip.$tooltip.addEventListener(
                'mouseleave',
                ({ relatedTarget }) => {
                    if (!relatedTarget || !$el.contains(relatedTarget)) {
                        $el._tooltip.hide();
                    }
                },
                { once: true },
            );
        } else {
            $el._tooltip.hide();
        }
    };
    $el._tooltip.focusListener = () => {
        $el._tooltip.show();
    };
    $el._tooltip.blurListener = () => {
        $el._tooltip.hide();
    };
    const registerListeners = () => {
        for (const trigger of options.trigger.split(' ')) {
            switch (trigger) {
                case 'mouseenter': {
                    if (!isTouchDevice()) {
                        $el.addEventListener('mouseenter', $el._tooltip.mouseEnterListener);
                        listeners.push({ el: $el, event: 'mouseenter', listener: $el._tooltip.mouseEnterListener });
                        $el.addEventListener('mouseleave', $el._tooltip.mouseLeaveListener);
                        listeners.push({ el: $el, event: 'mouseleave', listener: $el._tooltip.mouseLeaveListener });
                    }
                    break;
                }
                case 'click': {
                    $el.addEventListener('click', $el._tooltip.clickListener);
                    listeners.push({ el: $el, event: 'click', listener: $el._tooltip.clickListener });
                    if (!options.interactive) {
                        $el.addEventListener('mouseleave', $el._tooltip.mouseLeaveListener);
                        listeners.push({ el: $el, event: 'mouseleave', listener: $el._tooltip.mouseLeaveListener });
                    }
                    break;
                }
                case 'focus': {
                    $el.addEventListener('focus', $el._tooltip.focusListener);
                    listeners.push({ el: $el, event: 'focus', listener: $el._tooltip.focusListener });
                    break;
                }
                case 'blur': {
                    $el.addEventListener('blur', $el._tooltip.blurListener);
                    listeners.push({ el: $el, event: 'blur', listener: $el._tooltip.blurListener });
                    break;
                }
            }
        }
    };
    registerListeners();
};