// Static contract for the v2 bundled upstream media handoff.
//
// Scope: guard `StreamWebcamMedia` against reintroducing timed sink sleeps in
// the gRPC receive loop.
// Targets: `server/src/main/relay_service.rs` and
// `server/src/main/relay_service/upstream_media_rpc.rs`.
// Why: client->server freshness depends on draining bundled media as it
// arrives; presentation timing belongs in bounded handoff workers.

const RELAY_SERVICE: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/main/relay_service.rs"
));
const WEBCAM_RPC: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/main/relay_service/upstream_media_rpc.rs"
));
const MEDIA_V2: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/main/relay_service/media_v2.rs"
));
const WEBCAM_SINK: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/video_sinks/webcam_sink.rs"
));
const WEBCAM_CONSTRUCTOR: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/video_sinks/webcam_sink/constructor.rs"
));
const WEBCAM_FRAME_HANDOFF: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/video_sinks/webcam_sink/frame_handoff.rs"
));
const MJPEG_SPOOL: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/video_sinks/mjpeg_spool.rs"
));
const HEVC_MJPEG_GUARD: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/video_sinks/hevc_mjpeg_guard.rs"
));
const PROFILE_OFFSETS: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/calibration/profile_offsets.rs"
));
const MATRIX_SCRIPT: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/manual/run_server_to_rc_mode_matrix.sh"
));

#[test]
fn bundled_receive_loop_keeps_video_latest_only_instead_of_sleeping_for_handoff() {
    for expected in [
        "tokio::sync::mpsc::channel::<MediaV2ScheduledAudio>(32)",
        "tokio::sync::watch::channel::<Option<MediaV2ScheduledVideo>>(None)",
        "run_media_v2_audio_handoff(audio_handoff_rx",
        "tokio::spawn(run_media_v2_video_handoff",
        "let bundle_epoch = bundle_arrived_at + schedule.common_delay;",
        "let bundle_base_remote_pts_us = facts.capture_start_us;",
        "prepare_media_v2_audio(",
        "prepare_media_v2_video(",
        "bundle_base_remote_pts_us",
        "bundle_epoch",
        ".send(scheduled_audio)",
        "send_replace(Some(scheduled_video))",
        "media_v2_stream_idle_timeout()",
        "stream_webcam_media v2 idle timeout",
        "closing stale upstream leases",
    ] {
        assert!(
            WEBCAM_RPC.contains(expected),
            "v2 bundled RPC should enqueue handoff work with marker {expected}"
        );
    }

    for forbidden in [
        "push_media_v2_audio(",
        "feed_media_v2_video(",
        "sleep_until_media_v2(",
        "MediaV2Clock",
        "tokio::sync::mpsc::channel::<MediaV2ScheduledVideo>(32)",
    ] {
        assert!(
            !WEBCAM_RPC.contains(forbidden),
            "v2 bundled RPC receive loop must not perform timed sink handoff: {forbidden}"
        );
    }
}

#[test]
fn handoff_workers_own_timing_and_presentation_telemetry() {
    for expected in [
        "async fn run_media_v2_audio_handoff",
        "async fn run_media_v2_video_handoff",
        "sleep_until_media_v2(item.due_at).await;",
        "sink.push(&audio);",
        "sink.finish();",
        "relay.feed(item.packet);",
        "mark_audio_presented(pts, item.due_at)",
        "mark_video_presented_with_live_lag(",
        "Why: sleeping in the receive loop created HTTP/2 backlog",
    ] {
        assert!(
            RELAY_SERVICE.contains(expected) || MEDIA_V2.contains(expected),
            "handoff workers should preserve timing/telemetry marker {expected}"
        );
    }
}

#[test]
fn stream_shutdown_drains_handoff_workers_before_releasing_leases() {
    let audio_drop = WEBCAM_RPC.find("drop(audio_handoff_tx);").unwrap();
    let video_drop = WEBCAM_RPC.find("drop(video_handoff_tx);").unwrap();
    let audio_join = WEBCAM_RPC.find("audio_worker.await").unwrap();
    let video_join = WEBCAM_RPC.find("video_worker.await").unwrap();
    let close_camera = WEBCAM_RPC
        .rfind("upstream_media_rt.close_camera(camera_lease.generation);")
        .unwrap();
    let close_microphone = WEBCAM_RPC
        .rfind("upstream_media_rt.close_microphone(microphone_lease.generation);")
        .unwrap();

    assert!(audio_drop < audio_join);
    assert!(video_drop < video_join);
    assert!(audio_join < close_camera);
    assert!(video_join < close_microphone);
}

#[test]
fn hevc_ingress_decodes_to_existing_mjpeg_uvc_path() {
    for expected in [
        "let use_hevc = matches!(cfg.codec, CameraCodec::Hevc);",
        "video/x-h265",
        "h265parse",
        "require_hevc_decoder()",
        "configure_hevc_decoder(&decoder)",
        "discard-corrupted-frames",
        "automatic-request-sync-points",
        "jpegenc",
        "LESAVKA_UVC_HEVC_JPEG_QUALITY",
        "LESAVKA_UVC_HEVC_SPOOL_PULL_TIMEOUT_MS",
        "LESAVKA_UVC_HEVC_DECODE_MISS_LIMIT",
        "image/jpeg",
        "hevc_mjpeg_decoded_queue",
        "hevc_mjpeg_encoded_queue",
        "hevc_mjpeg_spool_sink",
        ".property(\"sync\", false)",
        ".property(\"enable-last-sample\", false)",
        "decode_recovery_needs_irap",
        "contains_hevc_irap(&pkt.data)",
        "failed to spool decoded HEVC frame for UVC helper",
        "produced no frames; freezing output until the next recovery keyframe",
        "HEVC camera uplink will be decoded and emitted as MJPEG/UVC",
    ] {
        assert!(
            WEBCAM_SINK.contains(expected)
                || WEBCAM_CONSTRUCTOR.contains(expected)
                || WEBCAM_FRAME_HANDOFF.contains(expected)
                || MJPEG_SPOOL.contains(expected)
                || HEVC_MJPEG_GUARD.contains(expected),
            "HEVC UVC sink should preserve decode-to-MJPEG marker {expected}"
        );
    }
    let hevc_caps = WEBCAM_CONSTRUCTOR.find("video/x-h265").unwrap();
    let hevc_encoder = WEBCAM_CONSTRUCTOR[hevc_caps..].find("jpegenc").unwrap();
    assert!(
        hevc_encoder > 0,
        "HEVC should be decoded before MJPEG encoding for the existing UVC output path"
    );
}

#[test]
fn mjpeg_ingress_remains_passthrough_and_profile_calibrated() {
    for expected in [
        "let use_mjpeg = matches!(cfg.codec, CameraCodec::Mjpeg);",
        "image/jpeg",
        "src.set_caps(Some(&caps_mjpeg));",
        "MjpegSpoolTiming::mjpeg_passthrough(pkt.pts)",
        "spool_mjpeg_frame_with_timing(path, &pkt.data, Some(timing))",
        "FACTORY_MJPEG_VIDEO_MODE_OFFSETS_US",
        "FACTORY_HEVC_VIDEO_MODE_OFFSETS_US",
        "LESAVKA_UPSTREAM_MJPEG_VIDEO_PLAYOUT_MODE_OFFSETS_US",
        "LESAVKA_UPSTREAM_HEVC_VIDEO_PLAYOUT_MODE_OFFSETS_US",
        "LESAVKA_UPSTREAM_HEVC_OPUS_AUDIO_PLAYOUT_MODE_OFFSETS_US",
        "LESAVKA_SERVER_RC_PROFILE=${LESAVKA_SERVER_RC_PROFILE:-mjpeg}",
        "LESAVKA_SERVER_RC_AUDIO_PROFILE=${LESAVKA_SERVER_RC_AUDIO_PROFILE:-pcm}",
        "LESAVKA_SERVER_RC_NORMALIZED_PROFILE=hevc",
    ] {
        assert!(
            WEBCAM_SINK.contains(expected)
                || WEBCAM_CONSTRUCTOR.contains(expected)
                || WEBCAM_FRAME_HANDOFF.contains(expected)
                || PROFILE_OFFSETS.contains(expected)
                || MATRIX_SCRIPT.contains(expected),
            "MJPEG/HEVC profile separation should contain marker {expected}"
        );
    }
    assert!(
        PROFILE_OFFSETS
            .find("FACTORY_MJPEG_VIDEO_MODE_OFFSETS_US")
            .unwrap()
            < PROFILE_OFFSETS
                .find("FACTORY_HEVC_VIDEO_MODE_OFFSETS_US")
                .unwrap(),
        "MJPEG factory map should stay separate from the additive HEVC map"
    );
}
