// @ts-check
/**
 * Minimal DOM shim for Server-Side Rendering
 * Provides just enough SVG element emulation to create chart structure without full DOM
 */

/**
 * Mock SVG element for SSR environment
 */
class SSRElement {
  /**
   * @param {string} nodeName
   * @param {any} namespaceURI
   */
  constructor(nodeName, namespaceURI = null) {
    this.nodeName = nodeName
    this.namespaceURI = namespaceURI
    this.attributes = new Map()
    /** @type {any[]} */
    this.children = []
    this.textContent = ''
    this.style = {}
    this.classList = new SSRClassList()
    this.parentNode = /** @type {SSRElement | null} */ (null)
    /** @type {number|undefined} */ this._ssrWidth = undefined
    /** @type {number|undefined} */ this._ssrHeight = undefined
    /** @type {boolean|undefined} */ this._ssrMode = undefined
  }

  /**
   * @param {string} name
   * @param {any} value
   */
  setAttribute(name, value) {
    this.attributes.set(name, value)
  }

  /**
   * @param {string} name
   */
  getAttribute(name) {
    return this.attributes.get(name)
  }

  /**
   * @param {string} name
   */
  removeAttribute(name) {
    this.attributes.delete(name)
  }

  /**
   * @param {string} name
   */
  hasAttribute(name) {
    return this.attributes.has(name)
  }

  /**
   * @param {any} child
   */
  appendChild(child) {
    if (child && child !== this) {
      // Mirror real DOM: re-parenting removes child from previous parent
      if (child.parentNode && child.parentNode !== this) {
        child.parentNode.removeChild(child)
      } else if (child.parentNode === this) {
        // Already a child — move to end (matches browser DOM behaviour)
        const index = this.children.indexOf(child)
        if (index !== -1) this.children.splice(index, 1)
      }
      child.parentNode = this
      this.children.push(child)
    }
    return child
  }

  /**
   * @param {any} child
   */
  removeChild(child) {
    const index = this.children.indexOf(child)
    if (index !== -1) {
      this.children.splice(index, 1)
      child.parentNode = null
    }
    return child
  }

  /**
   * @param {any} newNode
   * @param {any} referenceNode
   */
  insertBefore(newNode, referenceNode) {
    if (!referenceNode) {
      return this.appendChild(newNode)
    }
    // Mirror real DOM: remove from previous parent before inserting
    if (newNode.parentNode && newNode.parentNode !== this) {
      newNode.parentNode.removeChild(newNode)
    } else if (newNode.parentNode === this) {
      const existingIndex = this.children.indexOf(newNode)
      if (existingIndex !== -1) this.children.splice(existingIndex, 1)
    }
    const index = this.children.indexOf(referenceNode)
    if (index !== -1) {
      newNode.parentNode = this
      this.children.splice(index, 0, newNode)
    }
    return newNode
  }

  cloneNode(deep = false) {
    const clone = new SSRElement(this.nodeName, this.namespaceURI)
    clone.textContent = this.textContent

    // Copy attributes
    this.attributes.forEach((value, key) => {
      clone.attributes.set(key, value)
    })

    // Copy styles
    Object.assign(clone.style, this.style)

    // Deep clone children
    if (deep) {
      this.children.forEach((child) => {
        if (child.cloneNode) {
          clone.appendChild(child.cloneNode(true))
        }
      })
    }

    return clone
  }

  getBoundingClientRect() {
    // Return default dimensions for SSR
    return {
      width: this._ssrWidth || 0,
      height: this._ssrHeight || 0,
      top: 0,
      left: 0,
      right: this._ssrWidth || 0,
      bottom: this._ssrHeight || 0,
      x: 0,
      y: 0,
    }
  }

  getRootNode() {
    /** @type {SSRElement} */
    let root = this
    while (root.parentNode) {
      root = root.parentNode
    }
    return root
  }

  querySelector() {
    return null
  }

  querySelectorAll() {
    return []
  }

  getElementsByClassName() {
    return []
  }

  addEventListener() {}
  removeEventListener() {}

  get childNodes() {
    return this.children
  }

  toString() {
    let attrs = ''
    this.attributes.forEach((value, key) => {
      attrs += ` ${key}="${value}"`
    })

    if (this.children.length === 0 && !this.textContent) {
      return `<${this.nodeName}${attrs}/>`
    }

    const childrenStr = this.children.map((c) => c.toString()).join('')
    return `<${this.nodeName}${attrs}>${this.textContent}${childrenStr}</${this.nodeName}>`
  }

  // Property getters/setters
  get innerHTML() {
    return this.children.map((c) => c.toString()).join('')
  }

  set innerHTML(value) {
    this.children = []
    this.textContent = value
  }

  get outerHTML() {
    return this.toString()
  }

  get isConnected() {
    return true
  }
}

/**
 * Mock ClassList for SSR
 */
class SSRClassList {
  constructor() {
    this.classes = new Set()
  }

  add(/** @type {any[]} */ ...classNames) {
    classNames.forEach((name) => this.classes.add(name))
  }

  remove(/** @type {any[]} */ ...classNames) {
    classNames.forEach((name) => this.classes.delete(name))
  }

  /**
   * @param {string} className
   */
  contains(className) {
    return this.classes.has(className)
  }

  /**
   * @param {string} className
   * @param {any} force
   */
  toggle(className, force) {
    if (force === true) {
      this.classes.add(className)
      return true
    } else if (force === false) {
      this.classes.delete(className)
      return false
    } else {
      if (this.classes.has(className)) {
        this.classes.delete(className)
        return false
      } else {
        this.classes.add(className)
        return true
      }
    }
  }

  toString() {
    return Array.from(this.classes).join(' ')
  }
}

/**
 * Main DOM shim class
 */
export class SSRDOMShim {
  constructor() {
    this.SVGNS = 'http://www.w3.org/2000/svg'
    this.XLINKNS = 'http://www.w3.org/1999/xlink'
  }

  /**
   * Create SVG element with namespace
   * @param {string} namespaceURI - Namespace URI
   * @param {string} qualifiedName - Element tag name
   * @returns {SSRElement} Mock SVG element
   */
  createElementNS(namespaceURI, qualifiedName) {
    return new SSRElement(qualifiedName, namespaceURI)
  }

  /**
   * Create text node
   * @param {string} data - Text content
   * @returns {object} Text node mock
   */
  createTextNode(data) {
    const node = {
      nodeName: '#text',
      nodeType: 3,
      textContent: data,
      toString() {
        return node.textContent
      },
    }
    return node
  }

  /**
   * Query selector (returns null in SSR)
   * @returns {null}
   */
  querySelector() {
    return null
  }

  /**
   * Query selector all (returns empty array in SSR)
   * @returns {any[]}
   */
  querySelectorAll() {
    return []
  }

  /**
   * Get computed style (returns empty object in SSR)
   * @returns {object}
   */
  getComputedStyle() {
    return {}
  }

  /**
   * Get bounding client rect for element
   * @param {SSRElement} element - Element to measure
   * @returns {object} Mock dimensions
   */
  getBoundingClientRect(element) {
    if (element && element.getBoundingClientRect) {
      return element.getBoundingClientRect()
    }
    return {
      width: 0,
      height: 0,
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
      x: 0,
      y: 0,
    }
  }

  /**
   * Create mock XMLSerializer for SSR
   * @returns {object} XMLSerializer mock
   */
  createXMLSerializer() {
    return {
      /**
       * @param {Element} element
       */
      serializeToString(element) {
        return element.toString ? element.toString() : ''
      },
    }
  }

  /**
   * Create mock DOMParser for SSR
   * @returns {object} DOMParser mock
   */
  createDOMParser() {
    return {
      /**
       * @param {string} str
       * @param {string} _type
       */
      parseFromString(str, _type) {
        // Basic mock - returns a simple element
        const root = new SSRElement('root')
        root.innerHTML = str
        return {
          documentElement: root,
        }
      },
    }
  }
}

export { SSRElement, SSRClassList }
