// Performance contract for HEVC decode-to-MJPEG handoff timing.
//
// Scope: bound the synthetic decode/spool handoff model used by server-to-RCT
// calibration.
// Targets: server/src/video_sinks/webcam_sink.rs and
// server/src/video_sinks/mjpeg_spool.rs.
// Why: HEVC can save WAN bandwidth only if the server handoff stays
// comfortably below the freshness budget and does not create backlog.

#[derive(Clone, Copy)]
struct DecodeHandoffSample {
    decode_ms: f64,
    spool_ms: f64,
    guard_ms: f64,
}

impl DecodeHandoffSample {
    fn total_ms(self) -> f64 {
        self.decode_ms + self.spool_ms + self.guard_ms
    }
}

fn percentile_95(values: &mut [f64]) -> f64 {
    values.sort_by(|a, b| a.total_cmp(b));
    let index = ((values.len() as f64) * 0.95).ceil() as usize - 1;
    values[index.min(values.len() - 1)]
}

fn synthetic_rpi5_hevc_mjpeg_handoff_samples() -> Vec<DecodeHandoffSample> {
    (0..120)
        .map(|idx| DecodeHandoffSample {
            decode_ms: 72.0 + f64::from(idx % 7) * 3.0,
            spool_ms: 3.0 + f64::from(idx % 3),
            guard_ms: 0.5,
        })
        .chain([DecodeHandoffSample {
            decode_ms: 126.0,
            spool_ms: 8.0,
            guard_ms: 1.0,
        }])
        .collect()
}

#[test]
fn hevc_decode_to_mjpeg_handoff_p95_stays_under_phase_two_budget() {
    let mut totals: Vec<f64> = synthetic_rpi5_hevc_mjpeg_handoff_samples()
        .into_iter()
        .map(DecodeHandoffSample::total_ms)
        .collect();
    let p95 = percentile_95(&mut totals);

    assert!(
        p95 < 150.0,
        "HEVC decode + MJPEG handoff p95 should stay under 150ms, saw {p95:.1}ms"
    );
}
