import { Type } from '@ephox/katamari';

import type Editor from '../api/Editor';
import * as Events from '../api/Events';
import type { ParserArgs } from '../api/html/DomParser';
import Tools from '../api/util/Tools';

const preProcess = (editor: Editor, node: Element, args: ParserArgs): Node => {
  let oldDoc: Document | undefined;
  const dom = editor.dom;

  let clonedNode = node.cloneNode(true) as Element;

  // Nodes needs to be attached to something in WebKit/Opera
  // This fix will make DOM ranges and make Sizzle happy!
  const impl = document.implementation;
  if (impl.createHTMLDocument) {
    // Create an empty HTML document
    const doc = impl.createHTMLDocument('');

    // Add the element or it's children if it's a body element to the new document
    Tools.each(clonedNode.nodeName === 'BODY' ? clonedNode.childNodes : [ clonedNode ], (node) => {
      doc.body.appendChild(doc.importNode(node, true));
    });

    // Grab first child or body element for serialization
    if (clonedNode.nodeName !== 'BODY') {
      // We cast to a Element here, as this will be the cloned node imported and appended above
      clonedNode = doc.body.firstChild as Element;
    } else {
      clonedNode = doc.body;
    }

    // set the new document in DOMUtils so createElement etc works
    oldDoc = dom.doc;
    dom.doc = doc;
  }

  Events.firePreProcess(editor, { ...args, node: clonedNode });

  if (oldDoc) {
    dom.doc = oldDoc;
  }

  return clonedNode;
};

const shouldFireEvent = (editor: Editor | undefined, args: ParserArgs): editor is Editor => {
  return Type.isNonNullable(editor) && editor.hasEventListeners('PreProcess') && !args.no_events;
};

const process = (editor: Editor | undefined, node: Element, args: ParserArgs): Node => {
  return shouldFireEvent(editor, args) ? preProcess(editor, node, args) : node;
};

export {
  process
};
