/**
 * Socket.IO generic maps (type-only; no runtime dependency on socket.io):
 *   server: new Server<ClientToServerEvents, ServerToClientEvents, InterServerEvents, SocketData>()
 *   client: io() as Socket<ServerToClientEvents, ClientToServerEvents>
 *
 * The payload types describe what a well-behaved client sends. The server must still treat every
 * payload as untrusted and run it through `INBOUND_EVENTS[name]` before use, and must check that the
 * ack argument really is a function.
 */
import type { Ack, ClientAckData, HostAckData } from './acks';
import type { ClientEventName, HostEventName, InboundEventName, InboundPayloadInput } from './client';
import type { ServerEnvelope, ServerEventName } from './server';
import type { TokenClaims } from './tokens';

export type AckCallback<T> = (res: Ack<T>) => void;

/** Success data type of the ack for any inbound event. */
export type InboundAckData<E extends InboundEventName> = E extends ClientEventName
  ? ClientAckData<E>
  : E extends HostEventName
    ? HostAckData<E>
    : never;

export type ClientToServerEvents = {
  [E in InboundEventName]: (payload: InboundPayloadInput<E>, ack: AckCallback<InboundAckData<E>>) => void;
};

export type ServerToClientEvents = {
  [E in ServerEventName]: (envelope: ServerEnvelope<E>) => void;
};

/**
 * Nothing is exchanged between game-server instances: one process owns all rooms in memory
 * (ARCHITECTURE §1 "single-process limit"); the Redis adapter only fans out broadcasts.
 */
export type InterServerEvents = Record<never, never>;

/** Per-socket state the server sets in its connection middleware. */
export interface SocketData {
  /** Verified token claims; null for anonymous sockets (which may only `room.join` / `clock.sync`). */
  claims: TokenClaims | null;
  /** Client address used for the per-IP connection cap (proxy-aware). */
  ip: string;
  /** Room whose broadcast stream the socket receives; null until joined / attached. */
  roomId: string | null;
  /** Server time the connection was accepted (epoch ms). */
  connectedAt: number;
}
