/**
 * §4.1 control-frame payload codecs: WINDOW_UPDATE and GOAWAY.
 *
 * Per §4.1's explicit byte spec these are fixed-width big-endian integers (NOT CBOR):
 *   WINDOW_UPDATE payload = uint32 credit
 *   GOAWAY       payload = uint32 lastStreamId ‖ uint32 reason
 * (PLAN_RELAY_INDEX §2.1 loosely groups them under "CBOR payload codecs"; §4.1 is the
 *  authoritative wire and wins — see report deviation note.)
 */
import { ContractDecodeError } from '../errors.js'
import { assertUint32, readUint32BE, writeUint32BE } from '../bytes.js'
import {
  GOAWAY_CODE_TO_REASON,
  GOAWAY_REASON_TO_CODE,
  type GoAwayReason,
} from './types.js'

/** Map a GOAWAY wire code to its frozen reason label; throws on unknown code. */
export function decodeGoAwayReason(code: number): GoAwayReason {
  const reason = GOAWAY_CODE_TO_REASON[code]
  if (reason === undefined) {
    throw new ContractDecodeError(`unknown GOAWAY reason code ${code}`)
  }
  return reason
}

/** WINDOW_UPDATE: encode a uint32 credit increment. */
export function encodeWindowUpdate(credit: number): Uint8Array {
  assertUint32(credit, 'credit')
  const out = new Uint8Array(4)
  writeUint32BE(out, 0, credit)
  return out
}

/** WINDOW_UPDATE: decode the uint32 credit increment. */
export function decodeWindowUpdate(payload: Uint8Array): number {
  if (payload.length !== 4) {
    throw new ContractDecodeError(`WINDOW_UPDATE payload must be 4 bytes; got ${payload.length}`)
  }
  return readUint32BE(payload, 0)
}

/** GOAWAY: encode uint32 lastStreamId ‖ uint32 reason-code. */
export function encodeGoaway(lastStreamId: number, reason: GoAwayReason): Uint8Array {
  assertUint32(lastStreamId, 'lastStreamId')
  const code = GOAWAY_REASON_TO_CODE[reason]
  if (code === undefined) {
    throw new ContractDecodeError(`unknown GOAWAY reason label '${reason}'`)
  }
  const out = new Uint8Array(8)
  let offset = writeUint32BE(out, 0, lastStreamId)
  writeUint32BE(out, offset, code)
  return out
}

/** GOAWAY: decode uint32 lastStreamId ‖ uint32 reason-code. */
export function decodeGoaway(payload: Uint8Array): { lastStreamId: number; reason: GoAwayReason } {
  if (payload.length !== 8) {
    throw new ContractDecodeError(`GOAWAY payload must be 8 bytes; got ${payload.length}`)
  }
  const lastStreamId = readUint32BE(payload, 0)
  const reason = decodeGoAwayReason(readUint32BE(payload, 4))
  return { lastStreamId, reason }
}
