/**
 * HMR hub — tracks connected browser HMR sockets and broadcasts update events.
 *
 * Each open page holds one WebSocket to `/__wrnexus/hmr`. The in-process file
 * watcher (see index.ts) classifies a change and broadcasts a typed message:
 *
 *   { type: "css" }     -> the browser hot-swaps the stylesheet (no reload)
 *   { type: "reload" }  -> the browser asks for fresh HTML over the HMR socket
 *
 * Page/component/API/middleware/realtime changes invalidate their modules and
 * broadcast `reload` without closing the server or WebSocket. The browser asks
 * the same process for fresh HTML and performs a soft DOM morph.
 */

export type HmrMessage = { type: "css"; version: number } | { type: "reload"; version: number };

/** Minimal shape of a Bun ServerWebSocket we rely on. */
interface Socket {
  send(data: string): unknown;
}

export class HmrHub {
  private sockets = new Set<Socket>();
  private version = 0;

  add(ws: Socket): void {
    this.sockets.add(ws);
  }

  remove(ws: Socket): void {
    this.sockets.delete(ws);
  }

  broadcastJson(message: unknown): void {
    const payload = JSON.stringify(message);
    for (const ws of this.sockets) {
      try {
        ws.send(payload);
      } catch {
        this.sockets.delete(ws);
      }
    }
  }

  broadcast(message: HmrMessage): void {
    const payload = JSON.stringify(message);
    for (const ws of this.sockets) {
      try {
        ws.send(payload);
      } catch {
        this.sockets.delete(ws);
      }
    }
  }

  get size(): number {
    return this.sockets.size;
  }

  css(): void {
    this.broadcast({ type: "css", version: ++this.version });
  }

  reload(): void {
    this.broadcast({ type: "reload", version: ++this.version });
  }
}
