///|
/// Log body or attribute value accepted by the public logging API.
///
/// Values remain structured when bridged into the SDK and OTLP exporters. Use
/// scalar attributes for common query dimensions and structured bodies for
/// payloads that should stay grouped.
/// Spec: https://opentelemetry.io/docs/specs/otel/common/#anyvalue
pub(all) enum AnyValue {
  Int(Int64)
  Double(Double)
  String(String)
  Boolean(Bool)
  Bytes(Bytes)
  ListAny(Array[AnyValue])
  Map(Map[String, AnyValue])
} derive(Eq, ToJson, Debug)

///|
/// Public severity ladder matching the OpenTelemetry log data model.
///
/// The variants map to OpenTelemetry's 24 severity slots. `Severity::name()`
/// returns the canonical uppercase text such as `INFO`, `WARN3`, or `ERROR`.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#severity-fields
pub(all) enum Severity {
  Trace
  Trace2
  Trace3
  Trace4
  Debug
  Debug2
  Debug3
  Debug4
  Info
  Info2
  Info3
  Info4
  Warn
  Warn2
  Warn3
  Warn4
  Error
  Error2
  Error3
  Error4
  Fatal
  Fatal2
  Fatal3
  Fatal4
} derive(Eq, Compare, Hash, ToJson, Debug)

///|
/// Structured log attribute key/value pair used by the public logging API.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes
pub struct KeyValue {
  key : @common.Key
  value : AnyValue
} derive(Eq, ToJson, Debug)

///|
/// Mutable log record builder used by `Logger::emit()`.
///
/// Construct records only after checking `Logger::event_enabled()` when
/// formatting or attribute construction is expensive. `emit()` fills missing
/// timestamps and default body values when an SDK logger is installed.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#log-and-event-record-definition
pub struct LogRecord {
  mut event_name : String?
  mut target : String?
  mut timestamp_unix_nano : Int64?
  mut observed_timestamp_unix_nano : Int64?
  mut severity_text : String?
  mut severity_number : Severity?
  mut body : AnyValue?
  attributes : Array[KeyValue]
  mut trace_context : @common.SpanContext?
} derive(Eq, ToJson, Debug)

///|
/// Public logger handle.
///
/// A logger emits records for one instrumentation scope. When backed by a no-op
/// provider, all emissions are silently dropped.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#logger
pub struct Logger {
  emit_fn : async (LogRecord) -> Unit
  event_enabled_fn : (Severity, StringView, String?) -> Bool
}

///|
/// Public logger provider.
///
/// Applications install SDK-backed providers through the SDK facade while
/// library code depends only on this API type.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#loggerprovider
pub struct LoggerProvider {
  logger_with_scope_fn : (@common.InstrumentationScope) -> Logger
}

///|
/// Returns the canonical uppercase text form for one severity variant.
///
/// If `LogRecord::severity_text` is not set, SDK adapters derive it from this
/// method when a severity number exists.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#mapping-of-severitynumber
pub fn Severity::name(self : Severity) -> String {
  match self {
    Trace => "TRACE"
    Trace2 => "TRACE2"
    Trace3 => "TRACE3"
    Trace4 => "TRACE4"
    Debug => "DEBUG"
    Debug2 => "DEBUG2"
    Debug3 => "DEBUG3"
    Debug4 => "DEBUG4"
    Info => "INFO"
    Info2 => "INFO2"
    Info3 => "INFO3"
    Info4 => "INFO4"
    Warn => "WARN"
    Warn2 => "WARN2"
    Warn3 => "WARN3"
    Warn4 => "WARN4"
    Error => "ERROR"
    Error2 => "ERROR2"
    Error3 => "ERROR3"
    Error4 => "ERROR4"
    Fatal => "FATAL"
    Fatal2 => "FATAL2"
    Fatal3 => "FATAL3"
    Fatal4 => "FATAL4"
  }
}

///|
fn sorted_keys(values : Map[String, AnyValue]) -> Array[String] {
  let keys = values.keys().to_array()
  for i in 1.. 0 && current < keys[j - 1] {
      keys[j] = keys[j - 1]
      j = j - 1
    }
    keys[j] = current
  }
  keys
}

///|
/// Converts a shared attribute value into a public log value.
/// Spec: https://opentelemetry.io/docs/specs/otel/common/#anyvalue
pub fn AnyValue::from_common(value : @common.Value) -> AnyValue {
  match value {
    Bool(value) => Boolean(value)
    Int64(value) => Int(value)
    Double(value) => Double(value)
    String(value) => String(value)
    Bytes(value) => Bytes(value)
    Array(values) => {
      let converted = []
      for value in values {
        converted.push(AnyValue::from_common(value))
      }
      ListAny(converted)
    }
  }
}

///|
/// Creates a structured log attribute.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes
pub fn KeyValue::new(key : StringView, value : AnyValue) -> KeyValue {
  { key: @common.Key::new(key), value }
}

///|
/// Converts a shared attribute into a public log attribute.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes
pub fn KeyValue::from_common(attribute : @common.KeyValue) -> KeyValue {
  { key: attribute.key, value: AnyValue::from_common(attribute.value) }
}

///|
/// Returns a map value with deterministic key insertion order.
/// Spec: https://opentelemetry.io/docs/specs/otel/common/#anyvalue
pub fn AnyValue::map(values : Map[String, AnyValue]) -> AnyValue {
  let sorted : Map[String, AnyValue] = {}
  for key in sorted_keys(values) {
    guard values.get(key) is Some(value)
    sorted[key] = value
  }
  Map(sorted)
}

///|
/// Creates an empty mutable log record.
///
/// The record is not tied to a logger until passed to `Logger::emit()`.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#log-and-event-record-definition
pub fn LogRecord::new() -> LogRecord {
  {
    event_name: None,
    target: None,
    timestamp_unix_nano: None,
    observed_timestamp_unix_nano: None,
    severity_text: None,
    severity_number: None,
    body: None,
    attributes: [],
    trace_context: None,
  }
}

///|
/// Sets the event name that will later be exported through the OTLP
/// `event_name` field.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-eventname
pub fn LogRecord::set_event_name(self : LogRecord, name : StringView) -> Unit {
  self.event_name = Some(name.to_owned())
}

///|
/// Sets the log target used for export-time scope grouping.
/// Related spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-instrumentationscope
pub fn LogRecord::set_target(self : LogRecord, target : StringView) -> Unit {
  self.target = Some(target.to_owned())
}

///|
/// Sets the event timestamp in Unix nanoseconds.
///
/// When omitted, SDK-backed loggers use the current time.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-timestamp
pub fn LogRecord::set_timestamp(
  self : LogRecord,
  timestamp_unix_nano : Int64,
) -> Unit {
  self.timestamp_unix_nano = Some(timestamp_unix_nano)
}

///|
/// Sets the observed timestamp in Unix nanoseconds.
///
/// When omitted, SDK-backed loggers use the current time.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-observedtimestamp
pub fn LogRecord::set_observed_timestamp(
  self : LogRecord,
  observed_timestamp_unix_nano : Int64,
) -> Unit {
  self.observed_timestamp_unix_nano = Some(observed_timestamp_unix_nano)
}

///|
/// Sets the exact severity text to export.
///
/// If this is not set but a severity number is present, SDK-backed loggers
/// derive the text from `Severity::name()`.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitytext
pub fn LogRecord::set_severity_text(
  self : LogRecord,
  severity_text : StringView,
) -> Unit {
  self.severity_text = Some(severity_text.to_owned())
}

///|
/// Sets the structured severity number to export.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber
pub fn LogRecord::set_severity_number(
  self : LogRecord,
  severity_number : Severity,
) -> Unit {
  self.severity_number = Some(severity_number)
}

///|
/// Sets the log body.
///
/// If no body is set, SDK-backed loggers export an empty string body.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body
pub fn LogRecord::set_body(self : LogRecord, body : AnyValue) -> Unit {
  self.body = Some(body)
}

///|
/// Adds one attribute to the log record.
///
/// Attribute keys should be stable names. Prefer scalar values for fields that
/// users will search or group by.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes
pub fn LogRecord::add_attribute(
  self : LogRecord,
  key : StringView,
  value : AnyValue,
) -> Unit {
  self.attributes.push(KeyValue::new(key, value))
}

///|
/// Appends multiple pre-built attributes to the log record.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes
pub fn LogRecord::add_attributes(
  self : LogRecord,
  attributes : ArrayView[@common.KeyValue],
) -> Unit {
  for attribute in attributes {
    self.attributes.push(KeyValue::from_common(attribute))
  }
}

///|
/// Stores explicit trace correlation fields on the record.
///
/// Use this when bridging logs from code that already knows the trace and span
/// identifiers. When `trace_flags` is omitted, the trace flags default to zero.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#trace-context-fields
pub fn LogRecord::set_trace_context(
  self : LogRecord,
  trace_id : @common.TraceId,
  span_id : @common.SpanId,
  trace_flags? : @common.TraceFlags? = None,
) -> Unit {
  self.trace_context = Some(
    @common.SpanContext::new(
      trace_id,
      span_id,
      trace_flags=match trace_flags {
        Some(trace_flags) => trace_flags
        None => Default::default()
      },
    ),
  )
}

///|
pub impl Default for LogRecord with fn default() -> LogRecord {
  LogRecord::new()
}

///|
/// Builds a logger from emission and filtering callbacks.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#logger
pub fn Logger::from_functions(
  emit_fn : async (LogRecord) -> Unit,
  event_enabled_fn? : (Severity, StringView, String?) -> Bool = (_, _, _) => {
    true
  },
) -> Logger {
  { emit_fn, event_enabled_fn }
}

///|
/// Builds a logger provider from a scope-to-logger callback.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#loggerprovider
pub fn LoggerProvider::from_functions(
  logger_with_scope_fn : (@common.InstrumentationScope) -> Logger,
) -> LoggerProvider {
  { logger_with_scope_fn, }
}

///|
/// Returns a no-op logger provider.
///
/// Loggers from this provider report `event_enabled() == false` and drop
/// emitted records.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#loggerprovider
pub fn LoggerProvider::noop() -> LoggerProvider {
  LoggerProvider::from_functions(_ => {
    Logger::from_functions(_ => (), event_enabled_fn=(_, _, _) => false)
  })
}

///|
/// Returns a logger for one instrumentation name.
///
/// If the provider is no-op, the returned logger is also no-op.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#get-a-logger
pub fn LoggerProvider::logger(
  self : LoggerProvider,
  name : StringView,
) -> Logger {
  self.logger_with_scope(@common.InstrumentationScope::builder(name).build())
}

///|
/// Returns a logger for a fully constructed instrumentation scope.
///
/// The scope name, version, schema URL, and attributes are forwarded to the SDK
/// provider by SDK adapters.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#get-a-logger
pub fn LoggerProvider::logger_with_scope(
  self : LoggerProvider,
  scope : @common.InstrumentationScope,
) -> Logger {
  (self.logger_with_scope_fn)(scope)
}

///|
/// Creates a fresh mutable `LogRecord`.
///
/// The returned record is not pre-populated with scope or provider data.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord
pub fn Logger::create_log_record(self : Logger) -> LogRecord {
  ignore(self)
  LogRecord::new()
}

///|
/// Emits one log record through the underlying logger.
///
/// Missing timestamps, severity text, and body defaults are supplied by SDK
/// adapters. No-op loggers drop the record silently.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord
pub async fn Logger::emit(self : Logger, record : LogRecord) -> Unit {
  (self.emit_fn)(record)
}

///|
/// Returns whether log emission is currently enabled for this logger.
///
/// Use this as a guard before expensive message formatting or structured body
/// construction.
///
/// OTel specifies the enabled guard over context, severity, and event name;
/// `target` is this API's local filtering dimension.
/// Spec: https://opentelemetry.io/docs/specs/otel/logs/api/#enabled
pub fn Logger::event_enabled(
  self : Logger,
  severity : Severity,
  target : StringView,
  name? : String? = None,
) -> Bool {
  (self.event_enabled_fn)(severity, target, name)
}