///|
/// A rolling analysis window with quality and headline metrics.
pub(all) struct WindowedAnalysis {
  start : Int
  end : Int
  mean_rr : Double
  mean_hr : Double
  sdnn : Double
  rmssd : Double
  quality_ratio : Double
  readiness_proxy : Double
} derive(FromJson, ToJson, Debug, Eq)

///|
/// Analyze overlapping windows without computing expensive spectral features.
pub fn analyze_windows(
  intervals : Array[Double],
  window_size : Int,
  hop_size : Int,
  config : HrvConfig,
) -> Array[WindowedAnalysis] {
  let result = []
  let ranges = make_segment_ranges(intervals.length(), window_size, hop_size)
  for segment_range in ranges {
    let window = slice_segment(intervals, segment_range)
    let validation = validate_intervals(window, config)
    let cleaned = valid_intervals(window, config)
    let mean_rr = mean_value(cleaned)
    let rmssd = calculate_rmssd(cleaned)
    let quality = if window.length() == 0 {
      0.0
    } else {
      validation.valid.to_double() / window.length().to_double()
    }
    let mean_scale = if mean_rr == 0.0 { 1.0 } else { mean_rr }
    let proxy = (quality * 70.0 +
    (rmssd / mean_scale * 100.0).clamp(min=0.0, max=30.0)).clamp(
      min=0.0,
      max=100.0,
    )
    result.push({
      start: segment_range.start,
      end: segment_range.end,
      mean_rr,
      mean_hr: if mean_rr <= 0.0 {
        0.0
      } else {
        60000.0 / mean_rr
      },
      sdnn: calculate_sdnn(cleaned),
      rmssd,
      quality_ratio: quality,
      readiness_proxy: proxy,
    })
  }
  result
}

///|
/// Return the window with the highest readiness proxy.
pub fn best_window(windows : Array[WindowedAnalysis]) -> WindowedAnalysis? {
  if windows.length() == 0 {
    return None
  }
  let mut best = windows[0]
  for window in windows {
    if window.readiness_proxy > best.readiness_proxy {
      best = window
    }
  }
  Some(best)
}

///|
/// Return the window with the lowest quality.
pub fn worst_quality_window(
  windows : Array[WindowedAnalysis],
) -> WindowedAnalysis? {
  if windows.length() == 0 {
    return None
  }
  let mut worst = windows[0]
  for window in windows {
    if window.quality_ratio < worst.quality_ratio {
      worst = window
    }
  }
  Some(worst)
}

///|
/// Calculate the median and spread of window RMSSD values.
pub fn window_rmssd_summary(
  windows : Array[WindowedAnalysis],
) -> DistributionStats {
  let values = []
  for window in windows {
    values.push(window.rmssd)
  }
  summarize_distribution(values)
}

///|
/// Compare two adjacent windows for a meaningful quality change.
pub fn window_quality_change(
  left : WindowedAnalysis,
  right : WindowedAnalysis,
) -> Double {
  right.quality_ratio - left.quality_ratio
}

///|
/// Return the average recovery proxy across windows.
pub fn average_window_readiness(windows : Array[WindowedAnalysis]) -> Double {
  let values = []
  for window in windows {
    values.push(window.readiness_proxy)
  }
  mean_value(values)
}