import { ref } from 'vue'

const DRAG_THRESHOLD = 5
const CLICK_SUPPRESSION_MS = 400

export interface FlowPosition {
  x: number
  y: number
}

interface DragState {
  id: string
  startX: number
  startY: number
  ox: number
  oy: number
  moved: boolean
  raf: number | null
}

interface UseFlowCanvasInteractionsOptions {
  flowRef: { value: HTMLElement | null }
  renderEdges: () => void
  updatePositions: (positions: Record<string, FlowPosition>) => void
  selectAgent: (id: string) => void
  getPositions: () => Record<string, FlowPosition>
}

function findNode(target: EventTarget | null) {
  return (target as HTMLElement | null)?.closest('.node') as HTMLElement | null
}

export function useFlowCanvasInteractions(options: UseFlowCanvasInteractionsOptions) {
  const drag = ref<DragState | null>(null)
  const suppressClickUntil = ref(0)

  function onPointerDown(e: PointerEvent) {
    const node = findNode(e.target)
    if (!node) return

    e.preventDefault()
    const nr = node.getBoundingClientRect()

    drag.value = {
      id: node.dataset.id || '',
      startX: e.clientX,
      startY: e.clientY,
      ox: e.clientX - (nr.left + nr.width / 2),
      oy: e.clientY - (nr.top + nr.height / 2),
      moved: false,
      raf: null,
    }

    node.setPointerCapture(e.pointerId)
  }

  function onPointerMove(e: PointerEvent) {
    if (!drag.value) return

    const currentDrag = drag.value
    const dist = Math.hypot(e.clientX - currentDrag.startX, e.clientY - currentDrag.startY)
    if (!currentDrag.moved && dist < DRAG_THRESHOLD) return

    if (!currentDrag.moved) {
      currentDrag.moved = true
      const node = options.flowRef.value?.querySelector(`.node[data-id="${currentDrag.id}"]`) as HTMLElement | null
      if (node) node.classList.add('dragging')
    }

    const flow = options.flowRef.value
    if (!flow) return

    const fr = flow.getBoundingClientRect()
    const x = Math.max(8, Math.min(92, ((e.clientX - currentDrag.ox - fr.left) / fr.width) * 100))
    const y = Math.max(10, Math.min(92, ((e.clientY - currentDrag.oy - fr.top) / fr.height) * 100))

    const node = flow.querySelector(`.node[data-id="${currentDrag.id}"]`) as HTMLElement | null
    if (node) {
      node.style.left = x + '%'
      node.style.top = y + '%'
    }

    options.updatePositions({
      ...options.getPositions(),
      [currentDrag.id]: { x, y },
    })

    if (!currentDrag.raf) {
      currentDrag.raf = requestAnimationFrame(() => {
        options.renderEdges()
        if (drag.value) drag.value.raf = null
      })
    }
  }

  function onPointerUp(e: PointerEvent) {
    if (!drag.value) return

    const currentDrag = drag.value
    const endDistance = Math.hypot(e.clientX - currentDrag.startX, e.clientY - currentDrag.startY)
    const wasDragged = currentDrag.moved || endDistance >= DRAG_THRESHOLD
    const node = options.flowRef.value?.querySelector(`.node[data-id="${currentDrag.id}"]`) as HTMLElement | null
    if (node) node.classList.remove('dragging')

    if (wasDragged) {
      suppressClickUntil.value = performance.now() + CLICK_SUPPRESSION_MS
    }

    drag.value = null
  }

  function onClick(e: MouseEvent) {
    const node = findNode(e.target)
    if (!node) return
    if (performance.now() < suppressClickUntil.value) return

    const id = node.dataset.id
    if (id) options.selectAgent(id)
  }

  function onClickCapture(e: MouseEvent) {
    if (performance.now() >= suppressClickUntil.value) return
    if (!findNode(e.target)) return
    e.preventDefault()
    e.stopPropagation()
  }

  return {
    onClick,
    onClickCapture,
    onPointerDown,
    onPointerMove,
    onPointerUp,
  }
}
