// Integration coverage for client app support helpers.
//
// Scope: exercise server-address, camera-capability, and retry-delay helpers
// against deterministic local types.
// Targets: `client/src/app_support.rs`.
// Why: these startup decisions shape live camera codec negotiation and should
// stay covered without adding source-file LOC to the hygiene ratchet.

mod handshake {
    #[allow(dead_code)]
    #[derive(Clone, Debug)]
    pub struct PeerCaps {
        pub camera: bool,
        pub microphone: bool,
        pub bundled_webcam_media: bool,
        pub server_version: Option<String>,
        pub camera_output: Option<String>,
        pub camera_codec: Option<String>,
        pub camera_width: Option<u32>,
        pub camera_height: Option<u32>,
        pub camera_fps: Option<u32>,
        pub eye_width: Option<u32>,
        pub eye_height: Option<u32>,
        pub eye_fps: Option<u32>,
    }
}

mod input {
    pub mod camera {
        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        pub enum CameraCodec {
            Mjpeg,
            H264,
            Hevc,
        }

        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        pub struct CameraConfig {
            pub codec: CameraCodec,
            pub width: u32,
            pub height: u32,
            pub fps: u32,
        }
    }
}

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

use app_support::{
    DEFAULT_SERVER_ADDR, camera_config_from_caps, next_delay, resolve_server_addr,
    sanitize_video_queue,
};
use handshake::PeerCaps;
use input::camera::CameraCodec;
use serial_test::serial;
use std::time::Duration;

fn peer_caps(codec: &str) -> PeerCaps {
    PeerCaps {
        camera: true,
        microphone: false,
        bundled_webcam_media: false,
        server_version: None,
        camera_output: Some(String::from("uvc")),
        camera_codec: Some(codec.to_string()),
        camera_width: Some(1280),
        camera_height: Some(720),
        camera_fps: Some(25),
        eye_width: None,
        eye_height: None,
        eye_fps: None,
    }
}

#[test]
fn resolve_server_addr_prefers_cli_then_env_then_default() {
    assert_eq!(
        resolve_server_addr(&[String::from("http://cli:1")], Some("http://env:2")),
        "http://cli:1"
    );
    assert_eq!(
        resolve_server_addr(
            &[
                String::from("--no-launcher"),
                String::from("--server"),
                String::from("http://cli-flag:3"),
            ],
            Some("http://env:2"),
        ),
        "http://cli-flag:3"
    );
    assert_eq!(
        resolve_server_addr(&[String::from("--launcher")], Some("http://env:2")),
        "http://env:2"
    );
    assert_eq!(
        resolve_server_addr(&[], Some("http://env:2")),
        "http://env:2"
    );
    assert_eq!(resolve_server_addr(&[], None), DEFAULT_SERVER_ADDR);
}

#[test]
#[serial]
fn camera_config_from_caps_requires_complete_profile() {
    temp_env::with_var("LESAVKA_CAM_CODEC", None::<&str>, || {
        let mut caps = peer_caps("mjpeg");
        let config = camera_config_from_caps(&caps).expect("complete caps should map");
        assert!(matches!(config.codec, CameraCodec::Mjpeg));
        assert_eq!(config.width, 1280);

        caps.camera_codec = Some(String::from("h265"));
        let config = camera_config_from_caps(&caps).expect("h265 alias should map");
        assert!(matches!(config.codec, CameraCodec::Hevc));

        caps.camera_codec = Some(String::from("vp9"));
        assert!(camera_config_from_caps(&caps).is_none());
    });
}

#[test]
#[serial]
fn camera_config_from_caps_uses_negotiated_codec_over_launcher_default() {
    temp_env::with_var("LESAVKA_CAM_CODEC", Some("mjpeg"), || {
        let config = camera_config_from_caps(&peer_caps("hevc")).expect("caps codec should map");
        assert!(matches!(config.codec, CameraCodec::Hevc));
    });
}

#[test]
#[serial]
fn camera_config_from_caps_allows_explicit_forced_codec_override() {
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_CODEC", Some("mjpeg")),
            ("LESAVKA_CAM_CODEC_FORCE", Some("1")),
        ],
        || {
            let config = camera_config_from_caps(&peer_caps("hevc")).expect("forced override");
            assert!(matches!(config.codec, CameraCodec::Mjpeg));
        },
    );
}

#[test]
#[serial]
fn camera_config_force_flag_ignores_unknown_override() {
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_CODEC", Some("vp9")),
            ("LESAVKA_CAM_CODEC_FORCE", Some("1")),
        ],
        || {
            let config = camera_config_from_caps(&peer_caps("hevc")).expect("fallback codec");
            assert!(matches!(config.codec, CameraCodec::Hevc));
        },
    );
}

#[test]
fn sanitize_video_queue_enforces_floor() {
    assert_eq!(sanitize_video_queue(None), 8);
    assert_eq!(sanitize_video_queue(Some(1)), 4);
    assert_eq!(sanitize_video_queue(Some(32)), 32);
}

#[test]
fn next_delay_doubles_until_capped() {
    assert_eq!(next_delay(Duration::from_secs(1)), Duration::from_secs(2));
    assert_eq!(next_delay(Duration::from_secs(15)), Duration::from_secs(30));
    assert_eq!(next_delay(Duration::from_secs(31)), Duration::from_secs(30));
}
