import { useCallback, useEffect, useRef } from "react"
import type { UIEvent as ReactUIEvent } from "react"
import { MESSAGE_LIST_AUTO_SCROLL_THRESHOLD_PX } from "../../lib/constants"

export function useMessageListAutoScroll() {
  const messageListRef = useRef<HTMLDivElement | null>(null)
  const shouldAutoScrollMessageListRef = useRef(true)
  const messageListScrollFrameRef = useRef<number | null>(null)

  const isMessageListNearBottom = useCallback((element = messageListRef.current): boolean => {
    if (!element) {
      return true
    }

    const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight
    return distanceFromBottom <= MESSAGE_LIST_AUTO_SCROLL_THRESHOLD_PX
  }, [])

  const handleMessageListScroll = useCallback((event: ReactUIEvent<HTMLDivElement>) => {
    shouldAutoScrollMessageListRef.current = isMessageListNearBottom(event.currentTarget)
  }, [isMessageListNearBottom])

  const scrollMessageListToBottom = useCallback((options: { force?: boolean; behavior?: ScrollBehavior } = {}) => {
    const element = messageListRef.current
    if (!element) {
      return
    }

    const shouldScroll = options.force || shouldAutoScrollMessageListRef.current || isMessageListNearBottom(element)
    if (!shouldScroll) {
      return
    }

    shouldAutoScrollMessageListRef.current = true
    if (messageListScrollFrameRef.current) {
      window.cancelAnimationFrame(messageListScrollFrameRef.current)
    }
    messageListScrollFrameRef.current = window.requestAnimationFrame(() => {
      messageListScrollFrameRef.current = null
      const latestElement = messageListRef.current
      if (!latestElement) {
        return
      }
      latestElement.scrollTo({
        top: latestElement.scrollHeight,
        behavior: options.behavior ?? "smooth"
      })
    })
  }, [isMessageListNearBottom])

  const resetMessageListAutoScroll = useCallback(() => {
    shouldAutoScrollMessageListRef.current = true
  }, [])

  useEffect(() => {
    return () => {
      if (messageListScrollFrameRef.current) {
        window.cancelAnimationFrame(messageListScrollFrameRef.current)
        messageListScrollFrameRef.current = null
      }
    }
  }, [])

  return {
    messageListRef,
    resetMessageListAutoScroll,
    handleMessageListScroll,
    scrollMessageListToBottom
  }
}
