// A worked example wiring the two new pieces together: a dependency provides a
// value, a typed request body is deserialised + validated off its descriptor,
// and the handler uses both — FastAPI's `def greet(body: GreetReq, greeter: str
// = Depends(...))` in explicit MoonBit form.

///|
/// The request body of `POST /greet`. `derive(@json.FromJson)` lets
/// `Context::body_validated` build it after the descriptor accepts the payload;
/// its schema and struct fields agree (both require `name`), so a schema-valid
/// body always deserialises.
pub(all) struct GreetReq {
  name : String
} derive(ToJson, FromJson, Eq)

///|
/// The `GreetReq` descriptor — one required string field.
pub fn GreetReq::schema() -> Schema {
  Schema::object("GreetReq", [Field::new("name", SStr)])
}

///|
/// The dependency value type of the greet app. A sum type wrapping every
/// dependency this app injects — the explicit, exhaustive stand-in for FastAPI
/// resolving heterogeneous `Depends` values dynamically.
pub(all) enum Dep {
  Greeting(String)
} derive(Eq)

///|
/// Build the greet application over a caller-supplied dependency `container`, so
/// a test can register `dependency_overrides` on the same container before or
/// between requests. `POST /greet` resolves the `"greeting"` dependency, reads a
/// validated `GreetReq` body, and answers `{"message": ", "}`;
/// a malformed body gets a FastAPI-shaped `422`. The dependency scope brackets
/// each request, so any `yield` teardown runs once the handler returns.
pub fn greet_app(container : Container[Dep]) -> App {
  let app = App::new()
  let ep = Endpoint::new(request_body=Some(GreetReq::schema()), responses=[
    ResponseSpec::new(200, description="the greeting"),
    ResponseSpec::new(422, description="Validation Error"),
  ])
  app.post(
    "/greet",
    ctx => {
      container.run(scope => {
        let greeting = match scope.get("greeting") {
          Some(Greeting(g)) => g
          None => "Hello"
        }
        match (ctx.body_validated(GreetReq::schema()) : Result[GreetReq, _]) {
          Err(errs) => unprocessable(errs)
          Ok(req) => {
            let m : Map[String, Json] = Map([
              ("message", "\{greeting}, \{req.name}".to_json()),
            ])
            json(200, m.to_json())
          }
        }
      })
    },
    summary="greet a user",
    endpoint=Some(ep),
  )
  app
}