// packages/block-editor/src/components/provider/use-block-sync.js
import { useEffect, useRef } from "@wordpress/element";
import { useRegistry, useSelect } from "@wordpress/data";
import { cloneBlock } from "@wordpress/blocks";
import { store as blockEditorStore } from "../../store/index.mjs";
var noop = () => {
};
function cloneBlockWithMapping(block, mapping) {
  const clonedBlock = cloneBlock(block);
  mapping.externalToInternal.set(block.clientId, clonedBlock.clientId);
  mapping.internalToExternal.set(clonedBlock.clientId, block.clientId);
  if (block.innerBlocks?.length) {
    clonedBlock.innerBlocks = block.innerBlocks.map((innerBlock) => {
      const clonedInner = cloneBlockWithMapping(innerBlock, mapping);
      return clonedInner;
    });
  }
  return clonedBlock;
}
function restoreExternalIds(blocks, mapping) {
  return blocks.map((block) => {
    const externalId = mapping.internalToExternal.get(block.clientId);
    return {
      ...block,
      // Use external ID if available, otherwise keep internal ID (for new blocks)
      clientId: externalId ?? block.clientId,
      innerBlocks: restoreExternalIds(block.innerBlocks, mapping)
    };
  });
}
function restoreSelectionIds(selection, mapping) {
  const { selectionStart, selectionEnd, initialPosition } = selection;
  const restoreClientId = (selectionState) => {
    if (!selectionState?.clientId) {
      return selectionState;
    }
    const externalId = mapping.internalToExternal.get(
      selectionState.clientId
    );
    return {
      ...selectionState,
      clientId: externalId ?? selectionState.clientId
    };
  };
  return {
    selectionStart: restoreClientId(selectionStart),
    selectionEnd: restoreClientId(selectionEnd),
    initialPosition
  };
}
function useBlockSync({
  clientId = null,
  value: controlledBlocks,
  selection: controlledSelection,
  onChange = noop,
  onInput = noop
}) {
  const registry = useRegistry();
  const {
    resetBlocks,
    resetSelection,
    replaceInnerBlocks,
    setHasControlledInnerBlocks,
    __unstableMarkNextChangeAsNotPersistent
  } = registry.dispatch(blockEditorStore);
  const { getBlockName, getBlocks, getSelectionStart, getSelectionEnd } = registry.select(blockEditorStore);
  const isControlled = useSelect(
    (select) => {
      return !clientId || select(blockEditorStore).areInnerBlocksControlled(clientId);
    },
    [clientId]
  );
  const pendingChangesRef = useRef({ incoming: null, outgoing: [] });
  const subscribedRef = useRef(false);
  const idMappingRef = useRef({
    externalToInternal: /* @__PURE__ */ new Map(),
    internalToExternal: /* @__PURE__ */ new Map()
  });
  const setControlledBlocks = () => {
    if (!controlledBlocks) {
      return;
    }
    __unstableMarkNextChangeAsNotPersistent();
    if (clientId) {
      registry.batch(() => {
        setHasControlledInnerBlocks(clientId, true);
        idMappingRef.current.externalToInternal.clear();
        idMappingRef.current.internalToExternal.clear();
        const storeBlocks = controlledBlocks.map(
          (block) => cloneBlockWithMapping(block, idMappingRef.current)
        );
        if (subscribedRef.current) {
          pendingChangesRef.current.incoming = storeBlocks;
        }
        __unstableMarkNextChangeAsNotPersistent();
        replaceInnerBlocks(clientId, storeBlocks);
      });
    } else {
      if (subscribedRef.current) {
        pendingChangesRef.current.incoming = controlledBlocks;
      }
      resetBlocks(controlledBlocks);
    }
  };
  const unsetControlledBlocks = () => {
    __unstableMarkNextChangeAsNotPersistent();
    if (clientId) {
      setHasControlledInnerBlocks(clientId, false);
      __unstableMarkNextChangeAsNotPersistent();
      replaceInnerBlocks(clientId, []);
    } else {
      resetBlocks([]);
    }
  };
  const onInputRef = useRef(onInput);
  const onChangeRef = useRef(onChange);
  useEffect(() => {
    onInputRef.current = onInput;
    onChangeRef.current = onChange;
  }, [onInput, onChange]);
  useEffect(() => {
    if (pendingChangesRef.current.outgoing.includes(controlledBlocks)) {
      if (pendingChangesRef.current.outgoing[pendingChangesRef.current.outgoing.length - 1] === controlledBlocks) {
        pendingChangesRef.current.outgoing = [];
      }
    } else if (getBlocks(clientId) !== controlledBlocks) {
      pendingChangesRef.current.outgoing = [];
      setControlledBlocks();
      if (controlledSelection) {
        resetSelection(
          controlledSelection.selectionStart,
          controlledSelection.selectionEnd,
          controlledSelection.initialPosition
        );
      }
    }
  }, [controlledBlocks, clientId]);
  const isMountedRef = useRef(false);
  useEffect(() => {
    if (!isMountedRef.current) {
      isMountedRef.current = true;
      return;
    }
    if (!isControlled) {
      pendingChangesRef.current.outgoing = [];
      setControlledBlocks();
    }
  }, [isControlled]);
  useEffect(() => {
    const {
      getSelectedBlocksInitialCaretPosition,
      isLastBlockChangePersistent,
      __unstableIsLastBlockChangeIgnored,
      areInnerBlocksControlled
    } = registry.select(blockEditorStore);
    let blocks = getBlocks(clientId);
    let isPersistent = isLastBlockChangePersistent();
    let previousAreBlocksDifferent = false;
    subscribedRef.current = true;
    const unsubscribe = registry.subscribe(() => {
      if (clientId !== null && getBlockName(clientId) === null) {
        return;
      }
      const isStillControlled = !clientId || areInnerBlocksControlled(clientId);
      if (!isStillControlled) {
        return;
      }
      const newIsPersistent = isLastBlockChangePersistent();
      const newBlocks = getBlocks(clientId);
      const areBlocksDifferent = newBlocks !== blocks;
      blocks = newBlocks;
      if (areBlocksDifferent && (pendingChangesRef.current.incoming || __unstableIsLastBlockChangeIgnored())) {
        pendingChangesRef.current.incoming = null;
        isPersistent = newIsPersistent;
        return;
      }
      const didPersistenceChange = previousAreBlocksDifferent && !areBlocksDifferent && newIsPersistent && !isPersistent;
      if (areBlocksDifferent || didPersistenceChange) {
        isPersistent = newIsPersistent;
        const blocksForParent = clientId ? restoreExternalIds(blocks, idMappingRef.current) : blocks;
        const selection = {
          selectionStart: getSelectionStart(),
          selectionEnd: getSelectionEnd(),
          initialPosition: getSelectedBlocksInitialCaretPosition()
        };
        const selectionForParent = clientId ? restoreSelectionIds(selection, idMappingRef.current) : selection;
        pendingChangesRef.current.outgoing.push(blocksForParent);
        const updateParent = isPersistent ? onChangeRef.current : onInputRef.current;
        updateParent(blocksForParent, {
          selection: selectionForParent
        });
      }
      previousAreBlocksDifferent = areBlocksDifferent;
    }, blockEditorStore);
    return () => {
      subscribedRef.current = false;
      unsubscribe();
    };
  }, [registry, clientId]);
  useEffect(() => {
    return () => {
      unsetControlledBlocks();
    };
  }, []);
}
export {
  useBlockSync as default
};
//# sourceMappingURL=use-block-sync.mjs.map
