///|
/// Signal class for condition-monitoring channels.
pub(all) enum ReliabilitySignalKind {
  ReliabilityVibration
  ReliabilityTemperature
  ReliabilityPressure
  ReliabilityCurrent
  ReliabilityAcoustic
  ReliabilityCounter
} derive(Debug, Eq)

///|
/// Health state used by an operational monitoring service.
pub(all) enum ReliabilityHealthState {
  ReliabilityHealthy
  ReliabilityWatch
  ReliabilityAlert
  ReliabilityCritical
  ReliabilityUnknown
} derive(Debug, Eq)

///|
/// Sensor metadata and acceptance limits.
pub struct ReliabilitySensor {
  sensor_id : String
  asset_id : Int
  kind : ReliabilitySignalKind
  unit : String
  lower_limit : Double
  upper_limit : Double
  calibration_scale : Double
  calibration_offset : Double
  sample_period : Double
  enabled : Bool
}

///|
pub fn reliability_sensor(
  sensor_id : String,
  asset_id : Int,
  kind : ReliabilitySignalKind,
  unit : String,
  lower_limit : Double,
  upper_limit : Double,
  calibration_scale : Double,
  calibration_offset : Double,
  sample_period : Double,
  enabled : Bool,
) -> ReliabilitySensor {
  if asset_id < 0 ||
    upper_limit < lower_limit ||
    calibration_scale == 0.0 ||
    sample_period <= 0.0 {
    abort("invalid reliability sensor")
  }
  {
    sensor_id,
    asset_id,
    kind,
    unit,
    lower_limit,
    upper_limit,
    calibration_scale,
    calibration_offset,
    sample_period,
    enabled,
  }
}

///|
pub fn reliability_sensor_calibrate(
  sensor : ReliabilitySensor,
  raw : Double,
) -> Double {
  raw * sensor.calibration_scale + sensor.calibration_offset
}

///|
pub fn reliability_sensor_is_in_limit(
  sensor : ReliabilitySensor,
  value : Double,
) -> Bool {
  value >= sensor.lower_limit && value <= sensor.upper_limit
}

///|
pub fn reliability_sensor_limit_fraction(
  sensor : ReliabilitySensor,
  value : Double,
) -> Double {
  if sensor.upper_limit == sensor.lower_limit {
    0.0
  } else {
    ((value - sensor.lower_limit) / (sensor.upper_limit - sensor.lower_limit))
    .max(0.0)
    .min(1.0)
  }
}

///|
pub fn reliability_sensor_margin(
  sensor : ReliabilitySensor,
  value : Double,
) -> Double {
  (value - sensor.lower_limit).min(sensor.upper_limit - value)
}

///|
pub fn reliability_sensor_expected_samples(
  sensor : ReliabilitySensor,
  duration : Double,
) -> Int {
  if duration <= 0.0 {
    0
  } else {
    (duration / sensor.sample_period).floor().to_int() + 1
  }
}

///|
/// Calibrated sensor reading with provenance and quality weight.
pub struct ReliabilityReading {
  sensor_id : String
  asset_id : Int
  timestamp : Double
  raw_value : Double
  value : Double
  quality : Double
  sequence : Int
}

///|
pub fn reliability_reading(
  sensor : ReliabilitySensor,
  timestamp : Double,
  raw_value : Double,
  quality : Double,
  sequence : Int,
) -> ReliabilityReading {
  if timestamp < 0.0 || quality < 0.0 || quality > 1.0 || sequence < 0 {
    abort("invalid reliability reading")
  }
  {
    sensor_id: sensor.sensor_id,
    asset_id: sensor.asset_id,
    timestamp,
    raw_value,
    value: reliability_sensor_calibrate(sensor, raw_value),
    quality,
    sequence,
  }
}

///|
pub fn reliability_reading_is_usable(reading : ReliabilityReading) -> Bool {
  reading.quality > 0.0
}

///|
pub fn reliability_reading_weighted_value(
  reading : ReliabilityReading,
) -> Double {
  reading.value * reading.quality
}

///|
pub fn reliability_reading_gap(
  previous : ReliabilityReading,
  current : ReliabilityReading,
) -> Double {
  (current.timestamp - previous.timestamp).max(0.0)
}

///|
pub fn reliability_reading_rate(
  previous : ReliabilityReading,
  current : ReliabilityReading,
) -> Double {
  let gap = current.timestamp - previous.timestamp
  if gap <= 0.0 {
    0.0
  } else {
    (current.value - previous.value) / gap
  }
}

///|
pub fn reliability_readings_sorted(
  readings : Array[ReliabilityReading],
) -> Array[ReliabilityReading] {
  let result = readings.copy()
  result.sort_by((left, right) => {
    if left.timestamp < right.timestamp {
      -1
    } else if left.timestamp > right.timestamp {
      1
    } else {
      0
    }
  })
  result
}

///|
pub fn reliability_readings_values(
  readings : Array[ReliabilityReading],
) -> Array[Double] {
  readings.filter_map(reading => {
    if reliability_reading_is_usable(reading) {
      Some(reading.value)
    } else {
      None
    }
  })
}

///|
pub fn reliability_readings_mean(
  readings : Array[ReliabilityReading],
) -> Double {
  let values = reliability_readings_values(readings)
  if values.is_empty() {
    0.0
  } else {
    mean(values)
  }
}

///|
pub fn reliability_readings_variance(
  readings : Array[ReliabilityReading],
) -> Double {
  let values = reliability_readings_values(readings)
  if values.length() < 2 {
    0.0
  } else {
    variance(values)
  }
}

///|
pub fn reliability_readings_range(
  readings : Array[ReliabilityReading],
) -> Double {
  let values = reliability_readings_values(readings)
  if values.is_empty() {
    0.0
  } else {
    max_value(values) - min_value(values)
  }
}

///|
pub fn reliability_readings_slope(
  readings : Array[ReliabilityReading],
) -> Double {
  let ordered = reliability_readings_sorted(readings)
  if ordered.length() < 2 {
    0.0
  } else {
    let first = ordered[0]
    let last = ordered[ordered.length() - 1]
    let duration = last.timestamp - first.timestamp
    if duration <= 0.0 {
      0.0
    } else {
      (last.value - first.value) / duration
    }
  }
}

///|
pub fn reliability_readings_missing_fraction(
  readings : Array[ReliabilityReading],
  start : Double,
  end : Double,
  expected_period : Double,
) -> Double {
  if end <= start || expected_period <= 0.0 {
    0.0
  } else {
    let expected = ((end - start) / expected_period).floor().to_int() + 1
    if expected == 0 {
      0.0
    } else {
      (expected - readings.length()).max(0).to_double() / expected.to_double()
    }
  }
}

///|
/// A derived health feature calculated from a signal window.
pub struct ReliabilityHealthFeature {
  name : String
  value : Double
  baseline : Double
  scale : Double
  weight : Double
  direction : Double
}

///|
pub fn reliability_health_feature(
  name : String,
  value : Double,
  baseline : Double,
  scale : Double,
  weight : Double,
  direction : Double,
) -> ReliabilityHealthFeature {
  if scale <= 0.0 || weight < 0.0 || direction == 0.0 {
    abort("invalid health feature")
  }
  { name, value, baseline, scale, weight, direction }
}

///|
pub fn reliability_health_feature_score(
  feature : ReliabilityHealthFeature,
) -> Double {
  let deviation = feature.direction *
    (feature.value - feature.baseline) /
    feature.scale
  (1.0 - deviation.abs()).max(0.0).min(1.0)
}

///|
pub fn reliability_health_feature_risk(
  feature : ReliabilityHealthFeature,
) -> Double {
  1.0 - reliability_health_feature_score(feature)
}

///|
pub fn reliability_health_feature_contribution(
  feature : ReliabilityHealthFeature,
) -> Double {
  feature.weight * reliability_health_feature_risk(feature)
}

///|
pub fn reliability_health_index(
  features : Array[ReliabilityHealthFeature],
) -> Double {
  if features.is_empty() {
    1.0
  } else {
    let total_weight = features.fold(init=0.0, (sum, feature) => {
      sum + feature.weight
    })
    if total_weight == 0.0 {
      1.0
    } else {
      features.fold(init=0.0, (sum, feature) => {
        sum + feature.weight * reliability_health_feature_score(feature)
      }) /
      total_weight
    }
  }
}

///|
pub fn reliability_health_risk_index(
  features : Array[ReliabilityHealthFeature],
) -> Double {
  1.0 - reliability_health_index(features)
}

///|
/// Health classification policy with explicit escalation thresholds.
pub struct ReliabilityHealthPolicy {
  watch_threshold : Double
  alert_threshold : Double
  critical_threshold : Double
  minimum_quality : Double
  persistence : Int
}

///|
pub fn reliability_health_policy(
  watch_threshold : Double,
  alert_threshold : Double,
  critical_threshold : Double,
  minimum_quality : Double,
  persistence : Int,
) -> ReliabilityHealthPolicy {
  if watch_threshold < 0.0 ||
    alert_threshold < watch_threshold ||
    critical_threshold < alert_threshold ||
    critical_threshold > 1.0 ||
    minimum_quality < 0.0 ||
    minimum_quality > 1.0 ||
    persistence < 1 {
    abort("invalid health policy")
  }
  {
    watch_threshold,
    alert_threshold,
    critical_threshold,
    minimum_quality,
    persistence,
  }
}

///|
pub fn reliability_health_state(
  policy : ReliabilityHealthPolicy,
  risk : Double,
  quality : Double,
) -> ReliabilityHealthState {
  if quality < policy.minimum_quality {
    ReliabilityHealthState::ReliabilityUnknown
  } else if risk >= policy.critical_threshold {
    ReliabilityHealthState::ReliabilityCritical
  } else if risk >= policy.alert_threshold {
    ReliabilityHealthState::ReliabilityAlert
  } else if risk >= policy.watch_threshold {
    ReliabilityHealthState::ReliabilityWatch
  } else {
    ReliabilityHealthState::ReliabilityHealthy
  }
}

///|
pub fn reliability_health_state_is_actionable(
  state : ReliabilityHealthState,
) -> Bool {
  state is ReliabilityAlert ||
  state is ReliabilityCritical ||
  state is ReliabilityUnknown
}

///|
pub fn reliability_health_state_rank(state : ReliabilityHealthState) -> Int {
  match state {
    ReliabilityHealthy => 0
    ReliabilityWatch => 1
    ReliabilityAlert => 2
    ReliabilityCritical => 3
    ReliabilityUnknown => 4
  }
}

///|
/// Asset-level condition assessment with a traceable feature vector.
pub struct ReliabilityConditionAssessment {
  asset_id : Int
  timestamp : Double
  index : Double
  risk : Double
  quality : Double
  state : ReliabilityHealthState
  features : Array[ReliabilityHealthFeature]
  evidence_count : Int
}

///|
pub fn reliability_condition_assessment(
  asset_id : Int,
  timestamp : Double,
  features : Array[ReliabilityHealthFeature],
  quality : Double,
  policy : ReliabilityHealthPolicy,
) -> ReliabilityConditionAssessment {
  if asset_id < 0 || timestamp < 0.0 || quality < 0.0 || quality > 1.0 {
    abort("invalid condition assessment")
  }
  let index = reliability_health_index(features)
  let risk = 1.0 - index
  {
    asset_id,
    timestamp,
    index,
    risk,
    quality,
    state: reliability_health_state(policy, risk, quality),
    features,
    evidence_count: features.length(),
  }
}

///|
pub fn reliability_assessment_is_healthy(
  assessment : ReliabilityConditionAssessment,
) -> Bool {
  assessment.state is ReliabilityHealthy
}

///|
pub fn reliability_assessment_risk(
  assessment : ReliabilityConditionAssessment,
) -> Double {
  assessment.risk
}

///|
pub fn reliability_assessment_top_feature(
  assessment : ReliabilityConditionAssessment,
) -> String {
  if assessment.features.is_empty() {
    "none"
  } else {
    let mut best = 0
    for i in 1..
        reliability_health_feature_contribution(assessment.features[best]) {
        best = i
      }
    }
    assessment.features[best].name
  }
}

///|
/// A persistence-aware alarm decision.
pub struct ReliabilityAlarmDecision {
  asset_id : Int
  state : ReliabilityHealthState
  consecutive_actionable : Int
  escalated : Bool
  reason : String
}

///|
pub fn reliability_alarm_decision(
  assessment : ReliabilityConditionAssessment,
  consecutive_actionable : Int,
  policy : ReliabilityHealthPolicy,
) -> ReliabilityAlarmDecision {
  if consecutive_actionable < 0 {
    abort("consecutive count must be non-negative")
  }
  let escalated = reliability_health_state_is_actionable(assessment.state) &&
    consecutive_actionable >= policy.persistence
  let reason = if escalated {
    "persistent actionable condition"
  } else if reliability_health_state_is_actionable(assessment.state) {
    "condition requires confirmation"
  } else {
    "within operating envelope"
  }
  {
    asset_id: assessment.asset_id,
    state: assessment.state,
    consecutive_actionable,
    escalated,
    reason,
  }
}

///|
pub fn reliability_alarm_needs_dispatch(
  decision : ReliabilityAlarmDecision,
) -> Bool {
  decision.escalated
}

///|
pub fn reliability_alarm_priority(decision : ReliabilityAlarmDecision) -> Int {
  if !decision.escalated {
    0
  } else {
    reliability_health_state_rank(decision.state)
  }
}

///|
/// Remaining useful life estimate from a health trajectory.
pub struct ReliabilityRulEstimate {
  asset_id : Int
  current_age : Double
  estimated_rul : Double
  lower_rul : Double
  upper_rul : Double
  confidence : Double
  slope : Double
}

///|
pub fn reliability_rul_estimate(
  asset_id : Int,
  current_age : Double,
  estimated_rul : Double,
  lower_rul : Double,
  upper_rul : Double,
  confidence : Double,
  slope : Double,
) -> ReliabilityRulEstimate {
  if asset_id < 0 ||
    current_age < 0.0 ||
    estimated_rul < 0.0 ||
    lower_rul < 0.0 ||
    upper_rul < lower_rul ||
    confidence <= 0.0 ||
    confidence >= 1.0 {
    abort("invalid RUL estimate")
  }
  {
    asset_id,
    current_age,
    estimated_rul,
    lower_rul,
    upper_rul,
    confidence,
    slope,
  }
}

///|
pub fn reliability_rul_from_linear_health(
  asset_id : Int,
  current_age : Double,
  health : Double,
  health_rate : Double,
  target_health : Double,
  uncertainty : Double,
) -> ReliabilityRulEstimate {
  if health_rate >= 0.0 ||
    uncertainty < 0.0 ||
    target_health < 0.0 ||
    target_health > 1.0 {
    abort("invalid linear RUL input")
  }
  let rul = ((target_health - health) / health_rate).max(0.0)
  reliability_rul_estimate(
    asset_id,
    current_age,
    rul,
    (rul - uncertainty).max(0.0),
    rul + uncertainty,
    0.9,
    health_rate,
  )
}

///|
pub fn reliability_rul_is_due(
  estimate : ReliabilityRulEstimate,
  horizon : Double,
) -> Bool {
  horizon >= 0.0 && estimate.estimated_rul <= horizon
}

///|
pub fn reliability_rul_conservative(
  estimate : ReliabilityRulEstimate,
) -> Double {
  estimate.lower_rul
}

///|
pub fn reliability_rul_margin(estimate : ReliabilityRulEstimate) -> Double {
  estimate.upper_rul - estimate.lower_rul
}

///|
pub fn reliability_condition_batch_risk(
  assessments : Array[ReliabilityConditionAssessment],
) -> Double {
  if assessments.is_empty() {
    0.0
  } else {
    assessments.fold(init=0.0, (sum, assessment) => sum + assessment.risk) /
    assessments.length().to_double()
  }
}

///|
pub fn reliability_condition_actionable_assets(
  assessments : Array[ReliabilityConditionAssessment],
) -> Array[Int] {
  assessments.filter_map(assessment => {
    if reliability_health_state_is_actionable(assessment.state) {
      Some(assessment.asset_id)
    } else {
      None
    }
  })
}

///|
pub fn reliability_condition_ranked_assets(
  assessments : Array[ReliabilityConditionAssessment],
) -> Array[ReliabilityConditionAssessment] {
  let result = assessments.copy()
  result.sort_by((left, right) => {
    if left.risk > right.risk {
      -1
    } else if left.risk < right.risk {
      1
    } else {
      0
    }
  })
  result
}

///|
pub fn reliability_condition_coverage(
  assessments : Array[ReliabilityConditionAssessment],
  total_assets : Int,
) -> Double {
  if total_assets <= 0 {
    0.0
  } else {
    assessments.length().to_double() / total_assets.to_double()
  }
}

///|
pub fn reliability_condition_checksum(
  assessments : Array[ReliabilityConditionAssessment],
) -> Double {
  reliability_condition_batch_risk(assessments) * 100.0 +
  assessments.fold(init=0.0, (sum, assessment) => {
    sum + assessment.asset_id.to_double() + assessment.index
  })
}