/**
 * Per-stream flow control — PLAN_RELAY_AGENT T11. CONSUMES the §4.1 WINDOW_UPDATE credit protocol
 * (P1 owns the protocol). Per-stream credit means one heavy vim/top redraw can't starve another
 * stream. streamId 0 is the connection-level window applied to the whole link.
 */
export interface FlowController {
  consume(streamId: number, bytes: number): boolean
  grant(streamId: number, credit: number): void
  initWindow(streamId: number, initialCredit: number): void
}

const CONNECTION_STREAM_ID = 0

export function createFlowController(): FlowController {
  const windows = new Map<number, number>()
  // Connection-level window is unbounded until explicitly initialized.
  windows.set(CONNECTION_STREAM_ID, Number.POSITIVE_INFINITY)

  function remaining(streamId: number): number {
    return windows.get(streamId) ?? 0
  }

  return {
    initWindow(streamId: number, initialCredit: number): void {
      windows.set(streamId, initialCredit)
    },
    grant(streamId: number, credit: number): void {
      windows.set(streamId, remaining(streamId) + credit)
    },
    consume(streamId: number, bytes: number): boolean {
      const conn = remaining(CONNECTION_STREAM_ID)
      const stream = remaining(streamId)
      if (stream < bytes || conn < bytes) return false // credit exhausted → pause
      windows.set(streamId, stream - bytes)
      if (conn !== Number.POSITIVE_INFINITY) {
        windows.set(CONNECTION_STREAM_ID, conn - bytes)
      }
      return true
    },
  }
}
