import { useCallback, useState } from 'react';

type StorageType = 'localStorage' | 'sessionStorage' | 'cookie';

interface CacheStateOptions<T> {
  defaultValue: T;
  storage?: StorageType;
  getUrlParams?: () => Partial<T>;
  clearUrlParams?: () => void;
}

const getStorageValue = <T>(key: string, storage: StorageType): T | undefined => {
  try {
    if (storage === 'cookie') {
      const matches = document.cookie.match(
        new RegExp(`(?:^|; )${key.replace(/([.$?*|{}()[\]\\/+^])/g, '\\$1')}=([^;]*)`)
      );
      return matches?.[1] ? JSON.parse(decodeURIComponent(matches[1])) : undefined;
    }
    const storageObj = storage === 'sessionStorage' ? sessionStorage : localStorage;
    const value = storageObj.getItem(key);
    return value ? JSON.parse(value) : undefined;
  } catch {
    return undefined;
  }
};

const setStorageValue = <T>(key: string, value: T, storage: StorageType): void => {
  try {
    const serialized = JSON.stringify(value);
    if (storage === 'cookie') {
      document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(serialized)}; path=/`;
      return;
    }
    const storageObj = storage === 'sessionStorage' ? sessionStorage : localStorage;
    storageObj.setItem(key, serialized);
  } catch (error) {
    console.error(`Failed to set ${storage} value:`, error);
  }
};

const getInitialValue = <T>(
  key: string,
  storage: StorageType,
  defaultValue: T,
  getUrlParams?: () => Partial<T>,
  clearUrlParams?: () => void
): T => {
  const cachedValue = getStorageValue<T>(key, storage);
  const urlParams = getUrlParams?.();

  if (urlParams && Object.keys(urlParams).length > 0) {
    const mergedValue = {
      ...(cachedValue || defaultValue),
      ...urlParams,
    };
    setStorageValue(key, mergedValue, storage);
    clearUrlParams?.();
    return mergedValue;
  }

  return cachedValue !== undefined ? cachedValue : defaultValue;
};

export const useCacheState = <T = any>(key: string, options: CacheStateOptions<T>) => {
  const { defaultValue, storage = 'localStorage', getUrlParams, clearUrlParams } = options;

  const [state, setState] = useState<T>(() =>
    getInitialValue(key, storage, defaultValue, getUrlParams, clearUrlParams)
  );

  const setStateAndStorage = useCallback(
    (value: T | ((prev: T) => T)) => {
      setState((prevState) => {
        const nextState = typeof value === 'function' ? (value as (prev: T) => T)(prevState) : value;
        setStorageValue(key, nextState, storage);
        return nextState;
      });
    },
    [key, storage]
  );

  return [state, setStateAndStorage] as const;
};
