///|
pub fn parse_server_hello(data : BytesView) -> Result[ServerHello, ParseError] {
  if data.length() < 5 {
    return Err(IncompleteRecord(expected=5, available=data.length()))
  }
  if data[0].to_int() != 0x16 {
    return Err(NotTlsHandshake)
  }
  guard read_u16(data, 1) is Some(_) else {
    return Err(BadLength(field="record.version", offset=1))
  }
  guard read_u16_int(data, 3) is Some(record_len) else {
    return Err(BadLength(field="record.length", offset=3))
  }
  if record_len < 0 {
    return Err(BadLength(field="record.length", offset=3))
  }
  let record_end = 5 + record_len
  if record_end < 5 || data.length() < record_end {
    return Err(IncompleteRecord(expected=record_end, available=data.length()))
  }
  if record_len < 4 {
    return Err(BadLength(field="handshake.header", offset=5))
  }
  if data[5].to_int() != 2 {
    return Err(BadHandshakeType(data[5].to_int()))
  }
  guard read_u24_int(data, 6) is Some(handshake_len) else {
    return Err(BadLength(field="handshake.length", offset=6))
  }
  let body_offset = 9
  let body_end = body_offset + handshake_len
  if handshake_len < 0 || body_end < body_offset || body_end > record_end {
    return Err(BadLength(field="handshake.body", offset=body_offset))
  }

  // ServerHello minimum size check:
  // Version (2) + Random (32) + SessionID len (1) = 35 bytes
  if body_end - body_offset < 35 {
    return Err(BadLength(field="serverhello.fixed", offset=body_offset))
  }

  guard read_u16(data, body_offset) is Some(initial_version) else {
    return Err(BadLength(field="server.version", offset=body_offset))
  }
  let mut version = initial_version

  let random_arr = Array::new(capacity=32)
  for i in 0..<32 {
    random_arr.push(data[body_offset + 2 + i])
  }
  let random_bytes = Bytes::from_array(random_arr)

  let mut cursor = body_offset + 34
  guard read_u8(data, cursor) is Some(session_len) else {
    return Err(BadLength(field="session_id.length", offset=cursor))
  }

  if session_len < 0 || cursor + 1 + session_len > body_end {
    return Err(BadLength(field="session_id.body", offset=cursor))
  }
  let session_arr = Array::new(capacity=session_len)
  for i in 0.. body_end {
    return Err(BadLength(field="cipher_and_compression.header", offset=cursor))
  }
  guard read_u16(data, cursor) is Some(cipher_suite) else {
    return Err(BadLength(field="cipher_suite", offset=cursor))
  }
  let compression_method = data[cursor + 2]
  cursor = cursor + 3

  let extensions : Array[UInt16] = []
  let mut selected_alpn : String? = None

  if cursor < body_end {
    if cursor + 2 > body_end {
      return Err(BadLength(field="extensions.length", offset=cursor))
    }
    guard read_u16_int(data, cursor) is Some(ext_total_len) else {
      return Err(BadLength(field="extensions.length", offset=cursor))
    }
    cursor = cursor + 2
    if ext_total_len < 0 || cursor + ext_total_len != body_end {
      return Err(BadLength(field="extensions.body", offset=cursor))
    }
    let ext_end = cursor + ext_total_len
    while cursor + 4 <= ext_end {
      guard read_u16(data, cursor) is Some(ext_type) else {
        return Err(BadLength(field="extension.type", offset=cursor))
      }
      guard read_u16_int(data, cursor + 2) is Some(ext_len) else {
        return Err(BadLength(field="extension.length", offset=cursor + 2))
      }
      cursor = cursor + 4
      if ext_len < 0 ||
        cursor + ext_len > ext_end ||
        !has_range(data, cursor, ext_len) {
        return Err(BadLength(field="extension.body", offset=cursor))
      }
      if !is_grease(ext_type) {
        extensions.push(ext_type)
      }
      // ALPN Extension Type is 16
      if ext_type.to_int() == 16 {
        selected_alpn = parse_server_alpn(data[cursor:cursor + ext_len])
      }
      // Supported Versions Extension Type is 43
      if ext_type.to_int() == 43 {
        if ext_len == 2 {
          guard read_u16(data, cursor) is Some(negotiated_version) else {
            return Err(
              BadLength(field="supported_versions.negotiated", offset=cursor),
            )
          }
          version = negotiated_version
        }
      }
      cursor = cursor + ext_len
    }
    if cursor != ext_end {
      return Err(BadLength(field="extensions.trailing", offset=cursor))
    }
  }

  Ok({
    version,
    random: random_bytes,
    session_id: session_bytes,
    cipher_suite,
    compression_method,
    extensions,
    selected_alpn,
    raw_length: record_end,
  })
}

///|
fn parse_server_alpn(data : BytesView) -> String? {
  // Server-side ALPN selection contains list of protocol names (usually just one)
  // Layout: ALPN extension data length (2 bytes) + 1 byte name length + name
  if data.length() < 3 {
    return None
  }
  guard read_u16_int(data, 0) is Some(list_len) else { return None }
  if list_len + 2 > data.length() {
    return None
  }
  let len = data[2].to_int()
  if len + 3 > data.length() {
    return None
  }
  bytes_to_ascii(data, 3, len)
}

///|
pub fn ServerHello::ja4s_a(self : ServerHello) -> String {
  let ver_str = match self.version.to_int() {
    0x0304 => "s13"
    0x0303 => "s12"
    0x0302 => "s11"
    0x0301 => "s10"
    _ => "s00"
  }
  let alpn_str = if self.selected_alpn is Some(_) { "a" } else { "n" }
  let ext_count = self.extensions.filter(fn(v) { !is_grease(v) }).length()
  "\{ver_str}\{alpn_str}\{ext_count.to_string().pad_start(2, '0')}"
}