use super::{
    CameraCapture, CameraCodec, CameraConfig, hevc_keyframe_interval, resolved_capture_profile,
    resolved_output_profile,
};
use serial_test::serial;

#[test]
#[serial]
/// Keeps the selected local webcam mode independent from the UVC gadget mode.
fn local_capture_profile_keeps_launcher_quality_env_by_default() {
    let cfg = CameraConfig {
        codec: CameraCodec::Mjpeg,
        width: 640,
        height: 480,
        fps: 20,
    };
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_WIDTH", Some("1280")),
            ("LESAVKA_CAM_HEIGHT", Some("720")),
            ("LESAVKA_CAM_FPS", Some("30")),
            ("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE", None),
            ("LESAVKA_CAM_EMIT_UI_PROFILE", None),
        ],
        || assert_eq!(resolved_capture_profile(Some(cfg)), (1280, 720, 30)),
    );
}

#[test]
#[serial]
/// UVC output must match the gadget profile that browsers negotiate.
fn negotiated_output_profile_matches_server_uvc_contract_by_default() {
    let cfg = CameraConfig {
        codec: CameraCodec::Mjpeg,
        width: 640,
        height: 480,
        fps: 20,
    };
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_WIDTH", Some("1280")),
            ("LESAVKA_CAM_HEIGHT", Some("720")),
            ("LESAVKA_CAM_FPS", Some("30")),
            ("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE", None),
            ("LESAVKA_CAM_EMIT_UI_PROFILE", None),
        ],
        || {
            let capture_profile = resolved_capture_profile(Some(cfg));
            assert_eq!(capture_profile, (1280, 720, 30));
            assert_eq!(
                resolved_output_profile(Some(cfg), capture_profile),
                (640, 480, 20)
            );
        },
    );
}

#[test]
#[serial]
/// Keeps UI-profile emission explicit until the server can reconfigure UVC.
fn explicit_ui_profile_emission_keeps_lab_mode_available() {
    let cfg = CameraConfig {
        codec: CameraCodec::Mjpeg,
        width: 640,
        height: 480,
        fps: 20,
    };
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_WIDTH", Some("1280")),
            ("LESAVKA_CAM_HEIGHT", Some("720")),
            ("LESAVKA_CAM_FPS", Some("30")),
            ("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE", None),
            ("LESAVKA_CAM_EMIT_UI_PROFILE", Some("1")),
        ],
        || {
            let capture_profile = resolved_capture_profile(Some(cfg));
            assert_eq!(capture_profile, (1280, 720, 30));
            assert_eq!(
                resolved_output_profile(Some(cfg), capture_profile),
                (1280, 720, 30)
            );
        },
    );
}

#[test]
#[serial]
/// The safety lock wins if both experimental flags are set.
fn explicit_server_profile_lock_wins_over_ui_emission() {
    let cfg = CameraConfig {
        codec: CameraCodec::Mjpeg,
        width: 640,
        height: 480,
        fps: 20,
    };
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_WIDTH", Some("1280")),
            ("LESAVKA_CAM_HEIGHT", Some("720")),
            ("LESAVKA_CAM_FPS", Some("30")),
            ("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE", Some("1")),
            ("LESAVKA_CAM_EMIT_UI_PROFILE", Some("1")),
        ],
        || {
            let capture_profile = resolved_capture_profile(Some(cfg));
            assert_eq!(capture_profile, (1280, 720, 30));
            assert_eq!(
                resolved_output_profile(Some(cfg), capture_profile),
                (640, 480, 20)
            );
        },
    );
}

#[test]
#[serial]
/// HEVC lab fallback options must stay shaped for live transport.
fn hevc_lab_fallback_options_keep_low_latency_and_keyframes() {
    temp_env::with_var("LESAVKA_CAM_HEVC_KBIT", Some("2400"), || {
        let options = CameraCapture::encoder_options("x265enc", Some("key-int-max"), 30);

        assert!(options.starts_with("x265enc "));
        assert!(options.contains("tune=zerolatency"));
        assert!(options.contains("speed-preset=ultrafast"));
        assert!(options.contains("bitrate=2400"));
        assert!(options.contains("key-int-max=30"));
    });
}

#[test]
#[serial]
/// Vulkan H.264 hardware encode should stay live-call shaped when available.
fn vulkan_h264_encoder_options_keep_cbr_and_keyframes() {
    temp_env::with_var("LESAVKA_CAM_H264_KBIT", Some("6000"), || {
        let options = CameraCapture::encoder_options("vulkanh264enc", Some("idr-period"), 30);

        assert_eq!(
            options,
            "vulkanh264enc bitrate=6000 rate-control=cbr idr-period=30"
        );
    });
}

#[test]
#[serial]
/// HEVC should recover quickly after freshness drops without changing H.264 knobs.
fn hevc_keyframe_interval_defaults_short_and_honors_overrides() {
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_KEYFRAME_INTERVAL", None::<&str>),
            ("LESAVKA_CAM_HEVC_KEYFRAME_INTERVAL", None::<&str>),
        ],
        || {
            assert_eq!(hevc_keyframe_interval(30), 1);
            assert_eq!(hevc_keyframe_interval(2), 1);
        },
    );
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_KEYFRAME_INTERVAL", Some("5")),
            ("LESAVKA_CAM_HEVC_KEYFRAME_INTERVAL", Some("2")),
        ],
        || {
            assert_eq!(hevc_keyframe_interval(30), 2);
        },
    );
}

#[cfg(coverage)]
#[test]
/// Coverage builds use a deterministic HEVC encoder choice.
fn coverage_hevc_encoder_choice_is_stable() {
    assert_eq!(
        CameraCapture::choose_hevc_encoder().unwrap(),
        ("x265enc", Some("key-int-max"))
    );
}

#[cfg(coverage)]
#[test]
#[serial]
/// Coverage builds can exercise H.264 encoder branches without host GPU assumptions.
fn coverage_h264_encoder_choice_honors_stable_test_overrides() {
    let cases = [
        ("nvh264enc", ("nvh264enc", None)),
        ("nvautogpuh264enc", ("nvautogpuh264enc", None)),
        ("vulkanh264enc", ("vulkanh264enc", Some("idr-period"))),
        ("vah264enc", ("vah264enc", Some("key-int-max"))),
        ("vaapih264enc", ("vaapih264enc", Some("keyframe-period"))),
        ("v4l2h264enc", ("v4l2h264enc", Some("idrcount"))),
        ("unknown", ("x264enc", Some("key-int-max"))),
    ];

    for (override_value, expected) in cases {
        temp_env::with_var("LESAVKA_CAM_TEST_ENCODER", Some(override_value), || {
            assert_eq!(CameraCapture::choose_encoder().unwrap(), expected);
        });
    }
}

#[cfg(coverage)]
#[test]
/// Coverage mode keeps software video fallback enabled for deterministic tests.
fn coverage_software_video_fallback_is_enabled() {
    assert!(CameraCapture::software_video_fallback_allowed());
}

#[cfg(coverage)]
#[test]
/// Coverage mode replaces the FFmpeg preview reader with an inert cancellation flag.
fn coverage_ffmpeg_preview_tap_stub_starts_stopped() {
    let running = super::spawn_ffmpeg_raw_preview_tap(
        std::io::empty(),
        std::path::PathBuf::from("/tmp/lesavka-preview-tap.coverage"),
        1,
        1,
    );

    assert!(!running.load(std::sync::atomic::Ordering::Acquire));
}
