///|
/// Private C-owned scalar state. `moonbit_make_external_object` owns its
/// lifetime, so the pthread mutex is finalized with the enclosing MoonBit
/// object instead of leaking as an unmanaged external pointer.
#cfg(target="native")
pub type NativeScalarCell

///|
#cfg(target="native")
extern "C" fn native_scalar_new(initial : Double) -> NativeScalarCell = "moon_prometheus_scalar_new"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeScalarCell::get(self : NativeScalarCell) -> Double = "moon_prometheus_scalar_get"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeScalarCell::set(
  self : NativeScalarCell,
  value : Double,
) -> Unit = "moon_prometheus_scalar_set"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeScalarCell::add(
  self : NativeScalarCell,
  delta : Double,
) -> Unit = "moon_prometheus_scalar_add"

///|
/// Native scalar storage uses one pthread mutex per metric object.
#cfg(target="native")
pub struct ScalarCell {
  native : NativeScalarCell
}

///|
#cfg(target="native")
pub fn ScalarCell::new(initial : Double) -> ScalarCell {
  { native: native_scalar_new(initial) }
}

///|
#cfg(target="native")
pub fn ScalarCell::get(self : ScalarCell) -> Double {
  self.native.get()
}

///|
#cfg(target="native")
pub fn ScalarCell::set(self : ScalarCell, value : Double) -> Unit {
  self.native.set(value)
}

///|
#cfg(target="native")
pub fn ScalarCell::add(self : ScalarCell, delta : Double) -> Unit {
  self.native.add(delta)
}

///|
/// C-owned mutex object used to guard native Vec and Registry tables. It has
/// no user-visible lock type in the facade and is finalized automatically.
#cfg(target="native")
pub type NativeMutex

///|
#cfg(target="native")
extern "C" fn native_mutex_new() -> NativeMutex = "moon_prometheus_mutex_new"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeMutex::lock(self : NativeMutex) -> Unit = "moon_prometheus_mutex_lock"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeMutex::unlock(self : NativeMutex) -> Unit = "moon_prometheus_mutex_unlock"

///|
#cfg(target="native")
pub struct Mutex {
  native : NativeMutex
}

///|
#cfg(target="native")
pub fn Mutex::new() -> Mutex {
  { native: native_mutex_new() }
}

///|
#cfg(target="native")
pub fn Mutex::lock(self : Mutex) -> Unit {
  self.native.lock()
}

///|
#cfg(target="native")
pub fn Mutex::unlock(self : Mutex) -> Unit {
  self.native.unlock()
}

///|
#cfg(target="native")
pub type NativeHistogramCell

///|
#cfg(target="native")
priv type NativeHistogramSnapshot

///|
#cfg(target="native")
extern "C" fn native_histogram_new(length : Int) -> NativeHistogramCell = "moon_prometheus_histogram_new"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeHistogramCell::observe(
  self : NativeHistogramCell,
  first_bucket : Int,
  value : Double,
) -> Unit = "moon_prometheus_histogram_observe"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeHistogramCell::copy_snapshot(
  self : NativeHistogramCell,
) -> NativeHistogramSnapshot = "moon_prometheus_histogram_copy_snapshot"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeHistogramSnapshot::count_at(
  self : NativeHistogramSnapshot,
  index : Int,
) -> Int64 = "moon_prometheus_histogram_snapshot_count_at"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeHistogramSnapshot::count(
  self : NativeHistogramSnapshot,
) -> Int64 = "moon_prometheus_histogram_snapshot_count"

///|
#cfg(target="native")
#borrow(self)
extern "C" fn NativeHistogramSnapshot::sum(
  self : NativeHistogramSnapshot,
) -> Double = "moon_prometheus_histogram_snapshot_sum"

///|
/// Native histogram storage keeps the mutex-protected counters in C while
/// retaining immutable bucket boundaries in MoonBit for bucket lookup.
#cfg(target="native")
pub struct HistogramCell {
  boundaries : Array[Double]
  native : NativeHistogramCell
}

///|
#cfg(target="native")
pub fn HistogramCell::new(boundaries : Array[Double]) -> HistogramCell {
  {
    boundaries: boundaries.copy(),
    native: native_histogram_new(boundaries.length()),
  }
}

///|
#cfg(target="native")
fn HistogramCell::first_bucket(self : HistogramCell, value : Double) -> Int {
  for index, boundary in self.boundaries {
    if value <= boundary {
      return index
    }
  }
  self.boundaries.length()
}

///|
#cfg(target="native")
pub fn HistogramCell::observe(self : HistogramCell, value : Double) -> Unit {
  self.native.observe(self.first_bucket(value), value)
}

///|
#cfg(target="native")
pub fn HistogramCell::snapshot(self : HistogramCell) -> HistogramCellSnapshot {
  let native_snapshot = self.native.copy_snapshot()
  let counts : Array[Int64] = []
  for index in 0..