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;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { property } from 'lit/decorators.js';
import { v4 as uuidv4 } from 'uuid';
import { ROLE } from '../../utils/roles';
import Popover from '../popover/popover.component';
import { DEFAULTS as POPOVER_DEFAULTS, POPOVER_PLACEMENT } from '../popover/popover.constants';
import { hasOverflowMixin } from '../../utils/dom';
import { DEFAULTS, TOOLTIP_TYPES } from './tooltip.constants';
import styles from './tooltip.styles';
/**
 * A Tooltip is a special type of popovers that provide additional context to content on the screen. <br/>
 * Tooltip is triggered by mouse hover or by keyboard focus and will disappear upon mouse exit or focus change.
 *
 * Because of this, tooltips cannot contain content that can be focused or interacted with.
 * When a tooltip must contain a focusable element like a link or button, use a toggle tip instead.
 *
 * A tooltip is triggered by mouse hover or by keyboard focus
 * and will disappear upon mouse exit or focus change.
 *
 * Note:
 *  - Tooltips cannot contain content that can be focused or interacted with.
 *  - Tooltips will contain the default `aria-hidden="true"` so that VO will never focus the tooltip.
 *
 * @tagname mdc-tooltip
 *
 * @event shown - (React: onShown) This event is dispatched when the tooltip is shown
 * @event hidden - (React: onHidden) This event is dispatched when the tooltip is hidden
 * @event created - (React: onCreated) This event is dispatched when the tooltip is created (added to the DOM)
 * @event destroyed - (React: onDestroyed) This event is dispatched when the tooltip is destroyed (removed from the DOM)
 *
 * @cssproperty --mdc-tooltip-max-width - The maximum width of the tooltip.
 * @cssproperty --mdc-tooltip-padding - The padding of the tooltip.
 * @cssproperty --mdc-tooltip-text-color - The text color of the tooltip.
 * @cssproperty --mdc-tooltip-text-color-contrast - The text color of the tooltip when the color is contrast.
 *
 */
class Tooltip extends Popover {
    constructor() {
        super(...arguments);
        /**
         * The type of tooltip.
         * - **description** sets aria-describedby on the trigger component which refers to the tooltip id.
         * - **label** sets aria-labelledby on the trigger component which refers to the tooltip id.
         * - **none** no aria props set on trigger component referring to the tooltip id.
         * @default 'description'
         */
        this.tooltipType = DEFAULTS.TOOLTIP_TYPE;
        /**
         * If true, the tooltip will only be shown when the trigger element's content is overflowing on the x-axis.
         *
         * Supports the following components:
         * - mdc-button
         * - mdc-text
         *
         * @default false
         */
        this.onlyShowWhenTriggerOverflows = DEFAULTS.ONLY_SHOW_WHEN_TRIGGER_OVERFLOWS;
        /**
         * Cached auto-generated id so that if the consumer (or a React wrapper that re-passes props
         * on every render) clears the id we restore the same value instead of minting a fresh UUID
         * on every cycle.
         */
        this.autoGeneratedId = null;
    }
    connectedCallback() {
        super.connectedCallback();
        this.role = ROLE.TOOLTIP;
        // We don't want the tooltip to be visible for screen readers as they are not focusable
        this.ariaHidden = DEFAULTS.ARIA_HIDDEN;
        // Tooltip defaults
        this.backdrop = DEFAULTS.BACKDROP;
        this.delay = this.delay || DEFAULTS.DELAY;
        this.disableAriaExpanded = DEFAULTS.DISABLE_ARIA_EXPANDED;
        this.hideOnBlur = DEFAULTS.HIDE_ON_BLUR;
        this.hideOnEscape = DEFAULTS.HIDE_ON_ESCAPE;
        this.offset = this.offset || DEFAULTS.OFFSET;
        this.placement = this.placement || DEFAULTS.PLACEMENT;
        this.trigger = DEFAULTS.TRIGGER;
        // Popover defaults
        this.closeButton = POPOVER_DEFAULTS.CLOSE_BUTTON;
        this.disableFlip = POPOVER_DEFAULTS.DISABLE_FLIP;
        this.focusBackToTrigger = POPOVER_DEFAULTS.FOCUS_BACK;
        this.focusTrap = POPOVER_DEFAULTS.FOCUS_TRAP;
        this.hideOnOutsideClick = POPOVER_DEFAULTS.HIDE_ON_CLICK_OUTSIDE;
        this.interactive = POPOVER_DEFAULTS.INTERACTIVE;
        this.preventScroll = POPOVER_DEFAULTS.PREVENT_SCROLL;
        this.size = POPOVER_DEFAULTS.SIZE;
    }
    /**
     * Sets the type attribute for the tooltip component.
     * If the provided type is not included in the TOOLTIP_TYPES,
     * it defaults to the value specified in DEFAULTS.TOOLTIP_TYPE.
     *
     * @param type - The type to set.
     */
    setTooltipType(type) {
        this.setAttribute('tooltip-type', Object.values(TOOLTIP_TYPES).includes(type) ? type : DEFAULTS.TOOLTIP_TYPE);
    }
    /**
     * Ensures the tooltip has a non-empty id. Called from `willUpdate` so the assignment is folded
     * into the in-flight update cycle (no extra render pass, no risk of an update loop).
     *
     * - If the consumer provides an explicit id, we drop the cached auto id so a future clear can
     *   regenerate a fresh one.
     * - If the id is empty, we reuse the cached auto id when present, otherwise generate one.
     */
    ensureId() {
        if (this.id.length > 0) {
            if (this.autoGeneratedId && this.id !== this.autoGeneratedId) {
                this.autoGeneratedId = null;
            }
            return;
        }
        if (!this.autoGeneratedId) {
            this.autoGeneratedId = `mdc-tooltip-${uuidv4()}`;
        }
        this.id = this.autoGeneratedId;
    }
    /**
     * Writes the appropriate aria attribute on the trigger element based on the current tooltipType.
     * Used when only the id changed (tooltipType-driven aria updates are handled in
     * `onTooltipTypeUpdated`).
     */
    syncTriggerAriaForId() {
        if (!this.triggerElement)
            return;
        switch (this.tooltipType) {
            case TOOLTIP_TYPES.DESCRIPTION:
                this.triggerElement.setAttribute('aria-describedby', this.id);
                break;
            case TOOLTIP_TYPES.LABEL:
                this.triggerElement.setAttribute('aria-labelledby', this.id);
                break;
            default:
                break;
        }
    }
    /**
     * Updates the placement attribute if it is not a valid placement.
     * Overriding the default from Popover
     */
    onPlacementUpdated() {
        if (!Object.values(POPOVER_PLACEMENT).includes(this.placement)) {
            this.placement = DEFAULTS.PLACEMENT;
        }
    }
    /**
     * Updates the tooltip type attribute and sets the appropriate aria props on the trigger component.
     * @param changedProperties - The changed properties.
     */
    onTooltipTypeUpdated(changedProperties) {
        const previousTooltipType = changedProperties.get('tooltipType');
        if (!Object.values(TOOLTIP_TYPES).includes(this.tooltipType)) {
            this.setTooltipType(DEFAULTS.TOOLTIP_TYPE);
        }
        if (this.triggerElement) {
            switch (this.tooltipType) {
                case TOOLTIP_TYPES.DESCRIPTION:
                    if (previousTooltipType === TOOLTIP_TYPES.LABEL) {
                        this.triggerElement.removeAttribute('aria-labelledby');
                    }
                    this.triggerElement.setAttribute('aria-describedby', this.id);
                    break;
                case TOOLTIP_TYPES.LABEL:
                    if (previousTooltipType === TOOLTIP_TYPES.DESCRIPTION) {
                        this.triggerElement.removeAttribute('aria-describedby');
                    }
                    this.triggerElement.setAttribute('aria-labelledby', this.id);
                    break;
                default:
                    if (previousTooltipType === TOOLTIP_TYPES.DESCRIPTION) {
                        this.triggerElement.removeAttribute('aria-describedby');
                    }
                    else if (previousTooltipType === TOOLTIP_TYPES.LABEL) {
                        this.triggerElement.removeAttribute('aria-labelledby');
                    }
                    break;
            }
        }
    }
    async isOpenUpdated(oldValue, newValue) {
        const { triggerElement } = this;
        if (oldValue === newValue || !triggerElement) {
            return;
        }
        if (!newValue) {
            // Timing is critical when the popover pushed/popped from the stack.
            //
            // Timing here:
            // Tooltip closes -> New Popover opens -> Tooltip popped from the stack -> it popes out the new popover as well.
            //
            // It happens because by default the popped element automatically pop the element above it in the stack.
            // To avoid this, we explicitly remove the tooltip from the stack before it is popped.
            this.depthManager.remove([this]);
        }
        await super.isOpenUpdated(oldValue, newValue);
    }
    willUpdate(changedProperties) {
        super.willUpdate(changedProperties);
        // Ensure an id exists before render. Assigning `this.id` inside willUpdate is folded into
        // the current update cycle, so there is no extra render and no loop risk (the previous
        // implementation set the id from inside `update()` and awaited `updateComplete`, which
        // produced an infinite re-render loop under @lit/react wrappers that keep clearing the id).
        const idChanged = changedProperties.has('id');
        if (idChanged) {
            this.ensureId();
        }
        if (changedProperties.has('placement')) {
            this.onPlacementUpdated();
        }
        if (changedProperties.has('tooltipType')) {
            // onTooltipTypeUpdated also refreshes the trigger aria attribute, so it covers the id sync.
            this.onTooltipTypeUpdated(changedProperties);
        }
        else if (idChanged) {
            this.syncTriggerAriaForId();
        }
    }
    show() {
        if (this.onlyShowWhenTriggerOverflows && this.triggerElement && hasOverflowMixin(this.triggerElement)) {
            if (!this.triggerElement.isWidthOverflowing()) {
                return;
            }
        }
        super.show();
    }
}
Tooltip.styles = [...Popover.styles, ...styles];
__decorate([
    property({ type: String, attribute: 'tooltip-type', reflect: true }),
    __metadata("design:type", String)
], Tooltip.prototype, "tooltipType", void 0);
__decorate([
    property({ type: Boolean, attribute: 'only-show-when-trigger-overflows', reflect: true }),
    __metadata("design:type", Boolean)
], Tooltip.prototype, "onlyShowWhenTriggerOverflows", void 0);
export default Tooltip;
