<script setup>
import { TransitionPresets, useDebounceFn, useTransition, watchDebounced } from '@vueuse/core'
import { getBezierPath, useVueFlow } from '@vue-flow/core'
import { computed, ref } from 'vue'

const props = defineProps({
  id: {
    type: String,
    required: true,
  },
  source: {
    type: String,
    required: true,
  },
  target: {
    type: String,
    required: true,
  },
  sourceX: {
    type: Number,
    required: true,
  },
  sourceY: {
    type: Number,
    required: true,
  },
  targetX: {
    type: Number,
    required: true,
  },
  targetY: {
    type: Number,
    required: true,
  },
  sourcePosition: {
    type: String,
    required: true,
  },
  targetPosition: {
    type: String,
    required: true,
  },
  data: {
    type: Object,
    required: false,
  },
  markerEnd: {
    type: String,
    required: false,
  },
  style: {
    type: Object,
    required: false,
  },
})

const curve = ref()

const dot = ref()

const transform = ref({ x: 0, y: 0 })

const showDot = ref(false)

const { onNodeDoubleClick, fitBounds, fitView } = useVueFlow()

const path = computed(() =>
  getBezierPath({
    sourceX: props.sourceX,
    sourceY: props.sourceY,
    sourcePosition: props.sourcePosition,
    targetX: props.targetX,
    targetY: props.targetY,
    targetPosition: props.targetPosition,
  }),
)

const debouncedFitBounds = useDebounceFn(fitBounds, 1, { maxWait: 1 })

onNodeDoubleClick(({ node }) => {
  const isSource = props.source === node.id
  const isTarget = props.target === node.id

  if (!showDot.value && (isSource || isTarget)) {
    showDot.value = true
    let totalLength = curve.value.getTotalLength()
    const initialPos = ref(isSource ? 0 : totalLength)
    let stopHandle

    const output = useTransition(initialPos, {
      duration: Math.floor(totalLength / 2 / 100) * 1000,
      transition: TransitionPresets.easeOutCubic,
      onFinished: () => {
        stopHandle?.()
        showDot.value = false
        fitView({
          nodes: [isSource ? props.target : props.source],
          duration: 500,
        })
      },
    })

    transform.value = curve.value.getPointAtLength(output.value)

    debouncedFitBounds(
      {
        width: 100,
        height: 200,
        x: transform.value.x - 100,
        y: transform.value.y - 100,
      },
      { duration: 500 },
    )

    setTimeout(() => {
      initialPos.value = isSource ? totalLength : 0

      stopHandle = watchDebounced(
        output,
        (next) => {
          if (!showDot.value) return

          const nextLength = curve.value.getTotalLength()

          if (totalLength !== nextLength) {
            totalLength = nextLength
            initialPos.value = isSource ? totalLength : 0
          }

          transform.value = curve.value.getPointAtLength(next)

          debouncedFitBounds({
            width: 100,
            height: 200,
            x: transform.value.x - 100,
            y: transform.value.y - 100,
          })
        },
        { debounce: 1 },
      )
    }, 500)
  }
})
</script>

<script>
export default {
  inheritAttrs: false,
}
</script>

<template>
  <path :id="id" ref="curve" :style="style" class="vue-flow__edge-path" :d="path[0]" :marker-end="markerEnd" />

  <Transition name="fade">
    <circle
      v-if="showDot"
      ref="dot"
      r="5"
      cy="0"
      cx="0"
      :transform="`translate(${transform.x}, ${transform.y})`"
      style="fill: #fdd023"
    />
  </Transition>
</template>
