// Integration coverage for the server video-support helpers.
//
// Scope: exercise the pure policy helpers and cached env flag behavior from
// the root test taxonomy.
// Targets: `server/src/video_support.rs`.
// Why: these helpers are small but central to the adaptive video policy, so
// they are a good fit for a centralized contract test.

use gstreamer as gst;
use serial_test::serial;
use std::process::Command;
use temp_env::with_var;

use lesavka_server::video_support::{
    adjust_effective_fps, contains_hevc_irap, contains_idr, default_eye_fps, dev_mode_enabled,
    env_u32, env_usize, next_local_pts, pick_h264_decoder, pick_hevc_decoder, require_hevc_decoder,
    reserve_local_pts, should_send_frame,
};

#[test]
fn default_eye_fps_tracks_the_expected_bitrate_steps() {
    assert_eq!(default_eye_fps(0), 25);
    assert_eq!(default_eye_fps(2_500), 15);
    assert_eq!(default_eye_fps(2_501), 20);
    assert_eq!(default_eye_fps(4_001), 25);
}

#[test]
fn contains_idr_handles_short_and_multi_nal_annex_b_streams() {
    assert!(contains_idr(&[0, 0, 1, 0x65, 0x00]));
    assert!(contains_idr(&[0, 0, 0, 1, 0x41, 0x00, 0, 0, 1, 0x65, 0x00]));
    assert!(!contains_idr(&[0, 0, 0, 1, 0x41, 0x00, 0x00]));
    assert!(!contains_idr(&[0, 0, 2, 0x65, 0, 0, 0, 2, 0x65]));
}

#[test]
fn contains_hevc_irap_handles_short_and_multi_nal_annex_b_streams() {
    assert!(contains_hevc_irap(&[0, 0, 1, 0x26, 0x01, 0xaa]));
    assert!(contains_hevc_irap(&[
        0, 0, 0, 1, 0x02, 0x01, 0xaa, 0, 0, 1, 0x28, 0x01, 0xbb,
    ]));
    assert!(!contains_hevc_irap(&[0, 0, 0, 1, 0x02, 0x01, 0xaa]));
    assert!(!contains_hevc_irap(&[0, 0, 2, 0x26, 0, 0, 0, 2, 0x28]));
    assert!(!contains_hevc_irap(&[0, 0, 1, 0x26]));
}

#[test]
fn adjust_effective_fps_and_timestamp_helpers_stay_stable() {
    assert_eq!(adjust_effective_fps(20, 12, 25, 5, 10), 17);
    assert_eq!(adjust_effective_fps(20, 12, 25, 0, 20), 21);
    assert_eq!(adjust_effective_fps(12, 12, 25, 10, 10), 12);
    assert_eq!(adjust_effective_fps(18, 12, 25, 0, 0), 18);
    assert_eq!(adjust_effective_fps(0, 0, 25, 1, 100), 1);

    assert!(should_send_frame(0, 10, 25));
    assert!(!should_send_frame(40_000, 50_000, 25));
    assert!(should_send_frame(40_000, 90_000, 25));
    assert!(should_send_frame(40_000, 1_040_000, 0));

    let counter = std::sync::atomic::AtomicU64::new(0);
    assert_eq!(next_local_pts(&counter, 40_000), 0);
    assert_eq!(next_local_pts(&counter, 40_000), 40_000);

    let reserved = std::sync::atomic::AtomicU64::new(0);
    assert_eq!(reserve_local_pts(&reserved, 20_000, 0), 20_000);
    assert_eq!(reserve_local_pts(&reserved, 20_000, 40_000), 20_001);
    assert_eq!(reserve_local_pts(&reserved, 100_000, 40_000), 100_000);
}

#[test]
#[serial]
fn env_helpers_parse_and_fallback_without_panicking() {
    with_var("LESAVKA_TEST_U32", Some("42"), || {
        assert_eq!(env_u32("LESAVKA_TEST_U32", 7), 42);
    });
    with_var("LESAVKA_TEST_U32", Some("oops"), || {
        assert_eq!(env_u32("LESAVKA_TEST_U32", 7), 7);
    });
    with_var("LESAVKA_TEST_USIZE", Some("128"), || {
        assert_eq!(env_usize("LESAVKA_TEST_USIZE", 64), 128);
    });
    with_var("LESAVKA_TEST_USIZE", Some("oops"), || {
        assert_eq!(env_usize("LESAVKA_TEST_USIZE", 64), 64);
    });
    with_var("LESAVKA_TEST_USIZE", None::<&str>, || {
        assert_eq!(env_usize("LESAVKA_TEST_USIZE", 64), 64);
    });
}

#[test]
fn pick_h264_decoder_returns_a_known_decoder_name() {
    gst::init().expect("initialize gstreamer");
    assert!(matches!(
        pick_h264_decoder(),
        "v4l2h264dec"
            | "v4l2slh264dec"
            | "omxh264dec"
            | "vulkanh264dec"
            | "missing-hardware-h264dec"
    ));
}

#[test]
#[serial]
fn pick_hevc_decoder_defaults_to_hardware_or_loud_missing_decoder() {
    gst::init().expect("initialize gstreamer");
    with_var("LESAVKA_HEVC_DECODER", None::<&str>, || {
        with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", None::<&str>, || {
            assert!(matches!(
                pick_hevc_decoder(),
                "v4l2slh265dec" | "v4l2h265dec" | "vulkanh265dec" | "missing-hardware-hevcdec"
            ));
        });
    });
}

#[test]
#[serial]
fn pick_hevc_decoder_rejects_software_override_unless_lab_fallback_is_explicit() {
    gst::init().expect("initialize gstreamer");

    with_var("LESAVKA_HEVC_DECODER", Some(" avdec_h265 "), || {
        with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", None::<&str>, || {
            assert_eq!(pick_hevc_decoder(), "missing-hardware-hevcdec");
            assert!(require_hevc_decoder().is_err());
        });
        with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", Some("1"), || {
            assert_eq!(pick_hevc_decoder(), "avdec_h265");
        });
    });

    with_var("LESAVKA_HEVC_DECODER", Some(" definitely_missing "), || {
        with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", Some("1"), || {
            assert_eq!(pick_hevc_decoder(), "missing-hardware-hevcdec");
        });
    });

    with_var("LESAVKA_HEVC_DECODER", Some(" "), || {
        with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", None::<&str>, || {
            assert!(matches!(
                pick_hevc_decoder(),
                "v4l2slh265dec" | "v4l2h265dec" | "vulkanh265dec" | "missing-hardware-hevcdec"
            ));
        });
    });
}

fn run_ignored_probe(test_name: &str, envs: &[(&str, Option<&str>)]) -> std::process::Output {
    let exe = std::env::current_exe().expect("resolve test binary");
    let mut cmd = Command::new(exe);
    cmd.arg(test_name).arg("--ignored").arg("--exact");
    for (key, value) in envs {
        match value {
            Some(v) => {
                cmd.env(key, v);
            }
            None => {
                cmd.env_remove(key);
            }
        }
    }
    cmd.output().expect("run probe")
}

#[test]
#[serial]
fn dev_mode_flag_is_cached_per_process() {
    let enabled = run_ignored_probe(
        "probe_dev_mode_enabled_true",
        &[("LESAVKA_DEV_MODE", Some("1"))],
    );
    assert!(
        enabled.status.success(),
        "expected dev-mode probe to pass:\nstdout:\n{}\nstderr:\n{}",
        String::from_utf8_lossy(&enabled.stdout),
        String::from_utf8_lossy(&enabled.stderr)
    );

    let disabled = run_ignored_probe(
        "probe_dev_mode_enabled_false",
        &[("LESAVKA_DEV_MODE", None)],
    );
    assert!(
        disabled.status.success(),
        "expected dev-mode false probe to pass:\nstdout:\n{}\nstderr:\n{}",
        String::from_utf8_lossy(&disabled.stdout),
        String::from_utf8_lossy(&disabled.stderr)
    );
}

#[test]
#[ignore]
fn probe_dev_mode_enabled_true() {
    assert!(dev_mode_enabled());
}

#[test]
#[ignore]
fn probe_dev_mode_enabled_false() {
    assert!(!dev_mode_enabled());
}
