import { useCallback } from "preact/hooks";

type EventMap = Record<string, (...args: unknown[]) => void>;

const listeners = new Map<string, Set<(...args: unknown[]) => void>>();

/**
 * A Preact hook to publish and subscribe to custom events across components.
 * @returns An object with `emit` and `on` methods.
 */
export function useEventBus<T extends EventMap>() {
  const emit = useCallback(
    <K extends keyof T>(event: K, ...args: Parameters<T[K]>) => {
      const handlers = listeners.get(event as string);
      if (handlers) {
        handlers.forEach((handler) => handler(...args));
      }
    },
    []
  );

  const on = useCallback(<K extends keyof T>(event: K, handler: T[K]) => {
    let handlers = listeners.get(event as string);
    if (!handlers) {
      handlers = new Set();
      listeners.set(event as string, handlers);
    }
    handlers.add(handler);

    return () => {
      handlers!.delete(handler);
      if (handlers!.size === 0) {
        listeners.delete(event as string);
      }
    };
  }, []);

  return { emit, on };
}
