import { AppendChildrenAction } from '../../actions/append-child-nodes-action';
import { InsertChildrenAfterAction } from '../../actions/insert-children-after-block';
import { InsertChildrenBeforeAction } from '../../actions/insert-children-before-block';
import { RemoveChildrenAction } from '../../actions/remove-child-action';
import { SplitBlockWithNodeAction } from '../../actions/split-block-with-node-action';
import { NodeAnnotationTypeEnum } from '../../annotations/node-annotation.abstract';
import { RTETextBlock } from '../../blocks/text-block';
import { ActionHandleStrategy } from '../action-handle-strategy.abstract';
import { generateUUID } from '../../../../../utils/misc';
/**
 * Insert new paragraph into current list item block at special position.
 */
export class ListItemInsertNewParagraphStrategy extends ActionHandleStrategy {
  /**
   * @inheritDoc
   */
  handleAction(action, target) {
    action.newBlock = this.breakSingleListItemBlock(action.startContainerNode, action.startOffset, target);
  }
  /**
   * Breaks a single list item block at a specific position.
   *
   * @param {RTETextNode} splitNode - The node that needs to be split within a list item block.
   * @param {number} startOffsetOfNode - The index within the `splitNode` where the break operation should occur.
   * @param {RTEListItemBlock} target - The list item block.
   * @returns {Nullable<RTEBlock>} Returns either a new `RTEListItemBlock` if the caret
   * position is at the end of the `splitNode`, or it returns the result of splitting the block at the
   * caret position if it is neither at the start nor at the end.
   */
  breakSingleListItemBlock(splitNode, startOffsetOfNode, target) {
    const newListItem = target.createNewListItem();
    const newBreakNode = this.copyInlineStyleToNewNode(splitNode);
    newListItem.apply(new AppendChildrenAction([newBreakNode]));
    if (this.isCaretPositionAtBlockEnd(target, splitNode, startOffsetOfNode)) {
      return this.breakListItemAtEnd(newListItem, target);
    }
    else if (this.isCaretPositionAtBlockStart(target, splitNode, startOffsetOfNode)) {
      target.parent.apply(new InsertChildrenBeforeAction(target, newListItem));
    }
    else {
      const splitBlockAction = new SplitBlockWithNodeAction(splitNode, startOffsetOfNode);
      target.apply(splitBlockAction);
      return splitBlockAction.newBlock;
    }
    return newListItem;
  }
  /**
   * Breaks a list item at the end and handles the insertion of a new list item or paragraph accordingly.
   *
   * @param {RTEListItemBlock} newListItem - Item that represents a new list item to be added to the existing list.
   * @param {RTEListItemBlock} target - The list item block.
   * @returns {RTEBlock} Returns a `RTEBlock`.
   */
  breakListItemAtEnd(newListItem, target) {
    const lastItem = target.parent.children[target.parent.children.length - 1];
    const currentItemIsEmpty = target.children.length === 1 && target.children[0].isEmpty();
    if (target === lastItem && currentItemIsEmpty) {
      const parentParent = target.parent.parent;
      if (parentParent['type'] !== 'list-item') {
        const newParagraph = RTETextBlock.from({
          id: generateUUID(),
          type: 'paragraph',
          nodes: [],
        });
        newParagraph.apply(new AppendChildrenAction(newListItem.children));
        parentParent.apply(new InsertChildrenAfterAction(target.parent, newParagraph));
        target.parent.apply(new RemoveChildrenAction(target));
        return newParagraph;
      }
      else {
        parentParent.parent.apply(new InsertChildrenAfterAction(parentParent, newListItem));
        target.parent.apply(new RemoveChildrenAction(target));
      }
    }
    else {
      target.parent.apply(new InsertChildrenAfterAction(target, newListItem));
    }
    return newListItem;
  }
  /**
   * Copy the inline style to new node
   *
   * @param {RTETextNode} needCopedNode - The node that needed to be copied to
   * @returns {RTETextNode} Returns a `RTETextNode`.
   */
  copyInlineStyleToNewNode(needCopedNode) {
    const textNode = needCopedNode.cloneWithText('');
    // The link annotation doesn't need to inherit
    textNode.annotationMap.delete(NodeAnnotationTypeEnum.LINK);
    return textNode;
  }
}
