import ChangeManager from '../../../../helpers/change-manager/change-manager';
import stateEntityRenderingRegistry from '../../slimmers/controllers/state-entity-rendering-registry';
import domNodeSubjectFactory from '../../../../helpers/change-manager/subject/dom-node-subject/dom-node-subject-factory';
import { VegaInternalRichTextEditorFlushChanges } from '../../../../helpers/event-manager/event-id/vega-internal-event-id';
import { ModifyContentAction } from '../actions/modify-content-action.abstract';
import { NodeAnnotation } from '../annotations/node-annotation.abstract';
import { isNonNullable } from '../../../../types/type-guard';
import { CustomAttributeAnnotation } from '../annotations/custom-attribute-annotation';
import { CustomClassAnnotation } from '../annotations/custom-class-annotation';
import { CommonAnnotationTypeEnum } from '../annotations/common-annotation.abstract';
import { CustomStyleAnnotation } from '../annotations/custom-style-annotation';
import { methodPlaceholder } from 'vega-slimmer/core';
import { RTEAnnotationStyle } from '../annotations/annotation-style';
import { RTEFilterStylesStrategyRegistry } from '../filter-styles-strategies/filter-styles-strategy-registry';
export var NodeTypeEnum;
(function (NodeTypeEnum) {
  NodeTypeEnum["TEXT"] = "TEXT";
  NodeTypeEnum["IMAGE"] = "IMAGE";
  NodeTypeEnum["CODE_BLOCK_NODE"] = "CODE_BLOCK_NODE";
})(NodeTypeEnum || (NodeTypeEnum = {}));
/**
 * Abstract class for all nodes
 */
export class RTENode {
  constructor(id, annotationMap) {
    this.annotationMap = new Map();
    /**
     * Minimize-safe type discriminator. Each concrete sub-class declares its own
     * `static readonly dtoName = 'ClassName'`.
     */
    this.dtoName = this.constructor.name;
    this.id = id;
    if (annotationMap) {
      this.annotationMap = annotationMap;
    }
  }
  /**
   * Creates a new instance of the RTENode from the given JSON data.
   *
   * @param {unknown} json - The JSON data to create the node from.
   * @param {RTEBlock} parentBlock - The parent block of the node.
   * @param {VegaRTETransformOptions} options - Options for transforming the node.
   */
  static from(json, parentBlock, options) {
    throw new Error(`[Vega] ${this.constructor.name}.from(${JSON.stringify(json)}, ${parentBlock}, ${options}) is not implemented. Please implement this method in the subclass.`);
  }
  /**
   * Creates different types of text annotations based on the provided type and value.
   *
   * @param {keyof VegaRTETextAnnotations} type - The key of the type.
   * @param {unknown} value - The value that will be used to create the specific type of annotation entity based on the `type` provided.
   * @returns {Nullable<[RTENodeAnnotationMapKey, RTENodeAnnotationMapValue]>} Returning a nullable tuple containing a key and a
   * value from the `RTENodeAnnotationMapKey` and `RTENodeAnnotationMapValue` types.
   */
  static createAnnotationEntity(type, value) {
    switch (type) {
      case 'customAttribute':
        return CustomAttributeAnnotation.from(value);
      case 'customClass':
        return CustomClassAnnotation.from(value);
      case 'customStyle':
        return CustomStyleAnnotation.from(value);
    }
  }
  /**
   * Get annotation by type
   *
   * @typedef T generic type
   * @param {RTENodeAnnotationMapKey} type - Annotation type
   * @returns {Nullable<T>} - Annotation
   */
  getAnnotationByType(type) {
    return this.annotationMap.get(type);
  }
  /**
   * Delete annotation by type
   *
   * @param {RTENodeAnnotationMapKey} type - Annotation type
   */
  deleteAnnotationByType(type) {
    this.annotationMap.delete(type);
    this.flushChange();
  }
  /**
   * Apply action to node
   *
   * @param {AnnotationAction} action - Annotation action
   * @returns {this} - Node
   */
  apply(action) {
    this.doApply(action);
    if (action.isFlushable) {
      this.flushChange();
    }
    return this;
  }
  /**
   * 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);
    return rteAnnotationStyle.toJSON(options, ['textDecoration']);
  }
  /**
   * Get classes from annotations
   *
   * @param {RTERenderContext} [options={ standalone: false }] - Options to control style mode
   * @returns {string} - Annotation style
   */
  getClasses(options = { standalone: false }) {
    return Array.from(this.annotationMap.values())
      .filter((annotation) => annotation instanceof NodeAnnotation || 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 {CustomAttributeAnnotation} - custom attribute annotation
   */
  getCustomAttributesAnnotation() {
    return this.annotationMap.get(CommonAnnotationTypeEnum.CUSTOM_ATTRIBUTE);
  }
  /**
   * Check if the node is a text node.
   *
   * @returns {boolean} - True if the node is a text node, false otherwise.
   */
  isTextNode() {
    return false;
  }
  /**
   * Get the HTMLElement reference associated with this node.
   *
   * @returns {Nullable<HTMLElement>} The HTMLElement reference or null if not found.
   */
  getElementRef() {
    return stateEntityRenderingRegistry.getDOMByEntity(this);
  }
  /**
   * Generate attributes as string
   *
   * @param {VegaRTETransformOptions} [options] - Options for converting the node to HTML.
   * @param {...string} attrs - Additional attributes to include
   * @returns {string} - Attributes as string
   */
  generateAttributeString(options, ...attrs) {
    var _a;
    const nodeAnnotationStyle = new RTEAnnotationStyle(this);
    const fullStyles = nodeAnnotationStyle.toJSON({ standalone: true }, [
      'textDecoration',
    ]);
    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="${nodeAnnotationStyle.toCssText({ standalone: true }, undefined, styles)}"`
        : null,
      ...attrs,
    ].filter(isNonNullable);
    return attributes.length ? ` ${attributes.join(' ')}` : '';
  }
  /**
   * apply annotation action or modify content action
   *
   * @param {AnnotationAction | ModifyContentAction} action annotation type action or modify content type action
   */
  doApply(action) {
    if (action instanceof ModifyContentAction) {
      this.doModifyActionApply(action);
    }
    else {
      this.doAnnotationActionApply(action);
    }
  }
  /**
   * Flush the value change
   */
  flushChange() {
    const domNode = stateEntityRenderingRegistry.getDOMByEntity(this);
    if (domNode) {
      const nodeSubject = domNodeSubjectFactory.getSubjectFromParentNodeByEventId(domNode, VegaInternalRichTextEditorFlushChanges);
      if (nodeSubject) {
        ChangeManager.notify(nodeSubject, {});
      }
    }
  }
  /**
   * Clones the annotations of the current RTETextNode.
   *
   * @returns {NodeAnnotations} A new Map containing cloned annotations.
   */
  cloneAnnotations() {
    return new Map(Array.from(this.annotationMap.entries()).map(([key, value]) => [key, value.clone()]));
  }
  /* istanbul ignore next */
  /**
   * Placeholder for the method to be implemented in sub classes.
   *
   * @param {AnnotationAction} action Annotation action
   */
  doAnnotationActionApply(action) {
    methodPlaceholder(action);
  }
  /* istanbul ignore next */
  /**
   * Placeholder for the method to be implemented in sub classes.
   *
   * @param {ModifyContentAction} action ModifyContentAction
   */
  doModifyActionApply(action) {
    methodPlaceholder(action);
  }
}
