import { dynamic } from "@solidjs/web";
import { createMemo, createRenderEffect, createSignal, omit } from "solid-js";
import { useCheckboxGroupContext } from "../../checkbox-group/checkbox-group-context.jsx";
import { useBaseUiId } from "../../internals/base-ui-id.jsx";
import { createChangeEventDetails, REASONS } from "../../internals/change-event-details.jsx";
import { composeRefs, userRef } from "../../internals/compose-refs.jsx";
import { createControlledState } from "../../internals/create-controlled-state.jsx";
import { useFieldRootContext } from "../../internals/field-context.jsx";
import { fieldStateAttrs } from "../../internals/field-state-attrs.jsx";
import { HiddenInput } from "../../internals/hidden-input/hidden-input.jsx";
import { useAriaLabelledBy } from "../../internals/labelable-provider/use-aria-labelled-by.jsx";
import { readOption } from "../../internals/maybe-accessor.jsx";
import { mergeRootProps } from "../../internals/merge-root-props.jsx";
import { resolveClassStyle } from "../../internals/resolve-class-style.jsx";
import { ariaLabelledBy as seedAriaLabelledBy } from "../../internals/seed-aria-attr.jsx";
import { useButton } from "../../internals/use-button/use-button.jsx";
import { useFieldControlValidation } from "../../internals/use-field-control-validation/use-field-control-validation.jsx";
import { useValueChanged } from "../../internals/use-value-changed.jsx";
import { CheckboxRootContext } from "../checkbox-root-context.jsx";
import { createCheckbox } from "../create-checkbox.jsx";
/**
 * The checkbox root. Renders a `<button role="checkbox">` (or `as`
 * override) plus a visually-hidden `<input type="checkbox">` for native
 * form submission.
 *
 * Solid 2.0-native rewire (§1 wave 1): replaces `renderElement` with
 * `dynamic(() => props.as ?? "button")` and threads
 * `createCheckbox().trigger()` (carrying role, aria-checked/readonly/
 * required, data-checked/unchecked/indeterminate/disabled/readonly/
 * required, primitive onClick) through `mergeRootProps` together with
 * useButton + field-validation + a wrapper-level onClick that owns the
 * checkbox-group/parent routing (which bypasses the primitive's
 * onClick). Field-state attrs and `data-parent` are emitted via inline
 * getters reading from `state()` / `inGroupAsParent()`.
 */
export function CheckboxRoot(props) {
  const field = useFieldRootContext();
  const groupContext = useCheckboxGroupContext();
  const isParent = () => props.parent === true;
  const inGroupAsChild = () => groupContext.present === true && !isParent() && typeof props.value === "string";
  const inGroupAsParent = () => groupContext.present === true && isParent();
  const readOnly = () => readOption(props.readOnly) ?? false;
  const required = () => readOption(props.required) ?? false;
  const nativeButton = () => props.nativeButton ?? true;
  const id = useBaseUiId(typeof props.id === "string" ? props.id : undefined);

  // Controlled / uncontrolled checked state (Base UI `checked` /
  // `defaultChecked` contract, mirroring React's `useControlled` in
  // `CheckboxRoot`):
  //
  // - parent checkbox in a group → always controlled by the parent
  //   indicator's all/none math (`groupChecked` from `getParentProps()`);
  // - child checkbox with a `value` in a group → always controlled by the
  //   group's (resolved) value array (`groupValue.includes(value)`);
  // - standalone → the `checked` prop wins while its resolved value is
  //   defined, else an internal signal seeded once from `defaultChecked`
  //   (or, per Base UI, from the group's `defaultValue` when present).
  const [resolvedChecked, setUncontrolledChecked] = createControlledState({
    controlled: () => {
      if (inGroupAsParent()) {
        return groupContext.parent.checked();
      }
      if (inGroupAsChild() && typeof props.value === "string") {
        const values = groupContext.value() ?? [];
        return values.includes(props.value);
      }
      const controlled = readOption(props.checked);
      return controlled === undefined ? undefined : Boolean(controlled);
    },
    default: () => {
      // Base UI: `value && defaultGroupValue && !parent ?
      // defaultGroupValue.includes(value) : defaultChecked`.
      if (groupContext.present === true && !isParent() && typeof props.value === "string") {
        const defaultGroupValue = groupContext.defaultValue?.();
        if (defaultGroupValue !== undefined) {
          return defaultGroupValue.includes(props.value);
        }
      }
      return readOption(props.defaultChecked);
    },
    fallback: false,
    name: "<Checkbox.Root>",
    state: "checked"
  });
  const c = createCheckbox({
    checked: resolvedChecked,
    disabled: () => field.disabled() || groupContext.present === true && groupContext.disabled() || props.disabled === true,
    indeterminate: () => {
      if (inGroupAsParent()) {
        return groupContext.parent.indeterminate() || readOption(props.indeterminate) === true;
      }
      return readOption(props.indeterminate) === true;
    },
    readOnly,
    required,
    onCheckedChange: (next, event) => {
      const details = createChangeEventDetails(REASONS.none, event);
      // Base UI change flow: fire the consumer callback first, honor
      // `details.cancel()`, then write the uncontrolled state (a no-op
      // for reads while controlled / in a group).
      props.onCheckedChange?.(next, details);
      if (details.isCanceled) {
        event.preventDefault();
        return;
      }
      setUncontrolledChecked(next);
    }
  });
  const checked = c.checked;
  const {
    props: buttonProps,
    ref: buttonRef
  } = useButton({
    disabled: c.disabled,
    native: nativeButton
  });
  const controlRef = {
    current: null
  };
  const validation = useFieldControlValidation({
    value: () => checked() ?? false,
    id: () => id,
    controlRef,
    getValue: () => checked() ?? false,
    enabled: () => !groupContext.present
  });
  useValueChanged(() => checked() ?? false, () => {
    if (groupContext.present === true && !isParent()) return;
    const value = checked() ?? false;
    field.setFilled(value);
    field.setDirty(value !== field.validityData().initialValue);
    if (field.shouldValidateOnChange()) {
      void validation.commit(value);
    } else {
      void validation.commit(value, true);
    }
  });
  const [hiddenInputEl, setHiddenInputEl] = createSignal(null);
  const captureInput = el => {
    setHiddenInputEl(el);
  };
  createRenderEffect(() => ({
    el: hiddenInputEl(),
    value: c.indeterminate()
  }), ({
    el,
    value
  }) => {
    if (el) el.indeterminate = value;
  });
  const state = createMemo(() => ({
    ...field.state(),
    checked: checked() ?? false,
    disabled: c.disabled(),
    readOnly: readOnly(),
    required: required(),
    indeterminate: c.indeterminate()
  }));
  const contextValue = {
    checked: () => checked() ?? false,
    indeterminate: c.indeterminate,
    disabled: c.disabled,
    readOnly,
    required,
    state
  };
  const ariaLabelledBy = useAriaLabelledBy(() => {
    const v = props["aria-labelledby"];
    return typeof v === "string" ? v : undefined;
  });
  createRenderEffect(() => {
    if (!inGroupAsChild() || typeof props.value !== "string") {
      return null;
    }
    return {
      value: props.value,
      disabled: c.disabled(),
      map: groupContext.parent.disabledStates
    };
  }, payload => {
    if (!payload) return;
    payload.map.set(payload.value, payload.disabled);
    return () => {
      payload.map.delete(payload.value);
    };
  });

  // Field-state data-attrs not carried by `c.trigger()`. Composed via
  // the shared `fieldStateAttrs` helper plus checkbox-specific extras.
  // Spreading `fieldStateAttrs(...)` here would invoke its getters at
  // body time (object spread reads each property eagerly), breaking
  // reactivity AND tripping `[STRICT_READ_UNTRACKED]`. Build the
  // extras separately and let `mergeRootProps` compose the two
  // getter bags below.
  const checkboxExtraAttrs = {
    get id() {
      return id;
    },
    get "data-parent"() {
      return inGroupAsParent() ? "" : undefined;
    }
  };
  const checkboxStateAttrs = mergeRootProps(checkboxExtraAttrs, fieldStateAttrs(state));

  // Wrapper-level click handler. In group mode this bypasses the primitive
  // (the group's value array is the source of truth). The primitive's own
  // onClick (in c.trigger()) is overridden via mergeRootProps's right-most
  // wins for plain keys — but onClick is an event handler so it CHAINS
  // instead. We need the wrapper's logic to run INSTEAD of the primitive's
  // in group mode. Solution: chain ordering — we put the primitive bag
  // AFTER our wrapper handler in the mergeRootProps chain so the wrapper
  // runs first; if we're in group mode the wrapper handles the toggle and
  // calls preventDefault to stop the primitive's default toggle from
  // running. In standalone mode we skip the preventDefault and let the
  // primitive's onClick fire (which calls c.toggle through the primitive's
  // own internal path, applying the indeterminate→true rule).
  //
  // Actually simpler: the wrapper handler short-circuits group mode by
  // preventDefault'ing, and in standalone calls the primitive's setChecked
  // directly with the indeterminate→true rule. This way the primitive's
  // onClick can be replaced.
  //
  // Cleanest: skip c.trigger()'s onClick and manage the click here.
  const wrapperClick = event => {
    if (c.disabled() || readOnly()) return;
    if (inGroupAsParent() || inGroupAsChild()) {
      const wasIndeterminate = c.indeterminate();
      const current = c.checked();
      const next = wasIndeterminate ? true : !current;
      const details = createChangeEventDetails(REASONS.none, event);
      // Base UI change flow: consumer callback first, cancel gate, then
      // the (inert-while-grouped) uncontrolled write, then the group
      // value write — mirrors `CheckboxRoot`'s hidden-input onChange.
      props.onCheckedChange?.(next, details);
      if (details.isCanceled) return;
      setUncontrolledChecked(next);
      if (inGroupAsParent()) {
        groupContext.parent.getParentProps().onCheckedChange(next, details);
        return;
      }
      if (typeof props.value === "string") {
        const currentValues = groupContext.value() ?? [];
        const nextValues = next ? [...currentValues, props.value] : currentValues.filter(v => v !== props.value);
        groupContext.setValue(nextValues, details);
      }
      return;
    }
    c.toggle(event, "trigger-press");
  };
  const wrapperEventHandlers = {
    onFocus: () => {
      field.setFocused(true);
    },
    onBlur: () => {
      field.setTouched(true);
      field.setFocused(false);
      if (field.validationMode() === "onBlur") {
        void validation.commit(checked() ?? false);
      }
    },
    onClick: wrapperClick
  };
  const userStyling = resolveClassStyle(props, () => state());
  const userProps = omit(props, "class", "style", "attrs", "as", "aria-labelledby", "checked", "children", "defaultChecked", "disabled", "form", "id", "indeterminate", "name", "nativeButton", "onCheckedChange", "parent", "readOnly", "required", "uncheckedValue", "value");

  // Build c.trigger() once; we want all its keys EXCEPT onClick (since the
  // wrapper now owns the toggle in both group + standalone modes). Use
  // `omit` so the getter descriptors are preserved instead of being read
  // at body-time (a `{...rest}` destructure would invoke every getter,
  // breaking reactivity for aria-checked / data-checked / etc.).
  const triggerBag = c.trigger();
  const triggerWithoutClick = omit(triggerBag, "onClick");

  // Compose: useButton ∘ aria-labelledby ∘ field-validation ∘ c.trigger()(no-click)
  // ∘ fieldStateAttrs ∘ wrapperEventHandlers ∘ userProps.
  const styledUserProps = mergeRootProps(userStyling, userProps);
  let merged = mergeRootProps(buttonProps, seedAriaLabelledBy(ariaLabelledBy));
  merged = mergeRootProps(merged, validation.getValidationProps());
  merged = mergeRootProps(merged, triggerWithoutClick);
  merged = mergeRootProps(merged, checkboxStateAttrs);
  merged = mergeRootProps(merged, wrapperEventHandlers);
  merged = mergeRootProps(merged, styledUserProps);
  const Tag = dynamic(() => props.as ?? "button");
  const buttonElement = <Tag {...merged} ref={composeRefs(el => {
    controlRef.current = el;
  }, buttonRef, userRef(props))}>
			<CheckboxRootContext value={contextValue}>
				{props.children}
			</CheckboxRootContext>
		</Tag>;
  const resolvedName = () => {
    if (isParent()) return undefined;
    return field.name() ?? props.name;
  };
  const resolvedValue = () => props.value ?? resolvedName() ?? "";
  return <>
			{buttonElement}
			<HiddenInput type="checkbox" ref={captureInput} checked={checked() ?? false} disabled={c.disabled()} form={props.form} name={resolvedName()} required={required()} value={resolvedValue()} tabindex={-1} aria-hidden="true" />
			{!(checked() ?? false) && resolvedName() && props.uncheckedValue !== undefined ? <input type="hidden" form={props.form} name={resolvedName()} value={props.uncheckedValue} /> : null}
		</>;
}
//# sourceMappingURL=checkbox-root.jsx.map
