// The DNS message codec (RFC 1035) behind gRPC's `dns:///` resolver: encode a
// recursion-desired A/AAAA query, decode a response, and pull out the resolved
// addresses (§4.1.4 name-compression pointers are followed). This encodes a query for
// `grpc.example.com`, then hand-builds a response with two A records and one AAAA record
// — the answer bytes a UDP resolver would hand back — and decodes it.
//
//     moon run src/examples/24-dns-resolver

///|
fn be16(buf : Buffer, v : Int32) -> Unit {
  buf.write_byte(((v >> 8) & 0xFF).to_byte())
  buf.write_byte((v & 0xFF).to_byte())
}

///|
fn push_all(buf : Buffer, xs : Array[Int32]) -> Unit {
  for x in xs {
    buf.write_byte((x & 0xFF).to_byte())
  }
}

///|
/// Build the wire response `8.8.8.8` would return for `grpc.example.com`: a header, the
/// echoed question, and three answers. Answer names use the §4.1.4 compression pointer
/// (`0xC00C`) back to the question name at offset 12.
fn build_response() -> Bytes {
  let buf = Buffer()
  // @header.Header: id, flags (response, RD, RA, NOERROR), QD=1, AN=3, NS=0, AR=0.
  be16(buf, 0x1234)
  be16(buf, 0x8180)
  be16(buf, 1)
  be16(buf, 3)
  be16(buf, 0)
  be16(buf, 0)
  // Question: grpc.example.com, type A, class IN.
  push_all(buf, [4, 'g'.to_int(), 'r'.to_int(), 'p'.to_int(), 'c'.to_int()])
  push_all(buf, [
    7,
    'e'.to_int(),
    'x'.to_int(),
    'a'.to_int(),
    'm'.to_int(),
    'p'.to_int(),
    'l'.to_int(),
    'e'.to_int(),
  ])
  push_all(buf, [3, 'c'.to_int(), 'o'.to_int(), 'm'.to_int(), 0])
  be16(buf, 1)
  be16(buf, 1)
  // Answer 1: A 93.184.216.34.
  fn a_record(rdata : Array[Int32]) -> Unit {
    push_all(buf, [0xC0, 0x0C]) // pointer to the question name
    be16(buf, 1) // type A
    be16(buf, 1) // class IN
    push_all(buf, [0, 0, 1, 0x2C]) // TTL 300
    be16(buf, rdata.length())
    push_all(buf, rdata)
  }

  a_record([93, 184, 216, 34])
  a_record([93, 184, 216, 35])
  // Answer 3: AAAA 2606:2800:220:1:248:1893:25c8:1946.
  push_all(buf, [0xC0, 0x0C])
  be16(buf, 28) // type AAAA
  be16(buf, 1)
  push_all(buf, [0, 0, 1, 0x2C])
  be16(buf, 16)
  push_all(buf, [
    0x26, 0x06, 0x28, 0x00, 0x02, 0x20, 0x00, 0x01, 0x02, 0x48, 0x18, 0x93, 0x25,
    0xc8, 0x19, 0x46,
  ])
  buf.to_bytes()
}

///|
fn run() -> Unit {
  // Encode a recursion-desired A query.
  let query = @moonrpc.dns_encode_query(
    0x1234, "grpc.example.com", @moonrpc.dns_type_a,
  )
  println("A query (\{query.length()} octets): \{@base16.encode(query)}")

  // Decode the response and read the addresses out.
  let response = @moonrpc.dns_decode_response(build_response())
  println(
    "response id=\{response.id}, rcode=\{response.rcode}, \{response.answers.length()} answers:",
  )
  for rec in response.answers {
    let kind = if rec.rtype == @moonrpc.dns_type_a {
      "A"
    } else if rec.rtype == @moonrpc.dns_type_aaaa {
      "AAAA"
    } else {
      "other"
    }
    println("  \{rec.name} \{kind} ttl=\{rec.ttl} -> \{rec.address}")
  }
  println("resolver addresses:")
  for addr in response.addresses() {
    println("  \{addr}")
  }
}

///|
#coverage.skip
fn main {
  run()
}