/**
 * WordPress dependencies
 */
import { speak } from '@wordpress/a11y';
import { __, sprintf } from '@wordpress/i18n';
import { useDispatch, useSelect } from '@wordpress/data';
import { useCallback } from '@wordpress/element';
import { UP, DOWN, HOME, END, ESCAPE } from '@wordpress/keycodes';
import { store as blocksStore } from '@wordpress/blocks';

/**
 * Internal dependencies
 */
import { store as blockEditorStore } from '../../store';
import { getCommonDepthClientIds } from './utils';
export default function useBlockSelection() {
  const {
    clearSelectedBlock,
    multiSelect,
    selectBlock
  } = useDispatch(blockEditorStore);
  const {
    getBlockName,
    getBlockParents,
    getBlockSelectionStart,
    getSelectedBlockClientIds,
    hasMultiSelection,
    hasSelectedBlock
  } = useSelect(blockEditorStore);
  const {
    getBlockType
  } = useSelect(blocksStore);
  const updateBlockSelection = useCallback(async (event, clientId, destinationClientId, focusPosition) => {
    if (!event?.shiftKey && event?.keyCode !== ESCAPE) {
      selectBlock(clientId, focusPosition);
      return;
    }

    // To handle multiple block selection via the `SHIFT` key, prevent
    // the browser default behavior of opening the link in a new window.
    event.preventDefault();
    const isOnlyDeselection = event.type === 'keydown' && event.keyCode === ESCAPE;
    const isKeyPress = event.type === 'keydown' && (event.keyCode === UP || event.keyCode === DOWN || event.keyCode === HOME || event.keyCode === END);

    // Handle clicking on a block when no blocks are selected, and return early.
    if (!isKeyPress && !hasSelectedBlock() && !hasMultiSelection()) {
      selectBlock(clientId, null);
      return;
    }
    const selectedBlocks = getSelectedBlockClientIds();
    const clientIdWithParents = [...getBlockParents(clientId), clientId];
    if (isOnlyDeselection || isKeyPress && !selectedBlocks.some(blockId => clientIdWithParents.includes(blockId))) {
      // Ensure that shift-selecting blocks via the keyboard only
      // expands the current selection if focusing over already
      // selected blocks. Otherwise, clear the selection so that
      // a user can create a new selection entirely by keyboard.
      await clearSelectedBlock();
    }

    // Update selection, if not only clearing the selection.
    if (!isOnlyDeselection) {
      let startTarget = getBlockSelectionStart();
      let endTarget = clientId;

      // Handle keyboard behavior for selecting multiple blocks.
      if (isKeyPress) {
        if (!hasSelectedBlock() && !hasMultiSelection()) {
          // Set the starting point of the selection to the currently
          // focused block, if there are no blocks currently selected.
          // This ensures that as the selection is expanded or contracted,
          // the starting point of the selection is anchored to that block.
          startTarget = clientId;
        }
        if (destinationClientId) {
          // If the user presses UP or DOWN, we want to ensure that the block they're
          // moving to is the target for selection, and not the currently focused one.
          endTarget = destinationClientId;
        }
      }
      const startParents = getBlockParents(startTarget);
      const endParents = getBlockParents(endTarget);
      const {
        start,
        end
      } = getCommonDepthClientIds(startTarget, endTarget, startParents, endParents);
      await multiSelect(start, end, null);
    }

    // Announce deselected block, or number of deselected blocks if
    // the total number of blocks deselected is greater than one.
    const updatedSelectedBlocks = getSelectedBlockClientIds();

    // If the selection is greater than 1 and the Home or End keys
    // were used to generate the selection, then skip announcing the
    // deselected blocks.
    if ((event.keyCode === HOME || event.keyCode === END) && updatedSelectedBlocks.length > 1) {
      return;
    }
    const selectionDiff = selectedBlocks.filter(blockId => !updatedSelectedBlocks.includes(blockId));
    let label;
    if (selectionDiff.length === 1) {
      const title = getBlockType(getBlockName(selectionDiff[0]))?.title;
      if (title) {
        label = sprintf( /* translators: %s: block name */
        __('%s deselected.'), title);
      }
    } else if (selectionDiff.length > 1) {
      label = sprintf( /* translators: %s: number of deselected blocks */
      __('%s blocks deselected.'), selectionDiff.length);
    }
    if (label) {
      speak(label, 'assertive');
    }
  }, [clearSelectedBlock, getBlockName, getBlockType, getBlockParents, getBlockSelectionStart, getSelectedBlockClientIds, hasMultiSelection, hasSelectedBlock, multiSelect, selectBlock]);
  return {
    updateBlockSelection
  };
}
//# sourceMappingURL=use-block-selection.js.map