import { createContext, createMemo, untrack, useContext } from "solid-js";
import { makeSlotRefDirective } from "../internals/make-slot-ref-directive.jsx";
import { readOption } from "../internals/maybe-accessor.jsx";
import { mergeRootProps } from "../internals/merge-root-props.jsx";
import { SNAPSHOT_KEYS, snapshotKeys } from "../internals/observability/snapshot.jsx";

/**
 * The opaque identifier a consumer attaches to a tab + the matching panel.
 * `null`/`undefined` disables selection entirely. Mirrors the existing
 * `<Tabs>` wrapper's `TabsTabValue`.
 */

/**
 * Why the value changed. Emitted on the third arg of
 * `onValueChange(next, event, reason)` and accepted by
 * `c.setValue(next, event?, reason?)`.
 *
 * - `"trigger-press"` — click on a tab slot.
 * - `"focus"` — focus moved to a tab while `activateOnFocus` is enabled
 *   (driven by the wrapper, not this primitive).
 * - `"imperative"` — programmatic `setValue` with no originating DOM event.
 */

/**
 * Options for {@link createTabs}.
 *
 * Every reactive input is `MaybeAccessor<T>`. Read at use time via
 * `readOption(opt)`.
 *
 * **Solid-native shape (2026-04-30).** No internal state. `value` is
 * required and the consumer always owns the signal. The primitive is
 * a pure function from `(value, onValueChange) → api`. Migration from
 * pre-2026-04-30: `createTabs({ defaultValue: "a" })` becomes
 * `const [v, setV] = createSignal("a"); createTabs({ value: v, onValueChange: setV })`.
 * See `solidify.md` §6 for rationale.
 */

/**
 * Slot bag for the list slot — `<div role="tablist">`.
 */

/**
 * Slot bag for a per-tab slot — `<button role="tab">`.
 */

/**
 * Slot bag for a per-panel slot — `<div role="tabpanel">`.
 */

/**
 * Slot bag for the indicator slot — `<span role="presentation">`. The
 * primitive only emits the role + a hidden bit when nothing is selected;
 * positioning math (CSS custom props) lives in the structural wrapper.
 */

/**
 * Public API returned by {@link createTabs}. Flat accessors at the root,
 * imperative setters, slot factories per part, matching directive
 * factories, plus an opt-in Provider + `useTabs()`.
 */

const TabsContext = createContext(null);

/**
 * Read the nearest {@link TabsApi} provided by `c.Provider`. Throws if
 * used outside a Provider.
 */
export function useTabs() {
  const ctx = useContext(TabsContext);
  if (ctx == null) {
    throw new Error("Base UI: useTabs() must be called inside a <c.Provider>.");
  }
  return ctx;
}

/**
 * Solid 2.0-native compound primitive for a tabs widget. Owns logical
 * state (value/disabled/orientation, onValueChange). Composite keyboard
 * focus, indicator measurement, panel-id wiring, and activation-direction
 * math are NOT in this primitive — they belong to the wrapping
 * `<Tabs.Root/.List/.Tab/.Panel/.Indicator>` adapters.
 */
export function createTabs(options) {
  // `value` reads through the consumer's accessor — no internal signal,
  // no controlled-or-internal branching. Memoized so dependents see the
  // same identity within a tracking scope.
  const value = createMemo(() => readOption(options.value), {
    name: "tabs.value",
    equals: false
  });
  const disabled = createMemo(() => Boolean(readOption(options.disabled)), {
    name: "tabs.disabled"
  });
  const orientation = createMemo(() => readOption(options.orientation) ?? "horizontal", {
    name: "tabs.orientation"
  });

  // `setValue` is a pure event-emitter. It fires `onValueChange(next, event, reason)`;
  // the consumer is responsible for updating their value-signal in response.
  // There is no internal state to write — `event.preventDefault()` is now a
  // no-op for the primitive (the consumer's handler is the only writer).
  const setValue = (next, event, reason = "imperative") => {
    const ev = event ?? new Event("valuechange", {
      cancelable: true
    });
    options.onValueChange?.(next, ev, reason);
  };
  const isSelected = tabValue => value() === tabValue;

  // list
  const listInternal = {
    role: "tablist",
    get "aria-orientation"() {
      return orientation() === "vertical" ? "vertical" : undefined;
    }
  };
  const list = userProps => mergeRootProps(listInternal, userProps);

  // tab(value)
  const tab = (tabValue, userProps) => {
    const isTabDisabled = () => Boolean(disabled() || userProps?.disabled);
    const internal = {
      type: "button",
      role: "tab",
      get "aria-selected"() {
        return value() === tabValue ? "true" : "false";
      },
      get "data-selected"() {
        return value() === tabValue ? "" : undefined;
      },
      get "data-disabled"() {
        return isTabDisabled() ? "" : undefined;
      },
      get tabIndex() {
        return value() === tabValue ? 0 : -1;
      },
      get disabled() {
        return isTabDisabled() ? true : undefined;
      },
      onClick(event) {
        if (isTabDisabled()) return;
        if (untrack(() => value()) === tabValue) return;
        setValue(tabValue, event, "trigger-press");
      }
    };
    return mergeRootProps(internal, userProps);
  };

  // panel(value)
  const panel = (panelValue, userProps) => {
    const internal = {
      role: "tabpanel",
      get "data-selected"() {
        return value() === panelValue ? "" : undefined;
      },
      get hidden() {
        return value() === panelValue ? undefined : true;
      },
      get tabIndex() {
        return value() === panelValue ? 0 : -1;
      }
    };
    return mergeRootProps(internal, userProps);
  };

  // indicator
  const indicatorInternal = {
    role: "presentation",
    get hidden() {
      return value() == null ? true : undefined;
    }
  };
  const indicator = userProps => mergeRootProps(indicatorInternal, userProps);
  const listRef = () => makeSlotRefDirective(() => list());
  const tabRef = tabValue => makeSlotRefDirective(() => tab(tabValue));
  const panelRef = panelValue => makeSlotRefDirective(() => panel(panelValue));
  const indicatorRef = () => makeSlotRefDirective(() => indicator());
  const api = {
    value,
    disabled,
    orientation,
    setValue,
    isSelected,
    list,
    tab,
    panel,
    indicator,
    listRef,
    tabRef,
    panelRef,
    indicatorRef,
    Provider: props => <TabsContext value={api}>{props.children}</TabsContext>
  };
  api[SNAPSHOT_KEYS] = TABS_SNAPSHOT_KEYS;
  return api;
}
const TABS_SNAPSHOT_KEYS = snapshotKeys("value", "disabled", "orientation");
//# sourceMappingURL=create-tabs.jsx.map
