import { useEffect, useRef, useState } from 'react'

export type AutosaveState = 'idle' | 'saving' | 'saved' | 'error'

export function useAutosave(
  save: (value: string) => Promise<unknown>,
  value: string,
  opts?: { delayMs?: number },
): { state: AutosaveState } {
  const delayMs = opts?.delayMs ?? 1000
  const [state, setState] = useState<AutosaveState>('idle')

  const saveRef = useRef(save)
  const initialValueRef = useRef(value)
  const mountedRef = useRef(false)
  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const generationRef = useRef(0)

  useEffect(() => {
    saveRef.current = save
  })

  useEffect(() => {
    if (!mountedRef.current) {
      mountedRef.current = true
      initialValueRef.current = value
      return
    }

    if (value === initialValueRef.current) return

    if (timerRef.current !== null) clearTimeout(timerRef.current)

    const generation = ++generationRef.current

    timerRef.current = setTimeout(() => {
      timerRef.current = null
      setState('saving')
      void saveRef
        .current(value)
        .then(() => {
          if (generation === generationRef.current) setState('saved')
        })
        .catch(() => {
          if (generation === generationRef.current) setState('error')
        })
    }, delayMs)

    return () => {
      if (timerRef.current !== null) {
        clearTimeout(timerRef.current)
        timerRef.current = null
      }
    }
  }, [value, delayMs])

  useEffect(() => {
    return () => {
      generationRef.current += 1
      if (timerRef.current !== null) {
        clearTimeout(timerRef.current)
        timerRef.current = null
      }
    }
  }, [])

  return { state }
}
