import { BlockAnnotation, BlockAnnotationTypeEnum } from '../annotations/block-annotation.abstract';
import ChangeManager from '../../../../helpers/change-manager/change-manager';
import domNodeSubjectFactory from '../../../../helpers/change-manager/subject/dom-node-subject/dom-node-subject-factory';
import stateEntityRenderingRegistry from '../../slimmers/controllers/state-entity-rendering-registry';
import { VegaInternalRichTextEditorFlushChanges } from '../../../../helpers/event-manager/event-id/vega-internal-event-id';
import { ModifyContentAction } from '../actions/modify-content-action.abstract';
import { LinkGroupAnnotation } from '../annotations/link-group-annotation';
import { HorizontalAlignmentAnnotation } from '../annotations/horizontal-alignment-annotation';
import { IndentAnnotation } from '../annotations/indent-annotation';
import { ActionHandleStrategyRegistry } from '../action-handle-strategies/action-handle-strategy-registry';
import { isNonNullable } from '../../../../types/type-guard';
import { CustomAttributeAnnotation } from '../annotations/custom-attribute-annotation';
import { CustomClassAnnotation } from '../annotations/custom-class-annotation';
import { CommonAnnotation, CommonAnnotationTypeEnum } from '../annotations/common-annotation.abstract';
import { CustomStyleAnnotation } from '../annotations/custom-style-annotation';
import { InternalWrapperAnnotation } from '../annotations/internal-wrapper-annotation';
import RTEDTOClassManager from '../../../../helpers/rte-manager/dto-class-manager';
import { RTEAnnotationStyle } from '../annotations/annotation-style';
import { RTEFilterStylesStrategyRegistry } from '../filter-styles-strategies/filter-styles-strategy-registry';
/**
 * Abstract class for block
 */
export class RTEBlock {
  constructor(id) {
    this.annotationMap = new Map();
    this.id = id;
  }
  /**
   * Creates a new instance of the block from the given JSON object.
   *
   * @param {unknown} json - The JSON object to create the block from.
   * @param {VegaRTETransformOptions} options - Options for transforming the block.
   */
  static from(json, options) {
    throw new Error(`[Vega] ${this.constructor.name}.from(${JSON.stringify(json)}, ${options}) is not implemented. Please implement this method in the subclass.`);
  }
  /**
   * Generates block children from an array of VegaRTEContentBlock.
   *
   * @param {VegaRTEContentBlock[]} blockArray - The array of VegaRTEContentBlock to generate children from.
   * @param {VegaRTETransformOptions} [options] - Optional transformation options.
   * @param {VegaRTEContent | RTEBlock} [parentBlock] - Optional parent block.
   * @returns {RTEBlock[]} An array of generated RTEBlock children.
   */
  static generateBlockChildren(blockArray, options, parentBlock) {
    return blockArray
      .map((child) => {
      const BlockClass = RTEDTOClassManager.getRTEBlockClass(child.type);
      if (BlockClass) {
        const block = BlockClass.from(child, options);
        block.parent = parentBlock;
        return block;
      }
    })
      .filter(isNonNullable);
  }
  /**
   * The function `createAnnotationEntity` creates a block annotation entity based on the provided type
   * and value.
   *
   * @param {keyof VegaRTEBlockAnnotations} type - The `type` parameter is a key of the `VegaRTEBlockAnnotations` enum, which specifies
   * the type of annotation entity to create.
   * @param {unknown} value - The `value` parameter in the `createAnnotationEntity` function is the value
   * that will be used to create the annotation entity. It can be of any type depending on the specific
   * annotation being created.
   * @returns {Nullable<BlockAnnotationsEntity>} The `createAnnotationEntity` function returns a nullable tuple containing a
   * `BlockAnnotationTypeEnum` and a `BlockAnnotation` object.
   */
  static createAnnotationEntity(type, value) {
    switch (type) {
      case 'textAlign':
        return HorizontalAlignmentAnnotation.from(value);
      case 'indent':
        return IndentAnnotation.from(value);
      case 'customAttribute':
        return CustomAttributeAnnotation.from(value);
      case 'customClass':
        return CustomClassAnnotation.from(value);
      case 'customStyle':
        return CustomStyleAnnotation.from(value);
      case 'internalWrapper':
        return InternalWrapperAnnotation.from(value);
    }
  }
  /**
   * Converts annotations to an array of block annotation entities.
   *
   * @param {BlockAnnotations} annotationMap - The map to store the converted annotations.
   * @param {VegaRTEBlockAnnotations} annotations - The annotations to convert.
   */
  static convertAnnotationsToMap(annotationMap, annotations) {
    Object.entries(annotations).forEach(([type, value]) => {
      const item = this.createAnnotationEntity(type, value);
      if (isNonNullable(item)) {
        annotationMap.set(...item);
      }
    });
  }
  /**
   * Check if the block should be rendered as a wrapper.
   *
   * @returns {boolean} - Returns true if the block is an internal wrapper.
   */
  shouldRenderAsInternalWrapper() {
    const internalWrapperAnnotation = this.getAnnotationByType(BlockAnnotationTypeEnum.INTERNAL_WRAPPER);
    if (internalWrapperAnnotation) {
      return (internalWrapperAnnotation.internalWrapper &&
        Array.from(this.annotationMap.values()).filter((annotation) => !(annotation instanceof LinkGroupAnnotation) &&
          !(annotation instanceof InternalWrapperAnnotation)).length === 0);
    }
    else {
      return false;
    }
  }
  /**
   * Class to JSON object
   *
   * @returns {VegaRTEBlockBase} - JSON object
   */
  toJSON() {
    const annotations = Array.from(this.annotationMap.values())
      .filter((annotation) => annotation instanceof BlockAnnotation || annotation instanceof CommonAnnotation)
      .reduce((record, annotation) => {
      return Object.assign(Object.assign({}, record), annotation.toJSON());
    }, {});
    return Object.assign({ id: this.id }, (Object.keys(annotations).length > 0 ? { annotations } : {}));
  }
  /**
   * Get annotation by type
   *
   * @typedef T generic type
   * @param {string} type - Annotation type
   * @returns {Nullable<T>} - Annotation
   */
  getAnnotationByType(type) {
    return this.annotationMap.get(type);
  }
  /**
   * Apply action to block
   *
   * @param {AnnotationAction} action - Annotation action
   */
  apply(action) {
    this.doApply(action);
    if (action.isFlushable) {
      const domNode = stateEntityRenderingRegistry.getDOMByEntity(this);
      if (domNode) {
        const nodeSubject = domNodeSubjectFactory.getSubjectFromParentNodeByEventId(domNode, VegaInternalRichTextEditorFlushChanges);
        if (nodeSubject) {
          ChangeManager.notify(nodeSubject, {});
        }
      }
    }
  }
  /**
   * Get the first node of the block
   *
   * @returns {Nullable<RTENode>} The first RTENode or null if no children exist.
   */
  getFirstNode() {
    if (!this.children || !this.children[0])
      return null;
    const firstItem = this.children[0];
    if (firstItem instanceof RTEBlock) {
      return firstItem.getFirstNode();
    }
    return firstItem;
  }
  /**
   * Check if the block is a list item block
   *
   * @returns {boolean} True if the block is a list item block, false otherwise.
   */
  isListItemBlock() {
    return this.type === 'list-item';
  }
  /**
   * Get styles from annotations
   *
   * @param {RTERenderContext} [options={ standalone: false }] - Options to control style mode
   * @returns {AnnotationStyle} - Annotation styles
   */
  getStyles(options = { standalone: false }) {
    const rteAnnotationStyle = new RTEAnnotationStyle(this);
    const blockAnnotationStyle = rteAnnotationStyle.toJSON(options);
    return Object.assign(Object.assign({}, blockAnnotationStyle), (this.shouldRenderAsInternalWrapper() ? { display: 'contents' } : {}));
  }
  /**
   * Get classes from annotations
   *
   * @param {RTERenderContext} [options={ standalone: false }] - Options to control standalone mode
   * @returns {string} - Annotation style
   */
  getClasses(options = { standalone: false }) {
    return Array.from(this.annotationMap.values())
      .filter((annotation) => annotation instanceof BlockAnnotation || annotation instanceof CustomClassAnnotation)
      .map((annotation) => annotation.renderClass(options))
      .filter(isNonNullable)
      .reduce((pre, cur) => [pre, cur].join(' ').trim(), '');
  }
  /**
   * Get custom attribute annotation, excluding "class" and "style"
   *
   * @returns {Nullable<CustomAttributeAnnotation>} - custom attribute annotation
   */
  getCustomAttributesAnnotation() {
    return this.annotationMap.get(CommonAnnotationTypeEnum.CUSTOM_ATTRIBUTE);
  }
  /**
   * Generate attributes as string
   *
   * @param {VegaRTETransformOptions} options - Options to control the generation of the attribute string, such as whether to filter inline styles.
   * @param {...string} attrs - Additional attributes to include
   * @returns {string} - Attributes as string
   */
  generateAttributeString(options, ...attrs) {
    var _a;
    const blockAnnotationStyle = new RTEAnnotationStyle(this);
    const fullStyles = blockAnnotationStyle.toJSON({ standalone: true });
    const customClassAnnotation = this.annotationMap.get(CommonAnnotationTypeEnum.CUSTOM_CLASS);
    let styles = fullStyles;
    let classNames = customClassAnnotation === null || customClassAnnotation === void 0 ? void 0 : customClassAnnotation.renderClass();
    if (options && options.filterInlineStyles) {
      const filterResult = RTEFilterStylesStrategyRegistry.executeTheStrategyFilterAction(this, fullStyles, classNames);
      styles = filterResult.styles;
      classNames = filterResult.classNames;
    }
    const customAttributeString = ((_a = this.getCustomAttributesAnnotation()) === null || _a === void 0 ? void 0 : _a.toString(attrs)) || [];
    const attributes = [
      ...customAttributeString,
      classNames && classNames.length > 0 ? `class="${classNames.join(' ')}"` : null,
      styles && Object.keys(styles).length > 0
        ? `style="${blockAnnotationStyle.toCssText({ standalone: true }, undefined, styles)}"`
        : null,
      ...attrs,
    ].filter(isNonNullable);
    return attributes.length ? ` ${attributes.join(' ')}` : '';
  }
  /**
   * Apply action to block
   *
   * @param {AnnotationAction|ModifyContentAction} action - Annotation action
   */
  doApply(action) {
    if (action instanceof ModifyContentAction) {
      ActionHandleStrategyRegistry.executeTheStrategy(action, this);
    }
    else {
      this.applyAnnotationAction(action);
    }
  }
  /**
   * Apply annotation action to block
   *
   * @param {AnnotationAction} action - Annotation action
   */
  applyAnnotationAction(action) {
    const annotation = action.toAnnotation();
    if (!(annotation instanceof LinkGroupAnnotation)) {
      this.annotationMap.set(annotation.type, annotation);
    }
    if (typeof this.doAnnotationActionApply === 'function') {
      this.doAnnotationActionApply(action);
    }
    else {
      const strategy = ActionHandleStrategyRegistry.get(action.constructor.name, this.constructor.name);
      if (strategy) {
        strategy.execute(action, this);
      }
    }
  }
  /**
   * clone annotations of the block
   *
   * @returns {BlockAnnotations} - Cloned annotations
   */
  cloneAnnotations() {
    return new Map(Array.from(this.annotationMap.entries()).map(([key, value]) => [key, value.clone()]));
  }
}
