import { BaseHandler } from './base-handler';
import { AppendChildrenAction } from '../../../../dto/actions/append-child-nodes-action';
import ElementToDtoStrategyProcessor from '../element-to-dto-strategy/element-to-dto-strategy-processor';
import { StylePreservator } from '../../../../../../helpers/csp/styles-preservator';
import { PasteContentAction } from '../../../../dto/actions/paste-content-action';
/**
 * Handle user paste
 */
export class InsertPasteHandler extends BaseHandler {
  /**
   * Update the state value when user paste html elements.
   *
   * @param {InputEvent} event - Paste event.
   */
  handleEvent(event) {
    const blocks = this.parsePastedElements(event);
    const { startContainer } = this.selectionController.getCurrentRange();
    if (this.isRootContainer(startContainer)) {
      this.insertBlocksToRoot(blocks);
      // TODO: Need to check below logic, it seems not correct https://gethired.atlassian.net/browse/VD-7779
    }
    else if (this.isEmptyParagraph(startContainer)) {
      this.insertBlocksBeforeEmptyBlock(blocks);
    }
    else {
      this.insertBlocksToContent(blocks);
    }
  }
  /**
   * Insert the blocks to root
   *
   * @param { RTEContentBlock[]} blocks - blocks
   */
  insertBlocksToRoot(blocks) {
    this.currentState.apply(new AppendChildrenAction(blocks));
  }
  /**
   * Insert the blocks to content
   *
   * @param { RTEContentBlock[]} blocks - blocks
   */
  insertBlocksToContent(blocks) {
    const cursorNode = this.removeSelectionRange();
    if (cursorNode && cursorNode.parent) {
      const { startOffset } = this.selectionController.getCurrentRange();
      const pasteAction = new PasteContentAction(blocks, cursorNode, startOffset);
      cursorNode.parent.apply(pasteAction);
      if (pasteAction.futureFocusBlock) {
        this.switchCaretPositionToNextElement(pasteAction.futureFocusBlock);
      }
    }
  }
  /**
   * Insert the blocks before to current block
   *
   * @param { RTEContentBlock[]} blocks - blocks
   */
  insertBlocksBeforeEmptyBlock(blocks) {
    const cursorNode = this.removeSelectionRange();
    /* istanbul ignore next */
    const currentBlock = cursorNode === null || cursorNode === void 0 ? void 0 : cursorNode.parent;
    if (currentBlock) {
      const pasteAction = new PasteContentAction(blocks, currentBlock.children[0], 0, this.selectionController.getCurrentRange());
      currentBlock.apply(pasteAction);
      if (pasteAction.futureFocusBlock) {
        this.switchCaretPositionToNextElement(pasteAction.futureFocusBlock);
      }
    }
  }
  /**
   * Format and parse elements from user paste with simple style preservation.
   *
   * @param {InputEvent} event - Paste event.
   * @returns {RTEContentBlock[]} - RTEContentBlock.
   */
  parsePastedElements(event) {
    let pastedHTML = event.dataTransfer.getData('text/html');
    if (!pastedHTML) {
      const pastedText = event.dataTransfer.getData('text/plain');
      pastedHTML = this.parsePlainText(pastedText);
    }
    const context = StylePreservator.preserve(pastedHTML);
    const safeHtml = context.getProcessedHtml();
    const parser = new DOMParser();
    const parsedDoc = parser.parseFromString(safeHtml, 'text/html');
    const htmlElements = Array.from(parsedDoc.body.childNodes);
    context.restore(htmlElements);
    return ElementToDtoStrategyProcessor.process(htmlElements)
      .filter(Boolean)
      .map((output) => output.toDto())
      .filter(Boolean);
  }
  /**
   * Moves the caret to the next block element in a rich text editor.
   *
   * @param {RTEBlock} newBlock - Represents a new create block element in a rich text editor.
   */
  switchCaretPositionToNextElement(newBlock) {
    if (newBlock && newBlock.children) {
      this.selectionController.enqueueSelectionRangeFutureState(newBlock.children[0], 0);
    }
  }
  /**
   * Parses the content of a plain text and formats it into HTML.
   *
   * @param {string} content - The content of plain text.
   * @returns {string} - The formatted HTML string.
   */
  parsePlainText(content) {
    const lines = content.split('\n');
    return lines
      .map((line) => {
      if (line === '') {
        return '<p>\n</p>';
      }
      return `<p>${line}</p>`;
    })
      .join('');
  }
}
