import {
    useState,
    useEffect,
    useCallback,
    type Dispatch,
    type SetStateAction
} from "react";

type SetValue<T> = Dispatch<SetStateAction<T>>;

export function useLocalStorage<T>(
    key: string,
    initialValue: T
): [T, SetValue<T>] {
    // Get initial value from localStorage or use the provided initial value
    const readValue = useCallback((): T => {
        // Prevent build error "window is undefined" during SSR
        if (typeof window === "undefined") {
            return initialValue;
        }

        try {
            const item = window.localStorage.getItem(key);
            return item ? (JSON.parse(item) as T) : initialValue;
        } catch (error) {
            console.warn(`Error reading localStorage key "${key}":`, error);
            return initialValue;
        }
    }, [initialValue, key]);

    // State to store our value
    const [storedValue, setStoredValue] = useState<T>(readValue);

    // Return a wrapped version of useState's setter function that
    // persists the new value to localStorage
    const setValue: SetValue<T> = useCallback(
        (value) => {
            // Prevent build error "window is undefined" during SSR
            if (typeof window === "undefined") {
                console.warn(
                    `Tried setting localStorage key "${key}" even though environment is not a client`
                );
            }

            try {
                // Allow value to be a function so we have the same API as useState
                const newValue =
                    value instanceof Function ? value(storedValue) : value;

                // Save to local storage
                window.localStorage.setItem(key, JSON.stringify(newValue));

                // Save state
                setStoredValue(newValue);

                // Dispatch a custom event so every useLocalStorage hook is notified
                window.dispatchEvent(new Event("local-storage"));
            } catch (error) {
                console.warn(`Error setting localStorage key "${key}":`, error);
            }
        },
        [key, storedValue]
    );

    // Listen for changes to this key from other tabs/windows
    useEffect(() => {
        const handleStorageChange = (e: StorageEvent) => {
            if (e.key === key && e.newValue !== null) {
                try {
                    setStoredValue(JSON.parse(e.newValue));
                } catch (error) {
                    console.warn(
                        `Error parsing localStorage value for key "${key}":`,
                        error
                    );
                }
            }
        };

        // Listen for storage events (changes from other tabs)
        window.addEventListener("storage", handleStorageChange);

        // Listen for custom event (changes from same tab)
        const handleLocalStorageChange = () => {
            setStoredValue(readValue());
        };
        window.addEventListener("local-storage", handleLocalStorageChange);

        return () => {
            window.removeEventListener("storage", handleStorageChange);
            window.removeEventListener(
                "local-storage",
                handleLocalStorageChange
            );
        };
    }, [key, readValue]);

    return [storedValue, setValue];
}
