import { useState } from 'react';
import { useLocation, useNavigate } from 'react-router';
import { useQueryClient } from '@tanstack/react-query';
import { useGlobal } from '../useGlobal';
import { useDebounce } from '../useDebounce';

export const useQueryState = <T extends string | number | null>(key: string, defaultValue?: T, isGlobal = false) => {
  const queryClient = useQueryClient();
  const location = useLocation();
  const navigate = useNavigate();
  const [currentLocation, setCurrentLocation] = useGlobal<typeof location>('LOCATION', location);

  const parseValue = (val: string | null): T => {
    if (val === null) return defaultValue as T;
    if (typeof defaultValue === 'number') return Number(val) as T;
    return val as T;
  };

  useDebounce(
    () => {
      if (location && currentLocation) {
        if (location.pathname !== currentLocation.pathname) {
          const globalCache = queryClient
            .getQueryCache()
            .getAll()
            .filter((cache) => cache.queryKey[1] === 'QUERY_STATE')
            .map((cache) => ({
              key: cache.queryKey[2],
              data: cache.state.data,
            }));

          const globalQuery = globalCache
            .filter((cache) => cache.data !== null)
            .map((cache) => `${cache.key}=${cache.data}`)
            .toSorted()
            .join('&');

          const url = `${window.location.pathname}?${globalQuery}`;
          navigate(url);

          setCurrentLocation(location);
        }
      }
    },
    100,
    [location],
  );

  const [value, setValue] = isGlobal
    ? useGlobal(['QUERY_STATE', key], () => {
        const searchParams = new URLSearchParams(window.location.search);
        return parseValue(searchParams.get(key));
      })
    : useState<T>(() => {
        const searchParams = new URLSearchParams(window.location.search);
        return parseValue(searchParams.get(key));
      });

  const setTheValue: React.Dispatch<React.SetStateAction<T>> = (arg) => {
    const newValue = typeof arg === 'function' ? arg(value as T) : arg;

    const searchParams = new URLSearchParams(window.location.search);
    if (newValue !== null && newValue !== undefined) {
      searchParams.set(key, String(newValue));
    } else {
      searchParams.delete(key);
    }

    const url = `${window.location.pathname}?${searchParams.toString()}${window.location.hash}`;
    window.history.replaceState({}, '', url);
    setValue(newValue);
  };

  const reset = () => setTheValue(defaultValue as T);
  const clear = () => setTheValue(null as T);

  return [value as T, setTheValue, reset, clear] as const;
};
