/**
 * `useDialogRootInternal` — shared root-body helper consumed by
 * `<Dialog.Root>` and `<AlertDialog.Root>`. Mirrors Base UI React's
 * `useDialogRoot` hook (`packages/react/src/dialog/root/useDialogRoot.ts`)
 * in spirit: collects the open-state + floating-context + dismiss + role
 * wiring into one place so the two roots differ only in three knobs.
 *
 * Composition:
 *
 *  - The wrapper builds a headless primitive (`createDialog` /
 *    `createAlertDialog`) and threads its `DialogApi`-shaped surface in
 *    via `options.api`. `useDialogRootInternal` delegates `open`,
 *    `modal`, and `disablePointerDismissal` reads to the primitive
 *    instead of declaring its own. AlertDialog's primitive pins
 *    `modal: true` and `disablePointerDismissal: true` as fixed
 *    invariants, so the previous `modalOverride` / `disablePointerDismissalOverride`
 *    knobs are no longer needed.
 *
 * Knobs:
 *
 *  - `role`: `"dialog"` or `"alertdialog"`. Threaded into `useRole` +
 *    surfaced on the context so `<Dialog.Popup>` sets the correct DOM
 *    `role` attribute.
 *  - `api`: the primitive's reactive accessors for `open`, `modal`, and
 *    `disablePointerDismissal`.
 */

import { createEffect, createMemo, createSignal, onCleanup, untrack, useContext } from "solid-js";
import { createChangeEventDetails, REASONS } from "../../internals/change-event-details.jsx";
import { useDismiss } from "../../internals/floating-ui/hooks/use-dismiss.jsx";
import { useFloatingRootContext } from "../../internals/floating-ui/hooks/use-floating-root-context.jsx";
import { useInteractions } from "../../internals/floating-ui/hooks/use-interactions.jsx";
import { useRole } from "../../internals/floating-ui/hooks/use-role.jsx";
import { useOpenChangeComplete } from "../../internals/popups/use-open-change-complete.jsx";
import { useOpenInteractionType } from "../../internals/popups/use-open-interaction-type.jsx";
import { useScrollLock } from "../../internals/popups/use-scroll-lock.jsx";
import { useTransitionStatus } from "../../internals/use-transition-status/use-transition-status.jsx";
import { DialogRootContext, IsDrawerContext, useDialogRootContext } from "../dialog-root-context.jsx";
import { DialogStore } from "../store/dialog-store.jsx";

/**
 * Subset of the headless primitive surface that `useDialogRootInternal`
 * consumes. Both `DialogApi` and `AlertDialogApi` are structurally
 * assignable.
 */

/**
 * Shared `<Dialog.Root>` / `<AlertDialog.Root>` body. Returns the JSX
 * element (a `<IsDrawerContext>` + `<DialogRootContext>` provider pair
 * wrapping the resolved children).
 */
export function useDialogRootInternal(props, options) {
  const parentDialogContext = useDialogRootContext(true);
  const isDrawer = useContext(IsDrawerContext);
  const nested = parentDialogContext != null;

  // Delegate `modal` + `disablePointerDismissal` to the headless
  // primitive. AlertDialog's primitive pins both as fixed invariants;
  // Dialog's primitive resolves them from its props.
  const modal = options.api.modal;
  const disablePointerDismissal = options.api.disablePointerDismissal;

  // Adopt the handle's store if supplied; otherwise own one.
  const store = props.handle?.store ?? new DialogStore();
  const [preventUnmountingOnClose, setPreventUnmountingOnClose] = createSignal(false, {
    ownedWrite: true
  });

  // `open` is owned by the headless primitive (which threads the
  // wrapper's controlled/uncontrolled resolution in). `setOpenInternal`
  // mirrors Base UI's `DialogStore.setOpen`: fire `onOpenChange`, honor
  // `details.cancel()`, then write the uncontrolled state (ignored for
  // reads while the `open` prop is controlled).
  const open = options.api.open;
  const setOpenInternal = (next, details) => {
    props.onOpenChange?.(next, details);
    if (details.isCanceled) return;
    options.setUncontrolledOpen?.(next);
  };
  const {
    mounted,
    transitionStatus,
    setMounted
  } = useTransitionStatus(open, () => true, () => false);
  const popupRef = {
    current: null
  };
  const backdropRef = {
    current: null
  };
  const internalBackdropRef = {
    current: null
  };
  const outsidePressEnabledRef = {
    current: true
  };
  useOpenChangeComplete({
    open,
    ref: popupRef,
    onComplete: () => {
      if (!open() && !preventUnmountingOnClose()) {
        setMounted(false);
      }
      props.onOpenChangeComplete?.(open());
    }
  });
  const [titleId, setTitleId] = createSignal(undefined, {
    ownedWrite: true
  });
  const [descriptionId, setDescriptionId] = createSignal(undefined, {
    ownedWrite: true
  });
  const [activeTriggerIdInternal, setActiveTriggerIdInternal] = createSignal(null, {
    ownedWrite: true
  });
  const activeTriggerId = createMemo(() => {
    const controlled = props.triggerId;
    if (controlled !== undefined) return controlled;
    return activeTriggerIdInternal();
  });
  const setActiveTriggerId = id => {
    setActiveTriggerIdInternal(id);
  };
  const [triggerElement, setTriggerElementInternal] = createSignal(null, {
    ownedWrite: true
  });
  const setTriggerElement = el => {
    setTriggerElementInternal(el);
  };
  createEffect(() => activeTriggerId(), id => {
    if (id === null) return;
    const el = store.context.triggerElements.getById(id);
    if (el) setTriggerElementInternal(el);
  });
  const [popupElement, setPopupElement] = createSignal(null, {
    ownedWrite: true
  });
  const [viewportElement, setViewportElement] = createSignal(null, {
    ownedWrite: true
  });
  const [payload, setPayload] = createSignal(undefined, {
    ownedWrite: true
  });
  store.attachSetPayload(p => {
    setPayload(() => p);
  });
  onCleanup(() => store.attachSetPayload(null));
  const {
    openMethod
  } = useOpenInteractionType(open);
  const [ownNestedOpenDialogs, setOwnNestedOpenDialogs] = createSignal(0, {
    ownedWrite: true
  });
  const [ownNestedOpenDrawers, setOwnNestedOpenDrawers] = createSignal(0, {
    ownedWrite: true
  });
  const isTopmost = createMemo(() => ownNestedOpenDialogs() === 0);
  const floatingRootContext = useFloatingRootContext({
    open: untrack(open),
    onOpenChange: (nextOpen, details) => {
      setOpenInternal(nextOpen, details);
    }
  });
  createEffect(() => ({
    o: open(),
    trigger: triggerElement(),
    popup: popupElement(),
    // Capture transitionStatus in compute (snapshot pattern) — apply
    // runs unowned so a direct read would trip
    // `[STRICT_READ_UNTRACKED]`.
    status: transitionStatus()
  }), ({
    o,
    trigger,
    popup,
    status
  }) => untrack(() => {
    floatingRootContext.update({
      open: o,
      referenceElement: trigger,
      domReferenceElement: trigger,
      floatingElement: popup,
      transitionStatus: status
    });
  }));
  createEffect(() => transitionStatus(), status => untrack(() => {
    floatingRootContext.update({
      transitionStatus: status
    });
  }));

  // Dismiss — Escape, outside press, focus-out. For dialogs:
  // - Mouse outside-press: `intentional` (requires unbroken mouse
  //   sequence on the same target) for modal, `sloppy` for trap-focus.
  // - Touch: `sloppy`.
  // - Escape: only when topmost.
  const dismiss = useDismiss(floatingRootContext, {
    outsidePressEvent: {
      get mouse() {
        return untrack(() => modal() === "trap-focus" ? "sloppy" : "intentional");
      },
      touch: "sloppy"
    },
    // `outsidePress` may be invoked from useDismiss's effect-apply scope
    // during synthetic events. Wrap reads in `untrack`.
    outsidePress(event) {
      return untrack(() => {
        if (!outsidePressEnabledRef.current) return false;
        if (disablePointerDismissal()) return false;
        if ("button" in event && event.button !== 0) return false;
        if ("touches" in event && event.touches.length !== 1) return false;
        if (!isTopmost()) return false;
        return true;
      });
    },
    escapeKey: isTopmost
  });
  const role = useRole(floatingRootContext, {
    role: options.role
  });
  const interactions = useInteractions([dismiss, role]);
  useScrollLock({
    enabled: () => open() && modal() === true,
    referenceElement: () => popupElement()
  });
  const setOpen = (next, details) => {
    if (typeof details.preventUnmountOnClose !== "function") {
      details.preventUnmountOnClose = () => {
        setPreventUnmountingOnClose(true);
      };
    } else {
      const original = details.preventUnmountOnClose;
      details.preventUnmountOnClose = () => {
        setPreventUnmountingOnClose(true);
        original();
      };
    }
    if (next) setPreventUnmountingOnClose(false);
    setOpenInternal(next, details);

    // Base UI returns before trigger-state mirroring when the change
    // was canceled via `details.cancel()`.
    if (details.isCanceled) return;
    if (details.trigger && details.trigger.id) {
      setActiveTriggerIdInternal(details.trigger.id);
      setTriggerElementInternal(details.trigger);
    }
  };
  store.attachSetOpen((next, details) => {
    setOpen(next, details);
  });
  onCleanup(() => store.attachSetOpen(null));
  createEffect(() => open(), o => {
    store.setOpenMirror(o);
  });
  createEffect(() => open(), o => untrack(() => {
    if (!o) return;
    if (activeTriggerId() !== null) return;
    if (store.context.triggerElements.size !== 1) return;
    const entry = store.context.triggerElements.entries().next();
    if (!entry.done) {
      const [id, el] = entry.value;
      setActiveTriggerIdInternal(id);
      setTriggerElementInternal(el);
    }
  }));
  createEffect(() => ({
    o: open(),
    dialogs: ownNestedOpenDialogs(),
    drawers: ownNestedOpenDrawers()
  }), ({
    o,
    dialogs,
    drawers
  }) => {
    if (!parentDialogContext) return undefined;
    if (o) {
      parentDialogContext.onNestedDialogOpen(dialogs + 1, drawers + (isDrawer ? 1 : 0));
      return () => {
        parentDialogContext.onNestedDialogClose();
      };
    }
    parentDialogContext.onNestedDialogClose();
    return undefined;
  });
  const makeImperativeDetails = () => createChangeEventDetails(REASONS.imperativeAction, undefined, undefined, {
    preventUnmountOnClose: () => {}
  });
  const actions = {
    close: () => setOpen(false, makeImperativeDetails()),
    unmount: () => {
      setPreventUnmountingOnClose(false);
      setOpen(false, makeImperativeDetails());
      setMounted(false);
    }
  };
  const ref = props.actionsRef;
  if (typeof ref === "function") {
    ref(actions);
    onCleanup(() => ref(null));
  } else if (ref && typeof ref === "object") {
    ref.current = actions;
    onCleanup(() => {
      ref.current = null;
    });
  }
  const openMethodAccessor = () => openMethod();
  const onNestedDialogOpen = (dialogCount, drawerCount) => {
    setOwnNestedOpenDialogs(dialogCount);
    setOwnNestedOpenDrawers(drawerCount);
  };
  const onNestedDialogClose = () => {
    setOwnNestedOpenDialogs(0);
    setOwnNestedOpenDrawers(0);
  };
  const contextValue = {
    open,
    mounted,
    transitionStatus,
    modal,
    disablePointerDismissal,
    role: () => options.role,
    nested: () => nested,
    nestedOpenDialogCount: ownNestedOpenDialogs,
    nestedOpenDrawerCount: ownNestedOpenDrawers,
    openMethod: openMethodAccessor,
    titleId,
    setTitleId,
    descriptionId,
    setDescriptionId,
    floatingRootContext,
    interactions,
    setOpen,
    popupRef,
    backdropRef,
    internalBackdropRef,
    outsidePressEnabledRef,
    popupElement,
    setPopupElement,
    viewportElement,
    setViewportElement,
    triggerElement,
    setTriggerElement,
    activeTriggerId,
    setActiveTriggerId,
    store: store,
    payload,
    setPayload: p => setPayload(() => p),
    onNestedDialogOpen,
    onNestedDialogClose
  };
  const resolveChildren = () => {
    const ch = props.children;
    if (typeof ch === "function") {
      return ch({
        payload: payload()
      });
    }
    return ch;
  };
  return <IsDrawerContext value={false}>
			<DialogRootContext value={contextValue}>
				{resolveChildren()}
			</DialogRootContext>
		</IsDrawerContext>;
}
//# sourceMappingURL=use-dialog-root-internal.jsx.map
