import type { BlockSelection, BlockStdScope } from '@blocksuite/block-std';

const getSelection = (std: BlockStdScope) => std.selection;

function getBlockSelectionBySide(std: BlockStdScope, tail: boolean) {
  const selection = getSelection(std);
  const selections = selection.filter('block');
  const sel = selections.at(tail ? -1 : 0) as BlockSelection | undefined;
  return sel ?? null;
}

function getTextSelection(std: BlockStdScope) {
  const selection = getSelection(std);
  return selection.find('text');
}

const pathToBlock = (std: BlockStdScope, blockId: string) =>
  std.view.getBlock(blockId);

interface MoveBlockConfig {
  name: string;
  hotkey: string[];
  action: (std: BlockStdScope) => void;
}

export const moveBlockConfigs: MoveBlockConfig[] = [
  {
    name: 'Move Up',
    hotkey: ['Mod-Alt-ArrowUp', 'Mod-Shift-ArrowUp'],
    action: std => {
      const doc = std.doc;
      const textSelection = getTextSelection(std);
      if (textSelection) {
        const currentModel = pathToBlock(
          std,
          textSelection.from.blockId
        )?.model;
        if (!currentModel) return;

        const previousSiblingModel = doc.getPrev(currentModel);
        if (!previousSiblingModel) return;

        const parentModel = std.doc.getParent(previousSiblingModel);
        if (!parentModel) return;

        std.doc.moveBlocks(
          [currentModel],
          parentModel,
          previousSiblingModel,
          true
        );
        std.host.updateComplete
          .then(() => {
            std.range.syncTextSelectionToRange(textSelection);
          })
          .catch(console.error);
        return true;
      }
      const blockSelection = getBlockSelectionBySide(std, true);
      if (blockSelection) {
        const currentModel = pathToBlock(std, blockSelection.blockId)?.model;
        if (!currentModel) return;

        const previousSiblingModel = doc.getPrev(currentModel);
        if (!previousSiblingModel) return;

        const parentModel = doc.getParent(previousSiblingModel);
        if (!parentModel) return;

        doc.moveBlocks(
          [currentModel],
          parentModel,
          previousSiblingModel,
          false
        );
        return true;
      }
      return;
    },
  },
  {
    name: 'Move Down',
    hotkey: ['Mod-Alt-ArrowDown', 'Mod-Shift-ArrowDown'],
    action: std => {
      const doc = std.doc;
      const textSelection = getTextSelection(std);
      if (textSelection) {
        const currentModel = pathToBlock(
          std,
          textSelection.from.blockId
        )?.model;
        if (!currentModel) return;

        const nextSiblingModel = doc.getNext(currentModel);
        if (!nextSiblingModel) return;

        const parentModel = doc.getParent(nextSiblingModel);
        if (!parentModel) return;

        doc.moveBlocks([currentModel], parentModel, nextSiblingModel, false);
        std.host.updateComplete
          .then(() => {
            std.range.syncTextSelectionToRange(textSelection);
          })
          .catch(console.error);
        return true;
      }
      const blockSelection = getBlockSelectionBySide(std, true);
      if (blockSelection) {
        const currentModel = pathToBlock(std, blockSelection.blockId)?.model;
        if (!currentModel) return;

        const nextSiblingModel = doc.getNext(currentModel);
        if (!nextSiblingModel) return;

        const parentModel = doc.getParent(nextSiblingModel);
        if (!parentModel) return;

        doc.moveBlocks([currentModel], parentModel, nextSiblingModel, false);
        return true;
      }
      return;
    },
  },
];
