/* eslint-disable  @typescript-eslint/no-explicit-any */

import { useRef } from 'react'

import { useEventListener, useToggle } from 'hooks'

const getNormalizedProgress = (fraction: number) => {
  if (fraction > 1) return 1
  if (fraction < 0) return 0
  return fraction
}

type Args = {
  onChange: (progress: number, seeking: boolean) => void,
  onTouchEnd?: () => any,
  onTouchStart?: () => any,
}

export const useSlider = (props: Args) => {
  const { onChange } = props
  const ref = useRef<HTMLDivElement>(null)
  const lastProgress = useRef(0)
  const {
    close: setMouseDown,
    isOpen: isMouseUp,
    open: setMouseUp,
  } = useToggle(true)

  const handleProgress = (mouseX: number, seeking: boolean) => {
    const track = ref.current
    if (!track) return

    const x = mouseX - track.getBoundingClientRect().left
    const progress = getNormalizedProgress(x / track.offsetWidth)
    lastProgress.current = progress
    onChange(progress, seeking)
  }

  useEventListener({
    callback: ({ clientX, target }: MouseEvent) => {
      const track = ref.current
      if (target === track || track?.contains(target as Node)) {
        setMouseDown()
        handleProgress(clientX, !isMouseUp)
      }
    },
    event: 'mousedown',
  })

  useEventListener({
    callback: ({ target, targetTouches }: TouchEvent) => {
      const track = ref.current
      if (props.onTouchStart) props.onTouchStart()
      if (target === track || track?.contains(target as Node)) {
        setMouseDown()
        handleProgress(targetTouches[0].clientX, !isMouseUp)
      }
    },
    event: 'touchstart',
  })

  useEventListener({
    callback: () => {
      if (!isMouseUp) {
        onChange(lastProgress.current, false)
        setMouseUp()
        lastProgress.current = 0
      }
    },
    event: 'mouseup',
  })

  useEventListener({
    callback: () => {
      if (props.onTouchEnd) props.onTouchEnd()
      if (!isMouseUp) {
        onChange(lastProgress.current, false)
        setMouseUp()
        lastProgress.current = 0
      }
    },
    event: 'touchend',
  })

  useEventListener({
    callback: (e: MouseEvent) => {
      if (isMouseUp) return
      handleProgress(e.clientX, !isMouseUp)
    },
    event: 'mousemove',
  })

  useEventListener({
    callback: (e: TouchEvent) => {
      if (isMouseUp) return
      if (props.onTouchStart) props.onTouchStart()
      handleProgress(e.targetTouches[0].clientX, !isMouseUp)
    },
    event: 'touchmove',
  })

  return ref
}
