type Child = Node | object | string

export default function <K extends keyof HTMLElementTagNameMap>(
  tagName: K,
  ...children: Child[]
): HTMLElementTagNameMap[K] {
  const element = document.createElement(tagName)
  const properties = children[0]

  if (
    typeof properties === 'object' &&
    !Array.isArray(properties) &&
    !(properties instanceof Node)
  ) {
    children.shift()

    for (const [name, value] of Object.entries(properties)) {
      if (name === 'dataset') {
        Object.keys(value).forEach((dk) => (element.dataset[dk] = value[dk]))
      } else if (typeof value === 'undefined') {
        element.removeAttribute(name)
      } else if (name in element) {
        Object.assign(element, { [name]: value })
      } else {
        element.setAttribute(name, value)
      }
    }
  }

  appendChild(element, children)
  return element
}

function appendChild(element: Element, child: Child) {
  if (child) {
    if (Array.isArray(child) || child instanceof NodeList) {
      for (const c of child) {
        appendChild(element, c)
      }
    } else if (child instanceof Node) {
      element.appendChild(child)
    } else {
      element.appendChild(document.createTextNode(child.toString()))
    }
  }
}
