///|
/// The runtime template engine — moonctl's faithful `text/template` (goctl drives
/// codegen through Go's). MoonBit has no reflection, so template data is an
/// explicit tagged `Value`. This walks `render` over hand-built data
/// (`{{.field}}` interpolation, `{{range}}`, `{{if}}`, `|` pipelines, the built-in
/// function library, `printf`), a reusable `Template` with a caller-registered
/// `func`, and the codegen seam: `spec_to_value` exposes a parsed `.api` `Spec` as
/// `Value`, and `generate_with` / `generate(spec, template=...)` scaffold from a
/// caller's own template instead of the built-in generator.
///
///     moon run src/examples/09-template
#coverage.skip
fn dict(entries : Array[(String, @moonctl.Value)]) -> @moonctl.Value {
  Dict(Map(entries))
}

///|
#coverage.skip
fn main {
  let data = dict([
    ("service", Str("greet")),
    ("count", Int(233L)),
    (
      "routes",
      List([
        dict([("verb", Str("get")), ("path", Str("/ping"))]),
        dict([("verb", Str("post")), ("path", Str("/users"))]),
      ]),
    ),
  ])
  try {
    println("==== render over hand-built Value data ====\n")
    println(@moonctl.render("service = {{.service}} ({{.count}} routes)", data))
    println(
      @moonctl.render(
        "{{range .routes}}{{.verb | upper}} {{.path}}\n{{end}}", data,
      ),
    )
    println(@moonctl.render("{{printf \"%s -> %d\" .service .count}}", data))

    // A reusable Template with a caller-registered pipeline function; the piped
    // value arrives as the last argument, so `bang` chains under `|`.
    let bang = @moonctl.Template::new().func("bang", xs => {
      match xs[xs.length() - 1] {
        Str(s) => Str(s + "!")
        other => other
      }
    })
    println("\n==== Template::func — a custom pipeline function ====\n")
    println(bang.parse("{{.service | bang | bang}}").render(data))

    // The codegen seam: a parsed spec exposed as template data, then scaffolded
    // through a caller-supplied template two equivalent ways.
    let source =
      #|type User {
      #|  id:   int64
      #|  name: string
      #|}
      #|
      #|service greet {
      #|  get  /ping       ping        "health check"
      #|  post /users      create_user "create a user"
      #|}
    let spec = @moonctl.parse(source)
    let tmpl =
      #|// service: {{.service}}
      #|{{range .routes}}route {{.verb}} {{.path}} -> {{.handler}}{{if .hasSummary}} ({{.summary}}){{end}}
      #|{{end}}{{range .types}}type {{.name}}: {{range .fields}}{{.name}}:{{.type}} {{end}}
      #|{{end}}
    println("\n==== render(tmpl, spec_to_value(spec)) ====\n")
    println(@moonctl.render(tmpl, @moonctl.spec_to_value(spec)))
    let via_with = @moonctl.generate_with(spec, tmpl)
    let via_param = @moonctl.generate(spec, template=tmpl)
    println("generate_with == generate(template=): \{via_with == via_param}")
  } catch {
    _ => println("template render failed")
  }
}