import { useState, useEffect, useCallback, useRef } from 'react';
import {
  UiUrlState,
  readUiUrlState,
  buildUiUrlStateSearch,
  paramsEqualExcept,
} from '../lib/urlState';

export function useUrlState() {
  const initialRef = useRef<UiUrlState | null>(null);
  if (!initialRef.current) {
    initialRef.current = readUiUrlState();
  }
  const initial = initialRef.current;

  const [urlState, setUrlState] = useState<UiUrlState>(initial);
  const urlSearchRef = useRef<string>(typeof window === 'undefined' ? '' : window.location.search);

  const applyUiUrlState = useCallback((nextStateOrFn: UiUrlState | ((prev: UiUrlState) => UiUrlState)) => {
    setUrlState(prev => {
      const nextState = typeof nextStateOrFn === 'function' ? nextStateOrFn(prev) : nextStateOrFn;
      return nextState;
    });
  }, []);

  useEffect(() => {
    if (typeof window === 'undefined') return;
    const onPopState = () => {
      urlSearchRef.current = window.location.search;
      applyUiUrlState(readUiUrlState());
    };
    window.addEventListener('popstate', onPopState);
    return () => window.removeEventListener('popstate', onPopState);
  }, [applyUiUrlState]);

  const pushUrlState = useCallback((nextState: UiUrlState) => {
    if (typeof window === 'undefined') return;
    const nextSearch = buildUiUrlStateSearch(nextState);
    const previousSearch = urlSearchRef.current.startsWith('?')
      ? urlSearchRef.current.slice(1)
      : urlSearchRef.current;
    if (nextSearch === previousSearch) return;

    const nextParams = new URLSearchParams(nextSearch);
    const previousParams = new URLSearchParams(previousSearch);
    const nextUrl = `${window.location.pathname}${nextSearch ? `?${nextSearch}` : ''}${window.location.hash}`;
    const shouldReplace = paramsEqualExcept(previousParams, nextParams, ['q']);
    window.history[shouldReplace ? 'replaceState' : 'pushState'](window.history.state, '', nextUrl);
    urlSearchRef.current = nextSearch ? `?${nextSearch}` : '';
  }, []);

  return { urlState, setUrlState: applyUiUrlState as (nextStateOrFn: UiUrlState | ((prev: UiUrlState) => UiUrlState)) => void, pushUrlState };
}
