import type { Command, TextSelection } from '@blockeditor/block-std';
import { PathFinder } from '@blockeditor/block-std';
import { assertExists } from '@blockeditor/global/utils';
import type { Text } from '@blockeditor/store';

import { matchFlavours } from '../../../_common/utils/index.js';

export const deleteTextCommand: Command<
  'currentTextSelection' | 'host',
  never,
  {
    textSelection?: TextSelection;
  }
> = (ctx, next) => {
  const textSelection = ctx.textSelection ?? ctx.currentTextSelection;
  assertExists(
    textSelection,
    '`textSelection` is required, you need to pass it in args or use `getTextSelection` command before adding this command to the pipeline.'
  );

  const host = ctx.host;
  assertExists(
    host,
    '`host` is required, you need to use `withHost` command before adding this command to the pipeline.'
  );
  assertExists(host.rangeManager);

  const range = host.rangeManager.textSelectionToRange(textSelection);
  if (!range) return;
  const selectedElements = host.rangeManager.getSelectedBlockElementsByRange(
    range,
    {
      mode: 'flat',
    }
  );

  const { from, to } = textSelection;

  const fromElement = selectedElements.find(el =>
    PathFinder.equals(from.path, el.path)
  );
  assertExists(fromElement);

  let fromText: Text | undefined;
  if (matchFlavours(fromElement.model, ['affine:page'])) {
    fromText = fromElement.model.title;
  } else {
    fromText = fromElement.model.text;
  }
  assertExists(fromText);
  if (!to) {
    fromText.delete(from.index, from.length);
    ctx.std.selection.setGroup('note', [
      ctx.std.selection.create('text', {
        from: {
          path: from.path,
          index: from.index,
          length: 0,
        },
        to: null,
      }),
    ]);
    return next();
  }

  const toElement = selectedElements.find(el =>
    PathFinder.equals(to.path, el.path)
  );
  assertExists(toElement);

  const toText = toElement.model.text;
  assertExists(toText);

  fromText.delete(from.index, from.length);
  toText.delete(0, to.length);

  selectedElements
    .filter(
      el =>
        el.model.id !== fromElement.model.id &&
        el.model.id !== toElement.model.id
    )
    .forEach(el => {
      ctx.std.page.deleteBlock(el.model);
    });

  ctx.std.selection.setGroup('note', [
    ctx.std.selection.create('text', {
      from: {
        path: to.path,
        index: to.index,
        length: 0,
      },
      to: null,
    }),
  ]);

  next();
};

declare global {
  namespace BlockEditor {
    interface Commands {
      deleteText: typeof deleteTextCommand;
    }
  }
}
