///|
/// A source-neutral sample emitted by a wearable or an offline recorder.
///
/// The type deliberately keeps the original timestamp and sensor metadata so
/// downstream reports can explain how a metric was produced. A sample may
/// carry both an RR interval and a heart-rate value; the ingestion layer
/// reconciles them without silently discarding the source values.
pub(all) struct WearableSample {
  timestamp_seconds : Double
  rr_ms : Double
  heart_rate_bpm : Double
  movement_g : Double
  temperature_c : Double
  signal_quality : Double
  source_id : String
  sequence_number : Int
} derive(FromJson, ToJson, Debug, Eq)

///|
/// Disposition of a sample after validation.
pub(all) enum WearableSampleDisposition {
  Accepted
  Invalid
  Duplicate
  OutOfOrder
  LowQuality
} derive(FromJson, ToJson, Debug, Eq)

///|
/// An ingestion notice is machine-readable and suitable for audit logs.
pub(all) struct IngestNotice {
  index : Int
  code : String
  message : String
  severity : String
  disposition : WearableSampleDisposition
} derive(FromJson, ToJson, Debug, Eq)

///|
/// Ingestion settings shared by batch and streaming adapters.
pub(all) struct WearableIngestConfig {
  minimum_quality : Double
  maximum_gap_seconds : Double
  maximum_duration_seconds : Double
  allow_out_of_order : Bool
  reject_duplicates : Bool
  derive_missing_heart_rate : Bool
  derive_missing_rr : Bool
  source_name : String
} derive(FromJson, ToJson, Debug, Eq)

///|
/// Conservative defaults for wearable telemetry.
pub fn WearableIngestConfig::default() -> WearableIngestConfig {
  {
    minimum_quality: 0.70,
    maximum_gap_seconds: 5.0,
    maximum_duration_seconds: 86400.0,
    allow_out_of_order: false,
    reject_duplicates: true,
    derive_missing_heart_rate: true,
    derive_missing_rr: true,
    source_name: "unknown",
  }
}

///|
/// A stable summary returned by every ingestion operation.
pub(all) struct WearableIngestReport {
  samples : Array[WearableSample]
  notices : Array[IngestNotice]
  accepted_count : Int
  rejected_count : Int
  duplicate_count : Int
  out_of_order_count : Int
  low_quality_count : Int
  invalid_count : Int
  gap_count : Int
  duration_seconds : Double
  quality_ratio : Double
  complete : Bool
} derive(FromJson, ToJson, Debug, Eq)

///|
/// A fixed-width time window over accepted telemetry.
pub(all) struct TelemetryWindow {
  ordinal : Int
  start_seconds : Double
  end_seconds : Double
  samples : Array[WearableSample]
  rr_intervals : Array[Double]
  mean_quality : Double
  gap_count : Int
  complete : Bool
} derive(FromJson, ToJson, Debug, Eq)

///|
/// A normalized sample with a source-independent quality weight.
pub(all) struct NormalizedWearableSample {
  sample : WearableSample
  quality_weight : Double
  disposition : WearableSampleDisposition
  repaired : Bool
} derive(FromJson, ToJson, Debug, Eq)

///|
/// Make a sample without requiring a particular device SDK.
pub fn make_wearable_sample(
  timestamp_seconds : Double,
  rr_ms : Double,
  heart_rate_bpm : Double,
  signal_quality : Double,
  source_id : String,
  sequence_number : Int,
) -> WearableSample {
  {
    timestamp_seconds,
    rr_ms,
    heart_rate_bpm,
    movement_g: 0.0,
    temperature_c: 0.0,
    signal_quality,
    source_id,
    sequence_number,
  }
}

///|
/// Return a copy with optional movement and temperature channels attached.
pub fn wearable_sample_with_context(
  sample : WearableSample,
  movement_g : Double,
  temperature_c : Double,
) -> WearableSample {
  {
    timestamp_seconds: sample.timestamp_seconds,
    rr_ms: sample.rr_ms,
    heart_rate_bpm: sample.heart_rate_bpm,
    movement_g,
    temperature_c,
    signal_quality: sample.signal_quality,
    source_id: sample.source_id,
    sequence_number: sample.sequence_number,
  }
}

///|
/// Check whether a floating-point value is usable in a telemetry record.
pub fn wearable_finite(value : Double) -> Bool {
  !value.is_nan() && !value.is_inf()
}

///|
/// Return whether a sample carries at least one usable cardiac channel.
pub fn wearable_sample_has_cardiac_value(sample : WearableSample) -> Bool {
  (wearable_finite(sample.rr_ms) && sample.rr_ms > 0.0) ||
  (wearable_finite(sample.heart_rate_bpm) && sample.heart_rate_bpm > 0.0)
}

///|
/// Validate a sample against broad transport-level constraints.
pub fn wearable_sample_is_valid(
  sample : WearableSample,
  config : WearableIngestConfig,
) -> Bool {
  let timestamp_ok = wearable_finite(sample.timestamp_seconds) &&
    sample.timestamp_seconds >= 0.0
  let rr_ok = !wearable_finite(sample.rr_ms) ||
    sample.rr_ms == 0.0 ||
    (sample.rr_ms >= 200.0 && sample.rr_ms <= 5000.0)
  let hr_ok = !wearable_finite(sample.heart_rate_bpm) ||
    sample.heart_rate_bpm == 0.0 ||
    (sample.heart_rate_bpm >= 10.0 && sample.heart_rate_bpm <= 300.0)
  let quality_ok = wearable_finite(sample.signal_quality) &&
    sample.signal_quality >= 0.0 &&
    sample.signal_quality <= 1.0
  let context_ok = (
      !wearable_finite(sample.movement_g) || sample.movement_g >= 0.0
    ) &&
    (
      !wearable_finite(sample.temperature_c) ||
      (sample.temperature_c >= -100.0 && sample.temperature_c <= 100.0)
    )
  timestamp_ok &&
  rr_ok &&
  hr_ok &&
  quality_ok &&
  context_ok &&
  wearable_sample_has_cardiac_value(sample) &&
  sample.signal_quality >= config.minimum_quality
}

///|
/// Reconcile an RR interval and heart rate without changing source metadata.
pub fn reconcile_cardiac_channels(
  sample : WearableSample,
  config : WearableIngestConfig,
) -> (WearableSample, Bool) {
  let rr = if wearable_finite(sample.rr_ms) && sample.rr_ms > 0.0 {
    sample.rr_ms
  } else if config.derive_missing_rr && sample.heart_rate_bpm > 0.0 {
    60000.0 / sample.heart_rate_bpm
  } else {
    sample.rr_ms
  }
  let hr = if wearable_finite(sample.heart_rate_bpm) &&
    sample.heart_rate_bpm > 0.0 {
    sample.heart_rate_bpm
  } else if config.derive_missing_heart_rate && rr > 0.0 {
    60000.0 / rr
  } else {
    sample.heart_rate_bpm
  }
  (
    {
      timestamp_seconds: sample.timestamp_seconds,
      rr_ms: rr,
      heart_rate_bpm: hr,
      movement_g: sample.movement_g,
      temperature_c: sample.temperature_c,
      signal_quality: sample.signal_quality,
      source_id: sample.source_id,
      sequence_number: sample.sequence_number,
    },
    rr != sample.rr_ms || hr != sample.heart_rate_bpm,
  )
}

///|
/// Clamp quality to a finite unit interval for scoring.
pub fn wearable_quality_weight(value : Double) -> Double {
  if !wearable_finite(value) {
    0.0
  } else {
    value.clamp(min=0.0, max=1.0)
  }
}

///|
/// Normalize one sample and report whether a channel was derived.
pub fn normalize_wearable_sample(
  sample : WearableSample,
  config : WearableIngestConfig,
) -> NormalizedWearableSample {
  let (reconciled, repaired) = reconcile_cardiac_channels(sample, config)
  {
    sample: reconciled,
    quality_weight: wearable_quality_weight(reconciled.signal_quality),
    disposition: if wearable_sample_is_valid(reconciled, config) {
      Accepted
    } else {
      Invalid
    },
    repaired,
  }
}

///|
/// Return whether two samples represent the same transport event.
pub fn wearable_samples_are_duplicate(
  left : WearableSample,
  right : WearableSample,
) -> Bool {
  left.timestamp_seconds == right.timestamp_seconds &&
  left.source_id == right.source_id &&
  (
    left.sequence_number == right.sequence_number ||
    left.sequence_number < 0 ||
    right.sequence_number < 0
  )
}

///|
/// Add a notice to an array while keeping notice construction uniform.
fn add_ingest_notice(
  notices : Array[IngestNotice],
  index : Int,
  code : String,
  message : String,
  severity : String,
  disposition : WearableSampleDisposition,
) -> Unit {
  notices.push({ index, code, message, severity, disposition })
}

///|
/// Detect whether a gap exceeds the configured transport interval.
pub fn wearable_gap_detected(
  previous_timestamp : Double,
  current_timestamp : Double,
  maximum_gap_seconds : Double,
) -> Bool {
  maximum_gap_seconds > 0.0 &&
  current_timestamp - previous_timestamp > maximum_gap_seconds
}

///|
/// Ingest a batch, reject malformed events, and retain an audit trail.
pub fn ingest_wearable_samples(
  input : Array[WearableSample],
  config : WearableIngestConfig,
) -> WearableIngestReport {
  let accepted = []
  let notices = []
  let mut duplicates = 0
  let mut out_of_order = 0
  let mut low_quality = 0
  let mut invalid = 0
  let mut gaps = 0
  for i in 0.. 0 {
      let previous = accepted[accepted.length() - 1]
      if wearable_samples_are_duplicate(previous, sample) &&
        config.reject_duplicates {
        duplicates += 1
        add_ingest_notice(
          notices,
          i,
          "duplicate_sample",
          "sample has the same transport identity as the previous event",
          "warning",
          Duplicate,
        )
        continue
      }
      if sample.timestamp_seconds < previous.timestamp_seconds {
        out_of_order += 1
        if !config.allow_out_of_order {
          add_ingest_notice(
            notices,
            i,
            "out_of_order",
            "sample timestamp precedes the last accepted event",
            "error",
            OutOfOrder,
          )
          continue
        }
      }
      if wearable_gap_detected(
          previous.timestamp_seconds,
          sample.timestamp_seconds,
          config.maximum_gap_seconds,
        ) {
        gaps += 1
        add_ingest_notice(
          notices,
          i,
          "timestamp_gap",
          "timestamp gap exceeds the configured transport interval",
          "warning",
          Accepted,
        )
      }
    }
    accepted.push(sample)
  }
  let duration = if accepted.length() < 2 {
    0.0
  } else {
    accepted[accepted.length() - 1].timestamp_seconds -
    accepted[0].timestamp_seconds
  }
  let duration_ok = config.maximum_duration_seconds <= 0.0 ||
    duration <= config.maximum_duration_seconds
  let quality_ratio = if input.length() == 0 {
    0.0
  } else {
    accepted.length().to_double() / input.length().to_double()
  }
  {
    samples: accepted,
    notices,
    accepted_count: accepted.length(),
    rejected_count: input.length() - accepted.length(),
    duplicate_count: duplicates,
    out_of_order_count: out_of_order,
    low_quality_count: low_quality,
    invalid_count: invalid,
    gap_count: gaps,
    duration_seconds: if duration_ok {
      duration
    } else {
      0.0
    },
    quality_ratio,
    complete: duration_ok && invalid == 0 && out_of_order == 0,
  }
}

///|
/// Sort a batch by timestamp while preserving deterministic tie order.
pub fn sort_wearable_samples(
  samples : Array[WearableSample],
) -> Array[WearableSample] {
  let result = []
  for sample in samples {
    result.push(sample)
  }
  result.sort_by((left, right) => {
    if left.timestamp_seconds < right.timestamp_seconds {
      -1
    } else if left.timestamp_seconds > right.timestamp_seconds {
      1
    } else if left.sequence_number < right.sequence_number {
      -1
    } else if left.sequence_number > right.sequence_number {
      1
    } else {
      0
    }
  })
  result
}

///|
/// Ingest after ordering a device batch by timestamp.
pub fn ingest_sorted_wearable_samples(
  input : Array[WearableSample],
  config : WearableIngestConfig,
) -> WearableIngestReport {
  ingest_wearable_samples(sort_wearable_samples(input), config)
}

///|
/// Return the accepted RR stream in milliseconds.
pub fn wearable_rr_intervals(report : WearableIngestReport) -> Array[Double] {
  let result = []
  for sample in report.samples {
    if sample.rr_ms > 0.0 && wearable_finite(sample.rr_ms) {
      result.push(sample.rr_ms)
    }
  }
  result
}

///|
/// Return accepted timestamps in seconds.
pub fn wearable_timestamps(report : WearableIngestReport) -> Array[Double] {
  let result = []
  for sample in report.samples {
    result.push(sample.timestamp_seconds)
  }
  result
}

///|
/// Return the average quality of accepted events.
pub fn wearable_mean_quality(report : WearableIngestReport) -> Double {
  if report.samples.length() == 0 {
    0.0
  } else {
    let values = []
    for sample in report.samples {
      values.push(wearable_quality_weight(sample.signal_quality))
    }
    mean_value(values)
  }
}

///|
/// Return whether an ingestion report has enough accepted cardiac events.
pub fn wearable_ingest_is_usable(
  report : WearableIngestReport,
  minimum_samples : Int,
) -> Bool {
  report.accepted_count >=
  (if minimum_samples < 0 { 0 } else { minimum_samples }) &&
  report.quality_ratio > 0.0 &&
  report.duration_seconds >= 0.0 &&
  report.invalid_count == 0
}

///|
/// Split accepted samples at timestamp gaps.
pub fn split_wearable_gaps(
  samples : Array[WearableSample],
  maximum_gap_seconds : Double,
) -> Array[Array[WearableSample]] {
  let result = []
  if samples.length() == 0 {
    return result
  }
  let current = []
  current.push(samples[0])
  for i in 1.. Array[TelemetryWindow] {
  let result = []
  if samples.length() == 0 || window_seconds <= 0.0 || step_seconds <= 0.0 {
    return result
  }
  let segments = split_wearable_gaps(samples, maximum_gap_seconds)
  let mut ordinal = 0
  for segment in segments {
    if segment.length() == 0 {
      continue
    }
    let start = segment[0].timestamp_seconds
    let mut cursor = start
    let end = segment[segment.length() - 1].timestamp_seconds
    while cursor <= end {
      let window_end = cursor + window_seconds
      let selected = []
      for sample in segment {
        if sample.timestamp_seconds >= cursor &&
          sample.timestamp_seconds < window_end {
          selected.push(sample)
        }
      }
      if selected.length() > 0 {
        let rr = []
        let qualities = []
        for sample in selected {
          if sample.rr_ms > 0.0 && wearable_finite(sample.rr_ms) {
            rr.push(sample.rr_ms)
          }
          qualities.push(wearable_quality_weight(sample.signal_quality))
        }
        result.push({
          ordinal,
          start_seconds: cursor,
          end_seconds: window_end,
          samples: selected,
          rr_intervals: rr,
          mean_quality: mean_value(qualities),
          gap_count: 0,
          complete: cursor + window_seconds <= end + step_seconds,
        })
        ordinal += 1
      }
      cursor += step_seconds
    }
  }
  result
}

///|
/// Return the window with the strongest quality-weighted sample count.
pub fn best_telemetry_window(
  windows : Array[TelemetryWindow],
) -> TelemetryWindow? {
  if windows.length() == 0 {
    return None
  }
  let mut best = windows[0]
  let mut best_score = best.mean_quality *
    best.rr_intervals.length().to_double()
  for window in windows {
    let score = window.mean_quality * window.rr_intervals.length().to_double()
    if score > best_score {
      best = window
      best_score = score
    }
  }
  Some(best)
}

///|
/// Return the number of usable cardiac samples in a window.
pub fn telemetry_window_sample_count(window : TelemetryWindow) -> Int {
  window.rr_intervals.length()
}

///|
/// Return the fraction of windows that contain a usable RR stream.
pub fn telemetry_window_coverage(windows : Array[TelemetryWindow]) -> Double {
  if windows.length() == 0 {
    0.0
  } else {
    let mut usable = 0
    for window in windows {
      if window.rr_intervals.length() > 1 && window.mean_quality > 0.0 {
        usable += 1
      }
    }
    usable.to_double() / windows.length().to_double()
  }
}

///|
/// Produce a compact quality vector for downstream model tables.
pub fn wearable_ingest_feature_vector(
  report : WearableIngestReport,
) -> Array[Double] {
  [
    report.samples.length().to_double(),
    report.accepted_count.to_double(),
    report.rejected_count.to_double(),
    report.duplicate_count.to_double(),
    report.out_of_order_count.to_double(),
    report.low_quality_count.to_double(),
    report.invalid_count.to_double(),
    report.gap_count.to_double(),
    report.duration_seconds,
    report.quality_ratio,
    wearable_mean_quality(report),
  ]
}

///|
/// Serialize a report suitable for a log line or a metrics endpoint.
pub fn wearable_ingest_csv(report : WearableIngestReport) -> String {
  to_csv([
    [
      "accepted", "rejected", "duplicates", "out_of_order", "low_quality", "invalid",
      "gaps", "duration_seconds", "quality_ratio", "complete",
    ],
    [
      report.accepted_count.to_string(),
      report.rejected_count.to_string(),
      report.duplicate_count.to_string(),
      report.out_of_order_count.to_string(),
      report.low_quality_count.to_string(),
      report.invalid_count.to_string(),
      report.gap_count.to_string(),
      report.duration_seconds.to_string(),
      report.quality_ratio.to_string(),
      report.complete.to_string(),
    ],
  ])
}

///|
/// A compact state for an incremental ingest loop.
pub(all) struct IngestCursor {
  mut last_timestamp_seconds : Double
  mut accepted_count : Int
  mut rejected_count : Int
  mut gap_count : Int
  mut quality_sum : Double
  mut finished : Bool
} derive(FromJson, ToJson, Debug, Eq)

///|
/// Create an empty cursor for a streaming adapter.
pub fn IngestCursor::new() -> IngestCursor {
  {
    last_timestamp_seconds: -1.0,
    accepted_count: 0,
    rejected_count: 0,
    gap_count: 0,
    quality_sum: 0.0,
    finished: false,
  }
}

///|
/// Add one sample to a cursor without retaining the raw stream.
pub fn IngestCursor::push(
  self : IngestCursor,
  sample : WearableSample,
  config : WearableIngestConfig,
) -> IngestNotice? {
  if self.finished {
    return Some({
      index: self.accepted_count + self.rejected_count,
      code: "finished_cursor",
      message: "samples cannot be added after finish",
      severity: "error",
      disposition: Invalid,
    })
  }
  let normalized = normalize_wearable_sample(sample, config)
  if !wearable_sample_is_valid(normalized.sample, config) {
    self.rejected_count += 1
    Some({
      index: self.accepted_count + self.rejected_count - 1,
      code: "invalid_sample",
      message: "sample failed cursor validation",
      severity: "error",
      disposition: Invalid,
    })
  } else if self.last_timestamp_seconds >= 0.0 &&
    normalized.sample.timestamp_seconds < self.last_timestamp_seconds {
    self.rejected_count += 1
    Some({
      index: self.accepted_count + self.rejected_count - 1,
      code: "out_of_order",
      message: "cursor requires monotonic timestamps",
      severity: "error",
      disposition: OutOfOrder,
    })
  } else {
    if self.last_timestamp_seconds >= 0.0 &&
      wearable_gap_detected(
        self.last_timestamp_seconds,
        normalized.sample.timestamp_seconds,
        config.maximum_gap_seconds,
      ) {
      self.gap_count += 1
    }
    self.last_timestamp_seconds = normalized.sample.timestamp_seconds
    self.accepted_count += 1
    self.quality_sum += wearable_quality_weight(
      normalized.sample.signal_quality,
    )
    None
  }
}

///|
/// Mark a cursor complete and freeze further updates.
pub fn IngestCursor::finish(self : IngestCursor) -> Unit {
  self.finished = true
}

///|
/// Return the mean quality observed by a cursor.
pub fn IngestCursor::mean_quality(self : IngestCursor) -> Double {
  if self.accepted_count == 0 {
    0.0
  } else {
    self.quality_sum / self.accepted_count.to_double()
  }
}

///|
/// Return a compact cursor feature vector.
pub fn IngestCursor::feature_vector(self : IngestCursor) -> Array[Double] {
  [
    self.accepted_count.to_double(),
    self.rejected_count.to_double(),
    self.gap_count.to_double(),
    self.mean_quality(),
    if self.finished {
      1.0
    } else {
      0.0
    },
  ]
}