//! Linux system-audio capture via PipeWire / PulseAudio monitor sources.
//!
//! Unlike macOS (Core Audio process taps) and Windows (WASAPI loopback),
//! Linux audio servers expose system audio as a regular *input* device:
//! every output sink has a corresponding "monitor" source that captures
//! everything routed through the sink. Examples:
//!
//! ```text
//!   alsa_output.pci-0000_00_1f.3.analog-stereo            (the speakers)
//!   alsa_output.pci-0000_00_1f.3.analog-stereo.monitor    (capture of the mix)
//! ```
//!
//! On modern systems PipeWire impersonates PulseAudio, so the same
//! `.monitor` naming convention works. ALSA without a sound server
//! has no equivalent — users on bare ALSA need `snd_aloop` or a
//! similar loopback module.
//!
//! ## How this is meant to be used
//!
//! There is intentionally no `LinuxLoopbackListener` struct here. The
//! capture pipeline is identical to mic capture — same `cpal` input
//! stream, same VAD, same STT — the only difference is *which* input
//! device you select. So:
//!
//! ```rust,ignore
//! let mut config = car_voice::VoiceConfig::default();
//! config.input_device = car_voice::system_audio_listener_linux::default_monitor_source();
//! let mut listener = car_voice::CpalListener::new();
//! let rx = listener.start(config).await?;
//! ```
//!
//! [`default_monitor_source`] iterates cpal's input device list and
//! returns the first device whose name ends with `.monitor`. For finer
//! control (multiple monitor sources, or selecting a specific app's
//! sink), [`list_monitor_sources`] returns all matching device names
//! and the caller picks.

#![cfg(target_os = "linux")]

use cpal::traits::{DeviceTrait, HostTrait};

/// Names of all input devices that look like sink monitor sources.
///
/// Returns an empty Vec on hosts without monitor sources (e.g. raw
/// ALSA without PulseAudio/PipeWire compatibility).
pub fn list_monitor_sources() -> Vec<String> {
    let host = cpal::default_host();
    let Ok(devices) = host.input_devices() else {
        return Vec::new();
    };
    devices
        .filter_map(|d| d.name().ok())
        .filter(|name| name.ends_with(".monitor"))
        .collect()
}

/// First monitor source found, suitable for use as the `input_device`
/// field of [`crate::VoiceConfig`]. Returns `None` if no monitor
/// source is available — the caller should surface a clear error
/// rather than silently falling back to the user's microphone.
pub fn default_monitor_source() -> Option<String> {
    list_monitor_sources().into_iter().next()
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Smoke: the helpers don't panic on a CI host without audio.
    #[test]
    fn helpers_return_safely_without_audio_server() {
        let _ = list_monitor_sources();
        let _ = default_monitor_source();
    }
}
