import { NodeView, Dom, Node } from '@antv/x6'
import { isVue2, isVue3, createApp, h, Vue2 } from 'vue-demi'
import { shapeMaps } from './registry'
import { isActive, connect, disconnect } from './teleport'

export class VueShapeView extends NodeView<Node> {
  static action = 'vue' as any
  private vm: any

  getComponentContainer() {
    return this.selectors && (this.selectors.foContent as HTMLDivElement)
  }

  confirmUpdate(flag: number) {
    const ret = super.confirmUpdate(flag)
    return this.handleAction(ret, VueShapeView.action, () => {
      this.renderVueComponent()
    })
  }

  protected targetId() {
    return `${this.graph.view.cid}:${this.cell.id}`
  }

  protected renderVueComponent() {
    this.unmountVueComponent()
    const root = this.getComponentContainer()
    const node = this.cell
    const graph = this.graph

    if (root) {
      const { component } = shapeMaps[node.shape]
      if (component) {
        if (isVue2) {
          const Vue = Vue2 as any
          this.vm = new Vue({
            el: root,
            render(h: any) {
              return h(component, { node, graph })
            },
            provide() {
              return {
                getNode: () => node,
                getGraph: () => graph,
              }
            },
          })
        } else if (isVue3) {
          if (isActive()) {
            connect(this.targetId(), component, root, node, graph)
          } else {
            this.vm = createApp({
              render() {
                return h(component, { node, graph })
              },
              provide() {
                return {
                  getNode: () => node,
                  getGraph: () => graph,
                }
              },
            })
            this.vm.mount(root)
          }
        }
      }
    }
  }

  protected unmountVueComponent() {
    const root = this.getComponentContainer()
    if (this.vm) {
      isVue2 && this.vm.$destroy()
      isVue3 && this.vm.unmount()
      this.vm = null
    }
    if (root) {
      root.innerHTML = ''
    }
    return root
  }

  onMouseDown(e: Dom.MouseDownEvent, x: number, y: number) {
    const target = e.target as Element
    const tagName = target.tagName.toLowerCase()
    if (tagName === 'input') {
      const type = target.getAttribute('type')
      if (
        type == null ||
        [
          'text',
          'password',
          'number',
          'email',
          'search',
          'tel',
          'url',
        ].includes(type)
      ) {
        return
      }
    }

    super.onMouseDown(e, x, y)
  }

  unmount() {
    if (isActive()) {
      disconnect(this.targetId())
    }
    this.unmountVueComponent()
    super.unmount()
    return this
  }
}

VueShapeView.config({
  bootstrap: [VueShapeView.action],
  actions: {
    component: VueShapeView.action,
  },
})

NodeView.registry.register('vue-shape-view', VueShapeView, true)
