package no.zyon.shade.streams

import java.nio.ByteBuffer

/**
 * Deterministic AEAD nonce + AAD construction for stream chunks.
 * Mirror @shade/streams/nonce.ts.
 *
 *   nonce[0..4]  = u32_be(laneId)
 *   nonce[4..12] = u64_be(seq)
 *
 *   aad = streamId(16) || u32_be(laneId) || u64_be(seq) || u8(isLast)
 *
 * `seq` is unsigned-u64 on the wire. Kotlin's `Long` is signed; we accept it
 * for the bit pattern (same as TS `BigInt` would write), so values past
 * `Long.MAX_VALUE` arrive here as negative signed longs. `ByteBuffer.putLong`
 * writes the raw 8 bytes regardless of sign — that's what we want.
 *
 * Use `java.lang.Long.parseUnsignedLong("…")` to decode JSON strings
 * representing u64 values larger than 2^63 - 1.
 */

fun buildChunkNonce(laneId: Long, seq: Long): ByteArray {
    require(laneId in 0L..0xFFFFFFFFL) { "laneId must fit in u32: $laneId" }
    val out = ByteArray(StreamConstants.STREAM_NONCE_BYTES)
    val buf = ByteBuffer.wrap(out)
    buf.putInt(laneId.toInt())
    buf.putLong(seq)
    return out
}

fun buildChunkAad(
    streamId: ByteArray,
    laneId: Long,
    seq: Long,
    isLast: Boolean,
): ByteArray {
    require(streamId.size == StreamConstants.STREAM_ID_BYTES) {
        "streamId must be ${StreamConstants.STREAM_ID_BYTES} bytes"
    }
    require(laneId in 0L..0xFFFFFFFFL) { "laneId must fit in u32: $laneId" }

    val out = ByteArray(StreamConstants.STREAM_ID_BYTES + 4 + 8 + 1)
    streamId.copyInto(out, 0)
    val buf = ByteBuffer.wrap(out, StreamConstants.STREAM_ID_BYTES, 4 + 8 + 1)
    buf.putInt(laneId.toInt())
    buf.putLong(seq)
    out[out.size - 1] = if (isLast) 0x01 else 0x00
    return out
}
