import { useState, useEffect, useCallback } from 'react';
import { handleError } from '../utils/errorHandling';

/**
 * Function type for updating the stored value
 * @template T - The type of the stored value
 * @param value - Either a new value of type T or an updater function that takes the current value and returns a new value
 */
type SetValue<T> = (value: T | ((val: T) => T)) => void;

/**
 * Custom hook for managing state in localStorage with automatic serialization
 *
 * @template T - The type of the stored value
 * @param {string} key - The localStorage key to use
 * @param {T | (() => T)} initialValue - The initial value or a function that returns the initial value
 * @returns {[T, SetValue<T>, () => void]} A tuple containing:
 *   - The current stored value
 *   - A function to update the stored value
 *   - A function to remove the value from storage
 *
 * @example
 * ```tsx
 * const [theme, setTheme, removeTheme] = useLocalStorage('theme', 'light');
 * setTheme('dark'); // Updates both state and localStorage
 * removeTheme(); // Removes from localStorage and resets to initial value
 * ```
 *
 * @remarks
 * - SSR-safe: Returns initial value during server-side rendering
 * - Automatically serializes/deserializes JSON
 * - Handles localStorage quota exceeded errors
 * - Handles invalid JSON gracefully
 */
export function useLocalStorage<T>(
  key: string,
  initialValue: T | (() => T)
): [T, SetValue<T>, () => void] {
  // Get initial value - SSR safe
  const getInitialValue = useCallback((): T => {
    // Check if we're in a browser environment
    if (typeof window === 'undefined') {
      return initialValue instanceof Function ? initialValue() : initialValue;
    }

    try {
      const item = window.localStorage.getItem(key);
      if (item) {
        return JSON.parse(item) as T;
      }
    } catch (error) {
      handleError(error, `LocalStorage Read (key: ${key})`);
    }

    return initialValue instanceof Function ? initialValue() : initialValue;
  }, [key, initialValue]);

  const [storedValue, setStoredValue] = useState<T>(getInitialValue);

  // Update localStorage whenever storedValue changes
  useEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }

    try {
      window.localStorage.setItem(key, JSON.stringify(storedValue));
    } catch (error) {
      handleError(error, `LocalStorage Write (key: ${key})`);
    }
  }, [key, storedValue]);

  // Set value function that supports both direct values and updater functions
  const setValue: SetValue<T> = useCallback((value) => {
    try {
      setStoredValue((prevValue) => {
        const newValue = value instanceof Function ? value(prevValue) : value;
        return newValue;
      });
    } catch (error) {
      handleError(error, `LocalStorage Update (key: ${key})`);
    }
  }, [key]);

  // Remove value from localStorage and reset to initial value
  const removeValue = useCallback(() => {
    try {
      if (typeof window !== 'undefined') {
        window.localStorage.removeItem(key);
      }
      const resetValue = initialValue instanceof Function ? initialValue() : initialValue;
      setStoredValue(resetValue);
    } catch (error) {
      handleError(error, `LocalStorage Remove (key: ${key})`);
    }
  }, [key, initialValue]);

  return [storedValue, setValue, removeValue];
}
