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

const useUpdateProperties = () => {
  const promises = useRef([])
  const [loading, setLoading] = useState(false)
  const counter = useRef(0)
  const lastId = useRef(0)
  const lastIdByProperty = useRef({})

  const addPromise = (promise, callback, propertyIds = []) => {
    const id = counter.current + 1
    counter.current = id
    lastId.current = id
    const normalizedPropertyIds = Array.isArray(propertyIds)
      ? propertyIds.filter(Boolean)
      : [propertyIds].filter(Boolean)

    if (normalizedPropertyIds.length) {
      const propertySet = new Set(normalizedPropertyIds)
      promises.current = promises.current.filter(({ propertyIds: ids = [] }) =>
        !ids.some((pid) => propertySet.has(pid))
      )

      lastIdByProperty.current = normalizedPropertyIds.reduce((acc, propertyId) => {
        acc[propertyId] = id
        return acc
      }, { ...lastIdByProperty.current })
    }

    promises.current = [...promises.current, { promise, callback, id, propertyIds: normalizedPropertyIds }]
  }

  useEffect(() => {
    const resolvePromises = async () => {
      if (loading) return
      if (promises.current.length === 0) return

      setLoading(true)
      while (promises.current.length > 0) {
        const { promise: currentPromise, callback: currentCallback, id: currentId, propertyIds: currentPropertyIds = [] } = promises.current[0]
        try {
          const result = await currentPromise
          const isLatestForProperties = currentPropertyIds.length > 0
            ? currentPropertyIds.every((propertyId) => lastIdByProperty.current[propertyId] === currentId)
            : true
          const isLatestGlobal = currentId === lastId.current
          if (isLatestGlobal && isLatestForProperties) {
            await currentCallback?.(result)
          }
        } catch (e) {
          const isLatestForProperties = currentPropertyIds.length > 0
            ? currentPropertyIds.every((propertyId) => lastIdByProperty.current[propertyId] === currentId)
            : true
          const isLatestGlobal = currentId === lastId.current
          if (isLatestGlobal && isLatestForProperties) {
            await currentCallback?.(null, e)
          }
        }
        promises.current = promises.current.slice(1)
      }
      setLoading(false)
    }
    if (promises.current.length > 0) {
      resolvePromises()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [promises.current])

  return {
    addPromise,
    loading
  }
}

export default useUpdateProperties
