type Store = typeof localStorage | typeof sessionStorage

type SetTransform<T> = (args: T) => string
type GetTransform<T> = (args: null | string) => T

export function get(store: Store, name: string): string | null
export function get<T>(
  store: Store,
  name: string,
  transform: GetTransform<T>
): T
export function get<T>(
  store: Store,
  name: string,
  transform?: GetTransform<T>
) {
  const value = store.getItem(name)
  if (transform) {
    return transform(value)
  } else {
    return value
  }
}

export function set(store: Store, name: string, value: string): string
export function set<T>(
  store: Store,
  name: string,
  value: T,
  transform: SetTransform<T>
): string
export function set<T>(
  store: Store,
  name: string,
  value: T,
  transform?: SetTransform<T>
) {
  store.setItem(name, transform ? transform(value) : (value as string))
  return value
}

export function getFactory(store: Store): (name: string) => string | null
export function getFactory<T>(
  store: Store,
  transform: GetTransform<T>
): (name: string | null) => T
export function getFactory<T>(store: Store, transform?: GetTransform<T>) {
  return (name: string | null) =>
    transform ? get(store, name!, transform) : get(store, name!)
}

export function setFactory(
  store: Store
): (name: string, value: string) => string
export function setFactory<T>(
  store: Store,
  transform: SetTransform<T>
): (name: string, value: T) => string
export function setFactory<T>(store: Store, transform?: SetTransform<T>) {
  return (name: string, value: T) =>
    transform
      ? set(store, name, transform(value))
      : set(store, name, value as string)
}

export function localGetFactory(): (name: string) => string | null
export function localGetFactory<T>(
  transform: GetTransform<T>
): (name: string | null) => T
export function localGetFactory<T>(transform?: GetTransform<T>) {
  return getFactory(localStorage, transform!)
}

export function localSetFactory(): (name: string, value: string) => string
export function localSetFactory<T>(
  transform: SetTransform<T>
): (name: string, value: T) => string
export function localSetFactory<T>(transform?: SetTransform<T>) {
  return setFactory(localStorage, transform!)
}
export function localDel(key: string) {
  localStorage.removeItem(key)
}

export const local = {
  get: localGetFactory(),
  set: localSetFactory(),
  del: localDel
}
