import type { Ref } from 'vue'
import { ref, watch } from 'vue'

type LocalStoreDefaults = Record<string, unknown>
type LocalStoreResult<T extends LocalStoreDefaults> = { [K in keyof T]: Ref<T[K]> }
type LocalStoreTyped<T extends LocalStoreDefaults> = { [K in keyof T]: Ref<T[K] | null> }

const _localStoreCache: Record<string, Ref> = {}
let _storageListenerRegistered = false

const isBrowser = typeof window !== 'undefined'

function _ensureListener() {
	if (!isBrowser || _storageListenerRegistered) return
	_storageListenerRegistered = true
	window.addEventListener('storage', (e) => {
		if (e.key !== null && e.key in _localStoreCache) {
			_localStoreCache[e.key].value = e.newValue !== null ? JSON.parse(e.newValue) : null
		}
	})
}

function _getRef<T>(key: string, defaultValue: T | null = null): Ref<T | null> {
	if (!(key in _localStoreCache)) {
		const stored = isBrowser ? localStorage.getItem(key) : null
		_localStoreCache[key] = ref(stored !== null ? JSON.parse(stored) : defaultValue)
		watch(_localStoreCache[key], (val) => {
			if (isBrowser) localStorage.setItem(key, JSON.stringify(val))
		})
	}
	return _localStoreCache[key] as Ref<T | null>
}

export function useLocalStore<T extends LocalStoreDefaults>(defaults: T): LocalStoreResult<T>
// eslint-disable-next-line no-redeclare
export function useLocalStore<T extends LocalStoreDefaults>(): LocalStoreTyped<T>
// eslint-disable-next-line no-redeclare
export function useLocalStore<T extends LocalStoreDefaults>(defaults?: T) {
	_ensureListener()
	return new Proxy({} as Record<string, Ref>, {
		get(_, key: string) {
			return _getRef(key, defaults?.[key] ?? null)
		},
	})
}
