import { BezierEdge, BezierEdgeModel, h } from '@logicflow/core'
import { createApp, h as vh } from 'vue'
import { isActive, connect, disconnect } from './teleport'
import CustomLine from './CustomLine.vue'
function isMouseInElement(element: any, e: any) {
  const rect = element.getBoundingClientRect()
  return (
    e.clientX >= rect.left &&
    e.clientX <= rect.right &&
    e.clientY >= rect.top &&
    e.clientY <= rect.bottom
  )
}
const DEFAULT_WIDTH = 32
const DEFAULT_HEIGHT = 32
class CustomEdge2 extends BezierEdge {
  isMounted
  customLineApp?: any
  root?: any
  constructor() {
    super()
    this.isMounted = false
    this.handleMouseUp = (e: any) => {
      this.props.graphModel.clearSelectElements()
      this.props.model.isSelected = true
      const element = e.target.parentNode.parentNode.querySelector('.lf-custom-edge-wrapper')
      if (isMouseInElement(element, e)) {
        this.props.model.graphModel.deleteEdgeById(this.props.model.id)
      }
    }
  }
  /**
   * 渲染vue组件
   * @param root
   */
  protected renderVueComponent(root: any) {
    this.unmountVueComponent()
    this.root = root
    const { graphModel } = this.props
    if (root) {
      if (isActive()) {
        connect(
          this.targetId(),
          CustomLine,
          root,
          this.props.model,
          graphModel,
          (node: any, graph: any) => {
            return { model: node, graph }
          },
        )
      } else {
        this.customLineApp = createApp({
          render: () => vh(CustomLine, { model: this.props.model }),
        })
        this.customLineApp?.mount(root)
      }
    }
  }
  protected targetId() {
    return `${this.props.graphModel.flowId}:${this.props.model.id}`
  }
  /**
   * 组件即将卸载勾子
   */
  componentWillUnmount() {
    if (super.componentWillUnmount) {
      super.componentWillUnmount()
    }
    if (isActive()) {
      disconnect(this.targetId())
    }
    this.unmountVueComponent()
  }
  /**
   * 卸载vue
   * @returns
   */
  protected unmountVueComponent() {
    if (this.customLineApp) {
      this.customLineApp.unmount()
      this.customLineApp = null
    }
    if (this.root) {
      this.root.innerHTML = ''
    }
    return this.root
  }

  getEdge() {
    const { model } = this.props
    const id = model.id
    const { customWidth = DEFAULT_WIDTH, customHeight = DEFAULT_HEIGHT } = model.getProperties()
    const { startPoint, endPoint, path, isAnimation, arrowConfig } = model
    const animationStyle = model.getEdgeAnimationStyle()
    const {
      strokeDasharray,
      stroke,
      strokeDashoffset,
      animationName,
      animationDuration,
      animationIterationCount,
      animationTimingFunction,
      animationDirection,
    } = animationStyle
    const positionData = {
      x: (startPoint.x + endPoint.x - customWidth) / 2,
      y: (startPoint.y + endPoint.y - customHeight) / 2,
      width: customWidth,
      height: customHeight,
    }
    const style = model.getEdgeStyle()
    const wrapperStyle = {
      width: customWidth,
      height: customHeight,
    }

    setTimeout(() => {
      const s = document.getElementById(id)
      if (s && !this.isMounted) {
        this.isMounted = true
        this.renderVueComponent(s)
      }
    }, 0)

    delete style.stroke

    return h('g', {}, [
      h(
        'style' as any,
        { type: 'text/css' },
        '.lf-edge{stroke:#afafaf}.lf-edge:hover{stroke: #3370FF;}',
      ),
      h('path', {
        d: path,
        ...style,
        ...arrowConfig,
        ...(isAnimation
          ? {
              strokeDasharray,
              stroke,
              style: {
                strokeDashoffset,
                animationName,
                animationDuration,
                animationIterationCount,
                animationTimingFunction,
                animationDirection,
              },
            }
          : {}),
      }),
      h(
        'foreignObject',
        {
          ...positionData,
          y: positionData.y + 5,
          x: positionData.x + 5,
          style: {},
        },
        [
          h('div', {
            id,
            style: { ...wrapperStyle },
            className: 'lf-custom-edge-wrapper',
          }),
        ],
      ),
    ])
  }
}

class CustomEdgeModel2 extends BezierEdgeModel {
  getArrowStyle() {
    const arrowStyle = super.getArrowStyle()
    arrowStyle.offset = 1
    arrowStyle.verticalLength = 0
    return arrowStyle
  }

  getEdgeStyle() {
    const style = super.getEdgeStyle()
    // svg属性
    style.strokeWidth = 2
    style.stroke = '#BBBFC4'
    style.offset = 0
    return style
  }
  /**
   * 重写此方法，使保存数据是能带上锚点数据。
   */
  getData() {
    const data: any = super.getData()
    if (data) {
      data.sourceAnchorId = this.sourceAnchorId
      data.targetAnchorId = this.targetAnchorId
    }
    return data
  }
  /**
   * 给边自定义方案，使其支持基于锚点的位置更新边的路径
   */
  updatePathByAnchor() {
    const sourceNodeModel = this.graphModel.getNodeModelById(this.sourceNodeId)
    const sourceAnchor = sourceNodeModel
      .getDefaultAnchor()
      .find((anchor: any) => anchor.id === this.sourceAnchorId)

    const targetNodeModel = this.graphModel.getNodeModelById(this.targetNodeId)
    const targetAnchor = targetNodeModel
      .getDefaultAnchor()
      .find((anchor: any) => anchor.id === this.targetAnchorId)
    if (sourceAnchor && targetAnchor) {
      const startPoint = {
        x: sourceAnchor.x,
        y: sourceAnchor.y,
      }
      this.updateStartPoint(startPoint)
      const endPoint = {
        x: targetAnchor.x,
        y: targetAnchor.y,
      }

      this.updateEndPoint(endPoint)
    }

    // 这里需要将原有的pointsList设置为空，才能触发bezier的自动计算control点。
    this.pointsList = []
    this.initPoints()
  }
  setAttributes(): void {
    super.setAttributes()
    this.isHitable = true
    this.zIndex = 0
  }
}

export default {
  type: 'app-edge',
  view: CustomEdge2,
  model: CustomEdgeModel2,
}
