'use strict';

Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });

const constants = require('./constants.cjs');
const Event = require('./Event.cjs');
const ParentNode = require('./ParentNode.cjs');
const Element = require('./Element.cjs');
const SVGElement = require('./SVGElement.cjs');
const Text = require('./Text.cjs');
const Comment = require('./Comment.cjs');
const DocumentFragment = require('./DocumentFragment.cjs');
const HTMLTemplateElement = require('./HTMLTemplateElement.cjs');
const shared = require('./shared.cjs');
const HTMLBodyElement = require('./HTMLBodyElement.cjs');
const HTMLHeadElement = require('./HTMLHeadElement.cjs');
const HTMLHtmlElement = require('./HTMLHtmlElement.cjs');

class Document extends ParentNode.ParentNode {
  nodeType = (() => constants.NodeType.DOCUMENT_NODE)();
  [constants.NAME] = '#document';
  [constants.IS_CONNECTED] = true;
  constructor(defaultView) {
    super();
    this.defaultView = defaultView;
    this[constants.OWNER_DOCUMENT] = this;
    this.documentElement = setupElement(new HTMLHtmlElement.HTMLHtmlElement(), this, 'html');
    this.body = setupElement(new HTMLBodyElement.HTMLBodyElement(), this, 'body');
    this.head = setupElement(new HTMLHeadElement.HTMLHeadElement(), this, 'head');
    this.appendChild(this.documentElement);
    this.documentElement.appendChild(this.head);
    this.documentElement.appendChild(this.body);
  }
  createElement(localName) {
    return createElement(this, localName);
  }
  createElementNS(namespaceURI, localName) {
    return createElement(this, localName, namespaceURI);
  }
  createTextNode(data) {
    const text = createNode(new Text.Text(data), this);
    this[constants.HOOKS].createText?.(text, String(data));
    return text;
  }
  createComment(data) {
    return createNode(new Comment.Comment(data), this);
  }
  createDocumentFragment() {
    return createNode(new DocumentFragment.DocumentFragment(), this);
  }
  createEvent() {
    return new Event.Event('');
  }
  importNode(node, deep) {
    return shared.cloneNode(node, deep, this);
  }
  adoptNode(node) {
    if (node[constants.OWNER_DOCUMENT] === this) return node;
    node.parentNode?.removeChild(node);
    adoptNode(node, this);
    return node;
  }
}
function createNode(node, ownerDocument) {
  Object.defineProperty(node, constants.OWNER_DOCUMENT, {
    value: ownerDocument,
    writable: true,
    enumerable: false
  });
  return node;
}
function createElement(ownerDocument, name, namespace) {
  let element;
  const lowerName = String(name).toLowerCase();
  if (namespace === constants.NamespaceURI.SVG) {
    element = new SVGElement.SVGElement();
  } else if (lowerName === 'template') {
    element = new HTMLTemplateElement.HTMLTemplateElement();
  } else {
    const CustomElement = ownerDocument.defaultView.customElements.get(name);
    element = CustomElement ? new CustomElement() : new Element.Element();
  }
  return setupElement(element, ownerDocument, name, namespace);
}
function setupElement(element, ownerDocument, name, namespace) {
  createNode(element, ownerDocument);
  Object.defineProperty(element, constants.NAME, {
    value: name
  });
  if (namespace) {
    Object.defineProperty(element, constants.NS, {
      value: namespace
    });
  }
  ownerDocument[constants.HOOKS].createElement?.(element, namespace);
  return element;
}
function adoptNode(node, document) {
  node[constants.OWNER_DOCUMENT] = document;
  if (shared.isParentNode(node)) {
    for (const child of node.childNodes) {
      adoptNode(child, document);
    }
  }
}

exports.Document = Document;
exports.adoptNode = adoptNode;
exports.createElement = createElement;
exports.createNode = createNode;
exports.setupElement = setupElement;
