import { NodeType, NS, NamespaceURI, ATTRIBUTES } from './constants.mjs';
import { ParentNode } from './ParentNode.mjs';
import { NamedNodeMap } from './NamedNodeMap.mjs';
import { Attr } from './Attr.mjs';
import { serializeNode, serializeChildren, parseHtml } from './serialization.mjs';

class Element extends ParentNode {
  nodeType = (() => NodeType.ELEMENT_NODE)();
  [NS] = (() => NamespaceURI.XHTML)();
  get namespaceURI() {
    return this[NS];
  }
  get tagName() {
    return this.nodeName;
  }
  get slot() {
    return this.getAttribute('slot') ?? '';
  }
  set slot(slot) {
    const finalSlot = String(slot);
    if (this.getAttribute('slot') !== finalSlot) {
      this.attributes.setNamedItem(new Attr('slot', finalSlot));
    }
  }
  get attributes() {
    let attributes = this[ATTRIBUTES];
    if (!attributes) {
      attributes = new NamedNodeMap(this);
      this[ATTRIBUTES] = attributes;
    }
    return attributes;
  }
  getAttributeNames() {
    return [...this.attributes].map(attr => attr.name);
  }
  get firstElementChild() {
    return this.children[0] ?? null;
  }
  get lastElementChild() {
    return this.children[this.children.length - 1] ?? null;
  }
  get nextElementSibling() {
    let sib = this.nextSibling;
    while (sib && sib.nodeType !== 1) sib = sib.nextSibling;
    return sib;
  }
  get previousElementSibling() {
    let sib = this.previousSibling;
    while (sib && sib.nodeType !== 1) sib = sib.previousSibling;
    return sib;
  }
  setAttribute(name, value) {
    this.attributes.setNamedItem(new Attr(name, String(value)));
  }
  setAttributeNS(namespace, name, value) {
    this.attributes.setNamedItemNS(new Attr(name, String(value), namespace));
  }
  getAttribute(name) {
    const attr = this.attributes.getNamedItem(name);
    return attr && attr.value;
  }
  getAttributeNS(namespace, name) {
    const attr = this.attributes.getNamedItemNS(namespace, name);
    return attr && attr.value;
  }
  hasAttribute(name) {
    const attr = this.attributes.getNamedItem(name);
    return attr != null;
  }
  hasAttributeNS(namespace, name) {
    const attr = this.attributes.getNamedItemNS(namespace, name);
    return attr != null;
  }
  removeAttribute(name) {
    this.attributes.removeNamedItem(name);
  }
  removeAttributeNS(namespace, name) {
    this.attributes.removeNamedItemNS(namespace, name);
  }
  get outerHTML() {
    return serializeNode(this);
  }
  get innerHTML() {
    return serializeChildren(this);
  }
  set innerHTML(html) {
    if (html == null || html === '') {
      this.replaceChildren();
    } else {
      const fragment = parseHtml(String(html), this);
      this.replaceChildren(fragment);
    }
  }
}

export { Element };
