/**
 * In-process pending-request store for Web permission round-trips.
 *
 * Mirrors {@link import("../elicitation/GatewayElicitationBus.js").GatewayElicitationBus}
 * but for `permission_request` events: a tool that needs UI confirmation
 * registers a pending entry; the Web UI eventually calls
 * `Gateway.permissionDecide({ requestId, decision })` and the bus resolves.
 *
 * One bus per process, keyed by `sessionKey`. Pending entries are dropped
 * (rejected) when the turn ends so leaked promises do not hang.
 */

export type GatewayPermissionDecision = {
  requestId: string;
  decision: "allow" | "deny";
  remember?: boolean;
  reason?: string;
};

export type GatewayPermissionPending = {
  requestId: string;
  toolCallId: string;
  toolName: string;
  resolve(decision: GatewayPermissionDecision): void;
  reject(error: Error): void;
};

export class GatewayPermissionBus {
  private readonly bySession = new Map<string, Map<string, GatewayPermissionPending>>();

  register(sessionKey: string, entry: GatewayPermissionPending): void {
    let bucket = this.bySession.get(sessionKey);
    if (!bucket) {
      bucket = new Map();
      this.bySession.set(sessionKey, bucket);
    }
    bucket.set(entry.requestId, entry);
  }

  consume(sessionKey: string, requestId: string): GatewayPermissionPending | undefined {
    const bucket = this.bySession.get(sessionKey);
    if (!bucket) return undefined;
    const entry = bucket.get(requestId);
    if (!entry) return undefined;
    bucket.delete(requestId);
    if (bucket.size === 0) this.bySession.delete(sessionKey);
    return entry;
  }

  hasPending(sessionKey: string, requestId: string): boolean {
    return this.bySession.get(sessionKey)?.has(requestId) ?? false;
  }

  rejectSession(sessionKey: string, reason: string): void {
    const bucket = this.bySession.get(sessionKey);
    if (!bucket) return;
    for (const entry of bucket.values()) {
      entry.reject(new Error(reason));
    }
    this.bySession.delete(sessionKey);
  }

  pendingCount(sessionKey: string): number {
    return this.bySession.get(sessionKey)?.size ?? 0;
  }
}