// The GraphQL service tree: the schema generated from a `.api` spec under
// `internal/schema`, the author's resolvers under `internal/logic`, served as an
// ASGI application on mooncat.

///|
/// The resolvers seeded for `spec`: `ping` answers, and each type's lookup starts
/// out empty for the author to fill.
fn resolvers_src(spec : @api.Spec) -> String {
  let out = StringBuilder()
  out.write_string(
    "///|\n/// The root fields' resolvers. An object field reads its parent's member of the\n/// same name unless a resolver for it is added here.\npub fn resolvers(_svc : @svc.ServiceContext) -> @moongql.Resolvers {\n  let resolvers = @moongql.Resolvers::new()\n  resolvers.field(\"Query\", \"ping\", _ => Json::string(\"pong\"))\n",
  )
  for t in spec.types {
    out.write_string(
      "  resolvers.field(\"Query\", " +
      @text.quote(@text.lower_first(t.name)) +
      ", _ => Json::null())\n",
    )
  }
  out.write_string("  resolvers\n}\n")
  out.to_string()
}

///|
/// The layered GraphQL tree for `spec` under `dir`: `internal/schema` with
/// `build_schema()` (rewritten on every run), `internal/logic` with the resolvers
/// and a test that queries `ping`, `internal/config`/`internal/svc` (seeded once),
/// and the entry point serving `graphql_app` on 12000.
///
/// Hand the result to `tree_plan` to keep what the author has filled in.
pub fn generate_gql_tree(
  spec : @api.Spec,
  style? : @style.Style,
  dir~ : String = "",
) -> Array[TreeFile] {
  let st = style.unwrap_or(@style.Style::gozero())
  let mod_ = spec.service
  let gql = ("moonbitstack/moongql", "")
  let schema_pkg = mod_ + "/internal/schema"
  let logic_pkg = mod_ + "/internal/logic"
  let svc_pkg = mod_ + "/internal/svc"
  let config_pkg = mod_ + "/internal/config"
  let out = base_tree(
    mod_,
    st,
    dir,
    deps=["moonbitstack/moongql", "moonbitstack/mooncat"],
    imports=[
      gql,
      ("moonbitstack/mooncat", ""),
      (schema_pkg, ""),
      (logic_pkg, ""),
    ],
    main=asgi_main(
      mod_, "GraphQL", "@moongql.graphql_app(@schema.build_schema(), @logic.resolvers(svc))",
    ),
  )
  out.push({
    path: at(dir, "internal/schema/moon.pkg"),
    content: pkg_file([gql]),
    regen: Always,
  })
  out.push({
    path: at(dir, "internal/schema/" + st.format("schema") + ".mbt"),
    content: @gql.generate_gql(spec),
    regen: Always,
  })
  let tests = pkg_file([gql, (config_pkg, ""), (schema_pkg, "")]).replace(
    old="}\n",
    new="} for \"test\"\n",
  )
  out.push({
    path: at(dir, "internal/logic/moon.pkg"),
    content: pkg_file([gql, (svc_pkg, "")]) + "\n" + tests,
    regen: Once,
  })
  out.push({
    path: at(dir, "internal/logic/" + st.format("resolvers") + ".mbt"),
    content: resolvers_src(spec),
    regen: Once,
  })
  let test_src =
    #|///|
    #|test "ping answers" {
    #|  let svc = @svc.ServiceContext::new(@config.Config::new())
    #|  let got = @moongql.execute(@schema.build_schema(), @logic.resolvers(svc), "{ ping }")
    #|  assert_eq(got, { "data": { "ping": "pong" } })
    #|}
  out.push({
    path: at(dir, "internal/logic/" + st.format("resolvers") + "_test.mbt"),
    content: test_src + "\n",
    regen: Once,
  })
  out
}