import { identity } from "lodash"
import { useCallback, useEffect, useState } from "react"
import { useDebouncedCallback } from "@react-hookz/web"
import LocalStorage from "../localStorage"
import { LOCAL_STORAGE_KEY } from "~/options/store"

export enum S {
  ActiveTab = "options.activeTab",
  ActivePaneTab = "options.activePaneTab",
  PanelVisible = "options.panelVisible",
  PanelPosition = "options.panelPosition",
  ShowTOS = "cloud.showTOS",
  SearchTabSearch = "searchTab.search",
  SearchTabReplace = "searchTab.replace",
  FileTreeGroupView = "fileTree.groupView",
  FileTreeFilter = "fileTree.filter",
  FileTreeVisibleGroups = "fileTree.visibleGroups",
  FileTreeSorting = "fileTree.sorting",
}

export function useLocalStorageState<T>(
  key: string,
  initialValue: (() => T) | T,
  serialize: (value: T) => any = identity,
  hydrate: (value: any) => T = identity,
) {
  key = `${LOCAL_STORAGE_KEY}.${key}`

  const getFromLocalStorage = useCallback((): T => {
    const item = LocalStorage.getItem(key)
    if (item !== undefined) {
      return hydrate(item)
    } else if (typeof initialValue === "function") {
      return hydrate((initialValue as () => T)())
    }

    return initialValue
  }, [key, initialValue])

  const [value, setValue] = useState<T>(getFromLocalStorage)

  const debouncedSet = useDebouncedCallback(
    (next: T) => {
      LocalStorage.setItem(key, serialize(next))
    },
    [key],
    80,
  )

  useEffect(() => {
    const nextValue = getFromLocalStorage()
    if (nextValue !== value) {
      setValue(nextValue)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [key, getFromLocalStorage])

  const setter = useCallback(
    (next: T | ((value: T) => T)) => {
      if (typeof next === "function") {
        next = (next as any)(value)
      }

      debouncedSet(next as T)
      setValue(next)
    },
    [value, debouncedSet],
  )

  return [value, setter] as const
}
