///|
/// Standard magnitude scales supported by the analysis helpers.
pub enum MagnitudeScale {
  LocalMagnitude
  BodyWaveMagnitude
  SurfaceWaveMagnitude
  DurationMagnitude
  MomentMagnitude
}

///|
/// Return scale constructors for configuration-driven callers.
pub fn scale_local() -> MagnitudeScale {
  LocalMagnitude
}

///|
pub fn scale_body() -> MagnitudeScale {
  BodyWaveMagnitude
}

///|
pub fn scale_surface() -> MagnitudeScale {
  SurfaceWaveMagnitude
}

///|
pub fn scale_duration() -> MagnitudeScale {
  DurationMagnitude
}

///|
pub fn scale_moment() -> MagnitudeScale {
  MomentMagnitude
}

///|
/// A magnitude estimate with provenance and uncertainty metadata.
pub struct MagnitudeEstimate {
  scale : MagnitudeScale
  value : Double
  amplitude : Double
  distance : Double
  uncertainty : Double
}

///|
/// Estimate duration magnitude from event duration in seconds and distance.
pub fn duration_magnitude(
  duration_seconds : Double,
  distance_km : Double,
) -> Result[Double, String] {
  if duration_seconds <= 0.0 || distance_km <= 0.0 {
    return Err("Duration and distance must be positive")
  }
  Ok(2.0 * @math.log10(duration_seconds) + 0.003 * distance_km - 0.87)
}

///|
/// Estimate scalar moment magnitude from seismic moment in N-m.
pub fn moment_magnitude(moment_newton_meter : Double) -> Result[Double, String] {
  if moment_newton_meter <= 0.0 {
    return Err("Seismic moment must be positive")
  }
  Ok(2.0 / 3.0 * (@math.log10(moment_newton_meter) - 9.1))
}

///|
/// Convert peak millimetres on an analog record to micrometres.
pub fn millimeters_to_micrometers(value : Double) -> Double {
  value * 1000.0
}

///|
/// Apply a simple geometric spreading correction.
pub fn geometric_spreading(amplitude : Double, distance_km : Double) -> Double {
  if distance_km <= 0.0 {
    0.0
  } else {
    amplitude * distance_km.sqrt()
  }
}

///|
/// Estimate local magnitude and retain the measurement inputs.
pub fn estimate_local(
  peak_amplitude_mm : Double,
  distance_km : Double,
  uncertainty : Double,
) -> Result[MagnitudeEstimate, String] {
  match local_magnitude(peak_amplitude_mm, distance_km) {
    Ok(value) =>
      Ok({
        scale: LocalMagnitude,
        value,
        amplitude: peak_amplitude_mm,
        distance: distance_km,
        uncertainty: if uncertainty < 0.0 {
          0.0
        } else {
          uncertainty
        },
      })
    Err(message) => Err(message)
  }
}

///|
/// Estimate a local magnitude directly from a processed trace.
pub fn magnitude_from_trace(
  trace : Trace,
  distance_km : Double,
) -> Result[MagnitudeEstimate, String] {
  estimate_local(wood_anderson_peak(trace), distance_km, 0.2)
}

///|
/// Return a magnitude estimate as a compact text record.
pub fn magnitude_summary(estimate : MagnitudeEstimate) -> String {
  let label = match estimate.scale {
    LocalMagnitude => "ML"
    BodyWaveMagnitude => "mb"
    SurfaceWaveMagnitude => "Ms"
    DurationMagnitude => "Md"
    MomentMagnitude => "Mw"
  }
  label +
  "=" +
  estimate.value.to_string() +
  " +/- " +
  estimate.uncertainty.to_string()
}

///|
/// Build a duration magnitude estimate.
pub fn estimate_duration(
  duration_seconds : Double,
  distance_km : Double,
) -> Result[MagnitudeEstimate, String] {
  match duration_magnitude(duration_seconds, distance_km) {
    Ok(value) =>
      Ok({
        scale: DurationMagnitude,
        value,
        amplitude: duration_seconds,
        distance: distance_km,
        uncertainty: 0.3,
      })
    Err(message) => Err(message)
  }
}

///|
/// Build a moment magnitude estimate.
pub fn estimate_moment(
  moment_newton_meter : Double,
) -> Result[MagnitudeEstimate, String] {
  match moment_magnitude(moment_newton_meter) {
    Ok(value) =>
      Ok({
        scale: MomentMagnitude,
        value,
        amplitude: moment_newton_meter,
        distance: 0.0,
        uncertainty: 0.2,
      })
    Err(message) => Err(message)
  }
}