/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import { Fragment, useMemo } from '@wordpress/element';
import { __experimentalHStack as HStack, PanelBody, ToggleControl } from '@wordpress/components';
import { createBlock, store as blocksStore } from '@wordpress/blocks';
import { useDispatch, useSelect } from '@wordpress/data';

/**
 * Internal dependencies
 */
import { BlockIcon, InspectorControls } from '../components';
import { store as blockEditorStore } from '../store';
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
const EMPTY_OBJECT = {};
function BlockHooksControlPure({
  name,
  clientId,
  metadata: {
    ignoredHookedBlocks = []
  } = {}
}) {
  const blockTypes = useSelect(select => select(blocksStore).getBlockTypes(), []);

  // A hooked block added via a filter will not be exposed through a block
  // type's `blockHooks` property; however, if the containing layout has been
  // modified, it will be present in the anchor block's `ignoredHookedBlocks`
  // metadata.
  const hookedBlocksForCurrentBlock = useMemo(() => blockTypes?.filter(({
    name: blockName,
    blockHooks
  }) => blockHooks && name in blockHooks || ignoredHookedBlocks.includes(blockName)), [blockTypes, name, ignoredHookedBlocks]);
  const hookedBlockClientIds = useSelect(select => {
    const {
      getBlocks,
      getBlockRootClientId,
      getGlobalBlockCount
    } = select(blockEditorStore);
    const rootClientId = getBlockRootClientId(clientId);
    const _hookedBlockClientIds = hookedBlocksForCurrentBlock.reduce((clientIds, block) => {
      // If the block doesn't exist anywhere in the block tree,
      // we know that we have to set the toggle to disabled.
      if (getGlobalBlockCount(block.name) === 0) {
        return clientIds;
      }
      const relativePosition = block?.blockHooks?.[name];
      let candidates;
      switch (relativePosition) {
        case 'before':
        case 'after':
          // Any of the current block's siblings (with the right block type) qualifies
          // as a hooked block (inserted `before` or `after` the current one), as the block
          // might've been automatically inserted and then moved around a bit by the user.
          candidates = getBlocks(rootClientId);
          break;
        case 'first_child':
        case 'last_child':
          // Any of the current block's child blocks (with the right block type) qualifies
          // as a hooked first or last child block, as the block might've been automatically
          // inserted and then moved around a bit by the user.
          candidates = getBlocks(clientId);
          break;
        case undefined:
          // If we haven't found a blockHooks field with a relative position for the hooked
          // block, it means that it was added by a filter. In this case, we look for the block
          // both among the current block's siblings and its children.
          candidates = [...getBlocks(rootClientId), ...getBlocks(clientId)];
          break;
      }
      const hookedBlock = candidates?.find(candidate => candidate.name === block.name);

      // If the block exists in the designated location, we consider it hooked
      // and show the toggle as enabled.
      if (hookedBlock) {
        return {
          ...clientIds,
          [block.name]: hookedBlock.clientId
        };
      }

      // If no hooked block was found in any of its designated locations,
      // we set the toggle to disabled.
      return clientIds;
    }, {});
    if (Object.values(_hookedBlockClientIds).length > 0) {
      return _hookedBlockClientIds;
    }
    return EMPTY_OBJECT;
  }, [hookedBlocksForCurrentBlock, name, clientId]);
  const {
    getBlockIndex,
    getBlockCount,
    getBlockRootClientId
  } = useSelect(blockEditorStore);
  const {
    insertBlock,
    removeBlock
  } = useDispatch(blockEditorStore);
  if (!hookedBlocksForCurrentBlock.length) {
    return null;
  }

  // Group by block namespace (i.e. prefix before the slash).
  const groupedHookedBlocks = hookedBlocksForCurrentBlock.reduce((groups, block) => {
    const [namespace] = block.name.split('/');
    if (!groups[namespace]) {
      groups[namespace] = [];
    }
    groups[namespace].push(block);
    return groups;
  }, {});
  const insertBlockIntoDesignatedLocation = (block, relativePosition) => {
    const blockIndex = getBlockIndex(clientId);
    const innerBlocksLength = getBlockCount(clientId);
    const rootClientId = getBlockRootClientId(clientId);
    switch (relativePosition) {
      case 'before':
      case 'after':
        insertBlock(block, relativePosition === 'after' ? blockIndex + 1 : blockIndex, rootClientId,
        // Insert as a child of the current block's parent
        false);
        break;
      case 'first_child':
      case 'last_child':
        insertBlock(block,
        // TODO: It'd be great if insertBlock() would accept negative indices for insertion.
        relativePosition === 'first_child' ? 0 : innerBlocksLength, clientId,
        // Insert as a child of the current block.
        false);
        break;
      case undefined:
        // If we do not know the relative position, it is because the block was
        // added via a filter. In this case, we default to inserting it after the
        // current block.
        insertBlock(block, blockIndex + 1, rootClientId,
        // Insert as a child of the current block's parent
        false);
        break;
    }
  };
  return /*#__PURE__*/_jsx(InspectorControls, {
    children: /*#__PURE__*/_jsxs(PanelBody, {
      className: "block-editor-hooks__block-hooks",
      title: __('Plugins'),
      initialOpen: true,
      children: [/*#__PURE__*/_jsx("p", {
        className: "block-editor-hooks__block-hooks-helptext",
        children: __('Manage the inclusion of blocks added automatically by plugins.')
      }), Object.keys(groupedHookedBlocks).map(vendor => {
        return /*#__PURE__*/_jsxs(Fragment, {
          children: [/*#__PURE__*/_jsx("h3", {
            children: vendor
          }), groupedHookedBlocks[vendor].map(block => {
            const checked = (block.name in hookedBlockClientIds);
            return /*#__PURE__*/_jsx(ToggleControl, {
              checked: checked,
              label: /*#__PURE__*/_jsxs(HStack, {
                justify: "flex-start",
                children: [/*#__PURE__*/_jsx(BlockIcon, {
                  icon: block.icon
                }), /*#__PURE__*/_jsx("span", {
                  children: block.title
                })]
              }),
              onChange: () => {
                if (!checked) {
                  // Create and insert block.
                  const relativePosition = block.blockHooks[name];
                  insertBlockIntoDesignatedLocation(createBlock(block.name), relativePosition);
                  return;
                }

                // Remove block.
                removeBlock(hookedBlockClientIds[block.name], false);
              }
            }, block.title);
          })]
        }, vendor);
      })]
    })
  });
}
export default {
  edit: BlockHooksControlPure,
  attributeKeys: ['metadata'],
  hasSupport() {
    return true;
  }
};
//# sourceMappingURL=block-hooks.js.map