// TLS 1.3 handshake messages (RFC 8446 §4). These are the messages the QUIC CRYPTO
// frames carry: the ClientHello and ServerHello negotiate the cipher suite and the
// key_share that x25519 turns into the ECDHE secret, which the key schedule extracts.
// Each message is framed as a one-byte type, a 24-bit length, then its body.

///|
/// A TLS extension: a two-byte type and its opaque data (RFC 8446 §4.2).
pub(all) struct TlsExtension {
  ext_type : Int
  data : Bytes
} derive(Eq, Debug)

///|
/// The handshake message types this layer models (RFC 8446 §4).
pub let tls_client_hello : Int = 1

///|
/// ServerHello (RFC 8446 §4.1.3): the server's half of the key exchange.
pub let tls_server_hello : Int = 2

///|
/// Frame a handshake body: message type, then a 24-bit big-endian length (RFC 8446 §4).
pub fn tls_encode_handshake(msg_type : Int, body : Bytes) -> Bytes {
  let buf = Buffer()
  buf.write_byte(msg_type.to_byte())
  buf.write_byte(((body.length() >> 16) & 0xff).to_byte())
  buf.write_byte(((body.length() >> 8) & 0xff).to_byte())
  buf.write_byte((body.length() & 0xff).to_byte())
  buf.write_bytes(body[:])
  buf.to_bytes()
}

///|
/// Parse one handshake message, returning its type and body, or `None` if truncated.
pub fn tls_parse_handshake(b : BytesView) -> (Int, Bytes)? {
  if b.length() < 4 {
    return None
  }
  let msg_type = b[0].to_int()
  let len = (b[1].to_int() << 16) | (b[2].to_int() << 8) | b[3].to_int()
  if b.length() < 4 + len {
    return None
  }
  Some((msg_type, b[4:4 + len].to_owned()))
}

///|
/// Encode an extension block: a two-byte total length, then each extension as a
/// two-byte type, a two-byte length, and its data.
fn tls_encode_extensions(exts : Array[TlsExtension]) -> Bytes {
  let inner = Buffer()
  for e in exts {
    inner.write_byte(((e.ext_type >> 8) & 0xff).to_byte())
    inner.write_byte((e.ext_type & 0xff).to_byte())
    inner.write_byte(((e.data.length() >> 8) & 0xff).to_byte())
    inner.write_byte((e.data.length() & 0xff).to_byte())
    inner.write_bytes(e.data[:])
  }
  let ib = inner.to_bytes()
  let out = Buffer()
  out.write_byte(((ib.length() >> 8) & 0xff).to_byte())
  out.write_byte((ib.length() & 0xff).to_byte())
  out.write_bytes(ib[:])
  out.to_bytes()
}

///|
/// Decode an extension block (a two-byte length prefix then the extensions).
fn tls_decode_extensions(b : BytesView) -> Array[TlsExtension]? {
  if b.length() < 2 {
    return None
  }
  let total = (b[0].to_int() << 8) | b[1].to_int()
  let end = 2 + total
  if b.length() < end {
    return None
  }
  let out : Array[TlsExtension] = []
  let mut off = 2
  while off < end {
    if off + 4 > end {
      return None
    }
    let et = (b[off].to_int() << 8) | b[off + 1].to_int()
    let dl = (b[off + 2].to_int() << 8) | b[off + 3].to_int()
    off = off + 4
    if off + dl > end {
      return None
    }
    out.push({ ext_type: et, data: b[off:off + dl].to_owned(), })
    off = off + dl
  }
  Some(out)
}

///|
/// Look up the first extension of a given type, or `None`.
pub fn tls_find_extension(
  exts : Array[TlsExtension],
  ext_type : Int,
) -> TlsExtension? {
  exts.iter().find_first(fn(e) { e.ext_type == ext_type })
}

///|
/// A ClientHello (RFC 8446 §4.1.2), less the fixed legacy fields.
pub(all) struct TlsClientHello {
  random : Bytes
  session_id : Bytes
  cipher_suites : Array[Int]
  extensions : Array[TlsExtension]
} derive(Eq, Debug)

///|
/// Encode a ClientHello as a full handshake message. legacy_version is 0x0303 and
/// legacy_compression_methods is the single null method, per RFC 8446 §4.1.2.
pub fn encode_client_hello(ch : TlsClientHello) -> Bytes {
  let body = Buffer()
  body.write_byte(b'\x03')
  body.write_byte(b'\x03')
  body.write_bytes(ch.random[:])
  body.write_byte(ch.session_id.length().to_byte())
  body.write_bytes(ch.session_id[:])
  let cs_bytes = ch.cipher_suites.length() * 2
  body.write_byte(((cs_bytes >> 8) & 0xff).to_byte())
  body.write_byte((cs_bytes & 0xff).to_byte())
  for cs in ch.cipher_suites {
    body.write_byte(((cs >> 8) & 0xff).to_byte())
    body.write_byte((cs & 0xff).to_byte())
  }
  body.write_byte(b'\x01')
  body.write_byte(b'\x00')
  body.write_bytes(tls_encode_extensions(ch.extensions)[:])
  tls_encode_handshake(tls_client_hello, body.to_bytes())
}

///|
/// Decode a ClientHello from a handshake body (RFC 8446 §4.1.2).
pub fn decode_client_hello(body : BytesView) -> TlsClientHello? {
  if body.length() < 35 {
    return None
  }
  let random = body[2:34].to_owned()
  let sid_len = body[34].to_int()
  let mut off = 35
  if body.length() < off + sid_len {
    return None
  }
  let session_id = body[off:off + sid_len].to_owned()
  off = off + sid_len
  if body.length() < off + 2 {
    return None
  }
  let cs_len = (body[off].to_int() << 8) | body[off + 1].to_int()
  off = off + 2
  if body.length() < off + cs_len {
    return None
  }
  let cipher_suites : Array[Int] = []
  for i = 0; i < cs_len; i = i + 2 {
    cipher_suites.push(
      (body[off + i].to_int() << 8) | body[off + i + 1].to_int(),
    )
  }
  off = off + cs_len
  if body.length() < off + 1 {
    return None
  }
  let comp_len = body[off].to_int()
  off = off + 1 + comp_len
  guard tls_decode_extensions(body[off:]) is Some(extensions) else {
    return None
  }
  Some({ random, session_id, cipher_suites, extensions, })
}

///|
/// A ServerHello (RFC 8446 §4.1.3): a single chosen cipher suite, no compression.
pub(all) struct TlsServerHello {
  random : Bytes
  session_id : Bytes
  cipher_suite : Int
  extensions : Array[TlsExtension]
} derive(Eq, Debug)

///|
/// Encode a ServerHello as a full handshake message.
pub fn encode_server_hello(sh : TlsServerHello) -> Bytes {
  let body = Buffer()
  body.write_byte(b'\x03')
  body.write_byte(b'\x03')
  body.write_bytes(sh.random[:])
  body.write_byte(sh.session_id.length().to_byte())
  body.write_bytes(sh.session_id[:])
  body.write_byte(((sh.cipher_suite >> 8) & 0xff).to_byte())
  body.write_byte((sh.cipher_suite & 0xff).to_byte())
  body.write_byte(b'\x00')
  body.write_bytes(tls_encode_extensions(sh.extensions)[:])
  tls_encode_handshake(tls_server_hello, body.to_bytes())
}

///|
/// Decode a ServerHello from a handshake body (RFC 8446 §4.1.3).
pub fn decode_server_hello(body : BytesView) -> TlsServerHello? {
  if body.length() < 35 {
    return None
  }
  let random = body[2:34].to_owned()
  let sid_len = body[34].to_int()
  let mut off = 35
  if body.length() < off + sid_len {
    return None
  }
  let session_id = body[off:off + sid_len].to_owned()
  off = off + sid_len
  if body.length() < off + 3 {
    return None
  }
  let cipher_suite = (body[off].to_int() << 8) | body[off + 1].to_int()
  // Skip the cipher suite (2) and the single legacy compression byte (1).
  off = off + 3
  guard tls_decode_extensions(body[off:]) is Some(extensions) else {
    return None
  }
  Some({ random, session_id, cipher_suite, extensions, })
}