///|
/// Severity of an operational monitoring alert.
pub enum MonitoringAlertSeverity {
  AlertNotice
  AlertWarning
  AlertCritical
}

///|
/// An alert emitted from waveform or network metrics.
pub struct MonitoringAlert {
  code : String
  severity : MonitoringAlertSeverity
  station : String
  channel : String
  timestamp : Double
  value : Double
  threshold : Double
  message : String
  acknowledged : Bool
}

///|
/// A configurable alert rule.
pub struct MonitoringRule {
  code : String
  severity : MonitoringAlertSeverity
  threshold : Double
  greater_than : Bool
  message : String
}

///|
/// Aggregate alert state for a monitoring cycle.
pub struct MonitoringAlertSummary {
  total : Int
  notices : Int
  warnings : Int
  critical : Int
  unacknowledged : Int
  healthy : Bool
}

///|
/// Return all alert severity values.
pub fn supported_monitoring_alerts() -> Array[MonitoringAlertSeverity] {
  [AlertNotice, AlertWarning, AlertCritical]
}

///|
/// Return a stable alert severity label.
pub fn monitoring_alert_severity_name(
  severity : MonitoringAlertSeverity,
) -> String {
  match severity {
    AlertNotice => "notice"
    AlertWarning => "warning"
    AlertCritical => "critical"
  }
}

///|
/// Evaluate one rule against a value.
pub fn evaluate_monitoring_rule(
  rule : MonitoringRule,
  station : String,
  channel : String,
  timestamp : Double,
  value : Double,
) -> MonitoringAlert? {
  let violated = if rule.greater_than {
    value > rule.threshold
  } else {
    value < rule.threshold
  }
  if violated {
    Some({
      code: rule.code,
      severity: rule.severity,
      station,
      channel,
      timestamp,
      value,
      threshold: rule.threshold,
      message: rule.message,
      acknowledged: false,
    })
  } else {
    None
  }
}

///|
/// Build standard station health rules.
pub fn standard_monitoring_rules() -> Array[MonitoringRule] {
  [
    {
      code: "LOW_COMPLETENESS",
      severity: AlertWarning,
      threshold: 0.95,
      greater_than: false,
      message: "channel completeness is below target",
    },
    {
      code: "CLIPPING",
      severity: AlertCritical,
      threshold: 0.001,
      greater_than: true,
      message: "channel clipping fraction is high",
    },
    {
      code: "LOW_SNR",
      severity: AlertWarning,
      threshold: 3.0,
      greater_than: false,
      message: "signal-to-noise ratio is low",
    },
    {
      code: "QUALITY_FAIL",
      severity: AlertCritical,
      threshold: 0.4,
      greater_than: false,
      message: "station quality score is below minimum",
    },
  ]
}

///|
/// Evaluate standard rules for one quality report.
pub fn alerts_for_quality_report(
  report : StationQualityReport,
  timestamp : Double,
) -> Array[MonitoringAlert] {
  let alerts : Array[MonitoringAlert] = Array::new()
  let values = [
    report.completeness,
    report.clipping_fraction,
    report.signal_to_noise_db,
    report.score,
  ]
  let rules = standard_monitoring_rules()
  for index = 0
      index < rules.length() && index < values.length()
      index = index + 1 {
    match
      evaluate_monitoring_rule(
        rules[index],
        report.station,
        report.channel,
        timestamp,
        values[index],
      ) {
      Some(alert) => alerts.push(alert)
      None => ()
    }
  }
  alerts
}

///|
/// Count and classify alerts for an operator dashboard.
pub fn summarize_monitoring_alerts(
  alerts : Array[MonitoringAlert],
) -> MonitoringAlertSummary {
  let mut notices = 0
  let mut warnings = 0
  let mut critical = 0
  let mut unacknowledged = 0
  for alert in alerts {
    if !alert.acknowledged {
      unacknowledged += 1
    }
    match alert.severity {
      AlertNotice => notices += 1
      AlertWarning => warnings += 1
      AlertCritical => critical += 1
    }
  }
  {
    total: alerts.length(),
    notices,
    warnings,
    critical,
    unacknowledged,
    healthy: critical == 0,
  }
}

///|
/// Mark an alert acknowledged by code and station.
pub fn acknowledge_alert(
  alert : MonitoringAlert,
  code : String,
  station : String,
) -> MonitoringAlert {
  {
    code: alert.code,
    severity: alert.severity,
    station: alert.station,
    channel: alert.channel,
    timestamp: alert.timestamp,
    value: alert.value,
    threshold: alert.threshold,
    message: alert.message,
    acknowledged: alert.acknowledged ||
    (alert.code == code && alert.station == station),
  }
}

///|
/// Export alerts for a monitoring incident queue.
pub fn monitoring_alerts_to_csv(alerts : Array[MonitoringAlert]) -> String {
  let mut output = "code,severity,station,channel,timestamp,value,threshold,acknowledged,message\n"
  for alert in alerts {
    output = output +
      alert.code +
      "," +
      monitoring_alert_severity_name(alert.severity) +
      "," +
      alert.station +
      "," +
      alert.channel +
      "," +
      alert.timestamp.to_string() +
      "," +
      alert.value.to_string() +
      "," +
      alert.threshold.to_string() +
      "," +
      alert.acknowledged.to_string() +
      "," +
      alert.message +
      "\n"
  }
  output
}

///|
/// Format an alert summary for a health endpoint.
pub fn monitoring_alert_summary_text(
  summary : MonitoringAlertSummary,
) -> String {
  "total=" +
  summary.total.to_string() +
  " notices=" +
  summary.notices.to_string() +
  " warnings=" +
  summary.warnings.to_string() +
  " critical=" +
  summary.critical.to_string() +
  " unacknowledged=" +
  summary.unacknowledged.to_string() +
  " healthy=" +
  summary.healthy.to_string()
}