// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// How one request body is written.
///
/// An exporter needs four things from an encoding and they belong together: the
/// encoder, the `Content-Type` the body is sent under, and the way a reply's count
/// of refused records is read. All three move with the encoding, so a transport
/// cannot end up announcing one and carrying the other.
///
/// This package writes the JSON mapping and nothing else. Binary protobuf is its
/// own package, `otlp/binary`, because it needs the protocol's schema and a runtime
/// to walk it; a program that sends JSON should not compile either.
pub(open) trait Encoding {
  /// Which of the two encodings this is, and with it the media type.
  fn format(Self) -> BodyFormat
  /// One `/v1/traces` request body.
  fn traces(Self, Array[@trace.SpanData], Resource, Scope) -> Bytes
  /// One `/v1/logs` request body.
  fn logs(Self, Array[@log.LogRecord], Resource, Scope) -> Bytes
  /// One `/v1/metrics` request body.
  fn metrics(Self, Array[Metric], Resource, Scope) -> Bytes
  /// How many records a reply says the server refused.
  ///
  /// `None` when the reply says nothing: an accepted request that lost some data is
  /// still an accepted request, and "said nothing" is not "refused none".
  fn rejected(Self, Bytes) -> Int32?
}

///|
/// The protobuf JSON mapping, which is what this package writes on its own.
pub struct JsonEncoding {}

///|
/// The JSON encoding, ready to hand to an exporter.
pub let json : &Encoding = JsonEncoding::{ }

///|
impl Encoding for JsonEncoding with fn format(_self) {
  Json
}

///|
impl Encoding for JsonEncoding with fn traces(_self, spans, resource, scope) {
  @utf8.encode(encode_traces(spans, resource~, scope~))
}

///|
impl Encoding for JsonEncoding with fn logs(_self, records, resource, scope) {
  @utf8.encode(encode_logs(records, resource~, scope~))
}

///|
impl Encoding for JsonEncoding with fn metrics(_self, metrics, resource, scope) {
  @utf8.encode(encode_metrics(metrics, resource~, scope~))
}

///|
impl Encoding for JsonEncoding with fn rejected(_self, body) {
  rejected_in_json(@utf8.decode_lossy(body))
}