// Chaos contract for the client->server->RCT route.
//
// Scope: model jitter, loss, and reconnect effects on freshness-first bundled
// media without touching physical HID or RCT devices.
// Targets: scripts/manual/client_rct_transport_layers.py and
// client/src/sync_probe/runner/bundled_transport.rs.
// Why: the route should drop stale work, recover on fresh bundles, and point to
// the right bottleneck before we add invasive server introspection.

const TRANSPORT_LAYERS: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/manual/client_rct_transport_layers.py"
));
const CLIENT_RCT_SCRIPT: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/manual/run_client_to_rct_transport_probe.sh"
));
const BUNDLED_TRANSPORT: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/client/src/sync_probe/runner/bundled_transport.rs"
));
const PACKET_TIMING: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/client/src/sync_probe/runner/bundled_transport/packet_timing.rs"
));

#[derive(Clone, Copy)]
struct BundleArrival {
    pts_ms: u64,
    arrived_ms: u64,
    keyframe: bool,
}

fn accepted_fresh_bundles(events: &[BundleArrival], max_age_ms: u64) -> Vec<u64> {
    let mut waiting_for_keyframe = false;
    let mut accepted = Vec::new();
    for event in events {
        if event.arrived_ms.saturating_sub(event.pts_ms) > max_age_ms {
            waiting_for_keyframe = true;
            continue;
        }
        if waiting_for_keyframe && !event.keyframe {
            continue;
        }
        waiting_for_keyframe = false;
        accepted.push(event.pts_ms);
    }
    accepted
}

#[test]
fn jitter_loss_and_reconnect_recover_only_on_fresh_keyframe_bundles() {
    let route = [
        BundleArrival {
            pts_ms: 0,
            arrived_ms: 20,
            keyframe: true,
        },
        BundleArrival {
            pts_ms: 33,
            arrived_ms: 1_800,
            keyframe: false,
        },
        BundleArrival {
            pts_ms: 66,
            arrived_ms: 110,
            keyframe: false,
        },
        BundleArrival {
            pts_ms: 99,
            arrived_ms: 140,
            keyframe: true,
        },
    ];

    assert_eq!(accepted_fresh_bundles(&route, 1_000), vec![0, 99]);
}

#[test]
fn chaos_artifacts_classify_bottlenecks_before_deeper_introspection() {
    for marker in [
        "client_queue_or_bundle_generation",
        "server_receive_or_ingress_queue",
        "client_to_server_transport",
        "post_client_send_to_rct_path",
        "needs_deeper_introspection",
    ] {
        assert!(
            TRANSPORT_LAYERS.contains(marker),
            "route chaos summary should preserve bottleneck marker {marker}"
        );
    }

    assert!(
        CLIENT_RCT_SCRIPT.contains("client-send-bundles.jsonl"),
        "client-to-RCT harness should preserve send artifact filename"
    );

    for marker in [
        "local_age_ms",
        "video_capture_pts_us",
        "audio_first_capture_pts_us",
    ] {
        assert!(
            BUNDLED_TRANSPORT.contains(marker) || PACKET_TIMING.contains(marker),
            "bundled transport should preserve send artifact marker {marker}"
        );
    }
}
