// Include-based coverage for microphone source-name routing.
//
// Scope: include `client/src/input/microphone.rs` and cover selected-source
// routing heuristics for launcher catalog names.
// Targets: `client/src/input/microphone.rs`.
// Why: launcher-selected Pulse source names must not regress to PipeWire
// negotiation when the catalog already provides a concrete Pulse device name.

#[allow(warnings)]
mod live_capture_clock {
    include!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/tests/helpers/support/live_capture_clock_shim.rs"
    ));
}

#[allow(warnings)]
mod input {
    pub mod audio_codec {
        pub use lesavka_client::input::audio_codec::*;
    }
}

#[allow(warnings)]
mod microphone_source_contract {
    include!(env!("LESAVKA_CLIENT_MICROPHONE_SRC"));

    use serial_test::serial;
    use std::fs;
    use std::os::unix::fs::PermissionsExt;
    use std::path::Path;
    use temp_env::with_var;
    use tempfile::tempdir;

    fn write_executable(dir: &Path, name: &str, body: &str) {
        let path = dir.join(name);
        fs::write(&path, body).expect("write script");
        let mut perms = fs::metadata(&path).expect("metadata").permissions();
        perms.set_mode(0o755);
        fs::set_permissions(path, perms).expect("chmod");
    }

    fn with_fake_command(name: &str, script_body: &str, f: impl FnOnce()) {
        let dir = tempdir().expect("tempdir");
        write_executable(dir.path(), name, script_body);
        let prior = std::env::var("PATH").unwrap_or_default();
        let merged = if prior.is_empty() {
            dir.path().display().to_string()
        } else {
            format!("{}:{prior}", dir.path().display())
        };
        with_var("PATH", Some(merged), f);
    }

    fn with_fake_pactl(script_body: &str, f: impl FnOnce()) {
        with_fake_command("pactl", script_body, f);
    }

    fn with_fake_pw_dump(script_body: &str, f: impl FnOnce()) {
        with_fake_command("pw-dump", script_body, f);
    }

    #[test]
    fn pulse_source_name_heuristic_matches_launcher_catalog_names() {
        assert!(looks_like_pulse_source_name(
            "alsa_input.usb-Neat_Microphones_Bumblebee_II_USB_Microphone-00.mono-fallback"
        ));
        assert!(looks_like_pulse_source_name("bluez_input.headset"));
        assert!(!looks_like_pulse_source_name("PipeWire Nick Mic"));
    }

    #[test]
    #[serial]
    fn resolve_source_desc_prefers_pulse_for_pulse_catalog_names() {
        let pactl_script = r#"#!/usr/bin/env sh
if [ "$1" = "list" ] && [ "$2" = "short" ] && [ "$3" = "sources" ]; then
  echo "1 alsa_input.usb-Bumblebee_II-00.mono-fallback module-alsa-card.c s16le 1ch 48000Hz RUNNING"
  exit 0
fi
exit 0
"#;
        let pw_script = r#"#!/usr/bin/env sh
cat <<'JSON'
[
  {"info":{"props":{"media.class":"Audio/Source","node.name":"alsa_input.usb-Bumblebee_II-00.mono-fallback"}}}
]
JSON
"#;
        with_fake_pactl(pactl_script, || {
            with_fake_pw_dump(pw_script, || {
                let desc = MicrophoneCapture::resolve_source_desc("alsa_input.usb-Bumblebee_II")
                    .expect("pulse source");
                assert!(
                    desc.contains("pulsesrc device=alsa_input.usb-Bumblebee_II-00.mono-fallback"),
                    "Pulse catalog source names should route through pulsesrc: {desc}"
                );
                assert!(desc.contains("buffer-time=40000"));
                assert!(desc.contains("latency-time=10000"));
            });
        });
    }

    #[test]
    #[cfg(coverage)]
    #[serial]
    fn default_source_desc_can_fall_back_to_pulse_when_pipewire_is_disabled() {
        with_var("LESAVKA_MIC_TEST_SOURCE_DESC", None::<&str>, || {
            with_var("LESAVKA_MIC_DISABLE_PIPEWIRE", Some("1"), || {
                assert_eq!(
                    MicrophoneCapture::default_source_desc(),
                    "pulsesrc do-timestamp=true buffer-time=40000 latency-time=10000"
                );
            });
        });
    }
}
