/**
 * TamRank Tooltip — runtime positioning + portal + a11y layer
 *
 * Renders the bubble inside <body> ("portal") so any ancestor with
 * transform/filter/perspective can't trap position:fixed positioning.
 * Coordinates are computed from the trigger's getBoundingClientRect().
 *
 *  - ESC dismisses (WCAG 1.4.13 dismissible)
 *  - Auto-flip top↔bottom near viewport edges
 *  - Repositions on scroll / resize while a tooltip is visible
 *
 * Pure vanilla — no jQuery dependency.
 *
 * @package TamRank
 * @since 1.1.18
 */

(function () {
    'use strict';

    var GAP = 10;
    var EDGE_PAD = 8;
    var ADMIN_BAR = 32;

    // Portal layer at body-level. Bubbles get moved here on activation
    // so they escape any transformed/filtered parent stacking context.
    var portal = null;
    function getPortal() {
        if (portal && portal.parentNode) return portal;
        portal = document.createElement('div');
        portal.className = 'tr-tooltip-portal';
        portal.style.position = 'fixed';
        portal.style.top = '0';
        portal.style.left = '0';
        portal.style.width = '0';
        portal.style.height = '0';
        portal.style.zIndex = '999999';
        portal.style.pointerEvents = 'none';
        document.body.appendChild(portal);
        return portal;
    }

    var MODIFIERS = ['tr-tooltip--bottom', 'tr-tooltip--top', 'tr-tooltip--right', 'tr-tooltip--left',
                     'tr-tooltip--sm', 'tr-tooltip--md', 'tr-tooltip--lg', 'tr-tooltip--light'];

    function show(wrap) {
        var bubble = wrap.querySelector('.tr-tooltip__bubble') ||
                     (wrap._trBubble || null);
        if (!bubble) return;

        // Move bubble into portal so position:fixed is relative to viewport
        // (escapes any ancestor with transform/filter/perspective)
        if (bubble.parentNode !== getPortal()) {
            bubble._trOrigin = bubble.parentNode;
            bubble._trOriginWrap = wrap;
            wrap._trBubble = bubble;
            // Mirror modifier classes so caret + size + variant CSS applies
            MODIFIERS.forEach(function (cls) {
                bubble.classList.remove(cls);
                if (wrap.classList.contains(cls)) bubble.classList.add(cls);
            });
            getPortal().appendChild(bubble);
        }

        bubble.style.opacity = '1';
        bubble.style.visibility = 'visible';
        bubble.style.pointerEvents = 'auto';
        position(wrap, bubble);
    }

    function hide(wrap) {
        var bubble = wrap && wrap._trBubble;
        if (!bubble) return;
        bubble.style.opacity = '0';
        bubble.style.visibility = 'hidden';
        bubble.style.pointerEvents = 'none';
        // Return bubble to its origin so the DOM stays clean and the next
        // re-show can re-portal it with fresh classes.
        if (bubble._trOrigin) {
            // Strip mirrored modifier classes
            MODIFIERS.forEach(function (cls) { bubble.classList.remove(cls); });
            bubble._trOrigin.appendChild(bubble);
            bubble._trOrigin = null;
            bubble._trOriginWrap = null;
        }
        wrap._trBubble = null;
    }

    function position(wrap, bubble) {
        var trigger = wrap.querySelector('.tr-tooltip-trigger, .tr-tooltip-term') || wrap;
        var tr = trigger.getBoundingClientRect();
        var br = bubble.getBoundingClientRect();
        var bw = br.width;
        var bh = br.height;

        var side = wrap.classList.contains('tr-tooltip--bottom') ? 'bottom'
                 : wrap.classList.contains('tr-tooltip--right')  ? 'right'
                 : wrap.classList.contains('tr-tooltip--left')   ? 'left'
                 : 'top';

        // Auto-flip top → bottom near viewport top
        if (side === 'top' && tr.top - bh - GAP < ADMIN_BAR + EDGE_PAD) {
            side = 'bottom';
        }
        // Auto-flip bottom → top if no room below and there IS room above
        else if (side === 'bottom' && tr.bottom + bh + GAP > window.innerHeight - EDGE_PAD &&
                 tr.top - bh - GAP > ADMIN_BAR + EDGE_PAD) {
            side = 'top';
        }

        var top, left;
        switch (side) {
            case 'bottom':
                top = tr.bottom + GAP;
                left = tr.left + tr.width / 2 - bw / 2;
                break;
            case 'right':
                top = tr.top + tr.height / 2 - bh / 2;
                left = tr.right + GAP;
                break;
            case 'left':
                top = tr.top + tr.height / 2 - bh / 2;
                left = tr.left - bw - GAP;
                break;
            case 'top':
            default:
                top = tr.top - bh - GAP;
                left = tr.left + tr.width / 2 - bw / 2;
                break;
        }

        if (left < EDGE_PAD) left = EDGE_PAD;
        if (left + bw > window.innerWidth - EDGE_PAD) left = window.innerWidth - bw - EDGE_PAD;

        bubble.style.top = Math.round(top) + 'px';
        bubble.style.left = Math.round(left) + 'px';
    }

    var activeWrap = null;
    var activeBubble = null;

    function activate(wrap) {
        if (!wrap || activeWrap === wrap) return;
        if (activeWrap) hide(activeWrap);
        activeWrap = wrap;
        show(wrap);
        activeBubble = wrap.querySelector('.tr-tooltip__bubble') ||
                       getPortal().querySelector('.tr-tooltip__bubble[data-tr-mirror]');
    }

    function deactivate(wrap) {
        if (activeWrap === wrap) {
            hide(wrap);
            activeWrap = null;
            activeBubble = null;
        }
    }

    // ─── data-tr-tooltip support ─────────────────────────
    // Lightweight drop-in replacement for native title="…" on any element.
    // Usage: <button data-tr-tooltip="Delete">…</button>
    // A bubble is created on demand, portal'd to body, and positioned via
    // the same logic as the wrap-based .tr-tooltip system. Optional
    // attributes: data-tr-tooltip-position (top/bottom/left/right),
    // data-tr-tooltip-size (sm/md/lg), data-tr-tooltip-variant (light/dark).
    var autoBubble = null;
    var autoWrap = null;
    var autoTarget = null;

    function getAutoBubble() {
        if (autoBubble && autoBubble.parentNode) return autoBubble;
        autoBubble = document.createElement('span');
        autoBubble.className = 'tr-tooltip__bubble tr-tooltip--sm';
        autoBubble.setAttribute('role', 'tooltip');
        getPortal().appendChild(autoBubble);
        return autoBubble;
    }

    function showAuto(el) {
        var text = el.getAttribute('data-tr-tooltip');
        if (!text) return;
        // Suppress native title so we don't get a double-tooltip
        if (el.title) {
            el._trOrigTitle = el.title;
            el.title = '';
        }

        var bubble = getAutoBubble();
        // Apply modifiers from data-attrs (default: bottom + sm + dark)
        var pos = el.getAttribute('data-tr-tooltip-position') || 'bottom';
        var size = el.getAttribute('data-tr-tooltip-size') || 'sm';
        var variant = el.getAttribute('data-tr-tooltip-variant') || 'dark';

        ['tr-tooltip--bottom', 'tr-tooltip--top', 'tr-tooltip--right', 'tr-tooltip--left',
         'tr-tooltip--sm', 'tr-tooltip--md', 'tr-tooltip--lg', 'tr-tooltip--light'].forEach(function (cls) {
            bubble.classList.remove(cls);
        });
        if (pos !== 'top') bubble.classList.add('tr-tooltip--' + pos);
        bubble.classList.add('tr-tooltip--' + size);
        if (variant === 'light') bubble.classList.add('tr-tooltip--light');

        bubble.textContent = text;
        bubble.style.opacity = '1';
        bubble.style.visibility = 'visible';
        bubble.style.pointerEvents = 'auto';

        // Fake-wrap pretends to be a .tr-tooltip so position() works as-is
        autoWrap = {
            _trBubble: bubble,
            classList: { contains: function (cls) { return bubble.classList.contains(cls); } },
            querySelector: function () { return el; }
        };
        position(autoWrap, bubble);
        requestAnimationFrame(function () { position(autoWrap, bubble); });
        autoTarget = el;
    }

    function hideAuto(el) {
        if (autoBubble) {
            autoBubble.style.opacity = '0';
            autoBubble.style.visibility = 'hidden';
            autoBubble.style.pointerEvents = 'none';
        }
        autoWrap = null;
        autoTarget = null;
        // Restore native title
        if (el && el._trOrigTitle) {
            el.title = el._trOrigTitle;
            el._trOrigTitle = null;
        }
    }

    // Enter
    document.addEventListener('mouseover', function (e) {
        var t = e.target;
        if (!t.closest) return;
        var wrap = t.closest('.tr-tooltip');
        if (wrap) { activate(wrap); return; }
        var autoEl = t.closest('[data-tr-tooltip]');
        if (autoEl) showAuto(autoEl);
    }, true);

    document.addEventListener('focusin', function (e) {
        var t = e.target;
        if (!t.closest) return;
        var wrap = t.closest('.tr-tooltip');
        if (wrap) { activate(wrap); return; }
        var autoEl = t.closest('[data-tr-tooltip]');
        if (autoEl) showAuto(autoEl);
    }, true);

    // Leave — only deactivate when we genuinely left (not just into a child)
    document.addEventListener('mouseout', function (e) {
        var t = e.target;

        // Leaving the portal'd bubble itself: the bubble lives in the portal,
        // outside its .tr-tooltip wrap, so the wrap-based branch below never
        // matches it. Without this branch the tooltip sticks open after the
        // pointer exits via the bubble instead of via the trigger.
        if (portal && t.closest && portal.contains(t)) {
            var relP = e.relatedTarget;
            if (relP && (portal.contains(relP) ||
                (activeWrap && activeWrap.contains(relP)) ||
                (autoTarget && autoTarget.contains(relP)))) return;
            if (activeWrap) deactivate(activeWrap);
            if (autoTarget) hideAuto(autoTarget);
            return;
        }

        var wrap = t.closest && t.closest('.tr-tooltip');
        if (wrap) {
            var related = e.relatedTarget;
            if (related && (wrap.contains(related) || (portal && portal.contains(related)))) return;
            deactivate(wrap);
            return;
        }
        var autoEl = t.closest && t.closest('[data-tr-tooltip]');
        if (autoEl) {
            var rel = e.relatedTarget;
            if (rel && (autoEl.contains(rel) || (portal && portal.contains(rel)))) return;
            hideAuto(autoEl);
        }
    }, true);

    document.addEventListener('focusout', function (e) {
        var t = e.target;
        var wrap = t.closest && t.closest('.tr-tooltip');
        if (wrap) {
            var related = e.relatedTarget;
            if (related && wrap.contains(related)) return;
            deactivate(wrap);
            return;
        }
        var autoEl = t.closest && t.closest('[data-tr-tooltip]');
        if (autoEl) hideAuto(autoEl);
    }, true);

    // ESC closes (WCAG 1.4.13)
    document.addEventListener('keydown', function (e) {
        if (e.key !== 'Escape') return;
        if (activeWrap) {
            deactivate(activeWrap);
            if (document.activeElement && document.activeElement.blur) {
                document.activeElement.blur();
            }
        }
    });

    // Reposition on scroll / resize while a tooltip is open
    function reflow() {
        if (activeWrap && activeWrap._trBubble) {
            position(activeWrap, activeWrap._trBubble);
        }
    }
    window.addEventListener('scroll', reflow, true);
    window.addEventListener('resize', reflow);
})();
