import { isNonNullable } from '../../../../types/type-guard';
import { camelToDashCase } from '../../../../utils/string';
import { RTEBlock } from '../blocks/block.abstract';
import { BlockAnnotation } from './block-annotation.abstract';
import { CustomStyleAnnotation } from './custom-style-annotation';
import { NodeAnnotation } from './node-annotation.abstract';
/**
 * Class to handle the style representation of annotations for RTE blocks and nodes.
 */
export class RTEAnnotationStyle {
  constructor(dto) {
    this.dtoType = dto instanceof RTEBlock ? 'block' : 'node';
    this.annotationMap = dto.annotationMap;
  }
  /**
   * Convert an AnnotationStyle object to a CSS text string.
   *
   * @param {RTERenderContext} options - Rendering context options.
   * @param {string[]} canMergeKeyList - List of style keys that can be merged by concatenation.
   * @param {Nullable<AnnotationStyle>} annotationStyle - The annotation style object, if null, get from toJSON().
   * @returns {string} A string representing the CSS text.
   */
  toCssText(options = { standalone: false }, canMergeKeyList, annotationStyle) {
    annotationStyle = annotationStyle ? annotationStyle : this.toJSON(options, canMergeKeyList);
    return Object.entries(Object.assign({}, annotationStyle))
      .map(([key, value]) => `${camelToDashCase(key)}: ${value};`)
      .join('');
  }
  /**
   * Convert the annotation map to a JSON representation.
   *
   * @param {RTERenderContext} options - Rendering context options.
   * @param {string[]} canMergeKeyList - List of style keys that can be merged by concatenation.
   * @returns {AnnotationStyle} - The JSON representation of the annotation styles.
   */
  toJSON(options = { standalone: false }, canMergeKeyList) {
    const styles = Array.from(this.annotationMap.values())
      .filter((annotation) => this.filterAnnotation(annotation) || annotation instanceof CustomStyleAnnotation)
      .map((annotation) => annotation.renderStyle(options))
      .filter(isNonNullable);
    if (canMergeKeyList && canMergeKeyList.length > 0) {
      return this.mergeAnnotationStyles(styles, canMergeKeyList);
    }
    else {
      return styles.reduce((current, obj) => (Object.assign(Object.assign({}, current), obj)), {});
    }
  }
  /**
   * Filter annotations based on whether they are block or node annotations.
   *
   * @param {RTENodeAnnotationMapValue | RTEBlockAnnotationMapValue} annotation - The annotation to filter.
   * @returns {boolean} - True if the annotation matches the DTO type, false otherwise.
   */
  filterAnnotation(annotation) {
    return this.dtoType === 'block'
      ? annotation instanceof BlockAnnotation
      : annotation instanceof NodeAnnotation;
  }
  /**
   * Merge an array of annotation styles into a single style object.
   * e.g.,
   * { textDecoration: 'underline', textDecoration: 'line-through' }
   * -> { textDecoration: 'underline line-through' }
   *
   * @param {AnnotationStyle[]} styles - Array of annotation style objects to merge.
   * @param {string[]} canMergeKeyList - List of style keys that can be merged by concatenation.
   * @returns {AnnotationStyle} - The merged annotation style object.
   */
  mergeAnnotationStyles(styles, canMergeKeyList) {
    const merged = {};
    for (const style of styles) {
      for (const key in style) {
        const value = style[key];
        const existing = merged[key];
        if (typeof existing === 'string' &&
          typeof value === 'string' &&
          canMergeKeyList.includes(key)) {
          merged[key] = Array.from(new Set(`${existing} ${value}`.split(/\s+/).filter(Boolean))).join(' ');
        }
        else {
          merged[key] = value;
        }
      }
    }
    return merged;
  }
}
