///|
/// A half-open index range in an RR stream.
pub(all) struct SegmentRange {
  start : Int
  end : Int
  ordinal : Int
} derive(FromJson, ToJson, Debug, Eq)

///|
/// Metrics attached to one analysis segment.
pub(all) struct SegmentSummary {
  range : SegmentRange
  count : Int
  mean_rr : Double
  sdnn : Double
  rmssd : Double
  pnn50 : Double
  median_rr : Double
  quality_ratio : Double
  sd1 : Double
  sd2 : Double
} derive(FromJson, ToJson, Debug, Eq)

///|
/// A detected change point between two local windows.
pub(all) struct ChangePoint {
  index : Int
  before_mean : Double
  after_mean : Double
  magnitude : Double
  confidence : Double
} derive(FromJson, ToJson, Debug, Eq)

///|
/// Build overlapping segment ranges. The final partial segment is omitted.
pub fn make_segment_ranges(
  length : Int,
  segment_size : Int,
  hop_size : Int,
) -> Array[SegmentRange] {
  let result = []
  if length <= 0 || segment_size <= 0 || hop_size <= 0 || length < segment_size {
    return result
  }
  let mut start = 0
  let mut ordinal = 0
  while start + segment_size <= length {
    result.push({ start, end: start + segment_size, ordinal })
    start += hop_size
    ordinal += 1
  }
  result
}

///|
/// Copy one segment from an RR series.
pub fn slice_segment(
  values : Array[Double],
  range : SegmentRange,
) -> Array[Double] {
  let result = []
  let start = if range.start < 0 { 0 } else { range.start }
  let end = if range.end > values.length() {
    values.length()
  } else {
    range.end
  }
  if start >= end {
    return result
  }
  for i in start.. Array[SegmentSummary] {
  let result = []
  let ranges = make_segment_ranges(intervals.length(), segment_size, hop_size)
  for range in ranges {
    let segment = slice_segment(intervals, range)
    let validation = validate_intervals(segment, config)
    let poincare = calculate_poincare(segment)
    result.push({
      range,
      count: segment.length(),
      mean_rr: mean_value(segment),
      sdnn: calculate_sdnn(segment),
      rmssd: calculate_rmssd(segment),
      pnn50: calculate_pnn(segment, 50.0),
      median_rr: median_value(segment),
      quality_ratio: if segment.length() == 0 {
        0.0
      } else {
        validation.valid.to_double() / segment.length().to_double()
      },
      sd1: poincare.sd1,
      sd2: poincare.sd2,
    })
  }
  result
}

///|
/// Calculate a causal rolling RMSSD series.
pub fn rolling_rmssd(
  intervals : Array[Double],
  window_size : Int,
) -> Array[Double] {
  let result = []
  if window_size <= 0 {
    return result
  }
  for i in 0.. Array[Double] {
  let result = []
  if window_size <= 0 {
    return result
  }
  for i in 0.. Array[ChangePoint] {
  let result = []
  if window_size <= 0 || values.length() < window_size * 2 {
    return result
  }
  let mut index = window_size
  while index + window_size <= values.length() {
    let before = []
    let after = []
    for i in (index - window_size)..= minimum_magnitude {
      let scale = standard_deviation(before) + standard_deviation(after)
      let confidence = if scale == 0.0 {
        1.0
      } else {
        (magnitude / scale).clamp(min=0.0, max=1.0)
      }
      result.push({ index, before_mean, after_mean, magnitude, confidence })
    }
    index += window_size
  }
  result
}

///|
/// A small private absolute-value helper for change-point calculations.
fn absolute_difference(left : Double, right : Double) -> Double {
  let difference = left - right
  if difference < 0.0 {
    -difference
  } else {
    difference
  }
}

///|
/// Downsample a sequence by averaging fixed-size blocks.
pub fn downsample_mean(
  values : Array[Double],
  block_size : Int,
) -> Array[Double] {
  let result = []
  if block_size <= 0 {
    return result
  }
  let mut start = 0
  while start < values.length() {
    let stop = if start + block_size > values.length() {
      values.length()
    } else {
      start + block_size
    }
    let block = []
    for i in start.. Array[Array[Double]] {
  let result = []
  let mut current = []
  for interval in intervals {
    if interval > gap_threshold_ms && current.length() > 0 {
      result.push(current)
      current = []
    }
    current.push(interval)
  }
  if current.length() > 0 {
    result.push(current)
  }
  result
}

///|
/// Return the largest contiguous run after removing invalid intervals.
pub fn longest_valid_segment(
  intervals : Array[Double],
  config : HrvConfig,
) -> Array[Double] {
  let mut best = []
  let mut current = []
  for interval in intervals {
    if classify_interval(interval, config) is Normal {
      current.push(interval)
      if current.length() > best.length() {
        best = []
        for value in current {
          best.push(value)
        }
      }
    } else {
      current = []
    }
  }
  best
}

///|
/// Calculate a quality-weighted mean of segment RMSSD values.
pub fn weighted_segment_rmssd(summaries : Array[SegmentSummary]) -> Double {
  let values = []
  let weights = []
  for summary in summaries {
    values.push(summary.rmssd)
    weights.push(summary.quality_ratio)
  }
  weighted_mean(values, weights)
}