/**
 * Stage 4.4 port of Base UI React's `useHoverFloatingInteraction`.
 *
 * React source:
 *   `base-ui/packages/react/src/floating-ui-react/hooks/useHoverFloatingInteraction.ts`
 *
 * Attaches the hover event handlers that belong on the floating element.
 * Handles:
 *
 *   - Cancelling the reference-side close-timer when the cursor enters
 *     the floating surface (so the popup stays open while the user is
 *     inside it).
 *   - Closing the popup when the cursor leaves — respecting
 *     `safePolygon` handlers, tree-aware nesting (don't close while a
 *     child menu is open), and enabled-sibling triggers.
 *   - Pointer-events blocking for `blockPointerEvents: true`
 *     safe-polygons (covers menu nesting), including scope detection
 *     via `getScope` or a `[data-rootownerid]` ancestor.
 *   - `pointerdown` interaction-inside tracking used by the shared
 *     `isClickLikeOpenEvent` predicate.
 *
 * ### React → Solid deviations
 *
 * Same shape as the reference-side port above:
 *  - `useRef` → plain variables / `MutableRef<T>`.
 *  - `useStableCallback` → `createStableCallback`.
 *  - `useEffect` / `useIsoLayoutEffect` → `createEffect` /
 *    `useIsoLayoutEffect` (split-compute form).
 *  - `addEventListener` + `mergeCleanups` → direct
 *    `addEventListener` + an array-based cleanup in `onCleanup`.
 */

import { isElement } from "@floating-ui/utils/dom";
import { createEffect, onCleanup, untrack } from "solid-js";
import { createChangeEventDetails, REASONS } from "../../change-event-details.jsx";
import { ownerDocument } from "../../dom/owner-document.jsx";
import { readOption } from "../../maybe-accessor.jsx";
import { createStableCallback } from "../../stable-callback.jsx";
import { useIsoLayoutEffect } from "../../use-iso-layout-effect.jsx";
import { useTimeout } from "../../use-timeout.jsx";
import { useFloatingParentNodeId, useFloatingTree } from "../components/floating-tree.jsx";
import { contains, getTarget, isTargetInsideEnabledTrigger } from "../utils/element.jsx";
import { getNodeChildren } from "../utils/nodes.jsx";
import { applySafePolygonPointerEventsMutation, clearSafePolygonPointerEventsMutation, isInteractiveElement, useHoverInteractionSharedState } from "./use-hover-interaction-shared-state.jsx";
import { getDelay, isClickLikeOpenEvent as isClickLikeOpenEventShared } from "./use-hover-shared.jsx";
/**
 * Provides hover interactions that should be attached to the floating element.
 */
export function useHoverFloatingInteraction(context, parameters = {}) {
  /**
   * Resolve the active floating root store. Accepts either a bare context or
   * an accessor so navigation-menu List can swap the active trigger context
   * without re-installing the hook.
   */
  const resolveStore = () => {
    const ctx = typeof context === "function" ? context() : context;
    return "rootStore" in ctx ? ctx.rootStore : ctx;
  };
  const getEnabled = () => parameters.enabled === undefined ? true : readOption(parameters.enabled);
  const {
    closeDelay: closeDelayProp = 0,
    nodeId: nodeIdProp
  } = parameters;

  // One-shot snapshot used to seed shared state. `resolveStore` reads the
  // reactive `floatingRootContext` accessor (navigation-menu List passes an
  // accessor), so untrack the body-time read to avoid `[STRICT_READ_UNTRACKED]`.
  const initialStore = untrack(resolveStore);
  const instance = useHoverInteractionSharedState(initialStore);
  const tree = useFloatingTree();
  const parentId = useFloatingParentNodeId();
  const isClickLikeOpenEvent = createStableCallback(() => () => {
    const store = resolveStore();
    const {
      dataRef
    } = store.context;
    return isClickLikeOpenEventShared(dataRef.current.openEvent?.type, instance.interactedInside);
  });
  const isHoverOpen = createStableCallback(() => () => {
    const store = resolveStore();
    const type = store.context.dataRef.current.openEvent?.type;
    return Boolean(type?.includes("mouse")) && type !== "mousedown";
  });
  const isRelatedTargetInsideEnabledTrigger = createStableCallback(() => target => {
    const store = resolveStore();
    return isTargetInsideEnabledTrigger(target, store.context.triggerElements);
  });
  const closeWithDelay = createStableCallback(() => event => {
    const store = resolveStore();
    const closeDelay = getDelay(closeDelayProp, "close", instance.pointerType);
    const close = () => {
      store.setOpen(false, createChangeEventDetails(REASONS.triggerHover, event));
      tree?.events.emit("floating.closed", event);
    };
    if (closeDelay) {
      instance.openChangeTimeout.start(closeDelay, close);
    } else {
      instance.openChangeTimeout.clear();
      close();
    }
  });
  const clearPointerEvents = createStableCallback(() => () => {
    clearSafePolygonPointerEventsMutation(instance);
  });
  const handleInteractInside = createStableCallback(() => event => {
    const target = getTarget(event);
    if (!isInteractiveElement(target)) {
      instance.interactedInside = false;
      return;
    }
    instance.interactedInside = target?.closest("[aria-haspopup]") != null;
  });

  // When the popup closes, reset shared state + clear any pointer-events
  // mutation set by the safe-polygon path.
  useIsoLayoutEffect(() => ({
    enabled: getEnabled(),
    open: resolveStore().state.open
  }), ({
    enabled,
    open
  }) => {
    if (!enabled) {
      return;
    }
    if (!open) {
      instance.pointerType = undefined;
      instance.restTimeoutPending = false;
      instance.interactedInside = false;
      clearPointerEvents();
    }
  });
  onCleanup(() => {
    clearPointerEvents();
  });

  // Pointer-events mutation for `blockPointerEvents: true` safe-polygon.
  useIsoLayoutEffect(() => ({
    enabled: getEnabled(),
    open: resolveStore().state.open,
    domReferenceElement: resolveStore().state.domReferenceElement,
    floatingElement: resolveStore().state.floatingElement
  }), snapshot => {
    if (!snapshot.enabled) {
      return;
    }
    const {
      open,
      domReferenceElement,
      floatingElement
    } = snapshot;
    if (open && instance.handleCloseOptions?.blockPointerEvents && isHoverOpen() && isElement(domReferenceElement) && floatingElement) {
      const ref = domReferenceElement;
      const floatingEl = floatingElement;
      const doc = ownerDocument(floatingElement);
      const parentFloating = tree?.nodesRef.current.find(node => node.id === parentId)?.context?.elements.floating;
      if (parentFloating) {
        parentFloating.style.pointerEvents = "";
      }
      const scopeElement = instance.handleCloseOptions?.getScope?.() ?? instance.pointerEventsScopeElement ?? parentFloating ?? ref.closest("[data-rootownerid]") ?? doc.body;
      applySafePolygonPointerEventsMutation(instance, {
        scopeElement,
        referenceElement: ref,
        floatingElement: floatingEl
      });
      return () => {
        clearPointerEvents();
      };
    }
    return undefined;
  });
  const childClosedTimeout = useTimeout();
  createEffect(() => ({
    enabled: getEnabled(),
    floating: resolveStore().state.floatingElement
  }), ({
    enabled,
    floating
  }) => {
    if (!enabled) {
      return;
    }

    // Effect-apply scope: the compute above already tracks
    // `resolveStore().state.floatingElement` (and therefore the
    // `floatingRootContext` accessor), so this re-read is a snapshot —
    // untrack to avoid `[STRICT_READ_UNTRACKED]`.
    const store = untrack(resolveStore);
    const {
      dataRef
    } = store.context;
    function onFloatingMouseEnter() {
      instance.openChangeTimeout.clear();
      childClosedTimeout.clear();
      tree?.events.off("floating.closed", onNodeClosed);
      clearPointerEvents();
    }
    function onFloatingMouseLeave(event) {
      if (tree && parentId && getNodeChildren(tree.nodesRef.current, parentId).length > 0) {
        tree.events.on("floating.closed", onNodeClosed);
        return;
      }
      if (isRelatedTargetInsideEnabledTrigger(event.relatedTarget)) {
        return;
      }
      const currentNodeId = dataRef.current.floatingContext?.nodeId ?? nodeIdProp;
      const relatedTarget = event.relatedTarget;
      const isMovingIntoDescendantFloating = tree && currentNodeId && isElement(relatedTarget) && getNodeChildren(tree.nodesRef.current, currentNodeId, false).some(node => contains(node.context?.elements.floating ?? null, relatedTarget));
      if (isMovingIntoDescendantFloating) {
        return;
      }
      if (instance.handler) {
        instance.handler(event);
        return;
      }
      clearPointerEvents();
      if (!isClickLikeOpenEvent()) {
        closeWithDelay(event);
      }
    }
    function onNodeClosed(event) {
      if (!tree || !parentId || getNodeChildren(tree.nodesRef.current, parentId).length > 0) {
        return;
      }
      childClosedTimeout.start(0, () => {
        tree.events.off("floating.closed", onNodeClosed);
        resolveStore().setOpen(false, createChangeEventDetails(REASONS.triggerHover, event));
        tree.events.emit("floating.closed", event);
      });
    }
    if (!floating) {
      return;
    }
    floating.addEventListener("mouseenter", onFloatingMouseEnter);
    floating.addEventListener("mouseleave", onFloatingMouseLeave);
    floating.addEventListener("pointerdown", handleInteractInside, true);
    return () => {
      floating.removeEventListener("mouseenter", onFloatingMouseEnter);
      floating.removeEventListener("mouseleave", onFloatingMouseLeave);
      floating.removeEventListener("pointerdown", handleInteractInside, true);
      tree?.events.off("floating.closed", onNodeClosed);
    };
  });
}
//# sourceMappingURL=use-hover-floating-interaction.jsx.map
