import type { Ref, UnwrapRef } from 'vue'
import { onBeforeUnmount, ref, shallowRef } from 'vue'

import raf from '../../_util/raf'

export type Updater<State> = (prev: State) => State

export function useLayoutState<State>(
  defaultState: State,
): [Ref<State>, (updater: Updater<State>) => void] {
  const stateRef = shallowRef<State>(defaultState)
  let rafId: number
  const updateBatchRef = shallowRef<Updater<State>[]>([])
  function setFrameState(updater: Updater<State>) {
    updateBatchRef.value.push(updater)
    raf.cancel(rafId)
    rafId = raf(() => {
      const prevBatch = updateBatchRef.value
      // const prevState = stateRef.value;
      updateBatchRef.value = []
      prevBatch.forEach((batchUpdater) => {
        stateRef.value = batchUpdater(stateRef.value as State)
      })
    })
  }
  onBeforeUnmount(() => {
    raf.cancel(rafId)
  })

  return [stateRef as Ref<State>, setFrameState]
}

/** Lock frame, when frame pass reset the lock. */
export function useTimeoutLock<State>(
  defaultState?: State,
): [(state: UnwrapRef<State>) => void, () => UnwrapRef<State> | null] {
  const frameRef = ref<State | null>(defaultState || null)
  const timeoutRef = ref<any>()

  function cleanUp() {
    clearTimeout(timeoutRef.value)
  }

  function setState(newState: UnwrapRef<State>) {
    frameRef.value = newState
    cleanUp()

    timeoutRef.value = setTimeout(() => {
      frameRef.value = null
      timeoutRef.value = undefined
    }, 100)
  }

  function getState() {
    return frameRef.value
  }

  onBeforeUnmount(() => {
    cleanUp()
  })

  return [setState, getState]
}
