var d = Object.defineProperty;
var f = (i, u, e) => u in i ? d(i, u, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[u] = e;
var l = (i, u, e) => f(i, typeof u != "symbol" ? u + "" : u, e);
import * as c from "react";
import { Store as p } from "./Store.js";
import { useStore as b } from "./useStore.js";
import { useStableCallback as V } from "../useStableCallback.js";
import { useIsoLayoutEffect as a } from "../useIsoLayoutEffect.js";
import { NOOP as m } from "../empty.js";
class C extends p {
  /**
   * Creates a new ReactStore instance.
   *
   * @param state Initial state of the store.
   * @param context Non-reactive context values.
   * @param selectors Optional selectors for use with `useState`.
   */
  constructor(e, t = {}, s) {
    super(e);
    /**
     * Non-reactive values such as refs, callbacks, etc.
     */
    /**
     * Keeps track of which properties are controlled.
     */
    l(this, "controlledValues", /* @__PURE__ */ new Map());
    /** Gets the current value from the store using a selector with the provided key.
     *
     * @param key Key of the selector to use.
     */
    l(this, "select", (e, t, s, o) => {
      const n = this.selectors[e];
      return n(this.state, t, s, o);
    });
    /**
     * Returns a value from the store's state using a selector function.
     * Used to subscribe to specific parts of the state.
     * This methods causes a rerender whenever the selected state changes.
     *
     * @param key Key of the selector to use.
     */
    l(this, "useState", (e, t, s, o) => {
      c.useDebugValue(e);
      const n = this.selectors[e];
      return b(this, n, t, s, o);
    });
    this.context = t, this.selectors = s;
  }
  /**
   * Synchronizes a single external value into the store.
   *
   * Note that the while the value in `state` is updated immediately, the value returned
   * by `useState` is updated before the next render (similarly to React's `useState`).
   */
  useSyncedValue(e, t) {
    c.useDebugValue(e), a(() => {
      this.state[e] !== t && this.set(e, t);
    }, [e, t]);
  }
  /**
   * Synchronizes a single external value into the store and
   * cleans it up (sets to `undefined`) on unmount.
   *
   * Note that the while the value in `state` is updated immediately, the value returned
   * by `useState` is updated before the next render (similarly to React's `useState`).
   */
  useSyncedValueWithCleanup(e, t) {
    const s = this;
    a(() => (s.state[e] !== t && s.set(e, t), () => {
      s.set(e, void 0);
    }), [s, e, t]);
  }
  /**
   * Synchronizes multiple external values into the store.
   *
   * Note that the while the values in `state` are updated immediately, the values returned
   * by `useState` are updated before the next render (similarly to React's `useState`).
   */
  useSyncedValues(e) {
    const t = this;
    if (process.env.NODE_ENV !== "production") {
      c.useDebugValue(e, (r) => Object.keys(r));
      const o = c.useRef(Object.keys(e)).current, n = Object.keys(e);
      (o.length !== n.length || o.some((r, h) => r !== n[h])) && console.error("ReactStore.useSyncedValues expects the same prop keys on every render. Keys should be stable.");
    }
    const s = Object.values(e);
    a(() => {
      t.update(e);
    }, [t, ...s]);
  }
  /**
   * Registers a controllable prop pair (`controlled`, `defaultValue`) for a specific key.
   * - If `controlled` is non-undefined, the key is marked as controlled and the store's
   *   state at `key` is updated to match `controlled`. Local writes to that key are ignored.
   * - If `controlled` is undefined, the key is marked as uncontrolled. The store's state
   *   is initialized to `defaultValue` on first render and can be updated with local writes.
   */
  useControlledProp(e, t, s) {
    c.useDebugValue(e);
    const o = this, n = t !== void 0;
    if (process.env.NODE_ENV !== "production") {
      const r = this.controlledValues.get(e);
      r !== void 0 && r !== n && console.error(`A component is changing the ${n ? "" : "un"}controlled state of ${e.toString()} to be ${n ? "un" : ""}controlled. Elements should not switch from uncontrolled to controlled (or vice versa).`);
    }
    this.controlledValues.has(e) || (this.controlledValues.set(e, n), !n && !Object.is(this.state[e], s) && super.setState({
      ...this.state,
      [e]: s
    })), a(() => {
      n && !Object.is(o.state[e], t) && super.setState({
        ...o.state,
        [e]: t
      });
    }, [o, e, t, s, n]);
  }
  /**
   * Sets a specific key in the store's state to a new value and notifies listeners if the value has changed.
   * If the key is controlled (registered via {@link useControlledProp} with a non-undefined value),
   * the update is ignored and no listeners are notified.
   *
   * @param key The state key to update.
   * @param value The new value to set for the specified key.
   */
  set(e, t) {
    this.controlledValues.get(e) !== !0 && super.set(e, t);
  }
  /**
   * Merges the provided changes into the current state and notifies listeners if there are changes.
   * Controlled keys are filtered out and not updated.
   *
   * @param values An object containing the changes to apply to the current state.
   */
  update(e) {
    const t = {
      ...e
    };
    for (const s in t)
      if (Object.hasOwn(t, s) && this.controlledValues.get(s) === !0) {
        delete t[s];
        continue;
      }
    super.update(t);
  }
  /**
   * Updates the entire store's state and notifies all registered listeners.
   * Controlled keys are left unchanged; only uncontrolled keys from `newState` are applied.
   *
   * @param newState The new state to set for the store.
   */
  setState(e) {
    const t = {
      ...e
    };
    for (const s in t)
      if (Object.hasOwn(t, s) && this.controlledValues.get(s) === !0) {
        delete t[s];
        continue;
      }
    super.setState({
      ...this.state,
      ...t
    });
  }
  /**
   * Wraps a function with `useStableCallback` to ensure it has a stable reference
   * and assigns it to the context.
   *
   * @param key Key of the event callback. Must be a function in the context.
   * @param fn Function to assign.
   */
  useContextCallback(e, t) {
    c.useDebugValue(e);
    const s = V(t ?? m);
    this.context[e] = s;
  }
  /**
   * Returns a stable setter function for a specific key in the store's state.
   * It's commonly used to pass as a ref callback to React elements.
   *
   * @param key Key of the state to set.
   */
  useStateSetter(e) {
    const t = c.useRef(void 0);
    return t.current === void 0 && (t.current = (s) => {
      this.set(e, s);
    }), t.current;
  }
  /**
   * Observes changes derived from the store's selectors and calls the listener when the selected value changes.
   *
   * @param key Key of the selector to observe.
   * @param listener Listener function called when the selector result changes.
   */
  observe(e, t) {
    let s;
    typeof e == "function" ? s = e : s = this.selectors[e];
    let o = s(this.state);
    return t(o, o, this), this.subscribe((n) => {
      const r = s(n);
      if (!Object.is(o, r)) {
        const h = o;
        o = r, t(r, h, this);
      }
    });
  }
}
export {
  C as ReactStore
};
