import type { StoreApi } from 'zustand';

export interface PendingRequestsSlice {
  pendingRequests: Map<string, Promise<any>>;
  getPendingRequest: <T>(key: string) => Promise<T> | undefined;
  setPendingRequest: <T>(key: string, request: Promise<T>) => void;
  deletePendingRequest: (key: string) => void;
  clearPendingRequests: () => void;
}

export function createPendingRequestsSlice(
  set: (partial: any) => void,
  get: () => any
): PendingRequestsSlice {
  return {
    pendingRequests: new Map(),

    getPendingRequest: <T>(key: string) => {
      return get().pendingRequests.get(key) as Promise<T> | undefined;
    },

    setPendingRequest: <T>(key: string, request: Promise<T>) => {
      set((state: { pendingRequests: Map<string, Promise<any>> }) => {
        const newPendingRequests = new Map(state.pendingRequests);
        newPendingRequests.set(key, request);
        return { pendingRequests: newPendingRequests };
      });
    },

    deletePendingRequest: (key: string) => {
      set((state: { pendingRequests: Map<string, Promise<any>> }) => {
        const newPendingRequests = new Map(state.pendingRequests);
        newPendingRequests.delete(key);
        return { pendingRequests: newPendingRequests };
      });
    },

    clearPendingRequests: () => {
      set({ pendingRequests: new Map() });
    },
  };
}

export function createDedupe<TStore extends PendingRequestsSlice>(
  getStore: () => StoreApi<TStore>
) {
  return async function dedupe<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
    const store = getStore().getState();
    const pending = store.getPendingRequest<T>(key);
    if (pending) return pending;

    const request = fetcher().finally(() => {
      getStore().getState().deletePendingRequest(key);
    });
    store.setPendingRequest(key, request);
    return request;
  };
}
