// The exact bytes a gRPC client puts on the wire for one unary call —
// greet.Greeter/SayHello — assembled from moonrpc's pure codecs with no server
// and no socket: the HTTP/2 frame layer (RFC 7540), an HPACK header block
// (RFC 7541), the length-prefixed gRPC message, and the protobuf body. The same
// bytes are then decoded straight back, the way a reader drains them off a
// connection.
//
//     moon run src/examples/00-unary-on-the-wire

///|
fn ascii(s : String) -> Bytes {
  let buf = Buffer()
  for i = 0; i < s.length(); i = i + 1 {
    buf.write_byte((s[i].to_int() & 0xFF).to_byte())
  }
  buf.to_bytes()
}

///|
/// A `HelloRequest { name = 1 }` in the proto3 binary format.
fn hello_request(name : String) -> Bytes {
  let w = @moonrpc.PbWriter::new()
  w.string_(1, name)
  w.to_bytes()
}

///|
/// The HEADERS a unary caller sends: the HTTP/2 pseudo-headers, the gRPC content
/// type, and `te: trailers`.
fn request_headers(rpc : @moonrpc.Method) -> Array[@header.Header] {
  [
    { name: b":method", value: b"POST", },
    { name: b":scheme", value: b"http", },
    { name: b":path", value: ascii(rpc.path()), },
    { name: b":authority", value: b"127.0.0.1:50051", },
    { name: b"content-type", value: b"application/grpc+proto", },
    { name: b"te", value: b"trailers", },
  ]
}

///|
#coverage.skip
fn main {
  let rpc = @moonrpc.Method::{ service: "greet.Greeter", name: "SayHello", }
  let headers = request_headers(rpc)
  let block = @hpack.Encoder::new().encode(headers)
  let msg = @moonrpc.encode_message(hello_request("heke1228"))
  let head = @http2.Frame::Headers(
    stream_id=1,
    fragment=block,
    end_stream=false,
    end_headers=true,
    priority=None,
    padding=0,
  )
  let data = @http2.Frame::Data(
    stream_id=1,
    data=msg,
    end_stream=true,
    padding=0,
  )
  let wire = {
    let buf = Buffer()
    buf.write_bytes(head.encode())
    buf.write_bytes(data.encode())
    buf.to_bytes()
  }
  println("POST \{rpc.path()}")
  println(
    "\{wire.length()} octets on the wire: HEADERS + DATA(END_STREAM) on stream 1",
  )
  let dec = @hpack.Decoder::new()
  let mut off = 0
  try {
    while off < wire.length() {
      let (frame, consumed) = @http2.decode(wire, offset=off)
      off += consumed
      match frame {
        Headers(fragment~, ..) => {
          let back = dec.decode(fragment)
          let same = if back == headers { "ok" } else { "MISMATCH" }
          println("  \{back.length()} headers, HPACK round-trip: \{same}")
        }
        Data(data~, ..) => {
          let (compressed, payload) = @moonrpc.decode_message(data).unwrap()
          let r = @moonrpc.PbReader::new(payload)
          let (field, _) = r.read_tag()
          println(
            "  message: compressed=\{compressed}, field \{field} = \{r.read_string()}",
          )
        }
        _ => ()
      }
    }
  } catch {
    _ => println("  wire did not decode")
  }
  let ok : @moonrpc.Status = Ok
  let missing : @moonrpc.Status = NotFound
  println(
    "grpc-status \{ok.code()} = \{ok.name()}, \{missing.code()} = \{missing.name()}",
  )
}