import type { QueryClient, QueryKey } from '@tanstack/react-query';

/** Minimum surface of socket.io's client we use here. Kept loose so the
 *  helper can be unit-tested with a stub object without dragging the full
 *  socket.io dependency into the test runtime. */
export interface SocketLike {
  on(event: string, handler: (...args: unknown[]) => void): unknown;
  off(event: string, handler: (...args: unknown[]) => void): unknown;
}

export type EventMap = Record<string, QueryKey[]>;

/**
 * Pure subscription logic for `useRealtimeInvalidation`. Registers one
 * handler per event key. Each handler reads the latest eventMap from the
 * supplied getter so callers can pass a fresh object literal on every render
 * without re-subscribing.
 *
 * Returns a cleanup function that removes the registered handlers.
 *
 * Lives in its own JSX-free file so it can be unit-tested under vitest's
 * node environment without dragging the React provider into the bundle.
 */
export function subscribeRealtimeInvalidations(
  socket: SocketLike,
  eventKeys: string[],
  queryClient: Pick<QueryClient, 'invalidateQueries'>,
  getEventMap: () => EventMap,
): () => void {
  const handlers: Array<{ event: string; handler: (...args: unknown[]) => void }> = [];

  for (const event of eventKeys) {
    const handler = () => {
      // Read the LATEST map at fire-time - not at subscription time - so
      // callers passing inline `{ 'client:created': [...] }` literals don't
      // bind a stale snapshot if they re-render.
      const queryKeys = getEventMap()[event];
      if (!queryKeys) return;
      for (const key of queryKeys) {
        queryClient.invalidateQueries({ queryKey: key });
      }
    };
    socket.on(event, handler);
    handlers.push({ event, handler });
  }

  return () => {
    for (const { event, handler } of handlers) {
      socket.off(event, handler);
    }
  };
}
