use super::{
    SOFTWARE_VIDEO_FALLBACK_ENV, adjust_effective_fps, contains_hevc_irap, contains_idr,
    default_eye_fps, env_u32, env_usize, is_hardware_h264_decoder, is_hardware_hevc_decoder,
    next_local_pts, pick_h264_decoder, pick_hevc_decoder, require_h264_decoder,
    require_hevc_decoder, reserve_local_pts, should_send_frame, software_video_fallback_allowed,
};
use serial_test::serial;
use std::sync::atomic::AtomicU64;
use temp_env::with_var;

#[test]
fn default_eye_fps_tracks_bitrate_tiers() {
    assert_eq!(default_eye_fps(0), 25);
    assert_eq!(default_eye_fps(2_000), 15);
    assert_eq!(default_eye_fps(3_000), 20);
    assert_eq!(default_eye_fps(8_000), 25);
}

#[test]
fn contains_idr_finds_annex_b_keyframes() {
    let sample = [0, 0, 0, 1, 0x65, 0x88, 0x99];
    assert!(contains_idr(&sample));
    assert!(contains_idr(&[0, 0, 1, 0x65, 0x88]));
    assert!(!contains_idr(&[0, 0, 0, 1, 0x41, 0x99]));
    assert!(!contains_idr(&[0, 0, 2, 0x65, 0x88]));
}

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

#[test]
fn adjust_effective_fps_reacts_to_drop_windows() {
    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);
}

#[test]
fn should_send_frame_enforces_interval() {
    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, 40_001, 0));
    assert!(should_send_frame(40_000, 1_040_000, 0));
}

#[test]
fn next_local_pts_monotonically_advances() {
    let counter = AtomicU64::new(0);
    assert_eq!(next_local_pts(&counter, 40_000), 0);
    assert_eq!(next_local_pts(&counter, 40_000), 40_000);
}

#[test]
fn reserve_local_pts_prefers_preferred_value_but_stays_monotonic() {
    let counter = AtomicU64::new(0);
    assert_eq!(reserve_local_pts(&counter, 0, 40_000), 0);
    assert_eq!(reserve_local_pts(&counter, 10_000, 40_000), 40_000);
    assert_eq!(reserve_local_pts(&counter, 120_000, 40_000), 120_000);
}

#[test]
#[serial]
fn env_helpers_parse_values_and_fallbacks() {
    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", None::<&str>, || {
        assert_eq!(env_usize("LESAVKA_TEST_USIZE", 64), 64);
    });
}

#[test]
#[serial]
fn software_video_fallback_requires_clear_operator_opt_in() {
    for disabled in [
        None,
        Some(""),
        Some("0"),
        Some("false"),
        Some("no"),
        Some("off"),
    ] {
        with_var(SOFTWARE_VIDEO_FALLBACK_ENV, disabled, || {
            assert!(!software_video_fallback_allowed());
        });
    }

    for enabled in ["1", "true", "yes", "on", "lab"] {
        with_var(SOFTWARE_VIDEO_FALLBACK_ENV, Some(enabled), || {
            assert!(software_video_fallback_allowed());
        });
    }
}

#[test]
fn hardware_decoder_classification_keeps_software_fallbacks_out() {
    for name in [
        "v4l2h264dec",
        "v4l2slh264dec",
        "omxh264dec",
        "vulkanh264dec",
    ] {
        assert!(is_hardware_h264_decoder(name));
    }
    assert!(!is_hardware_h264_decoder("avdec_h264"));

    for name in [
        "v4l2slh265dec",
        "v4l2h265dec",
        "vulkanh265dec",
        "nvh265dec",
        "nvh265sldec",
    ] {
        assert!(is_hardware_hevc_decoder(name));
    }
    assert!(!is_hardware_hevc_decoder("libde265dec"));
}

#[test]
#[serial]
fn explicit_bad_decoder_overrides_fail_loudly_without_registry_assumptions() {
    gstreamer::init().expect("initialize gstreamer registry for decoder lookup");

    with_var(
        "LESAVKA_H264_DECODER",
        Some("definitely-not-a-decoder"),
        || {
            let err = require_h264_decoder().expect_err("bogus H.264 decoder should fail");
            assert!(
                err.to_string().contains("not buildable"),
                "unexpected H.264 override error: {err:#}"
            );
            assert_eq!(pick_h264_decoder(), "missing-hardware-h264dec");
        },
    );

    with_var(
        "LESAVKA_HEVC_DECODER",
        Some("definitely-not-a-decoder"),
        || {
            let err = require_hevc_decoder().expect_err("bogus HEVC decoder should fail");
            assert!(
                err.to_string().contains("not buildable"),
                "unexpected HEVC override error: {err:#}"
            );
            assert_eq!(pick_hevc_decoder(), "missing-hardware-hevcdec");
        },
    );
}

#[test]
fn adjust_effective_fps_keeps_current_rate_when_no_samples() {
    assert_eq!(adjust_effective_fps(18, 12, 25, 0, 0), 18);
}
