class Tooltip {

    constructor() {
        this.currentTooltip = null;
        this.tooltipContainer = null;
        this.tooltipContent = null;
        this.tooltipArrow = null;

        this.OFFSET = 12;
        this.MARGIN = 10;
        this.ARROW_PADDING = 12;

        this.init();
    }

    init() {
        this.createTooltipElement();
        this.attachEventListeners();
    }

    createTooltipElement() {
        this.tooltipContainer = document.createElement('div');
        this.tooltipContainer.className = 'tooltip-container';
        this.tooltipContainer.innerHTML = `
            <div class="tooltip-content"></div>
            <div class="tooltip-arrow"></div>
        `;
        this.tooltipContainer.style.display = 'none';
        document.body.appendChild(this.tooltipContainer);

        this.tooltipContent = this.tooltipContainer.querySelector('.tooltip-content');
        this.tooltipArrow = this.tooltipContainer.querySelector('.tooltip-arrow');
    }

    attachEventListeners() {
        document.addEventListener('mouseover', this.handleMouseOver.bind(this));
        document.addEventListener('mouseout', this.handleMouseOut.bind(this));
        document.addEventListener('scroll', this.handleScroll.bind(this));
        window.addEventListener('resize', this.handleResize.bind(this));
    }

    add(element, text, position = 'auto') {
        if (!element || !text) {
            console.warn('Tooltip: Element and text are required');
            return;
        }

        element.setAttribute('data-tooltip', text);
        element.setAttribute('data-tooltip-position', position);
        element.classList.add('has-tooltip');
    }

    remove(element) {
        if (!element) return;

        element.removeAttribute('data-tooltip');
        element.removeAttribute('data-tooltip-position');
        element.classList.remove('has-tooltip');

        if (this.currentTooltip === element) {
            this.hide();
        }
    }

    update(element, newText) {
        if (!element || !newText) return;
        element.setAttribute('data-tooltip', newText);
    }

    handleMouseOver(e) {
        const element = e.target.closest('[data-tooltip]');
        if (element && element !== this.currentTooltip) {
            this.show(element);
        }
    }

    handleMouseOut(e) {
        const element = e.target.closest('[data-tooltip]');
        if (element && element === this.currentTooltip) {

            if (!element.contains(e.relatedTarget)) {
                this.hide();
            }
        }
    }

    handleScroll() {
        if (this.currentTooltip) {
            this.hide();
        }
    }

    handleResize() {
        if (this.currentTooltip) {
            this.hide();
        }
    }

    show(element) {
        const text = element.getAttribute('data-tooltip');
        const position = element.getAttribute('data-tooltip-position') || 'auto';

        this.tooltipContent.textContent = text;
        this.tooltipContainer.style.display = 'block';
        this.currentTooltip = element;

        void this.tooltipContainer.offsetWidth;

        this.positionTooltip(element, position);
    }

    hide() {
        if (this.tooltipContainer) {
            this.tooltipContainer.style.display = 'none';
        }
        this.currentTooltip = null;
    }

    positionTooltip(element, preferredPosition) {
        const tooltip = this.tooltipContainer;
        const elementRect = element.getBoundingClientRect();
        const tooltipRect = tooltip.getBoundingClientRect();
        const viewportWidth = window.innerWidth;
        const viewportHeight = window.innerHeight;

        let position = preferredPosition;
        let left, top;

        if (position === 'auto') {
            position = this.detectBestPosition(elementRect, tooltipRect);
        }

        const isMobile = viewportWidth <= 768;
        const isFloatingButton = element.closest('.floating-storage-button, .floating-button');

        if (isMobile && isFloatingButton) {
            position = 'top';
        }

        ({ left, top } = this.calculatePosition(elementRect, tooltipRect, position));

        ({ position, left, top } = this.adjustForViewport(
            elementRect, tooltipRect, position, left, top
        ));

        left = Math.max(this.MARGIN, Math.min(viewportWidth - tooltipRect.width - this.MARGIN, left));
        top = Math.max(this.MARGIN, Math.min(viewportHeight - tooltipRect.height - this.MARGIN, top));

        tooltip.style.left = left + 'px';
        tooltip.style.top = top + 'px';

        this.tooltipContainer.className = 'tooltip-container';
        void this.tooltipContainer.offsetWidth;
        this.tooltipContainer.className = `tooltip-container tooltip-${position}`;

        this.positionArrow(element, position, left, top);
    }

    detectBestPosition(elementRect, tooltipRect) {
        const viewportWidth = window.innerWidth;
        const viewportHeight = window.innerHeight;

        const spaces = {
            top: elementRect.top - this.MARGIN,
            bottom: viewportHeight - elementRect.bottom - this.MARGIN,
            left: elementRect.left - this.MARGIN,
            right: viewportWidth - elementRect.right - this.MARGIN
        };

        const required = {
            top: tooltipRect.height + this.OFFSET,
            bottom: tooltipRect.height + this.OFFSET,
            left: tooltipRect.width + this.OFFSET,
            right: tooltipRect.width + this.OFFSET
        };

        if (spaces.top >= required.top) return 'top';
        if (spaces.bottom >= required.bottom) return 'bottom';
        if (spaces.right >= required.right) return 'right';
        if (spaces.left >= required.left) return 'left';

        const maxSpace = Math.max(...Object.values(spaces));
        return Object.keys(spaces).find(key => spaces[key] === maxSpace);
    }

    calculatePosition(elementRect, tooltipRect, position) {
        let left, top;

        switch (position) {
            case 'top':
                left = elementRect.left + (elementRect.width / 2) - (tooltipRect.width / 2);
                top = elementRect.top - tooltipRect.height - this.OFFSET;
                break;
            case 'bottom':
                left = elementRect.left + (elementRect.width / 2) - (tooltipRect.width / 2);
                top = elementRect.bottom + this.OFFSET;
                break;
            case 'left':
                left = elementRect.left - tooltipRect.width - this.OFFSET;
                top = elementRect.top + (elementRect.height / 2) - (tooltipRect.height / 2);
                break;
            case 'right':
                left = elementRect.right + this.OFFSET;
                top = elementRect.top + (elementRect.height / 2) - (tooltipRect.height / 2);
                break;
            default:

                left = elementRect.left - tooltipRect.width - this.OFFSET;
                top = elementRect.top + (elementRect.height / 2) - (tooltipRect.height / 2);
        }

        return { left, top };
    }

    adjustForViewport(elementRect, tooltipRect, position, left, top) {
        const viewportWidth = window.innerWidth;
        const viewportHeight = window.innerHeight;

        if ((position === 'left' && left < this.MARGIN) ||
            (position === 'right' && left + tooltipRect.width > viewportWidth - this.MARGIN)) {

            if (position === 'left' && elementRect.right + tooltipRect.width + this.OFFSET < viewportWidth - this.MARGIN) {
                position = 'right';
                ({ left, top } = this.calculatePosition(elementRect, tooltipRect, position));
            } else if (position === 'right' && elementRect.left - tooltipRect.width - this.OFFSET > this.MARGIN) {
                position = 'left';
                ({ left, top } = this.calculatePosition(elementRect, tooltipRect, position));
            } else {

                position = elementRect.top - tooltipRect.height - this.OFFSET > this.MARGIN ? 'top' : 'bottom';
                ({ left, top } = this.calculatePosition(elementRect, tooltipRect, position));
            }
        }

        if ((position === 'top' && top < this.MARGIN) ||
            (position === 'bottom' && top + tooltipRect.height > viewportHeight - this.MARGIN)) {

            if (position === 'top' && elementRect.bottom + tooltipRect.height + this.OFFSET < viewportHeight - this.MARGIN) {
                position = 'bottom';
                ({ left, top } = this.calculatePosition(elementRect, tooltipRect, position));
            } else if (position === 'bottom' && elementRect.top - tooltipRect.height - this.OFFSET > this.MARGIN) {
                position = 'top';
                ({ left, top } = this.calculatePosition(elementRect, tooltipRect, position));
            }
        }

        return { position, left, top };
    }

    positionArrow(element, position, tooltipLeft, tooltipTop) {
        const elementRect = element.getBoundingClientRect();
        const tooltipRect = this.tooltipContainer.getBoundingClientRect();
        const arrow = this.tooltipArrow;

        arrow.style.left = '';
        arrow.style.right = '';
        arrow.style.top = '';
        arrow.style.bottom = '';
        arrow.style.transform = '';

        switch (position) {
            case 'top':
            case 'bottom':

                const elementCenterX = elementRect.left + (elementRect.width / 2);
                const arrowLeft = elementCenterX - tooltipLeft;
                const constrainedArrowLeft = Math.max(
                    this.ARROW_PADDING,
                    Math.min(tooltipRect.width - this.ARROW_PADDING, arrowLeft)
                );

                arrow.style.left = constrainedArrowLeft + 'px';
                arrow.style.transform = 'translateX(-50%)';

                if (position === 'top') {
                    arrow.style.bottom = '-8px';
                } else {
                    arrow.style.top = '-8px';
                }
                break;

            case 'left':
            case 'right':

                const elementCenterY = elementRect.top + (elementRect.height / 2);
                const arrowTop = elementCenterY - tooltipTop;
                const constrainedArrowTop = Math.max(
                    this.ARROW_PADDING,
                    Math.min(tooltipRect.height - this.ARROW_PADDING, arrowTop)
                );

                arrow.style.top = constrainedArrowTop + 'px';
                arrow.style.transform = 'translateY(-50%)';

                if (position === 'left') {
                    arrow.style.right = '-8px';
                } else {
                    arrow.style.left = '-8px';
                }
                break;
        }
    }

    destroy() {

        document.removeEventListener('mouseover', this.handleMouseOver.bind(this));
        document.removeEventListener('mouseout', this.handleMouseOut.bind(this));
        document.removeEventListener('scroll', this.handleScroll.bind(this));
        window.removeEventListener('resize', this.handleResize.bind(this));

        if (this.tooltipContainer && this.tooltipContainer.parentNode) {
            this.tooltipContainer.parentNode.removeChild(this.tooltipContainer);
        }

        this.tooltipContainer = null;
        this.tooltipContent = null;
        this.tooltipArrow = null;
        this.currentTooltip = null;
    }

}

if (typeof module !== 'undefined' && module.exports) {
    module.exports = Tooltip;
}

if (typeof window !== 'undefined') {
    window.Tooltip = Tooltip;
}