///|
/// A station magnitude measurement with quality provenance.
pub struct StationMagnitudeMeasurement {
  station : String
  scale : MagnitudeScale
  value : Double
  distance_km : Double
  amplitude : Double
  uncertainty : Double
  weight : Double
}

///|
/// Network magnitude summary with dispersion diagnostics.
pub struct NetworkMagnitudeSummary {
  scale : MagnitudeScale
  value : Double
  standard_deviation : Double
  median_absolute_deviation : Double
  station_count : Int
  weighted_count : Double
  minimum : Double
  maximum : Double
}

///|
/// Build a station measurement from a magnitude estimate.
pub fn station_magnitude_measurement(
  station : String,
  estimate : MagnitudeEstimate,
) -> StationMagnitudeMeasurement {
  {
    station,
    scale: estimate.scale,
    value: estimate.value,
    distance_km: estimate.distance,
    amplitude: estimate.amplitude,
    uncertainty: estimate.uncertainty,
    weight: 1.0 / estimate.uncertainty.max(0.05),
  }
}

///|
/// Compute a weighted magnitude consensus.
pub fn network_magnitude_summary(
  measurements : Array[StationMagnitudeMeasurement],
) -> NetworkMagnitudeSummary {
  let values = measurements.map(measurement => measurement.value)
  let mut weighted_total = 0.0
  let mut weights = 0.0
  for measurement in measurements {
    weighted_total += measurement.value * measurement.weight
    weights += measurement.weight
  }
  let consensus = if weights <= 0.0 {
    median_samples(values)
  } else {
    weighted_total / weights
  }
  {
    scale: if measurements.is_empty() {
      LocalMagnitude
    } else {
      measurements[0].scale
    },
    value: consensus,
    standard_deviation: standard_deviation(values),
    median_absolute_deviation: absolute_deviation_median(
      values,
      median_samples(values),
    ),
    station_count: measurements.length(),
    weighted_count: weights,
    minimum: minimum_sample(values),
    maximum: maximum_sample(values),
  }
}

///|
/// Remove measurements farther than a robust dispersion threshold.
pub fn magnitude_inliers(
  measurements : Array[StationMagnitudeMeasurement],
  threshold : Double,
) -> Array[StationMagnitudeMeasurement] {
  let center = median_samples(
    measurements.map(measurement => measurement.value),
  )
  let mad = absolute_deviation_median(
    measurements.map(measurement => measurement.value),
    center,
  ).max(1.0e-6)
  measurements.filter(measurement => {
    (measurement.value - center).abs() <= threshold.abs() * 1.4826 * mad
  })
}

///|
/// Estimate local magnitude from every trace and retain valid stations.
pub fn local_magnitudes_from_traces(
  traces : Array[Trace],
  distances_km : Array[Double],
) -> Array[StationMagnitudeMeasurement] {
  let result = Array::new()
  for index = 0
      index < traces.length() && index < distances_km.length()
      index = index + 1 {
    match magnitude_from_trace(traces[index], distances_km[index]) {
      Ok(estimate) =>
        result.push(
          station_magnitude_measurement(traces[index].station, estimate),
        )
      Err(_) => ()
    }
  }
  result
}

///|
/// Format a network magnitude summary.
pub fn network_magnitude_text(summary : NetworkMagnitudeSummary) -> String {
  "magnitude=" +
  summary.value.to_string() +
  " stations=" +
  summary.station_count.to_string() +
  " weighted=" +
  summary.weighted_count.to_string() +
  " std=" +
  summary.standard_deviation.to_string() +
  " mad=" +
  summary.median_absolute_deviation.to_string() +
  " min=" +
  summary.minimum.to_string() +
  " max=" +
  summary.maximum.to_string()
}