///|
/// The limits a server advertises through `GET /pre-flight-checks`.
///
/// chroma-go calls this `PreFlight` and uses it to learn the server's
/// `max_batch_size` before sending a batch; the field is optional because
/// older servers answer with an empty object.
pub(all) struct PreflightChecks {
  /// The largest batch the server accepts on a write endpoint, when it
  /// advertises one.
  max_batch_size : Int32?
  /// The raw response, so a caller can read a field this client does not
  /// model yet.
  raw : Json
} derive(Eq, Debug)

///|
/// Reads a preflight response.
pub fn PreflightChecks::from_json(json : Json) -> PreflightChecks {
  let max_batch_size = match json {
    Json::Object(fields) =>
      match fields.get("max_batch_size") {
        Some(Json::Number(n, ..)) => Some(n.to_int())
        Some(Json::String(s)) => Some(@string.parse_int(s) catch { _ => 0 })
        _ => None
      }
    _ => None
  }
  { max_batch_size, raw: json, }
}

///|
/// Asks the server what limits it applies.
///
/// `GET /pre-flight-checks` is served from the API root (it is not scoped to a
/// tenant or a database), which is why it does not go through
/// `database_prefix`. The answer is advisory: a server that does not implement
/// the endpoint answers 404, and a caller that does not care can ignore it.
pub fn[T : Transport] ChromaClient::preflight(
  self : ChromaClient[T],
) -> PreflightChecks raise ChromaError {
  PreflightChecks::from_json(self.send_json(Get, "pre-flight-checks"))
}

///|
/// The identity the server is serving.
///
/// chroma-go returns this from `GetIdentity`, and only a server with
/// authentication enabled fills in the tenant and database lists.
pub(all) struct Identity {
  user_id : String
  tenant : String
  databases : Array[String]
} derive(Eq, Debug)

///|
/// Reads an identity response, tolerating the fields an older server omits.
pub fn Identity::from_json(json : Json) -> Identity {
  {
    user_id: identity_string(json, "user_id"),
    tenant: identity_string(json, "tenant"),
    databases: identity_strings(json, "databases"),
  }
}

///|
/// Reads an optional string field, defaulting to the empty string.
fn identity_string(json : Json, key : String) -> String {
  match json {
    Json::Object(fields) =>
      match fields.get(key) {
        Some(Json::String(s)) => s
        _ => ""
      }
    _ => ""
  }
}

///|
/// Reads an optional array-of-strings field, defaulting to empty.
fn identity_strings(json : Json, key : String) -> Array[String] {
  match json {
    Json::Object(fields) =>
      match fields.get(key) {
        Some(Json::Array(items)) => {
          let out : Array[String] = []
          for item in items {
            match item {
              Json::String(s) => out.push(s)
              _ => ()
            }
          }
          out
        }
        _ => []
      }
    _ => []
  }
}

///|
pub extend PreflightChecks with Eq::{not_equal, equal}

///|
pub extend PreflightChecks with @debug.Debug::{to_repr}

///|
pub extend Identity with Eq::{not_equal, equal}

///|
pub extend Identity with @debug.Debug::{to_repr}

///|
/// The asynchronous counterpart of `preflight`.
pub async fn[T : AsyncTransport] ChromaClient::preflight_async(
  self : ChromaClient[T],
) -> PreflightChecks raise ChromaError {
  PreflightChecks::from_json(self.send_json_async(Get, "pre-flight-checks"))
}

///|
/// The asynchronous counterpart of `identity`.
pub async fn[T : AsyncTransport] ChromaClient::identity_async(
  self : ChromaClient[T],
) -> Identity raise ChromaError {
  Identity::from_json(self.send_json_async(Get, "auth/identity"))
}

///|
/// Returns the identity the server reports.
///
/// `GET /auth/identity` only answers with a user when the server runs with
/// authentication; otherwise it returns an empty object, which decodes to an
/// `Identity` with blank fields.
pub fn[T : Transport] ChromaClient::identity(
  self : ChromaClient[T],
) -> Identity raise ChromaError {
  Identity::from_json(self.send_json(Get, "auth/identity"))
}