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)`.
 *
 * - `"trigger-press"` — click / keyboard activation that bubbles to click on
 *   the checkbox root, OR a change on the co-located hidden `<input>`.
 * - `"imperative"` — programmatic `setChecked` / `toggle` with no originating
 *   DOM event.
 */

/**
 * Options for {@link createCheckbox}. Every reactive input is
 * `MaybeAccessor<T>` — pass an accessor to make the value reactive.
 *
 * Design note on tri-state: the rollout doc floated
 * `checked: boolean | "indeterminate"` for the api shape; this primitive
 * mirrors React's two-signal convention instead (`checked` + `indeterminate`
 * are independent). Reason: indeterminate is a presentation concern (what
 * `aria-checked` reports + native `<input>.indeterminate` property) layered
 * on top of the underlying boolean. Folding them into a single union loses
 * the "what does the next click flip to" answer (native checkboxes flip an
 * indeterminate one to `true`, regardless of the underlying boolean), and
 * forces consumers to encode/decode the union at every read site.
 *
 * **Solid-native shape (2026-04-30).** No internal state. `checked` is
 * required and the consumer always owns the signal. See `solidify.md` §6.
 */

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

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

const CheckboxContext = createContext(null);

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

/**
 * Solid 2.0-native compound primitive for a checkbox. One call owns logical
 * state (checked/indeterminate/disabled/readOnly/required, onCheckedChange).
 * Form wiring (the hidden `<input>`, name/value, indeterminate DOM-property
 * sync, field-control validation) and CheckboxGroup parent/child integration
 * stay in the `<Checkbox.Root>` wrapper — those are integration concerns
 * layered on top of this primitive.
 */
export function createCheckbox(options) {
  // `checked` reads through the consumer's accessor — no internal signal,
  // no controlled-or-internal branching.
  const checked = createMemo(() => Boolean(readOption(options.checked)), {
    name: "checkbox.checked"
  });
  const indeterminate = createMemo(() => Boolean(readOption(options.indeterminate)), {
    name: "checkbox.indeterminate"
  });
  const disabled = createMemo(() => Boolean(readOption(options.disabled)), {
    name: "checkbox.disabled"
  });
  const readOnly = createMemo(() => Boolean(readOption(options.readOnly)), {
    name: "checkbox.readOnly"
  });
  const required = createMemo(() => Boolean(readOption(options.required)), {
    name: "checkbox.required"
  });

  // `setChecked` / `toggle` are pure event-emitters. The consumer is
  // responsible for updating their checked-signal in response.
  const setChecked = (next, event, reason = "imperative") => {
    if (untrack(disabled) || untrack(readOnly)) return;
    const ev = event ?? new Event("checkedchange", {
      cancelable: true
    });
    options.onCheckedChange?.(next, ev, reason);
  };
  const toggle = (event, reason = "imperative") => {
    // Indeterminate → click lands on `true` (matches native
    // <input type="checkbox">.indeterminate semantics). Otherwise invert.
    const next = untrack(indeterminate) ? true : !untrack(checked);
    setChecked(next, event, reason);
  };
  const triggerInternal = {
    type: "button",
    role: "checkbox",
    get "aria-checked"() {
      if (indeterminate()) return "mixed";
      return checked() ? "true" : "false";
    },
    get "aria-readonly"() {
      return readOnly() ? "true" : undefined;
    },
    get "aria-required"() {
      return required() ? "true" : undefined;
    },
    get "data-checked"() {
      return !indeterminate() && checked() ? "" : undefined;
    },
    get "data-unchecked"() {
      return !indeterminate() && !checked() ? "" : undefined;
    },
    get "data-indeterminate"() {
      return indeterminate() ? "" : 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;
      const next = indeterminate() ? true : !untrack(checked);
      setChecked(next, event, "trigger-press");
    }
  };
  const trigger = userProps => mergeRootProps(triggerInternal, userProps);
  const triggerRef = () => makeSlotRefDirective(() => trigger());
  const api = {
    checked,
    indeterminate,
    disabled,
    readOnly,
    required,
    toggle,
    setChecked,
    trigger,
    triggerRef,
    Provider: props => <CheckboxContext value={api}>{props.children}</CheckboxContext>
  };
  api[SNAPSHOT_KEYS] = CHECKBOX_SNAPSHOT_KEYS;
  return api;
}
const CHECKBOX_SNAPSHOT_KEYS = snapshotKeys("checked", "indeterminate", "disabled", "readOnly", "required");
//# sourceMappingURL=create-checkbox.jsx.map
