"use client";
import { cnb } from "cnbuilder";
import { useCallback, useEffect, useId, useRef } from "react";
import { useHoverMode } from "../hoverMode/useHoverMode.js";
import { useUserInteractionMode } from "../interaction/UserInteractionModeProvider.js";
import { useFixedPositioning } from "../positioning/useFixedPositioning.js";
import { usePageInactive } from "../usePageInactive.js";
import { parseCssLengthUnit } from "../utils/parseCssLengthUnit.js";
import { useTooltipHoverMode } from "./TooltipHoverModeProvider.js";
import { DEFAULT_TOOLTIP_DENSE_SPACING, DEFAULT_TOOLTIP_MARGIN, DEFAULT_TOOLTIP_POSITION, DEFAULT_TOOLTIP_SPACING, DEFAULT_TOOLTIP_THRESHOLD, TOOLTIP_SPACING_VAR } from "./constants.js";
import { useTooltipPosition } from "./useTooltipPosition.js";
import { getAnchor } from "./utils.js";
const noop = ()=>{
// do nothing
};
/**
 * @example
 * Simple Usage
 * ```tsx
 * import { Button, useTooltip, Tooltip } from "@react-md/core";
 *
 * function Example() {
 *   const { elementProps, tooltipProps } = useTooltip();
 *
 *   return (
 *     <>
 *       <Button {...elementProps}>Button</Button>
 *       <Tooltip {...tooltipProps}>
 *         Tooltip Content
 *       </Tooltip>
 *     </>
 *   );
 * }
 * ```
 *
 * @example
 * Overflow-only Tooltips
 * ```tsx
 * import {
 *   cssUtils,
 *   Link,
 *   Tooltip,
 *   useTooltip,
 *   type LinkProps,
 * } from "@react-md/core";
 * import { type ReactElement } from "react";
 *
 * function NavigationLink(props: LinkProps): ReactElement {
 *   const { children, ...remaining } = props;
 *
 *   // using the `overflowRef` is optional and will default to the
 *   // `event.currentTarget` when `null`
 *   const { overflowRef, elementProps, tooltipProps } = useOverflowTooltip({
 *     // just to pass any event handlers
 *     ...remaining,
 *     overflowOnly: true,
 *   });
 *
 *   return (
 *     <Link {...remaining} {...elementProps} style={{ width: "100%" }}>
 *       <span ref={overflowRef} className={cssUtils({ textOverflow: "ellipsis" })}>
 *         {children}
 *       </span>
 *       <Tooltip {...tooltipProps}>
 *         {children}
 *       </Tooltip>
 *     </Link>
 *   );
 * }
 *
 * function Example(): ReactElement {
 *   return (
 *     <div style={{ width: "10rem", overflow: "auto" }}>
 *       <NavigationLink href="/">Home</NavigationLink>
 *       <NavigationLink href="/some-path">
 *         Super long text that will be truncated with ellipsis and
 *         have a tooltip appear
 *       </NavigationLink>
 *     </div>
 *   );
 * }
 * ```
 *
 * ## Inspecting Tooltip Styles
 *
 * Since tooltips will disappear on blur, mouseleave, etc, it is a bit hard to
 * inspect the tooltip styles. In dev mode, you can manually set the visibility
 * to `true` through the dev tools.
 * - find your tooltip implementation
 * - expand the Tooltip hook
 * - expand the HoverMode hook
 * - set the first boolean state to `true`
 *
 * The tooltip will now remain visible allowing you to find it within the
 * "Inspector" tab in the dev tools.
 *
 * @remarks
 * \@since 2.8.0
 * \@since 6.0.0 Uses a separate `TooltipHoverModeProvider`.
 *
 * TODO: I need to fix the tooltip for click events and history changes since
 * the mouseleave event will not be correctly bubbled if hovering a child
 * element when the click or history update happens. this causes the tooltip to
 * stay visible
 */ export function useTooltip(options = {}) {
    const { id: propId, style: propStyle, disabled = false, describedBy, dense = false, hoverTimeout, leaveTimeout, vwMargin = DEFAULT_TOOLTIP_MARGIN, vhMargin = DEFAULT_TOOLTIP_MARGIN, spacing = DEFAULT_TOOLTIP_SPACING, denseSpacing = DEFAULT_TOOLTIP_DENSE_SPACING, disableSwapping, disableAutoSpacing, position: determinedPosition, defaultPosition = DEFAULT_TOOLTIP_POSITION, threshold = DEFAULT_TOOLTIP_THRESHOLD, onBlur = noop, onFocus = noop, onMouseEnter = noop, onMouseLeave = noop, onTouchStart = noop, onTouchEnd = noop, onContextMenu = noop, onEnter = noop, onEntering, onEntered = noop, onExited, overflowOnly } = options;
    const fallbackId = useId();
    const id = propId || fallbackId;
    const tooltipId = `${id}-tooltip`;
    const { animatedOnceRef, hoverTimeoutRef, leaveTimeoutRef, enableHoverMode, disableHoverMode, startDisableTimer, clearDisableTimer } = useTooltipHoverMode();
    const { visible, setVisible, startShowFlow, startHideFlow, clearVisibilityTimeout } = useHoverMode({
        hoverTimeout,
        hoverTimeoutRef,
        leaveTimeout,
        leaveTimeoutRef,
        enableHoverMode,
        disableHoverMode,
        startDisableTimer,
        clearDisableTimer
    });
    const [position, updatePosition] = useTooltipPosition({
        position: determinedPosition,
        defaultPosition,
        threshold
    });
    const mode = useUserInteractionMode();
    const elementRef = useRef(null);
    const tooltipRef = useRef(null);
    const overflowRef = useRef(null);
    const initiatedBy = useRef(null);
    const { ref, style, callbacks } = useFixedPositioning({
        nodeRef: tooltipRef,
        style: propStyle,
        fixedTo: elementRef,
        anchor: getAnchor(position),
        disableSwapping: disableSwapping ?? !!determinedPosition,
        getFixedPositionOptions () {
            let tooltipSpacing = dense ? denseSpacing : spacing;
            const tooltip = tooltipRef.current;
            if (!disableAutoSpacing && tooltip) {
                tooltipSpacing = window.getComputedStyle(tooltip).getPropertyValue(TOOLTIP_SPACING_VAR) || spacing;
            }
            const currentSpacing = parseCssLengthUnit({
                value: tooltipSpacing
            });
            const horizontal = position === "left" || position === "right";
            return {
                vwMargin,
                vhMargin,
                xMargin: horizontal ? currentSpacing : undefined,
                yMargin: horizontal ? undefined : currentSpacing
            };
        },
        onEnter (appearing) {
            onEnter(appearing);
            // This allows you to inspect the tooltip styles through the element
            // inspector without first hovering or focusing the tooltipped element
            // beforehand by setting the `HoverMode` hook to `true`
            if (process.env.NODE_ENV !== "production" && !elementRef.current) {
                elementRef.current = document.getElementById(id);
            }
        },
        onEntering,
        onEntered (appearing) {
            onEntered(appearing);
            animatedOnceRef.current = true;
        },
        onExited
    });
    const hideTooltip = useCallback(()=>{
        initiatedBy.current = null;
        disableHoverMode();
        clearVisibilityTimeout();
        setVisible(false);
    }, [
        clearVisibilityTimeout,
        disableHoverMode,
        setVisible
    ]);
    useEffect(()=>{
        if (!visible) {
            return;
        }
        const handleKeyDown = (event)=>{
            if (event.key === "Escape") {
                hideTooltip();
            }
        };
        window.addEventListener("keydown", handleKeyDown);
        window.addEventListener("scroll", hideTooltip, true);
        window.addEventListener("touchend", hideTooltip, true);
        return ()=>{
            window.removeEventListener("keydown", handleKeyDown);
            window.removeEventListener("scroll", hideTooltip, true);
            window.removeEventListener("touchend", hideTooltip, true);
        };
    }, [
        hideTooltip,
        visible
    ]);
    const refocusFrame = useRef(0);
    const pageInactive = useRef(false);
    usePageInactive({
        disabled,
        onDisabledCleanup: hideTooltip,
        onChange (active) {
            if (active) {
                refocusFrame.current = window.requestAnimationFrame(()=>{
                    pageInactive.current = false;
                });
                return;
            }
            pageInactive.current = true;
            hideTooltip();
        }
    });
    const isNotOverflown = (currentTarget)=>{
        if (!overflowOnly) {
            return false;
        }
        const element = overflowRef.current || currentTarget;
        return !element || element.offsetWidth >= element.scrollWidth;
    };
    return {
        visible,
        setVisible,
        hideTooltip,
        animatedOnce: animatedOnceRef.current,
        initiatedBy,
        overflowRef,
        startShowFlow,
        startHideFlow,
        clearVisibilityTimeout,
        tooltipProps: {
            id: tooltipId,
            ref,
            dense,
            style,
            visible,
            position,
            ...callbacks
        },
        elementProps: {
            "aria-describedby": cnb(visible && tooltipId, describedBy) || undefined,
            id,
            onMouseEnter (event) {
                onMouseEnter(event);
                if (disabled || mode === "touch" || initiatedBy.current !== null || isNotOverflown(event.currentTarget)) {
                    return;
                }
                initiatedBy.current = "mouse";
                elementRef.current = event.currentTarget;
                updatePosition(event.currentTarget);
                startShowFlow(id);
            },
            onMouseLeave (event) {
                onMouseLeave(event);
                if (disabled || initiatedBy.current !== "mouse") {
                    return;
                }
                startHideFlow();
                initiatedBy.current = null;
            },
            onBlur (event) {
                onBlur(event);
                if (disabled) {
                    return;
                }
                initiatedBy.current = null;
                startHideFlow();
            },
            onFocus (event) {
                onFocus(event);
                // skip the focus events when the browser is re-focused if the user
                // pressed alt-tab, minimized the browser, etc
                if (disabled || mode !== "keyboard" || initiatedBy.current !== null || pageInactive.current || isNotOverflown(event.currentTarget)) {
                    pageInactive.current = false;
                    return;
                }
                initiatedBy.current = "keyboard";
                elementRef.current = event.currentTarget;
                updatePosition(event.currentTarget);
                startShowFlow(id);
            },
            onTouchStart (event) {
                onTouchStart(event);
                if (disabled || initiatedBy.current !== null || isNotOverflown(event.currentTarget)) {
                    return;
                }
                initiatedBy.current = "touch";
                elementRef.current = event.currentTarget;
                updatePosition(event.currentTarget);
                startShowFlow(id);
            },
            onTouchEnd (event) {
                onTouchEnd(event);
                if (disabled) {
                    return;
                }
                initiatedBy.current = null;
                startHideFlow();
            },
            onContextMenu (event) {
                onContextMenu(event);
                if (disabled || initiatedBy.current !== "touch" || isNotOverflown(event.currentTarget)) {
                    return;
                }
                event.preventDefault();
                const selection = window.getSelection();
                const node = selection?.anchorNode?.parentElement;
                if (node && event.currentTarget.contains(node)) {
                    selection.empty();
                }
            }
        }
    };
}

//# sourceMappingURL=useTooltip.js.map