#[cfg(coverage)]
use super::hdmi_mirror_config;
use super::selection::{parse_hdmi_mode, parse_hdmi_modes, preferred_hdmi_mode};
use super::{
    CameraCodec, CameraConfig, CameraOutput, HdmiConnector, HdmiMode, current_camera_config,
    update_camera_config,
};
use serial_test::serial;
use std::fs;
use temp_env::with_var;

#[test]
#[serial]
fn camera_config_env_override_prefers_uvc_values() {
    with_var("LESAVKA_CAM_OUTPUT", Some("uvc"), || {
        with_var("LESAVKA_UVC_WIDTH", Some("800"), || {
            with_var("LESAVKA_UVC_HEIGHT", Some("600"), || {
                with_var("LESAVKA_UVC_FPS", Some("24"), || {
                    let cfg = update_camera_config();
                    assert_eq!(cfg.output, CameraOutput::Uvc);
                    assert_eq!(cfg.codec, CameraCodec::Hevc);
                    assert_eq!(cfg.width, 800);
                    assert_eq!(cfg.height, 600);
                    assert_eq!(cfg.fps, 24);

                    let cached = current_camera_config();
                    assert_eq!(cached.output, CameraOutput::Uvc);
                    assert_eq!(cached.codec, CameraCodec::Hevc);
                    assert_eq!(cached.width, 800);
                    assert_eq!(cached.height, 600);
                    assert_eq!(cached.fps, 24);
                });
            });
        });
    });
}

#[test]
#[cfg(not(coverage))]
#[serial]
fn uvc_camera_profile_honors_live_attached_descriptor() {
    let temp = tempfile::tempdir().expect("temp configfs dir");
    let frame_dir = temp.path().join("streaming/mjpeg/m/720p");
    fs::create_dir_all(&frame_dir).expect("mock UVC frame descriptor");
    fs::write(frame_dir.join("wWidth"), "640\n").expect("width");
    fs::write(frame_dir.join("wHeight"), "480\n").expect("height");
    fs::write(frame_dir.join("dwDefaultFrameInterval"), "500000\n").expect("interval");

    let configfs_base = temp.path().to_string_lossy().into_owned();
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_OUTPUT", Some("uvc")),
            ("LESAVKA_UVC_WIDTH", Some("1280")),
            ("LESAVKA_UVC_HEIGHT", Some("720")),
            ("LESAVKA_UVC_FPS", Some("30")),
            ("LESAVKA_UVC_INTERVAL", None),
            ("LESAVKA_UVC_CONFIGFS_BASE", Some(configfs_base.as_str())),
        ],
        || {
            let cfg = update_camera_config();
            assert_eq!(cfg.output, CameraOutput::Uvc);
            assert_eq!(cfg.codec, CameraCodec::Hevc);
            assert_eq!((cfg.width, cfg.height, cfg.fps), (640, 480, 20));
        },
    );
}

#[test]
#[serial]
fn camera_config_env_override_ignores_uvc_descriptor_codec_for_uplink() {
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_OUTPUT", Some("uvc")),
            ("LESAVKA_UVC_CODEC", Some("h264")),
            ("LESAVKA_CAM_CODEC", None),
            ("LESAVKA_UPLINK_CAMERA_CODEC", None),
        ],
        || {
            let cfg = update_camera_config();
            assert_eq!(cfg.output, CameraOutput::Uvc);
            assert_eq!(cfg.codec, CameraCodec::Hevc);
        },
    );
}

#[test]
#[serial]
fn camera_config_env_override_honors_explicit_uplink_codec() {
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_OUTPUT", Some("uvc")),
            ("LESAVKA_CAM_CODEC", Some("mjpeg")),
            ("LESAVKA_UPLINK_CAMERA_CODEC", Some("h265")),
        ],
        || {
            let cfg = update_camera_config();
            assert_eq!(cfg.output, CameraOutput::Uvc);
            assert_eq!(cfg.codec, CameraCodec::Hevc);
        },
    );
}

#[test]
#[serial]
fn uvc_camera_profile_migrates_legacy_h264_uplink_to_mjpeg() {
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_OUTPUT", Some("uvc")),
            ("LESAVKA_CAM_CODEC", Some("h264")),
            ("LESAVKA_UPLINK_CAMERA_CODEC", None),
        ],
        || {
            let cfg = update_camera_config();
            assert_eq!(cfg.output, CameraOutput::Uvc);
            assert_eq!(cfg.codec, CameraCodec::Mjpeg);
        },
    );
}

#[test]
#[serial]
fn hdmi_camera_profile_honors_installed_1080p_override() {
    with_var("LESAVKA_CAM_OUTPUT", Some("hdmi"), || {
        with_var("LESAVKA_CAM_CODEC", Some("h264"), || {
            with_var("LESAVKA_CAM_WIDTH", Some("1920"), || {
                with_var("LESAVKA_CAM_HEIGHT", Some("1080"), || {
                    with_var("LESAVKA_CAM_FPS", Some("30"), || {
                        let cfg = update_camera_config();
                        assert_eq!(cfg.output, CameraOutput::Hdmi);
                        assert_eq!(cfg.codec, CameraCodec::H264);
                        assert_eq!(cfg.width, 1920);
                        assert_eq!(cfg.height, 1080);
                        assert_eq!(cfg.fps, 30);
                    });
                });
            });
        });
    });
}

#[test]
#[serial]
fn hdmi_camera_profile_prefers_mjpeg_without_hardware_decoder() {
    with_var("LESAVKA_CAM_OUTPUT", Some("hdmi"), || {
        with_var("LESAVKA_HW_H264", None::<&str>, || {
            with_var("LESAVKA_CAM_CODEC", None::<&str>, || {
                let cfg = update_camera_config();
                assert_eq!(cfg.output, CameraOutput::Hdmi);
                assert_eq!(cfg.codec, CameraCodec::Mjpeg);
                assert_eq!(cfg.width, 1280);
                assert_eq!(cfg.height, 720);
                assert_eq!(cfg.fps, 30);
            });
        });
    });
}

#[test]
#[serial]
fn hdmi_camera_profile_honors_explicit_codec_override() {
    with_var("LESAVKA_CAM_OUTPUT", Some("hdmi"), || {
        with_var("LESAVKA_HW_H264", None::<&str>, || {
            with_var("LESAVKA_CAM_CODEC", Some("h264"), || {
                let cfg = update_camera_config();
                assert_eq!(cfg.output, CameraOutput::Hdmi);
                assert_eq!(cfg.codec, CameraCodec::H264);
            });
        });
    });
}

#[test]
fn hdmi_mode_parsing_accepts_sysfs_and_override_shapes() {
    assert_eq!(
        parse_hdmi_mode("1920x1080"),
        Some(HdmiMode {
            width: 1920,
            height: 1080,
        })
    );
    assert_eq!(
        parse_hdmi_mode("1280x720p60"),
        Some(HdmiMode {
            width: 1280,
            height: 720,
        })
    );
    assert_eq!(parse_hdmi_mode("not-a-mode"), None);

    let modes = parse_hdmi_modes("1920x1080\n1024x768,800x600\n");
    assert_eq!(modes.len(), 3);
    assert_eq!(modes[0].width, 1920);
    assert_eq!(modes[2].height, 600);
}

#[test]
fn preferred_hdmi_mode_chooses_standard_capture_adapter_mode() {
    let modes = parse_hdmi_modes("1024x768\n1920x1080\n800x600\n");
    assert_eq!(
        preferred_hdmi_mode(&modes),
        Some(HdmiMode {
            width: 1920,
            height: 1080,
        })
    );

    let modes = parse_hdmi_modes("1600x900\n1024x768\n");
    assert_eq!(
        preferred_hdmi_mode(&modes),
        Some(HdmiMode {
            width: 1600,
            height: 900,
        })
    );

    let modes = parse_hdmi_modes("1024x768\n800x600\n");
    assert_eq!(
        preferred_hdmi_mode(&modes),
        Some(HdmiMode {
            width: 1024,
            height: 768,
        })
    );
}

#[test]
#[serial]
fn hdmi_display_size_uses_adapter_mode_without_changing_uplink_profile() {
    let cfg = CameraConfig {
        output: CameraOutput::Hdmi,
        codec: CameraCodec::H264,
        width: 1280,
        height: 720,
        fps: 30,
        hdmi: Some(HdmiConnector {
            name: String::from("card1-HDMI-A-2"),
            id: Some(43),
            modes: parse_hdmi_modes("1920x1080\n1024x768\n800x600\n"),
        }),
    };

    with_var("LESAVKA_HDMI_WIDTH", None::<&str>, || {
        with_var("LESAVKA_HDMI_HEIGHT", None::<&str>, || {
            assert_eq!((cfg.width, cfg.height), (1280, 720));
            assert_eq!(cfg.hdmi_display_size(), (1920, 1080));
        });
    });
}

#[test]
#[serial]
fn hdmi_display_size_honors_explicit_local_override() {
    let cfg = CameraConfig {
        output: CameraOutput::Hdmi,
        codec: CameraCodec::H264,
        width: 1280,
        height: 720,
        fps: 30,
        hdmi: Some(HdmiConnector {
            name: String::from("card1-HDMI-A-2"),
            id: Some(43),
            modes: parse_hdmi_modes("1920x1080\n"),
        }),
    };

    with_var("LESAVKA_HDMI_WIDTH", Some("1024"), || {
        with_var("LESAVKA_HDMI_HEIGHT", Some("768"), || {
            assert_eq!(cfg.hdmi_display_size(), (1024, 768));
        });
    });
}

#[test]
fn non_hdmi_display_size_uses_camera_profile() {
    let cfg = CameraConfig {
        output: CameraOutput::Uvc,
        codec: CameraCodec::Mjpeg,
        width: 800,
        height: 600,
        fps: 25,
        hdmi: None,
    };

    assert_eq!(cfg.hdmi_display_size(), (800, 600));
}

#[test]
#[cfg(coverage)]
#[serial]
fn hdmi_mirror_config_keeps_primary_uplink_profile_and_detects_display() {
    let primary = CameraConfig {
        output: CameraOutput::Uvc,
        codec: CameraCodec::Mjpeg,
        width: 1280,
        height: 720,
        fps: 20,
        hdmi: None,
    };

    temp_env::with_vars(
        [
            ("LESAVKA_HDMI_CONNECTOR", Some("card1-HDMI-A-2")),
            ("LESAVKA_HDMI_MODES", Some("1920x1080,1280x720")),
            ("LESAVKA_HDMI_WIDTH", None),
            ("LESAVKA_HDMI_HEIGHT", None),
        ],
        || {
            let mirror = hdmi_mirror_config(&primary);
            assert_eq!(mirror.output, CameraOutput::Hdmi);
            assert_eq!(mirror.codec, primary.codec);
            assert_eq!((mirror.width, mirror.height, mirror.fps), (1280, 720, 20));
            assert_eq!(mirror.hdmi_display_size(), (1920, 1080));
        },
    );
}

#[test]
#[cfg(coverage)]
#[serial]
fn coverage_hdmi_connector_env_sets_adapter_display_mode() {
    temp_env::with_vars(
        [
            ("LESAVKA_CAM_OUTPUT", Some("hdmi")),
            ("LESAVKA_CAM_WIDTH", Some("1280")),
            ("LESAVKA_CAM_HEIGHT", Some("720")),
            ("LESAVKA_CAM_FPS", Some("30")),
            ("LESAVKA_HDMI_CONNECTOR", Some("card1-HDMI-A-2")),
            ("LESAVKA_HDMI_MODES", Some("1920x1080,1280x720")),
            ("LESAVKA_HDMI_WIDTH", None),
            ("LESAVKA_HDMI_HEIGHT", None),
        ],
        || {
            let cfg = update_camera_config();
            assert_eq!(cfg.output, CameraOutput::Hdmi);
            assert_eq!((cfg.width, cfg.height, cfg.fps), (1280, 720, 30));
            let hdmi = cfg.hdmi.as_ref().expect("HDMI connector from env");
            assert_eq!(hdmi.name, "card1-HDMI-A-2");
            assert_eq!(hdmi.id, None);
            assert_eq!(cfg.hdmi_display_size(), (1920, 1080));
        },
    );
}
