import { useState, useEffect, Dispatch, SetStateAction } from 'react';

type StorageType = 'localStorage' | 'sessionStorage';

type UseLocalStorageOptions<T> = {
  storage?: StorageType;
  serializer?: (value: T) => string;
  deserializer?: (value: string) => T;
};

/**
 * Custom hook that wraps useState with automatic storage persistence
 * @param key - The storage key to use
 * @param initialValue - The initial value if no stored value exists
 * @param options - Configuration options for storage type and serialization
 * @returns A stateful value and a function to update it, just like useState
 */
function useLocalStorage<T>(
  key: string,
  initialValue: T,
  options: UseLocalStorageOptions<T> = {},
): [T, Dispatch<SetStateAction<T>>] {
  const {
    storage = 'localStorage',
    serializer = JSON.stringify,
    deserializer = JSON.parse,
  } = options;

  // Get the storage object (localStorage or sessionStorage)
  const storageObject = typeof window !== 'undefined' ? window[storage] : null;

  // Initialize state with value from storage or initial value
  const [storedValue, setStoredValue] = useState<T>(() => {
    if (!storageObject) {
      return initialValue;
    }

    try {
      const item = storageObject.getItem(key);
      return item ? deserializer(item) : initialValue;
    } catch (error) {
      console.error(`Error reading ${storage} key "${key}":`, error);
      return initialValue;
    }
  });

  // Update storage whenever the state changes
  useEffect(() => {
    if (!storageObject) {
      return;
    }

    try {
      storageObject.setItem(key, serializer(storedValue));
    } catch (error) {
      console.error(`Error setting ${storage} key "${key}":`, error);
    }
  }, [key, storedValue, storageObject, serializer, storage]);

  // Listen for storage changes from other tabs/windows
  useEffect(() => {
    if (!storageObject || storage !== 'localStorage') {
      return;
    }

    const handleStorageChange = (e: StorageEvent) => {
      if (e.key === key && e.newValue !== null) {
        try {
          setStoredValue(deserializer(e.newValue));
        } catch (error) {
          console.error(`Error parsing ${storage} key "${key}":`, error);
        }
      }
    };

    window.addEventListener('storage', handleStorageChange);
    return () => window.removeEventListener('storage', handleStorageChange);
  }, [key, deserializer, storageObject, storage]);

  return [storedValue, setStoredValue];
}

export default useLocalStorage;
