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";

/**
 * Why the checked state changed. Emitted on the third arg of
 * `onCheckedChange(next, event, reason)` and accepted by
 * `c.select(event?, reason?)`.
 *
 * - `"trigger-press"` — click (or keyboard activation that bubbles to click)
 *   on the trigger slot.
 * - `"imperative"` — programmatic `select()` with no originating DOM event.
 *
 * Outer compounds that embed Radio (RadioGroup) translate this to their own
 * reason taxonomy.
 */

/**
 * Options for {@link createRadio}.
 *
 * Every reactive input is `MaybeAccessor<T>` — pass an accessor to make the
 * value reactive, or a bare value for one-shot config.
 *
 * **Solid-native shape (2026-04-30).** No internal state. `checked` is
 * required and the consumer always owns the signal. The primitive is a pure
 * function from `(checked, onCheckedChange) → api`. See `solidify.md` §6.
 */

/**
 * Public API returned by {@link createRadio}. Flat accessors and setters at
 * the root; no `state` snapshot blob.
 */

/** Shape of attributes emitted by `c.trigger()`. */

const RadioContext = createContext(null);

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

/**
 * Solid 2.0-native compound primitive for a single radio input. One call owns
 * logical state (checked/disabled/readOnly/required, onCheckedChange). Group
 * coordination, hidden-input form wiring, and field-control validation are
 * the `<Radio.Root>` / `<RadioGroup>` wrappers' job.
 *
 * Radios are one-way: `select()` always sends `next === true`. Clicking an
 * already-checked radio is a no-op (matches Base UI React).
 */
export function createRadio(options) {
  // `checked` 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 checked = createMemo(() => Boolean(readOption(options.checked)), {
    name: "radio.checked"
  });
  const disabled = createMemo(() => Boolean(readOption(options.disabled)), {
    name: "radio.disabled"
  });
  const readOnly = createMemo(() => Boolean(readOption(options.readOnly)), {
    name: "radio.readOnly"
  });
  const required = createMemo(() => Boolean(readOption(options.required)), {
    name: "radio.required"
  });

  // `select` is a pure event-emitter. It fires `onCheckedChange(true, event, reason)`;
  // the consumer is responsible for updating their checked-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 select = (event, reason = "imperative") => {
    if (untrack(disabled) || untrack(readOnly)) return;
    if (untrack(checked)) return;
    const ev = event ?? new Event("checkedchange", {
      cancelable: true
    });
    options.onCheckedChange?.(true, ev, reason);
  };
  const triggerInternal = {
    type: "button",
    role: "radio",
    get "aria-checked"() {
      return checked() ? "true" : "false";
    },
    get "aria-readonly"() {
      return readOnly() ? "true" : undefined;
    },
    get "aria-required"() {
      return required() ? "true" : undefined;
    },
    get "data-checked"() {
      return checked() ? "" : undefined;
    },
    get "data-unchecked"() {
      return checked() ? undefined : "";
    },
    get "data-disabled"() {
      return disabled() ? "" : undefined;
    },
    get "data-readonly"() {
      return readOnly() ? "" : undefined;
    },
    get "data-required"() {
      return required() ? "" : undefined;
    },
    onClick(event) {
      if (disabled() || readOnly()) return;
      if (untrack(checked)) return;
      select(event, "trigger-press");
    }
  };
  const trigger = userProps => mergeRootProps(triggerInternal, userProps);
  const triggerRef = () => makeSlotRefDirective(() => trigger());
  const api = {
    checked,
    disabled,
    readOnly,
    required,
    select,
    trigger,
    triggerRef,
    Provider: props => <RadioContext value={api}>{props.children}</RadioContext>
  };
  api[SNAPSHOT_KEYS] = RADIO_SNAPSHOT_KEYS;
  return api;
}
const RADIO_SNAPSHOT_KEYS = snapshotKeys("checked", "disabled", "readOnly", "required");
//# sourceMappingURL=create-radio.jsx.map
