// Module-path coverage for client-side H.264 decoder selection.
//
// Scope: include the client decoder selection helper directly.
// Targets: `client/src/video_support.rs`.
// Why: operator decoder overrides should fall back cleanly on machines with
// different GStreamer plugin sets.

#[path = "../../../../client/src/video_support.rs"]
mod video_support;

use serial_test::serial;
use temp_env::with_var;

#[test]
#[serial]
fn decoder_override_accepts_decodebin_only_with_explicit_lab_fallback() {
    with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", Some("1"), || {
        with_var("LESAVKA_H264_DECODER", Some("decodebin"), || {
            assert_eq!(video_support::pick_h264_decoder(), "decodebin");
        });
    });
    with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", None::<&str>, || {
        with_var("LESAVKA_H264_DECODER", Some("decodebin"), || {
            assert_eq!(
                video_support::pick_h264_decoder(),
                "missing-hardware-h264dec"
            );
        });
    });
}

#[test]
#[serial]
fn decoder_override_accepts_nonhardware_element_only_with_explicit_lab_fallback() {
    with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", Some("1"), || {
        with_var("LESAVKA_H264_DECODER", Some("fakesink"), || {
            assert_eq!(video_support::pick_h264_decoder(), "fakesink");
        });
    });
    with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", None::<&str>, || {
        with_var("LESAVKA_H264_DECODER", Some("fakesink"), || {
            assert_eq!(
                video_support::pick_h264_decoder(),
                "missing-hardware-h264dec"
            );
        });
    });
}

#[test]
#[serial]
fn explicit_decodebin_lab_override_is_available_for_driver_debugging() {
    with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", Some("1"), || {
        with_var("LESAVKA_H264_DECODER", Some("decodebin"), || {
            assert_eq!(video_support::pick_h264_decoder(), "decodebin");
        });
    });
}

#[test]
#[serial]
fn decoder_override_ignores_blank_or_unknown_values() {
    with_var("LESAVKA_H264_DECODER", Some("   "), || {
        assert!(!video_support::pick_h264_decoder().trim().is_empty());
    });
    with_var("LESAVKA_H264_DECODER", Some("not-a-real-decoder"), || {
        assert!(!video_support::pick_h264_decoder().trim().is_empty());
    });
}

#[test]
#[serial]
fn decoder_auto_order_supports_proprietary_and_open_source_routes() {
    with_var("LESAVKA_H264_DECODER_PREFERENCE", None::<&str>, || {
        with_var("LESAVKA_ALLOW_VULKAN_H264_DECODER", None::<&str>, || {
            with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", None::<&str>, || {
                let order = video_support::h264_decoder_preference_order();
                assert_eq!(order.first(), Some(&"nvh264dec"));
                assert!(order.contains(&"nvh264sldec"));
                assert!(order.contains(&"vah264dec"));
                assert!(order.contains(&"vaapih264dec"));
                assert!(order.contains(&"v4l2h264dec"));
                assert!(order.contains(&"v4l2slh264dec"));
                assert!(
                    !order.contains(&"avdec_h264") && !order.contains(&"openh264dec"),
                    "software decoders should require explicit lab fallback"
                );
                assert!(
                    !order.contains(&"vulkanh264dec"),
                    "Vulkan decode should be opt-in because it is choppy on the NVIDIA desktop path"
                );
            });
        });
    });
}

#[test]
#[serial]
fn vulkan_decoder_is_opt_in_for_driver_diagnostics() {
    with_var("LESAVKA_ALLOW_VULKAN_H264_DECODER", Some("1"), || {
        with_var("LESAVKA_H264_DECODER_PREFERENCE", None::<&str>, || {
            let order = video_support::h264_decoder_preference_order();
            assert_eq!(order.first(), Some(&"nvh264dec"));
            assert!(order.contains(&"vulkanh264dec"));
            assert!(
                !order.contains(&"avdec_h264") && !order.contains(&"openh264dec"),
                "Vulkan opt-in should still preserve the no-software production rule"
            );
        });
    });
}

#[test]
#[serial]
fn production_auto_order_keeps_cpu_decoders_out() {
    with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", None::<&str>, || {
        with_var("LESAVKA_H264_DECODER_PREFERENCE", None::<&str>, || {
            let order = video_support::h264_decoder_preference_order();
            assert!(
                !order.contains(&"avdec_h264") && !order.contains(&"openh264dec"),
                "production auto-selection should not select CPU decoders"
            );
        });
    });
}

#[test]
fn vulkan_decoder_fragment_downloads_gpu_memory_before_cpu_sinks() {
    let fragment = video_support::h264_decoder_launch_fragment("vulkanh264dec");
    assert!(fragment.starts_with("vulkanh264dec "));
    assert!(fragment.contains("discard-corrupted-frames=true"));
    assert!(fragment.contains("automatic-request-sync-points=true"));
    assert!(fragment.contains("vulkandownload"));
    assert!(
        !fragment.contains(" name="),
        "preview launch fragments should not set decoder names because some binary decoder wrappers reject them"
    );

    let named = video_support::h264_decoder_launch_fragment_named("vulkanh264dec", "decoder1");
    assert!(named.starts_with("vulkanh264dec "));
    assert!(named.contains("vulkandownload"));

    assert_eq!(
        video_support::h264_decoder_launch_fragment_named("nvh264dec", "left_decoder"),
        "nvh264dec"
    );
}

#[test]
#[serial]
fn video_fallback_flags_require_clear_opt_in_values() {
    for disabled in [
        None,
        Some(""),
        Some("0"),
        Some("false"),
        Some("no"),
        Some("off"),
    ] {
        with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", disabled, || {
            assert!(!video_support::software_video_fallback_allowed());
        });
        with_var("LESAVKA_ALLOW_VULKAN_H264_DECODER", disabled, || {
            assert!(!video_support::h264_decoder_preference_order().contains(&"vulkanh264dec"));
        });
    }

    for enabled in ["1", "true", "yes", "on", "lab"] {
        with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", Some(enabled), || {
            assert!(video_support::software_video_fallback_allowed());
        });
        with_var("LESAVKA_ALLOW_VULKAN_H264_DECODER", Some(enabled), || {
            assert!(video_support::h264_decoder_preference_order().contains(&"vulkanh264dec"));
        });
    }
}

#[test]
fn hardware_decoder_classifier_names_each_supported_route() {
    for name in [
        "nvh264dec",
        "nvh264sldec",
        "vulkanh264dec",
        "vah264dec",
        "vaapih264dec",
        "v4l2h264dec",
        "v4l2slh264dec",
    ] {
        assert!(video_support::is_hardware_h264_decoder(name));
    }
    assert!(!video_support::is_hardware_h264_decoder("decodebin"));
    assert!(!video_support::is_hardware_h264_decoder("avdec_h264"));
}

#[test]
fn contains_idr_handles_short_and_multi_nal_annex_b_streams() {
    assert!(video_support::contains_idr(&[0, 0, 0, 1, 0x65, 0x88]));
    assert!(video_support::contains_idr(&[
        0, 0, 0, 1, 0x41, 0x99, 0, 0, 1, 0x65, 0x88
    ]));
    assert!(!video_support::contains_idr(&[
        0, 0, 0, 1, 0x41, 0x99, 0, 0, 1, 0x61, 0x88
    ]));
    assert!(!video_support::contains_idr(&[0, 0, 2, 0x65]));
}

#[test]
#[serial]
fn decoder_auto_order_can_prefer_software_for_driver_comparisons() {
    with_var("LESAVKA_ALLOW_SOFTWARE_VIDEO", Some("1"), || {
        with_var("LESAVKA_H264_DECODER_PREFERENCE", Some("software"), || {
            let order = video_support::h264_decoder_preference_order();
            assert_eq!(order.first(), Some(&"avdec_h264"));
            assert!(
                order
                    .iter()
                    .position(|name| *name == "openh264dec")
                    .unwrap()
                    < order.iter().position(|name| *name == "nvh264dec").unwrap(),
                "software preference should require explicit lab fallback"
            );
        });
    });
}

#[test]
#[serial]
#[cfg(coverage)]
fn decoder_selection_falls_back_when_no_factory_can_build() {
    with_var("TEST_DISABLE_H264_DECODER_FACTORY", Some("1"), || {
        with_var("LESAVKA_H264_DECODER", Some("fakesink"), || {
            assert_eq!(
                video_support::pick_h264_decoder(),
                "missing-hardware-h264dec"
            );
        });
    });
    with_var("TEST_FAIL_GST_INIT", Some("1"), || {
        with_var("LESAVKA_H264_DECODER", None::<&str>, || {
            assert_eq!(
                video_support::pick_h264_decoder(),
                "missing-hardware-h264dec"
            );
        });
    });
}
