///|
/// Quality record for one phase pick.
pub struct PhasePickQuality {
  station : String
  phase : String
  pick_time : Double
  snr_db : Double
  polarization_score : Double
  waveform_score : Double
  residual_seconds : Double
  confidence : Double
  status : String
}

///|
/// Compute a combined phase-pick confidence score.
pub fn phase_pick_confidence(
  snr_db : Double,
  polarization : Double,
  waveform : Double,
  residual_seconds : Double,
) -> Double {
  let snr_score = ((snr_db + 5.0) / 35.0).max(0.0).min(1.0)
  let residual_score = @math.exp(-residual_seconds.abs() / 2.0)
  (0.35 * snr_score +
  0.35 * polarization.max(0.0).min(1.0) +
  0.2 * waveform.max(0.0).min(1.0) +
  0.1 * residual_score).min(1.0)
}

///|
/// Convert confidence into an operator review status.
pub fn phase_pick_status(confidence : Double) -> String {
  if confidence >= 0.8 {
    "accepted"
  } else if confidence >= 0.55 {
    "review"
  } else {
    "rejected"
  }
}

///|
/// Build a phase quality record.
pub fn make_phase_pick_quality(
  station : String,
  phase : String,
  pick_time : Double,
  snr_db : Double,
  polarization : Double,
  waveform : Double,
  residual_seconds : Double,
) -> PhasePickQuality {
  let confidence = phase_pick_confidence(
    snr_db, polarization, waveform, residual_seconds,
  )
  {
    station,
    phase,
    pick_time,
    snr_db,
    polarization_score: polarization,
    waveform_score: waveform,
    residual_seconds,
    confidence,
    status: phase_pick_status(confidence),
  }
}

///|
/// Return accepted picks from a quality set.
pub fn accepted_phase_picks(
  picks : Array[PhasePickQuality],
) -> Array[PhasePickQuality] {
  picks.filter(pick => pick.status == "accepted")
}

///|
/// Compute median time difference between two phase sets.
pub fn phase_pick_time_difference(
  first : Array[PhasePickQuality],
  second : Array[PhasePickQuality],
) -> Double {
  let differences = Array::new()
  for left in first {
    for right in second {
      if left.station == right.station {
        differences.push(right.pick_time - left.pick_time)
      }
    }
  }
  median_samples(differences)
}

///|
/// Export phase-pick quality rows.
pub fn phase_pick_quality_to_csv(picks : Array[PhasePickQuality]) -> String {
  let mut output = "station,phase,pick_time,snr_db,polarization,waveform,residual,confidence,status\n"
  for pick in picks {
    output = output +
      pick.station +
      "," +
      pick.phase +
      "," +
      pick.pick_time.to_string() +
      "," +
      pick.snr_db.to_string() +
      "," +
      pick.polarization_score.to_string() +
      "," +
      pick.waveform_score.to_string() +
      "," +
      pick.residual_seconds.to_string() +
      "," +
      pick.confidence.to_string() +
      "," +
      pick.status +
      "\n"
  }
  output
}

///|
/// Format phase-pick quality summary.
pub fn phase_pick_quality_text(pick : PhasePickQuality) -> String {
  "station=" +
  pick.station +
  " phase=" +
  pick.phase +
  " time=" +
  pick.pick_time.to_string() +
  " snr_db=" +
  pick.snr_db.to_string() +
  " confidence=" +
  pick.confidence.to_string() +
  " status=" +
  pick.status
}