import type {
  AnchorSpec,
  ConnectorSpec,
  ConnectParams,
  EndpointOptions,
  JsPlumbInstance,
} from '@jsplumb/browser-ui'
import { BezierConnector, DotEndpoint, newInstance } from '@jsplumb/browser-ui'

export interface JsplumbConfig {
  connector?: ConnectorSpec
  type?: 'input' | 'output'
  stops?: [[number, string], [number, string]]
  // 连接线条是否变透明一些
  transparent?: boolean
}

export interface ConnectArg {
  sourceId: string
  targetId: string
  anchor: AnchorSpec
  config?: JsplumbConfig
}

const defaultConfig: JsplumbConfig = {
  connector: {
    type: BezierConnector.type,
    options: {
      curviness: 70,
      stub: 10,
    },
  },
  type: 'input',
}

export class Jsplumb {
  instance!: JsPlumbInstance
  containerId: string
  config: JsplumbConfig

  constructor(eleId: string, config = {} as JsplumbConfig) {
    this.containerId = eleId
    this.config = { ...defaultConfig, ...config }
    onMounted(() => {
      this.init()
    })
  }

  init = () => {
    if (this.instance) {
      return
    }
    this.instance = newInstance({
      container: document.querySelector(`#${this.containerId}`)!, // 或指定共同的父容器
    })
  }

  getStops = (type?: 'input' | 'output'): [[number, string], [number, string]] => {
    if (type === 'input') {
      return [
        [0, '#FF6161'],
        [1, '#D76976'],
      ]
    }

    return [
      [0, '#0093EB'],
      [1, '#00D2D1'],
    ]
  }

  _connect = (
    sourceId: string,
    targetId: string,
    anchor: AnchorSpec,
    _config = {} as JsplumbConfig,
  ) => {
    const config = {
      ...defaultConfig,
      ...this.config,
      ..._config,
    }
    this.init()
    // 连接两个元素
    const sourceElement = document.querySelector(`#${sourceId}`)
    const targetElement = document.querySelector(`#${targetId}`)
    const stops = _config.stops ?? this.getStops(config.type)
    // 如果config.transparent为true，则将stops都加一些透明度
    if (config.transparent) {
      stops[0][1] = stops[0][1] + '80'
      stops[1][1] = stops[1][1] + '80'
    }
    if (targetElement && sourceElement) {
      this.instance.connect({
        source: sourceElement,
        target: targetElement,
        connector: config.connector,
        anchor: anchor,
        paintStyle: {
          stroke: stops[0][1],
          strokeWidth: 2.5,
          dashstyle: '0',
          zIndex: 100,
          opacity: 0.9,
          gradient: {
            stops: stops,
            type: 'linear',
          },
        },
        sourceEndpointStyle: { fill: stops[0][1] },

        endpoint: {
          type: DotEndpoint.type,
          options: { radius: 5 },
        },
        cssClass: `jtk-connector-${config.type}`
      } as unknown as ConnectParams<unknown>)

      // 为源元素添加端点
      this.instance.addEndpoint(sourceElement, {
        anchor: (anchor as [AnchorSpec, AnchorSpec])[0],
        paintStyle: { fill: stops[0][1], zIndex: 100 }, // source端点颜色
      } as unknown as EndpointOptions)

      // 为目标元素添加端点
      this.instance.addEndpoint(targetElement, {
        anchor: (anchor as [AnchorSpec, AnchorSpec])[1],
        paintStyle: { fill: stops[1][1], zIndex: 100 }, // target端点颜色
      } as unknown as EndpointOptions)
    }
  }

  connect = (
    sourceId: string,
    targetId: string,
    anchor: AnchorSpec,
    config = {} as JsplumbConfig,
  ) => {
    this._connect(sourceId, targetId, anchor, config)
  }

  connects = (args: ConnectArg[]) => {
    this.instance.batch(() => {
      args.forEach(({ sourceId, targetId, anchor, config }) => {
        this._connect(sourceId, targetId, anchor, config)
      })
    })
  }

  repaintEverything = () => {
    // 重新验证元素位置
    const container = document.querySelector(`#${this.containerId}`)
    if (container) {
      const elements = container.querySelectorAll('[id^="task-results-"]')
      elements.forEach((element) => {
        this.instance.revalidate(element)
      })
    }
    this.instance.repaintEverything()
  }

  reset = () => {
    this.instance.deleteEveryConnection()
    const allEndpoints = this.instance.selectEndpoints()
    allEndpoints.each((endpoint) => {
      this.instance.deleteEndpoint(endpoint)
    })
  }
}
