// Integration coverage for client-shaped bundles entering server v2 planning.
//
// Scope: include the server entrypoint and feed it complete upstream bundles
// that match the client transport schema.
// Targets: `server/src/main/relay_service.rs` and
// `server/src/main/relay_service/upstream_media_rpc.rs`.
// Why: the client can preserve sync only if the server keeps using one capture
// clock, rejects stale whole bundles, and resumes HEVC only from clean pictures.

#[allow(warnings)]
mod server_main {
    include!(env!("LESAVKA_SERVER_MAIN_SRC"));

    mod upstream_bundle_tests {
        use super::*;
        use lesavka_common::lesavka::{
            AudioEncoding, AudioPacket, UpstreamMediaBundle, VideoPacket,
        };
        use serial_test::serial;
        use temp_env::with_vars;

        fn hevc_irap() -> Vec<u8> {
            vec![0, 0, 0, 1, 0x26, 0x01, 0xaa]
        }

        fn hevc_delta() -> Vec<u8> {
            vec![0, 0, 0, 1, 0x02, 0x01, 0xbb]
        }

        fn bundle(seq: u64, video_data: Vec<u8>, max_queue_age_ms: u32) -> UpstreamMediaBundle {
            UpstreamMediaBundle {
                session_id: 99,
                seq,
                capture_start_us: 980_000,
                capture_end_us: 1_010_000,
                video: Some(VideoPacket {
                    seq,
                    pts: 1_000_000,
                    data: video_data,
                    client_capture_pts_us: 1_000_000,
                    client_send_pts_us: 1_005_000,
                    client_queue_age_ms: max_queue_age_ms,
                    ..Default::default()
                }),
                audio: vec![
                    AudioPacket {
                        seq: seq.saturating_mul(2),
                        pts: 980_000,
                        data: vec![0x11; 1_920],
                        client_capture_pts_us: 980_000,
                        client_send_pts_us: 1_005_000,
                        client_queue_age_ms: 25,
                        ..Default::default()
                    },
                    AudioPacket {
                        seq: seq.saturating_mul(2).saturating_add(1),
                        pts: 1_010_000,
                        data: vec![0x22; 1_920],
                        client_capture_pts_us: 1_010_000,
                        client_send_pts_us: 1_015_000,
                        client_queue_age_ms: 5,
                        ..Default::default()
                    },
                ],
                audio_sample_rate: 48_000,
                audio_channels: 2,
                audio_encoding: AudioEncoding::PcmS16le as i32,
                video_width: 1280,
                video_height: 720,
                video_fps: 30,
            }
        }

        #[test]
        #[serial]
        fn client_bundle_summary_preserves_one_capture_span_for_server_handoff() {
            let bundle = bundle(1, hevc_irap(), 25);
            let facts = summarize_media_v2_bundle(&bundle).expect("bundle facts");

            assert!(facts.has_audio);
            assert!(facts.has_video);
            assert_eq!(facts.capture_start_us, 980_000);
            assert_eq!(facts.capture_end_us, 1_010_000);
            assert_eq!(facts.capture_span_us, 30_000);
            assert_eq!(facts.max_queue_age_ms, 25);
        }

        #[test]
        #[serial]
        fn server_rejects_stale_client_bundle_before_handoff_workers_can_play_it() {
            with_vars(
                [
                    ("LESAVKA_UPSTREAM_V2_MAX_LIVE_AGE_MS", Some("1000")),
                    ("LESAVKA_UPSTREAM_V2_PLAYOUT_DELAY_MS", Some("20")),
                ],
                || {
                    let facts = summarize_media_v2_bundle(&bundle(2, hevc_irap(), 1_000))
                        .expect("bundle facts");

                    assert!(
                        media_v2_handoff_schedule(facts, 0, 0).is_none(),
                        "queue age at the max-live budget must drop the whole bundle before UAC/UVC handoff"
                    );
                },
            );
        }

        #[test]
        #[serial]
        fn server_schedule_preserves_relative_audio_video_offsets_without_blocking_ingress() {
            with_vars(
                [
                    ("LESAVKA_UPSTREAM_V2_MAX_LIVE_AGE_MS", Some("1000")),
                    ("LESAVKA_UPSTREAM_V2_PLAYOUT_DELAY_MS", Some("20")),
                ],
                || {
                    let facts = summarize_media_v2_bundle(&bundle(3, hevc_irap(), 30))
                        .expect("bundle facts");
                    let schedule =
                        media_v2_handoff_schedule(facts, 0, 120_000).expect("handoff schedule");

                    assert!(schedule.audio_due_at.is_some());
                    assert!(schedule.video_due_at.is_some());
                    assert_eq!(schedule.common_delay, Duration::from_millis(20));
                    assert_eq!(schedule.relative_audio_delay, Duration::ZERO);
                    assert_eq!(schedule.relative_video_delay, Duration::from_millis(120));
                },
            );
        }

        #[test]
        fn server_hevc_recovery_holds_delta_frames_until_the_next_irap_bundle() {
            let delta = bundle(4, hevc_delta(), 10);
            let keyframe = bundle(5, hevc_irap(), 10);

            assert!(media_v2_should_hold_hevc_video_for_recovery(
                true,
                camera::CameraCodec::Hevc,
                delta.video.as_ref()
            ));
            assert!(!media_v2_should_hold_hevc_video_for_recovery(
                true,
                camera::CameraCodec::Hevc,
                keyframe.video.as_ref()
            ));
            assert!(media_v2_has_hevc_recovery_keyframe(
                camera::CameraCodec::Hevc,
                keyframe.video.as_ref()
            ));
        }
    }
}
