import { AnnotationGeneratorStrategyAbstract } from './annotation-generator-strategy.abstract';
import { CustomAttributeAnnotationHandler } from './custom-attribute-annotation-handler';
import { CustomStyleAnnotationHandler } from './custom-style-annotation-handler';
import { CustomClassAnnotationHandler } from './custom-class-annotation-handler';
import { RTETextNode } from '../../../../../dto/nodes/text-node';
import { ALWAYS_INLINE_TAGS, DEDICATED_STRATEGY_TAGS } from '../strategy-tags';
import { generateUUID } from '../../../../../../../utils/misc';
/**
 * Handle inline html annotation
 */
export class InlineHtmlAnnotationHandler extends AnnotationGeneratorStrategyAbstract {
  constructor() {
    super(...arguments);
    this.customAttributeHandler = new CustomAttributeAnnotationHandler();
    this.customStyleAnnotationHandler = new CustomStyleAnnotationHandler();
    this.customClassAnnotationHandler = new CustomClassAnnotationHandler();
  }
  /**
   * @inheritDoc
   */
  canHandle(targetDto, options) {
    return targetDto.name === 'RTETextNode' && options.autoMatchFormat === false;
  }
  /**
   * @inheritDoc
   */
  handle(element, parentAnnotations) {
    // Skip block-level dedicated tags (P, H1-H6, LI, UL, OL, IMG).
    // Tags in ALWAYS_INLINE_TAGS (CODE, A) are exempt and still produce inlineHtml annotations.
    if (DEDICATED_STRATEGY_TAGS.has(element.nodeName) && !ALWAYS_INLINE_TAGS.has(element.nodeName)) {
      return {};
    }
    const inlineHtml = this.generateInlineHtmlAnnotation(element, Object.assign({}, parentAnnotations));
    if (inlineHtml) {
      return { inlineHtml: inlineHtml };
    }
    return {};
  }
  /**
   * Generate inline html annotation.
   *
   * @param {HTMLElement} child - Current element.
   * @param {VegaRTETextAnnotations} annotations - Current annotations.
   * @returns {Nullable<VegaInlineHtmlSchema>} - Inline html annotation.
   */
  generateInlineHtmlAnnotation(child, annotations) {
    let { inlineHtml } = annotations;
    const hasElementChildren = Array.from(child.childNodes).some((c) => c.nodeType === Node.ELEMENT_NODE);
    if (!RTETextNode.supportsHtmlTag(child.nodeName.toLowerCase()) || hasElementChildren) {
      const childInlineHtmlNode = Object.assign(Object.assign({ htmlTag: child.nodeName.toLowerCase() }, (hasElementChildren ? { groupId: generateUUID() } : {})), { customAttribute: this.customAttributeHandler.handle(child).customAttribute, customStyle: this.customStyleAnnotationHandler.handle(child).customStyle, customClass: this.customClassAnnotationHandler.handle(child).customClass });
      if (!inlineHtml) {
        inlineHtml = childInlineHtmlNode;
      }
      else {
        inlineHtml = this.appendToInlineHtmlChain(inlineHtml, childInlineHtmlNode, child.parentNode);
      }
    }
    return inlineHtml ? Object.assign({}, inlineHtml) : null;
  }
  /**
   * Immutably appends `toAppend` at the correct position in the chain — the
   * deepest node whose `child.htmlTag` still matches `parentNode.nodeName`.
   * Each level is reconstructed via spread so the original chain is never mutated.
   *
   * @param {VegaInlineHtmlSchema} node - Current chain node.
   * @param {VegaInlineHtmlSchema} toAppend - Node to append at the tail.
   * @param {ParentNode | null} parentNode - DOM parent of the element being processed.
   * @returns {VegaInlineHtmlSchema} A new chain with `toAppend` inserted.
   */
  appendToInlineHtmlChain(node, toAppend, parentNode) {
    if (node.child && parentNode && node.child.htmlTag === parentNode.nodeName.toLowerCase()) {
      return Object.assign(Object.assign({}, node), { child: this.appendToInlineHtmlChain(node.child, toAppend, parentNode) });
    }
    return Object.assign(Object.assign({}, node), { child: toAppend });
  }
}
