import type { Dispatch } from 'react'
import { useCallback, useState } from 'react'

import isFunction from 'lodash/isFunction'

type Updater<S> = (prevState: S) => Partial<S>
const isUpdater = <S extends object>(value: any): value is Updater<S> => (
  isFunction(value)
)

/**
 * Дженерик для создания типа сеттера, получает тип стейта как параметр
 */
export type SetPartialState<S> = Dispatch<Partial<S> | Updater<S>>

/**
 * Хук на основе useState с возможносьтю мержа стейта как в классовых компонентах
 */
export const useObjectState = <S extends object>(initialState: S) => {
  const [state, setState] = useState(initialState)

  const setPartialState: SetPartialState<S> = useCallback((newStateOrUpdater) => {
    setState((oldState) => {
      const newState = isUpdater(newStateOrUpdater)
        ? newStateOrUpdater(oldState)
        : newStateOrUpdater
      // если updater функция вернула старый стейт то и здесь
      // возвращаем тот же стейт чтобы избежать лишнего ререндера
      if (newState === oldState) return oldState

      return { ...oldState, ...newState }
    })
  }, [])

  return [state, setPartialState] as const
}
