///|
/// Explicit host capability. The caller supplies the built binary and its
/// receipt; binary verification does not attest a caller's build provenance.
pub struct NativeTelemetry {
  priv program : String
  priv helper : String
  priv endpoint : String
  priv protocol : String
  priv timeout : Int32
  priv binary_identity : String
}

///|
pub fn NativeTelemetry::new(
  program : String,
  helper : String,
  endpoint : String,
  protocol : String,
  pin : Json,
  timeout~ : Int32 = 1000,
) -> NativeTelemetry raise {
  let platform = if @platform.system_type() == "macos" {
    "darwin"
  } else {
    @platform.system_type()
  }
  if !@platform.process_supported() ||
    !((platform == "darwin" && @platform.architecture() == "arm64") ||
    (platform == "linux" && @platform.architecture() == "x64")) {
    raise @config.Invalid("observability.native_adapter_host_unavailable")
  }
  if program == "" ||
    helper == "" ||
    !native_telemetry_endpoint(endpoint) ||
    protocol != "http/json" ||
    timeout < 1 ||
    timeout > 5000 {
    raise @config.Invalid("observability.native_adapter_configuration")
  }
  if build_text(pin, "profile") != "morphir-native-otel-pin-v1" ||
    build_text(pin, "platform") != platform ||
    build_text(pin, "arch") != @platform.architecture() ||
    build_text(pin, "compilerVersion") != "0.10.20+65efd5b93" ||
    build_text(pin, "otelArchiveIdentity") !=
    "2039d8609065199553e7275deefa8f9380f9db10d2b23e428eea98d299a2e7da" {
    raise @config.Invalid("observability.native_adapter_pin")
  }
  let binary_identity = build_text(pin, "binaryIdentity")
  if @platform.kind(program) != 1 ||
    binary_identity.length() != 64 ||
    binary_identity != @identity.sha256(@platform.read_bytes(program)) ||
    @platform.kind(helper) != 1 {
    raise @config.Invalid("observability.native_adapter_identity")
  }
  { program, helper, endpoint, protocol, timeout, binary_identity, }
}

///|
pub fn TelemetrySession::flush_native(
  self : TelemetrySession,
  adapter : NativeTelemetry,
  node : String,
) -> Unit {
  self.flush_to_process(
    node,
    adapter.helper,
    adapter.program,
    [adapter.endpoint, adapter.protocol, adapter.timeout.to_string()],
    timeout=adapter.timeout,
    program_identity=Some(adapter.binary_identity),
    clear_otel_environment=true,
  )
}

///|
/// Current adapter accepts an ASCII HTTP(S) origin with DNS/IPv4 authority and
/// optional TCP port. Path prefixes, credentials, queries, fragments and IPv6
/// are outside this profile. Validation is pure and usable during dry-run.
pub fn native_telemetry_endpoint(endpoint : String) -> Bool {
  if endpoint.length() > 4096 {
    return false
  }
  let authority = if endpoint.has_prefix("http://") {
    endpoint[7:].to_owned()
  } else if endpoint.has_prefix("https://") {
    endpoint[8:].to_owned()
  } else {
    return false
  }
  let pieces = authority.split(":").collect()
  if pieces.length() > 2 || pieces.is_empty() {
    return false
  }
  let host = pieces[0]
  if host.length() == 0 || host.length() > 253 {
    return false
  }
  if pieces.length() == 2 {
    let port = pieces[1]
    if port.length() == 0 ||
      port.length() > 5 ||
      !port.code_units().all(c => c is ('0'..<='9')) {
      return false
    }
    let number = @string.parse_int(port) catch { _ => 0 }
    if number < 1 || number > 65535 {
      return false
    }
  }
  let labels = host.split(".").collect()
  for label in labels {
    if label.length() == 0 ||
      label.length() > 63 ||
      !label
      .code_units()
      .all(c => c is ('0'..<='9' | 'a'..<='z' | 'A'..<='Z' | '-')) ||
      label.has_prefix("-") ||
      label.has_suffix("-") {
      return false
    }
  }
  if labels.length() == 4 &&
    labels.all(label => label.code_units().all(c => c is ('0'..<='9'))) {
    for label in labels {
      if label.length() > 3 || (label.length() > 1 && label.has_prefix("0")) {
        return false
      }
      let octet = @string.parse_int(label) catch { _ => 256 }
      if octet > 255 {
        return false
      }
    }
  }
  true
}