// Contract tests for the client-origin transport-to-RCT probe.
//
// Scope: statically guard the black-box transport harness and client timeline
// artifacts used for client->server tuning.
// Targets: `scripts/manual/run_client_to_rct_transport_probe.sh`,
// `scripts/manual/client_rct_transport_summary.py`, and
// `scripts/manual/client_rct_uvc_frame_meta_fetch.sh`, and
// `client/src/sync_probe/`.
// Why: this probe is the bridge between synthetic bundled client media and the
// final RCT sync/freshness/smoothness evidence, so it must not drift into
// split-stream or sudo-mutating behavior.

const CLIENT_RCT_SCRIPT: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/manual/run_client_to_rct_transport_probe.sh"
));
const CLIENT_RCT_REMOTE_CAPTURE: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/manual/client_rct_remote_capture.sh"
));
const CLIENT_RCT_SUMMARY: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/manual/client_rct_transport_summary.py"
));
const CLIENT_RCT_LAYERS: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/manual/client_rct_transport_layers.py"
));
const UVC_FRAME_META_FETCH: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/manual/client_rct_uvc_frame_meta_fetch.sh"
));
const UVC_FRAME_META_SUMMARY: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/manual/summarize_uvc_frame_meta_log.py"
));
const SYNC_PROBE_CONFIG: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/client/src/sync_probe/config.rs"
));
const SYNC_PROBE_RUNNER: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/client/src/sync_probe/runner.rs"
));
const SYNC_PROBE_BUNDLED_TRANSPORT: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/client/src/sync_probe/runner/bundled_transport.rs"
));
const SYNC_PROBE_TIMELINE: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/client/src/sync_probe/timeline.rs"
));

#[test]
fn client_rct_probe_injects_synthetic_media_through_bundled_transport() {
    for expected in [
        "lesavka-sync-probe",
        "--event-width-codes \"${PROBE_EVENT_WIDTH_CODES}\"",
        "PROBE_EVENT_WIDTH_CODES=${PROBE_EVENT_WIDTH_CODES:-1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}",
        "--timeline-json \"${LOCAL_CLIENT_TIMELINE_JSON}\"",
        "stream_webcam_media",
        "UpstreamMediaBundle",
        "server does not advertise bundled webcam media",
        "refusing to measure split upstream",
    ] {
        assert!(
            CLIENT_RCT_SCRIPT.contains(expected)
                || SYNC_PROBE_RUNNER.contains(expected)
                || SYNC_PROBE_BUNDLED_TRANSPORT.contains(expected)
                || SYNC_PROBE_CONFIG.contains(expected),
            "client-to-RCT transport probe should contain {expected}"
        );
    }
    for forbidden in [
        ".stream_camera(Request::new(outbound))",
        ".stream_microphone(Request::new(outbound))",
    ] {
        assert!(
            !SYNC_PROBE_RUNNER.contains(forbidden)
                && !SYNC_PROBE_BUNDLED_TRANSPORT.contains(forbidden),
            "synthetic transport probe must not use split upstream RPC {forbidden}"
        );
    }
}

#[test]
fn client_rct_probe_preserves_black_box_rct_measurement_artifacts() {
    for expected in [
        "LOCAL_CAPTURE=\"${LOCAL_REPORT_DIR}/capture.mkv\"",
        "LOCAL_REPORT_JSON=\"${LOCAL_REPORT_DIR}/report.json\"",
        "LOCAL_EVENTS_CSV=\"${LOCAL_REPORT_DIR}/events.csv\"",
        "LOCAL_CLIENT_TIMELINE_JSON=\"${LOCAL_REPORT_DIR}/client-transport-timeline.json\"",
        "LOCAL_TRANSPORT_SUMMARY_JSON=\"${LOCAL_REPORT_DIR}/client-rct-transport-summary.json\"",
        "LOCAL_RUN_LOG=\"${LOCAL_REPORT_DIR}/client-rct-run.log\"",
        "capture_start_unix_ns=",
        "lesavka-sync-analyze",
        "client_rct_transport_summary.py",
        "smoothness",
        "freshness_budget_ms",
        "freshness_bottleneck",
        "client_send_summary",
        "post_client_send_worst_p95_ms",
        "LESAVKA_CLIENT_RCT_UVC_FRAME_META_LOG_REMOTE",
        "uvc_frame_meta_log_remote=${LESAVKA_CLIENT_RCT_UVC_FRAME_META_LOG_REMOTE:-disabled}",
        "client_rct_uvc_frame_meta_fetch.sh",
        "summarize_uvc_frame_meta_log.py",
        "required UVC frame metadata log could not be summarized",
        "optional UVC frame metadata log could not be summarized",
        "uvc_frame_meta_summary_json",
        "lesavka.uvc-mjpeg-spool-summary.v1",
        "uvc_spool",
        "UVC spool boundary",
        "synthetic evidence",
        "video_age_p95_ms",
        "audio_age_p95_ms",
        "artifact_dir: ${LOCAL_REPORT_DIR}",
    ] {
        assert!(
            CLIENT_RCT_SCRIPT.contains(expected)
                || CLIENT_RCT_SUMMARY.contains(expected)
                || CLIENT_RCT_LAYERS.contains(expected)
                || UVC_FRAME_META_FETCH.contains(expected)
                || UVC_FRAME_META_SUMMARY.contains(expected)
                || CLIENT_RCT_REMOTE_CAPTURE.contains(expected),
            "client-to-RCT probe should preserve artifact/summary marker {expected}"
        );
    }
}

#[test]
fn client_rct_probe_keeps_shell_harness_focused_under_loc_limit() {
    for (name, contents) in [
        ("run_client_to_rct_transport_probe.sh", CLIENT_RCT_SCRIPT),
        ("client_rct_remote_capture.sh", CLIENT_RCT_REMOTE_CAPTURE),
        ("client_rct_uvc_frame_meta_fetch.sh", UVC_FRAME_META_FETCH),
        ("client_rct_transport_summary.py", CLIENT_RCT_SUMMARY),
    ] {
        let loc = contents.lines().count();
        assert!(
            loc <= 500,
            "{name} should stay under the 500 LOC refactor guard; saw {loc}"
        );
    }
}

#[test]
fn client_rct_probe_is_non_mutating_and_passwordless_by_default() {
    for expected in [
        "SSH_OPTS=${SSH_OPTS:-\"-o BatchMode=yes -o ConnectTimeout=5\"}",
        "no remote sudo/reconfigure will be attempted by this script",
        "LESAVKA_SERVER_ADDR=${LESAVKA_SERVER_ADDR:-auto}",
        "LESAVKA_CLIENT_RCT_MODE=${LESAVKA_CLIENT_RCT_MODE:-auto}",
        "LESAVKA_CLIENT_RCT_UPSTREAM_MODE=${LESAVKA_CLIENT_RCT_UPSTREAM_MODE:-${LESAVKA_CLIENT_RCT_MODE}}",
        "LESAVKA_CLIENT_RCT_UPSTREAM_CAMERA_CODEC=${LESAVKA_CLIENT_RCT_UPSTREAM_CAMERA_CODEC:-auto}",
        "LESAVKA_CLIENT_RCT_UPSTREAM_AUDIO_CODEC=${LESAVKA_CLIENT_RCT_UPSTREAM_AUDIO_CODEC:-${LESAVKA_UPLINK_AUDIO_CODEC:-pcm}}",
        "--camera-mode",
        "--camera-codec",
        "LESAVKA_CLIENT_RCT_START_DELAY_SECONDS=${LESAVKA_CLIENT_RCT_START_DELAY_SECONDS:-0}",
        "LESAVKA_CLIENT_RCT_START_DELAY_SECONDS must be a non-negative number",
        "start_delay=${LESAVKA_CLIENT_RCT_START_DELAY_SECONDS}s",
        "REMOTE_CAPTURE_PREROLL_SETTLE_SECONDS=${REMOTE_CAPTURE_PREROLL_SETTLE_SECONDS:-1}",
        "settling %ss after preroll discard",
        "timeout --kill-after=5 --signal=INT \"$((seconds + 5))\"",
        "ExitOnForwardFailure=yes",
        "127.0.0.1:${local_port}:127.0.0.1:${SERVER_TUNNEL_REMOTE_PORT}",
    ] {
        assert!(
            CLIENT_RCT_SCRIPT.contains(expected) || CLIENT_RCT_REMOTE_CAPTURE.contains(expected),
            "client-to-RCT probe should keep unattended marker {expected}"
        );
    }
    for forbidden in ["sudo -S", "read -s", "PASSWORD", "VAULT", "vault"] {
        assert!(
            !CLIENT_RCT_SCRIPT.contains(forbidden) && !UVC_FRAME_META_FETCH.contains(forbidden),
            "client-to-RCT probe should not prompt for or retrieve secrets: {forbidden}"
        );
    }
}

#[test]
fn client_timeline_schema_captures_origin_and_event_timing() {
    for expected in [
        "lesavka.client-transport-probe-timeline.v1",
        "client-generated",
        "client-post-capture-uplink-bundle",
        "client_uplink_included: true",
        "client_start_unix_ns",
        "client_capture_unix_ns",
        "planned_start_us",
        "planned_end_us",
        "marker_tick_period",
        "let client_start_unix_ns = capture.start_unix_ns();",
        "ProbeTimeline::new(camera, &schedule, config.duration, client_start_unix_ns)",
    ] {
        assert!(
            SYNC_PROBE_TIMELINE.contains(expected) || SYNC_PROBE_RUNNER.contains(expected),
            "client timeline should contain {expected}"
        );
    }
    assert!(
        SYNC_PROBE_RUNNER.find("ProbeTimeline::new").unwrap()
            < SYNC_PROBE_RUNNER
                .rfind("run_bundled_probe_stream(")
                .unwrap(),
        "timeline should be written before bundled transport starts so origin evidence matches generated media"
    );
    assert!(
        SYNC_PROBE_BUNDLED_TRANSPORT.contains("let bundled_task = tokio::spawn"),
        "bundled transport module should own the spawned streaming task"
    );
}
