import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
// (C) 2025-2026 GoodData Corporation
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
import { FloatingArrow, FloatingPortal, safePolygon, useClick, useFocus, useHover, useInteractions, } from "@floating-ui/react";
import { ConditionalScopedThemeProvider, useIsScopeThemed, useTheme } from "@gooddata/sdk-ui-theme-provider";
import { bem } from "../@utils/bem.js";
import { useCloseOnEscape } from "../hooks/useCloseOnEscape.js";
import { FLOATING_ELEMENT_DATA_ATTR, useCloseOnOutsideClick, useRegisterFloatingAnchor, } from "../hooks/useCloseOnOutsideClick.js";
import { useFloatingPosition } from "../UiFloatingElement/useFloatingPosition.js";
import { ARROW_HEIGHT, ARROW_WIDTH, HIDE_DELAY, SHOW_DELAY } from "./constants.js";
import { computeArrowOffset, computeTooltipShift, getDimensionsFromRect, getFlipFallbackOrder, getOppositeBasicPlacement, } from "./utils.js";
const { b, e } = bem("gd-ui-kit-tooltip");
/**
 * @internal
 */
export function UiTooltip({ id, anchor, content, behaviour = "tooltip", arrowPlacement = "top", triggerBy = [], hoverOpenDelay = SHOW_DELAY, hoverCloseDelay = HIDE_DELAY, showArrow = true, width, offset: offsetProp, optimalPlacement = false, accessibilityConfig, accessibilityHidden = false, variant = "default", disabled, isOpen: isOpenProp, onOpen, onClose, onOpenChange, anchorWrapperStyles, }) {
    const [isOpenInternal, setIsOpen] = useState(false);
    const isControlled = isOpenProp !== undefined;
    const isOpen = !disabled && (isControlled ? isOpenProp : isOpenInternal || triggerBy.length === 0);
    const arrowRef = useRef(null);
    const themeFromContext = useTheme();
    const isScopeThemed = useIsScopeThemed();
    const theme = isScopeThemed ? themeFromContext : undefined;
    const handleOpenChange = useCallback((open) => {
        setIsOpen(open);
        onOpenChange?.(open);
        if (open) {
            onOpen?.();
        }
        else {
            onClose?.();
        }
    }, [onClose, onOpen, onOpenChange]);
    const handleClose = useCallback(() => {
        setIsOpen(false);
        onOpenChange?.(false);
        onClose?.();
    }, [onClose, onOpenChange]);
    // Custom shift middleware for tooltip arrow positioning
    const customShiftMiddleware = useMemo(() => ({
        name: "customShift",
        fn(args) {
            if (behaviour === "popover") {
                return {};
            }
            const { x, y, rects, middlewareData } = args;
            const currentFloatingDimensions = getDimensionsFromRect(rects?.floating);
            const enable = !middlewareData?.["flip"]?.index;
            const shift = computeTooltipShift(arrowPlacement, currentFloatingDimensions, enable, theme);
            return {
                x: shift.x ? x + shift.x : x,
                y: shift.y ? y + shift.y : y,
            };
        },
    }), [arrowPlacement, behaviour, theme]);
    // Use shared floating position hook
    const { refs, floatingStyles, zIndex, context, middlewareData } = useFloatingPosition({
        isOpen,
        onOpenChange: handleOpenChange,
        placement: getOppositeBasicPlacement(arrowPlacement, behaviour),
        offset: offsetProp ?? ARROW_HEIGHT,
        autoFlip: optimalPlacement,
        fallbackPlacements: optimalPlacement ? getFlipFallbackOrder(arrowPlacement, behaviour) : undefined,
        arrowRef: showArrow ? arrowRef : undefined,
        customMiddleware: [customShiftMiddleware],
    });
    // Get dimensions for width calculation
    const triggerDimensions = useMemo(() => {
        const rect = refs.reference.current?.getBoundingClientRect?.();
        return getDimensionsFromRect(rect ?? null);
    }, [refs.reference, isOpen]); // eslint-disable-line react-hooks/exhaustive-deps
    // Use state + layoutEffect to get dimensions AFTER DOM paints
    // This fixes arrow positioning for *-end placements on initial render
    const [floatingDimensions, setFloatingDimensions] = useState({ width: 0, height: 0 });
    useLayoutEffect(() => {
        if (isOpen && refs.floating.current) {
            const rect = refs.floating.current.getBoundingClientRect();
            setFloatingDimensions(getDimensionsFromRect(rect));
        }
        else {
            setFloatingDimensions({ width: 0, height: 0 });
        }
    }, [isOpen, refs.floating]);
    // Trigger events using floating-ui interactions (handles hover, focus, click).
    // When controlled the open state is owned by the parent, so the anchor's own
    // triggers are suppressed — otherwise an anchor that is shared with another
    // popover (e.g. the labels picker reusing the permission-menu button) would
    // toggle both.
    const hover = useHover(context, {
        enabled: !isControlled && triggerBy.includes("hover"),
        move: false,
        handleClose: safePolygon({ requireIntent: true }),
        delay: {
            open: hoverOpenDelay,
            close: hoverCloseDelay,
        },
    });
    const focus = useFocus(context, {
        enabled: !isControlled && triggerBy.includes("focus"),
    });
    const click = useClick(context, {
        enabled: !isControlled && triggerBy.includes("click"),
    });
    // Read the anchor lazily — floating-ui's `refs.reference` is a mutable
    // ref that only has a current value once the reference element mounts,
    // which is independent of our render cycle.
    const setFloatingWithAnchorRegistry = useRegisterFloatingAnchor(refs.setFloating, () => refs.reference.current instanceof Element ? refs.reference.current : null);
    // Outside-click / Escape close via DOM-based hooks rather than floating-ui's
    // useDismiss. useDismiss bails out whenever the press lands inside the parent
    // floating element's React tree (its `insideReactTree` guard) — so a popover
    // portalled inside a modal (e.g. the OLP share dialog) never closed on any
    // click within that modal (F1-2608). useCloseOnOutsideClick keys off real DOM
    // containment plus the floating-anchor registry, so it correctly ignores our
    // own subtree and nested child popups while still closing on sibling/outside
    // clicks. It stays enabled when controlled so the close request still flows
    // through handleClose → onOpenChange.
    useCloseOnOutsideClick(isOpen, handleClose, {
        floatingRef: refs.floating,
        anchorRef: refs.reference,
    });
    useCloseOnEscape(isOpen, handleClose);
    const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus, click]);
    return (_jsxs(_Fragment, { children: [
            _jsx("div", { className: e("anchor"), ref: refs.setReference, style: anchorWrapperStyles, ...getReferenceProps(), children: anchor }), accessibilityHidden ? null : (_jsx("span", { className: "sr-only", id: id, children: isOpen
                    ? typeof content === "function"
                        ? content({ onClose: handleClose, type: "screen-reader" })
                        : content
                    : null })), isOpen ? (_jsx(FloatingPortal, { children: _jsx(ConditionalScopedThemeProvider, { children: _jsxs("div", { className: b({
                            width: width === "same-as-anchor" ? "same-as-anchor" : false,
                            variant,
                        }), ref: setFloatingWithAnchorRegistry, style: {
                            zIndex,
                            ...floatingStyles,
                            width: width === "same-as-anchor" ? triggerDimensions?.width : width,
                        }, [FLOATING_ELEMENT_DATA_ATTR]: true, "aria-hidden": accessibilityHidden || undefined, "aria-label": accessibilityConfig?.ariaLabel, "aria-labelledby": accessibilityConfig?.ariaLabelledBy, "aria-describedby": accessibilityConfig?.ariaDescribedBy, "aria-expanded": accessibilityConfig?.ariaExpanded, role: accessibilityConfig?.role ?? "tooltip", ...getFloatingProps(), children: [typeof content === "function"
                                ? content({ onClose: handleClose, type: "live" })
                                : content, showArrow ? (_jsx(FloatingArrow, { staticOffset: computeArrowOffset(arrowPlacement, floatingDimensions, !middlewareData?.["flip"]?.index, theme), className: e("arrow"), ref: arrowRef, tipRadius: 1, context: context, height: ARROW_HEIGHT, width: ARROW_WIDTH })) : null] }) }) })) : null] }));
}
