// packages/data/src/components/use-select/index.js
import { createQueue } from "@wordpress/priority-queue";
import {
  useRef,
  useCallback,
  useMemo,
  useSyncExternalStore,
  useDebugValue
} from "@wordpress/element";
import { isShallowEqual } from "@wordpress/is-shallow-equal";
import useRegistry from "../registry-provider/use-registry.mjs";
import useAsyncMode from "../async-mode-provider/use-async-mode.mjs";
var renderQueue = createQueue();
function warnOnUnstableReference(a, b) {
  if (!a || !b) {
    return;
  }
  const keys = typeof a === "object" && typeof b === "object" ? Object.keys(a).filter((k) => a[k] !== b[k]) : [];
  console.warn(
    "The `useSelect` hook returns different values when called with the same state and parameters.\nThis can lead to unnecessary re-renders and performance issues if not fixed.\n\nNon-equal value keys: %s\n\n",
    keys.join(", ")
  );
}
function Store(registry, suspense) {
  const select = suspense ? registry.suspendSelect : registry.select;
  const queueContext = {};
  let lastMapSelect;
  let lastMapResult;
  let lastMapResultValid = false;
  let lastIsAsync;
  let subscriber;
  let didWarnUnstableReference;
  const storeStatesOnMount = /* @__PURE__ */ new Map();
  function getStoreState(name) {
    return registry.stores[name]?.store?.getState?.() ?? {};
  }
  const createSubscriber = (stores) => {
    const activeStores = [...stores];
    const activeSubscriptions = /* @__PURE__ */ new Set();
    function subscribe(listener) {
      if (lastMapResultValid) {
        for (const name of activeStores) {
          if (storeStatesOnMount.get(name) !== getStoreState(name)) {
            lastMapResultValid = false;
          }
        }
      }
      storeStatesOnMount.clear();
      const onStoreChange = () => {
        lastMapResultValid = false;
        listener();
      };
      const onChange = () => {
        if (lastIsAsync) {
          renderQueue.add(queueContext, onStoreChange);
        } else {
          onStoreChange();
        }
      };
      const unsubs = [];
      function subscribeStore(storeName) {
        unsubs.push(registry.subscribe(onChange, storeName));
      }
      for (const storeName of activeStores) {
        subscribeStore(storeName);
      }
      activeSubscriptions.add(subscribeStore);
      return () => {
        activeSubscriptions.delete(subscribeStore);
        for (const unsub of unsubs.values()) {
          unsub?.();
        }
        renderQueue.cancel(queueContext);
      };
    }
    function updateStores(newStores) {
      for (const newStore of newStores) {
        if (activeStores.includes(newStore)) {
          continue;
        }
        activeStores.push(newStore);
        for (const subscription of activeSubscriptions) {
          subscription(newStore);
        }
      }
    }
    return { subscribe, updateStores };
  };
  return (mapSelect, isAsync) => {
    function updateValue() {
      if (lastMapResultValid && mapSelect === lastMapSelect) {
        return lastMapResult;
      }
      const listeningStores = { current: null };
      const mapResult = registry.__unstableMarkListeningStores(
        () => mapSelect(select, registry),
        listeningStores
      );
      if (globalThis.SCRIPT_DEBUG) {
        if (!didWarnUnstableReference) {
          const secondMapResult = mapSelect(select, registry);
          if (!isShallowEqual(mapResult, secondMapResult)) {
            warnOnUnstableReference(mapResult, secondMapResult);
            didWarnUnstableReference = true;
          }
        }
      }
      if (!subscriber) {
        for (const name of listeningStores.current) {
          storeStatesOnMount.set(name, getStoreState(name));
        }
        subscriber = createSubscriber(listeningStores.current);
      } else {
        subscriber.updateStores(listeningStores.current);
      }
      if (!isShallowEqual(lastMapResult, mapResult)) {
        lastMapResult = mapResult;
      }
      lastMapSelect = mapSelect;
      lastMapResultValid = true;
    }
    function getValue() {
      updateValue();
      return lastMapResult;
    }
    if (lastIsAsync && !isAsync) {
      lastMapResultValid = false;
      renderQueue.cancel(queueContext);
    }
    updateValue();
    lastIsAsync = isAsync;
    return { subscribe: subscriber.subscribe, getValue };
  };
}
function _useStaticSelect(storeName) {
  return useRegistry().select(storeName);
}
function _useMappingSelect(suspense, mapSelect, deps) {
  const registry = useRegistry();
  const isAsync = useAsyncMode();
  const store = useMemo(
    () => Store(registry, suspense),
    [registry, suspense]
  );
  const selector = useCallback(mapSelect, deps);
  const { subscribe, getValue } = store(selector, isAsync);
  const result = useSyncExternalStore(subscribe, getValue, getValue);
  useDebugValue(result);
  return result;
}
function useSelect(mapSelect, deps) {
  const staticSelectMode = typeof mapSelect !== "function";
  const staticSelectModeRef = useRef(staticSelectMode);
  if (staticSelectMode !== staticSelectModeRef.current) {
    const prevMode = staticSelectModeRef.current ? "static" : "mapping";
    const nextMode = staticSelectMode ? "static" : "mapping";
    throw new Error(
      `Switching useSelect from ${prevMode} to ${nextMode} is not allowed`
    );
  }
  return staticSelectMode ? _useStaticSelect(mapSelect) : _useMappingSelect(false, mapSelect, deps);
}
function useSuspenseSelect(mapSelect, deps) {
  return _useMappingSelect(true, mapSelect, deps);
}
export {
  useSelect as default,
  useSuspenseSelect
};
//# sourceMappingURL=index.mjs.map
