///|
/// Read a string field off a representation object, or "".
fn rep_str(rep : Json, key : String) -> String {
  match rep {
    Object(m) =>
      match m.get(key) {
        Some(String(s)) => s
        _ => ""
      }
    _ => ""
  }
}

///|
/// Read an integer field off a representation object, or 0.
fn rep_int(rep : Json, key : String) -> Int32 {
  match rep {
    Object(m) =>
      match m.get(key) {
        Some(Number(n, ..)) => n.to_int()
        _ => 0
      }
    _ => 0
  }
}

///|
/// Apollo Federation v2. `Federation` layers subgraph metadata over a plain schema:
/// `entity` declares a `@key` type with a reference resolver, and `shareable` /
/// `override_` / `requires` / `provides` / `inaccessible` attach the v2 field
/// directives. `apply` installs the federation machinery, `sdl` prints the subgraph
/// SDL, and a gateway drives the subgraph through `_service { sdl }` and
/// `_entities(representations:)` — the latter resolving each `{ __typename, key }`
/// reference back into a full object.
///
///     moon run src/examples/20-federation
#coverage.skip
fn main {
  let s = @moongql.Schema::new()
  let q = s.object("Query")
  q.field("me", Named("User"))
  let u = s.object("User")
  u.field("id", NonNull(Scalar("ID")))
  u.field("name", NonNull(Scalar("String")))
  u.field("ssn", Scalar("String"))
  let p = s.object("Product")
  p.field("upc", NonNull(Scalar("String")))
  p.field("name", NonNull(Scalar("String")))
  p.field("price", Scalar("Int"))
  p.field("weight", Scalar("Int"))
  p.field("shippingEstimate", Scalar("Int"))
  let r = @moongql.Resolvers::new()
  let fed = @moongql.Federation::new(v2=true)
  fed.entity(name="User", key="id", resolve=(rep, _ctx) => {
    let id = rep_str(rep, "id")
    { "id": Json(id), "name": Json("User#" + id) }
  })
  fed.entity(name="Product", key="upc", extends=true, external=["weight"], resolve=(
    rep,
    _ctx,
  ) => {
    let upc = rep_str(rep, "upc")
    let weight = rep_int(rep, "weight")
    { "upc": Json(upc), "shippingEstimate": Json(weight * 2) }
  })
  fed.shareable("Product", "name")
  fed.override_("Product", "price", from="legacy")
  fed.requires("Product", "shippingEstimate", fields="weight")
  fed.provides("Query", "me", fields="name")
  fed.inaccessible("User", "ssn")
  fed.apply(s, r)
  println(fed.sdl(s))
  println(@moongql.execute(s, r, "{ _service { sdl } }").stringify())
  let reps : Array[Json] = [
    { "__typename": "User", "id": "7" },
    { "__typename": "Product", "upc": "abc", "weight": 10 },
  ]
  let vars : Map[String, Json] = { "reps": Json(reps) }
  let query =
    #|query ($reps: [_Any!]!) {
    #|  _entities(representations: $reps) {
    #|    ... on User { id name }
    #|    ... on Product { upc shippingEstimate }
    #|  }
    #|}
  println(@moongql.execute(s, r, query, variables=vars).stringify())
}