import { cloneDeep, isEqual } from 'lodash'
import { computed, ComputedGetter, ComputedRef, readonly, Ref, ref, watch, watchEffect } from 'vue'

export function computedIter<T>(fn: ComputedGetter<Iterable<T>>): ComputedRef<T[]> {
  return computed(() => Array.from(fn()))
}

export const useId = (() => {
  let _id = 0
  return function useId(prefix = 'component') {
    return readonly(ref(`${prefix}-${_id++}`))
  }
})()

export function useAsyncFunction<F extends (...args: never[]) => ReturnType<F>>(
  fn: F,
): { isLoading: Ref<boolean>; fn: (...args: Parameters<F>) => ReturnType<F> } {
  const isLoading = ref(false)
  function wrapper(...args: Parameters<F>): ReturnType<F> {
    isLoading.value = true
    try {
      return fn(...args)
    } finally {
      isLoading.value = false
    }
  }
  return { fn: wrapper, isLoading }
}

export function useUpdatableState<T>(
  externalStateRef: Ref<T>,
  onUpdate: (value: T) => void,
  clone: (value: T) => T = cloneDeep,
): Ref<T> {
  const localRef = ref()
  watchEffect(() => {
    localRef.value = clone(externalStateRef.value)
  })
  watch(
    localRef,
    (newValue) => {
      if (!isEqual(newValue, externalStateRef.value)) {
        onUpdate(newValue)
      }
    },
    { deep: true },
  )
  return localRef
}