///|
/// A handler that serves the current metric set in the text exposition format,
/// with the Prometheus content type — the scrape target go-zero's
/// `prometheus.StartAgent` publishes at `/metrics`.
pub fn metrics_handler(m : @metric.ServerMetrics) -> @moonapi.ApiHandler {
  _ctx => {
    @moonasgi.Response::new(
      200,
      [("content-type", @metric.exposition_content_type)],
      @utf8.encode(m.to_exposition()),
    )
  }
}

///|
/// Register the `GET /metrics` scrape endpoint on `app` (← go-zero's
/// `prometheus.StartAgent`), so a Prometheus server can pull the exposition.
pub fn mount_metrics(app : @moonapi.App, m : @metric.ServerMetrics) -> Unit {
  app.get("/metrics", metrics_handler(m))
}

///|
/// Metrics middleware (← go-zero's `prometheus` interceptor): time each HTTP
/// request on the shared clock and, when the response starts, count it under
/// `"  "` and record its latency in milliseconds. The
/// record is taken once per request even if a downstream (under a recovery race)
/// emits a second start. Non-HTTP scopes pass through unmeasured.
pub fn metrics(m : @metric.ServerMetrics, clock : @clock.Clock) -> Middleware {
  inner => {
    (scope, receive, send) => {
      match scope {
        Http(hs) => {
          let start = clock.now()
          let verb = hs.http_method
          let path = hs.path
          let recorded : Ref[Bool] = { val: false, }
          let observed : @moonasgi.Send = event => {
            match event {
              HttpResponseStart(status~, headers~, trailers~) => {
                if !recorded.val {
                  recorded.val = true
                  m.requests.inc(verb + " " + path + " " + status.to_string())
                  m.latency.observe((clock.now() - start).to_double())
                }
                send(
                  @moonasgi.Event::HttpResponseStart(
                    status~,
                    headers~,
                    trailers~,
                  ),
                )
              }
              other => send(other)
            }
          }
          inner(scope, receive, observed)
        }
        _ => inner(scope, receive, send)
      }
    }
  }
}