var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { LitElement } from 'lit';
import { property } from 'lit/decorators.js';
// Tailwind content scanning requires complete class names (no template literals).
const POSITION_CLASSES = {
    top: 'btu-tooltip-top',
    bottom: 'btu-tooltip-bottom',
    left: 'btu-tooltip-left',
    right: 'btu-tooltip-right',
};
const OFFSET_CLASSES = {
    sm: 'btu-tooltip-offset-sm',
    md: 'btu-tooltip-offset-md',
    lg: 'btu-tooltip-offset-lg',
    xl: 'btu-tooltip-offset-xl',
};
/**
 * Mixin that adds declarative tooltip support to any LitElement component.
 *
 * Uses the native Popover API for top-layer rendering and CSS anchor positioning
 * for viewport-aware placement. The tooltip element is appended as the last child
 * of the host (safe from Lit re-render interference in Light DOM components).
 *
 * For click triggers (`popover="auto"`), the browser provides light dismiss
 * (Escape key and click-outside). For hover/focus triggers (`popover="manual"`),
 * show/hide is controlled by the mixin.
 *
 * @mixin
 *
 * @cssprop --tooltip-color-background - Background color (default: white)
 * @cssprop --tooltip-color-text - Text color (default: oklch gray-900)
 * @cssprop --tooltip-border-radius - Border radius (default: 0.5rem / lg)
 *
 * @example
 * ```typescript
 * class MyComponent extends TooltipMixin(EventEmitterMixin(ReadyMixin(LitElement))) {
 *   // Component now supports tooltip, tooltip-position, tooltip-delay attributes
 * }
 * ```
 *
 * ```html
 * <my-component tooltip="Hello!" tooltip-position="bottom"></my-component>
 * ```
 */
export const TooltipMixin = (superClass) => {
    class TooltipMixinClass extends superClass {
        constructor() {
            super(...arguments);
            /**
             * Tooltip text content. Set to empty string to remove the tooltip.
             * @attr
             */
            this.tooltip = '';
            /**
             * Position relative to the host element. Auto-flips when constrained by the viewport.
             * @attr
             */
            this.tooltipPosition = 'top';
            /**
             * Show delay in milliseconds (hover trigger only).
             * @attr
             */
            this.tooltipDelay = 300;
            /**
             * Gap between host and tooltip (sm=4px, md=8px, lg=12px, xl=16px).
             * @attr
             */
            this.tooltipOffset = null;
            /**
             * Hide the arrow pseudo-element.
             * @attr
             */
            this.tooltipNoArrow = false;
            /**
             * Space-separated trigger types: 'hover', 'focus', 'click'.
             * @attr
             */
            this.tooltipTrigger = 'hover focus';
            /** @internal */
            this._tooltipEl = null;
            /** @internal */
            this._tooltipId = `btu-tooltip-${crypto.randomUUID()}`;
            /** @internal */
            this._anchorName = `--btu-trigger-${crypto.randomUUID()}`;
            /** @internal */
            this._showTimer = null;
            /** @internal */
            this._isVisible = false;
            /** @internal Parsed set of active triggers */
            this._activeTriggers = new Set();
            /** @internal bound listeners for cleanup */
            this._onMouseEnter = this._handleMouseEnter.bind(this);
            this._onMouseLeave = this._handleMouseLeave.bind(this);
            this._onFocusIn = this._handleFocusIn.bind(this);
            this._onFocusOut = this._handleFocusOut.bind(this);
            this._onClick = this._handleClick.bind(this);
            this._onNativeToggle = this._handleNativeToggle.bind(this);
            this._onKeyDown = this._handleKeyDown.bind(this);
            this._onDocumentClick = null;
        }
        connectedCallback() {
            super.connectedCallback();
            this._bindTriggerListeners();
        }
        disconnectedCallback() {
            this._clearShowTimer();
            this._removeDocumentClickListener();
            this._unbindTriggerListeners();
            if (this._tooltipEl) {
                try {
                    this._tooltipEl.hidePopover();
                }
                catch (e) {
                    if (!(e instanceof DOMException && e.name === 'InvalidStateError')) {
                        console.warn('[btu-tooltip] Failed to hide tooltip during disconnect:', e);
                    }
                }
                this._tooltipEl.removeEventListener('toggle', this._onNativeToggle);
                this._tooltipEl.remove();
                this._tooltipEl = null;
            }
            this._removeAriaDescribedBy();
            this.style.removeProperty('anchor-name');
            this._isVisible = false;
            super.disconnectedCallback();
        }
        updated(changedProperties) {
            super.updated(changedProperties);
            if (changedProperties.has('tooltipTrigger')) {
                this._unbindTriggerListeners();
                this._bindTriggerListeners();
                // Update popover mode when trigger changes
                if (this._tooltipEl) {
                    if (this._isVisible) {
                        this._hide();
                    }
                    this._tooltipEl.setAttribute('popover', this._getPopoverMode());
                }
            }
            if (changedProperties.has('tooltip') ||
                changedProperties.has('tooltipPosition') ||
                changedProperties.has('tooltipDelay') ||
                changedProperties.has('tooltipOffset') ||
                changedProperties.has('tooltipNoArrow')) {
                this._syncTooltipElement();
            }
        }
        /** @internal
         * Always manual — popovertarget (needed for popover="auto" click toggle)
         * only works on <button>/<input>, not arbitrary custom element hosts.
         */
        _getPopoverMode() {
            return 'manual';
        }
        /** @internal */
        _syncTooltipElement() {
            if (!this.tooltip) {
                // Remove tooltip if text cleared
                if (this._tooltipEl) {
                    this._hide();
                    this._tooltipEl.removeEventListener('toggle', this._onNativeToggle);
                    this._tooltipEl.remove();
                    this._tooltipEl = null;
                    this._removeAriaDescribedBy();
                    this.style.removeProperty('anchor-name');
                    document.removeEventListener('keydown', this._onKeyDown);
                }
                return;
            }
            // Set unique anchor name on host
            this.style.setProperty('anchor-name', this._anchorName);
            if (!this._tooltipEl) {
                this._tooltipEl = this._createTooltipElement();
                this.appendChild(this._tooltipEl);
                this._appendAriaDescribedBy();
                // Always manual mode — Escape not handled by browser, add only when tooltip exists
                document.addEventListener('keydown', this._onKeyDown);
            }
            // Update text content
            this._tooltipEl.textContent = this.tooltip;
            // Update classes (includes offset)
            this._tooltipEl.className = this._buildTooltipClasses();
        }
        /** @internal */
        _createTooltipElement() {
            const el = document.createElement('div');
            el.id = this._tooltipId;
            el.className = this._buildTooltipClasses();
            el.setAttribute('role', 'tooltip');
            el.setAttribute('data-tooltip-internal', '');
            el.setAttribute('popover', this._getPopoverMode());
            el.style.setProperty('position-anchor', this._anchorName);
            el.textContent = this.tooltip;
            // Listen for native toggle events — single source of truth for state
            el.addEventListener('toggle', this._onNativeToggle);
            return el;
        }
        /** @internal Builds the full class string for the tooltip element */
        _buildTooltipClasses() {
            const classes = ['btu-tooltip', POSITION_CLASSES[this.tooltipPosition]];
            if (this.tooltipOffset && OFFSET_CLASSES[this.tooltipOffset])
                classes.push(OFFSET_CLASSES[this.tooltipOffset]);
            if (this.tooltipNoArrow)
                classes.push('btu-tooltip-no-arrow');
            return classes.join(' ');
        }
        /** @internal */
        _hasTrigger(trigger) {
            return this._activeTriggers.has(trigger);
        }
        /** @internal Parses trigger string and attaches relevant listeners */
        _bindTriggerListeners() {
            const VALID_TRIGGERS = new Set(['hover', 'focus', 'click']);
            this._activeTriggers = new Set(this.tooltipTrigger.trim().split(/\s+/));
            for (const trigger of this._activeTriggers) {
                if (!VALID_TRIGGERS.has(trigger)) {
                    console.warn(`[btu-tooltip] Unrecognized tooltip-trigger value: "${trigger}". Valid values: hover, focus, click.`);
                }
            }
            if (this._hasTrigger('hover')) {
                this.addEventListener('mouseenter', this._onMouseEnter);
                this.addEventListener('mouseleave', this._onMouseLeave);
            }
            if (this._hasTrigger('focus')) {
                this.addEventListener('focusin', this._onFocusIn);
                this.addEventListener('focusout', this._onFocusOut);
            }
            if (this._hasTrigger('click')) {
                this.addEventListener('click', this._onClick);
            }
            // Keydown listener for Escape is added in _syncTooltipElement when tooltip is set
        }
        /** @internal Removes all trigger listeners */
        _unbindTriggerListeners() {
            this.removeEventListener('mouseenter', this._onMouseEnter);
            this.removeEventListener('mouseleave', this._onMouseLeave);
            this.removeEventListener('focusin', this._onFocusIn);
            this.removeEventListener('focusout', this._onFocusOut);
            this.removeEventListener('click', this._onClick);
            document.removeEventListener('keydown', this._onKeyDown);
        }
        /** @internal */
        _show() {
            if (this._isVisible || !this._tooltipEl)
                return;
            try {
                this._tooltipEl.showPopover();
            }
            catch (e) {
                if (!(e instanceof DOMException && e.name === 'InvalidStateError')) {
                    console.warn('[btu-tooltip] Failed to show tooltip:', e);
                }
            }
            // Click-outside dismiss for click-triggered tooltips
            if (this._hasTrigger('click')) {
                // Use next tick to avoid the opening click from immediately dismissing
                setTimeout(() => {
                    this._removeDocumentClickListener();
                    this._onDocumentClick = (e) => {
                        const path = e.composedPath();
                        if (!path.includes(this) && (!this._tooltipEl || !path.includes(this._tooltipEl))) {
                            this._hide();
                        }
                    };
                    document.addEventListener('click', this._onDocumentClick);
                }, 0);
            }
        }
        /** @internal */
        _hide() {
            this._clearShowTimer();
            this._removeDocumentClickListener();
            if (!this._isVisible || !this._tooltipEl)
                return;
            try {
                this._tooltipEl.hidePopover();
            }
            catch (e) {
                if (!(e instanceof DOMException && e.name === 'InvalidStateError')) {
                    console.warn('[btu-tooltip] Failed to hide tooltip:', e);
                }
            }
        }
        /** @internal */
        _removeDocumentClickListener() {
            if (this._onDocumentClick) {
                document.removeEventListener('click', this._onDocumentClick);
                this._onDocumentClick = null;
            }
        }
        /** @internal Native toggle event — single source of truth for state changes */
        _handleNativeToggle(e) {
            const { newState } = e;
            this._isVisible = newState === 'open';
            // Event is constructed separately so CEM's dispatchEvent scanner doesn't
            // emit an unnamed event entry for the non-literal name expression.
            const eventName = this._isVisible ? 'btu-tooltip-show' : 'btu-tooltip-hide';
            const event = new CustomEvent(eventName, { bubbles: true, composed: true });
            this.dispatchEvent(event);
        }
        /** @internal Escape dismisses for popover="manual" (hover/focus triggers) */
        _handleKeyDown(e) {
            if (e.key === 'Escape') {
                this._clearShowTimer();
                this._hide();
            }
        }
        /** @internal */
        _clearShowTimer() {
            if (this._showTimer !== null) {
                clearTimeout(this._showTimer);
                this._showTimer = null;
            }
        }
        /** @internal */
        _handleMouseEnter() {
            this._clearShowTimer();
            this._showTimer = setTimeout(() => {
                this._show();
            }, this.tooltipDelay);
        }
        /** @internal */
        _handleMouseLeave() {
            this._clearShowTimer();
            this._hide();
        }
        /** @internal */
        _handleFocusIn() {
            this._clearShowTimer();
            this._show();
        }
        /** @internal */
        _handleFocusOut() {
            this._clearShowTimer();
            this._hide();
        }
        /** @internal Click trigger toggles visibility */
        _handleClick() {
            this._clearShowTimer();
            if (this._isVisible) {
                this._hide();
            }
            else {
                this._show();
            }
        }
        /** @internal Appends tooltip id to existing aria-describedby */
        _appendAriaDescribedBy() {
            const existing = this.getAttribute('aria-describedby');
            if (existing) {
                if (!existing.includes(this._tooltipId)) {
                    this.setAttribute('aria-describedby', `${existing} ${this._tooltipId}`);
                }
            }
            else {
                this.setAttribute('aria-describedby', this._tooltipId);
            }
        }
        /** @internal Removes tooltip id from aria-describedby */
        _removeAriaDescribedBy() {
            const existing = this.getAttribute('aria-describedby');
            if (!existing)
                return;
            const ids = existing
                .split(/\s+/)
                .filter(id => id !== this._tooltipId)
                .join(' ')
                .trim();
            if (ids) {
                this.setAttribute('aria-describedby', ids);
            }
            else {
                this.removeAttribute('aria-describedby');
            }
        }
    }
    __decorate([
        property({ attribute: 'tooltip' })
    ], TooltipMixinClass.prototype, "tooltip", void 0);
    __decorate([
        property({ attribute: 'tooltip-position' })
    ], TooltipMixinClass.prototype, "tooltipPosition", void 0);
    __decorate([
        property({ attribute: 'tooltip-delay', type: Number })
    ], TooltipMixinClass.prototype, "tooltipDelay", void 0);
    __decorate([
        property({ attribute: 'tooltip-offset' })
    ], TooltipMixinClass.prototype, "tooltipOffset", void 0);
    __decorate([
        property({ attribute: 'tooltip-no-arrow', type: Boolean })
    ], TooltipMixinClass.prototype, "tooltipNoArrow", void 0);
    __decorate([
        property({ attribute: 'tooltip-trigger' })
    ], TooltipMixinClass.prototype, "tooltipTrigger", void 0);
    return TooltipMixinClass;
};
//# sourceMappingURL=TooltipMixin.js.map