import RTEDTOClassManager from '../../../../helpers/rte-manager/dto-class-manager';
import { LogUtility } from '../../../../utils/log';
import { generateUUID } from '../../../../utils/misc';
import stateEntityRenderingRegistry from '../../slimmers/controllers/state-entity-rendering-registry';
import { CommonAnnotationTypeEnum } from '../annotations/common-annotation.abstract';
import { CustomStyleAnnotation } from '../annotations/custom-style-annotation';
/**
 * The base action handle strategy class, use to modify the rich text node content or modify the annotation.
 */
export class ActionHandleStrategy {
  /**
   * Public method to invoke the handle logic.
   *
   * @typedef R generic type
   * @param {ModifyContentAction | AnnotationAction} action - The action instance. Contains the action type and necessary params.
   * @param {RTENode | RTEBlock | VegaRTEContent} target - The rich text node instance that will be modified.
   * @returns {R} - The invoke result in special strategy.
   */
  execute(action, target) {
    LogUtility.log(`Executing action ${action.constructor.name} on ${target.constructor.name} ${target['id']}`);
    return this.handleAction(action, target);
  }
  /**
   * Update the custom style annotation of a block or node
   *
   * @param {RTEBlock | RTETextNode} item - block or node
   * @param {string[]} styleKeys - custom style keys, for examples ['fontSize', 'fontWeight'].
   */
  updateCustomStyleAnnotation(item, styleKeys) {
    const customStyleAnnotation = item.annotationMap.get(CommonAnnotationTypeEnum.CUSTOM_STYLE);
    if (customStyleAnnotation && this.isCustomStyle(customStyleAnnotation, styleKeys)) {
      item.annotationMap.set(customStyleAnnotation.type, new CustomStyleAnnotation(customStyleAnnotation.removeStyles(styleKeys)));
    }
  }
  /**
   * Create a new paragraph block with the given text.
   *
   * @param {string} text - The text content for the new paragraph.
   * @returns {Nullable<RTETextBlock>} - The newly created paragraph block, or null if creation failed.
   */
  createNewParagraph(text) {
    const dtoClass = RTEDTOClassManager.getRTEBlockClass('paragraph');
    if (dtoClass) {
      const newParagraph = dtoClass.from({
        id: generateUUID(),
        type: 'paragraph',
        nodes: [{ id: generateUUID(), type: 'text', text }],
      });
      return newParagraph;
    }
  }
  /**
   * Get the root content from a child block or node.
   *
   * @param {RTEBlock | RTENode} child - The child block or node.
   * @returns {Nullable<VegaRTEContent>} The root content, if found.
   */
  getRootContent(child) {
    const parent = child.parent;
    if (parent) {
      if (parent.constructor.name === 'VegaRTEContent') {
        return parent;
      }
      else {
        return this.getRootContent(parent);
      }
    }
  }
  /**
   * Check the caret position at the start of a block.
   *
   * @param {RTEBlock} block - The RTE Block.
   * @param {RTENode} node - The RTE Node.
   * @param {number} offset - The offset position.
   * @returns {boolean} - Whether the caret is at the start of the block.
   */
  isCaretPositionAtBlockStart(block, node, offset) {
    return node === block.children[0] && offset === 0;
  }
  /**
   * Check the caret position at the end of a block.
   *
   * @param {RTEBlock} block - The RTE Block.
   * @param {RTENode} node - The RTE Node.
   * @param {number} offset - The offset position.
   * @returns {boolean} - Whether the caret is at the end of the block.
   */
  isCaretPositionAtBlockEnd(block, node, offset) {
    if (!node.isContentEditable()) {
      return this.isCaretPositionAtDecoratorBlockEnd(block, node, offset);
    }
    else {
      return this.isCaretPositionAtTextBlockEnd(block, node, offset);
    }
  }
  /**
   * Get the registered DOM element by the RTE entity.
   *
   * @param {RTEBlock | RTENode} instance - The RTE block or node instance.
   * @returns {Nullable<HTMLElement>} The registered DOM element.
   */
  getDOMByEntity(instance) {
    return stateEntityRenderingRegistry.getDOMByEntity(instance);
  }
  /**
   * Checks if the caret position is at the end of a specified RTETextNode.
   *
   * @param {RTETextBlock} textBlock - `RTETextBlock` - a text block.
   * @param {RTETextNode} node - `RTETextNode` - a node representing an image in a rich text editor.
   * @param {number} startOffset - The position within the text node where the caret is located.
   * @returns {boolean} a boolean value, which indicates whether the caret position is at the start of the RTETextNode.
   */
  isCaretPositionAtTextBlockEnd(textBlock, node, startOffset) {
    const lastNode = textBlock.children[textBlock.children.length - 1];
    return node === lastNode && (node.isEmpty() || startOffset === lastNode.text.length);
  }
  /**
   * Checks if the caret position is at the end of a given decorator node.
   *
   * @param {RTEBlock} target - The image block.
   * @param {RTENode} node - `node` is a RTEDecoratorNode or non-editable text node.
   * @param {number} startOffset - The position within the `RTEDecoratorNode` where the caret is located.
   * @returns {boolean}Returns a boolean value indicating whether the caret position is at the end of the `RTEDecoratorNode`.
   */
  isCaretPositionAtDecoratorBlockEnd(target, node, startOffset) {
    return node === target.children[target.children.length - 1] && startOffset !== 0;
  }
  /**
   * Determines if the custom style annotation includes any the custom style.
   *
   * @param {CustomStyleAnnotation} customStyleAnnotation - custom style annotation
   * @param {string[]} styleKeys - custom style keys, for examples ['fontSize', 'fontWeight'].
   * @returns {boolean} - True if the custom key includes the custom style, false otherwise.
   */
  isCustomStyle(customStyleAnnotation, styleKeys) {
    return styleKeys.some((item) => Object.keys(customStyleAnnotation.customStyle).includes(item));
  }
}
