// The TLS 1.3 key_share extension (RFC 8446 §4.2.8): the ephemeral (EC)DHE public keys the
// two peers exchange so each can compute the shared secret the key schedule extracts into the
// Handshake Secret. A ClientHello carries a list of KeyShareEntry (a named group plus the
// public key); a ServerHello carries the single entry for the group the server picked. For
// TLS 1.3 over QUIC the group is x25519 (0x001d) and the key is a 32-byte Montgomery-u
// coordinate. This is the wire format that turns the crypto primitives (`x25519`, the key
// schedule) into a real key agreement between two endpoints — the piece a handshake needs
// before it can derive handshake-space keys.

///|
/// The x25519 named group (RFC 8446 §4.2.7).
pub let tls_group_x25519 : Int = 0x001d

///|
/// The key_share extension type (RFC 8446 §4.2.8).
pub let tls_ext_key_share : Int = 0x0033

///|
/// The `(group, key)` of a ServerHello's key_share extension, or `None` if it has none.
pub fn tls_server_hello_key_share(sh : TlsServerHello) -> (Int, Bytes)? {
  match tls_find_extension(sh.extensions, tls_ext_key_share) {
    Some(ext) => tls_decode_key_share_server(ext.data[:])
    None => None
  }
}

///|
/// The list of `(group, key)` shares a ClientHello's key_share extension offers.
pub fn tls_client_hello_key_shares(
  extensions : Array[TlsExtension],
) -> Array[(Int, Bytes)] {
  match tls_find_extension(extensions, tls_ext_key_share) {
    Some(ext) => tls_decode_key_share_client(ext.data[:])
    None => []
  }
}

///|
/// Build a ServerHello key_share extension carrying `key` for `group`.
pub fn tls_key_share_extension(group : Int, key : Bytes) -> TlsExtension {
  {
    ext_type: tls_ext_key_share,
    data: tls_encode_key_share_server(group, key),
  }
}

///|
fn ks_write_u16(buf : Buffer, v : Int) -> Unit {
  buf.write_byte(((v >> 8) & 0xff).to_byte())
  buf.write_byte((v & 0xff).to_byte())
}

///|
fn ks_read_u16(view : BytesView, off : Int) -> Int {
  (view[off].to_int() << 8) | view[off + 1].to_int()
}

///|
/// The ephemeral x25519 public key for a private scalar: `x25519(private, base_point)`.
pub fn tls13_x25519_public(private_key : Bytes) -> Bytes {
  x25519(private_key, x25519_base())
}

///|
/// Encode one KeyShareEntry: the named group, the key length, then the key (RFC 8446 §4.2.8).
pub fn tls_encode_key_share_entry(group : Int, key : Bytes) -> Bytes {
  let buf = Buffer()
  ks_write_u16(buf, group)
  ks_write_u16(buf, key.length())
  buf.write_bytes(key)
  buf.to_bytes()
}

///|
/// Encode the ServerHello key_share extension body: a single KeyShareEntry.
pub fn tls_encode_key_share_server(group : Int, key : Bytes) -> Bytes {
  tls_encode_key_share_entry(group, key)
}

///|
/// Encode the ClientHello key_share extension body: the client_shares length, then the
/// KeyShareEntry list.
pub fn tls_encode_key_share_client(entries : Array[(Int, Bytes)]) -> Bytes {
  let inner = Buffer()
  for e in entries {
    inner.write_bytes(tls_encode_key_share_entry(e.0, e.1))
  }
  let body = inner.to_bytes()
  let buf = Buffer()
  ks_write_u16(buf, body.length())
  buf.write_bytes(body)
  buf.to_bytes()
}

///|
/// Decode one KeyShareEntry from the front of `view`: returns `(group, key, bytes-consumed)`,
/// or `None` on a partial read.
pub fn tls_decode_key_share_entry(view : BytesView) -> (Int, Bytes, Int)? {
  if view.length() < 4 {
    return None
  }
  let group = ks_read_u16(view, 0)
  let len = ks_read_u16(view, 2)
  if view.length() < 4 + len {
    return None
  }
  Some((group, view[4:4 + len].to_owned(), 4 + len))
}

///|
/// Decode a ServerHello key_share extension body: its single KeyShareEntry as `(group, key)`.
pub fn tls_decode_key_share_server(view : BytesView) -> (Int, Bytes)? {
  match tls_decode_key_share_entry(view) {
    Some((group, key, _)) => Some((group, key))
    None => None
  }
}

///|
/// Decode a ClientHello key_share extension body: the list of `(group, key)` shares.
pub fn tls_decode_key_share_client(view : BytesView) -> Array[(Int, Bytes)] {
  let shares : Array[(Int, Bytes)] = []
  if view.length() < 2 {
    return shares
  }
  let total = ks_read_u16(view, 0)
  let end = 2 + total
  let mut off = 2
  while off < end && off < view.length() {
    match tls_decode_key_share_entry(view[off:]) {
      Some((group, key, consumed)) => {
        shares.push((group, key))
        off = off + consumed
      }
      None => break
    }
  }
  shares
}