//! Shared PipeWire/PulseAudio command helpers used by the per-device call-audio
//! backends. Thin wrappers over `pactl` and `pw-link` plus env-var config
//! reading. Everything runs on the calling (worker) thread; keep it off the UI.

use std::process::Command;
use std::thread;
use std::time::Duration;

/// Switch a card to a UCM profile (earpiece/speaker/voice-call/hifi).
pub fn set_card_profile( card: &str, profile: &str )
{
	pactl( &[ "set-card-profile", card, profile ] );
}

pub fn set_source_mute( source: &str, muted: bool )
{
	pactl( &[ "set-source-mute", source, if muted { "1" } else { "0" } ] );
}

/// Create a PipeWire link, retrying because a card-profile switch tears down
/// and recreates the codec nodes — the link's source/target can take a second
/// or two to reappear. We retry quietly until then (≈6 s budget) so we don't
/// race the node creation; only the final give-up is logged.
pub fn link( output: &str, input: &str )
{
	for _ in 0..50
	{
		if run_quiet( "pw-link", &[ output, input ] )
		{
			return;
		}
		thread::sleep( Duration::from_millis( 120 ) );
	}
	eprintln!( "eydos-calls: pw-link {output} -> {input} failed after retries" );
}

/// Drop a PipeWire link. Best-effort and silent: the link (or node) is
/// routinely already gone (e.g. cleanup before the first link exists).
pub fn unlink( output: &str, input: &str )
{
	run_quiet( "pw-link", &[ "-d", output, input ] );
}

/// Single best-effort link attempt (no retry). An already-linked pair just
/// fails quietly — fine for the self-healing bring-up loop.
pub fn link_once( output: &str, input: &str )
{
	run_quiet( "pw-link", &[ output, input ] );
}

/// True if a node (sink or source) is currently in the RUNNING state, i.e. its
/// stream is actually flowing.
pub fn running( node: &str ) -> bool
{
	for kind in [ "sinks", "sources" ]
	{
		if let Ok( o ) = Command::new( "pactl" ).args( [ "list", "short", kind ] ).output()
		{
			let out = String::from_utf8_lossy( &o.stdout );
			if out.lines().any( |l| l.contains( node ) && l.contains( "RUNNING" ) )
			{
				return true;
			}
		}
	}
	false
}

pub fn pactl( args: &[ &str ] ) -> bool
{
	run( "pactl", args )
}

pub fn run( bin: &str, args: &[ &str ] ) -> bool
{
	match Command::new( bin ).args( args ).output()
	{
		Ok( o ) if o.status.success() => true,
		Ok( o ) =>
		{
			eprintln!(
				"eydos-calls: {bin} {args:?} failed: {}",
				String::from_utf8_lossy( &o.stderr ).trim(),
			);
			false
		}
		Err( e ) =>
		{
			eprintln!( "eydos-calls: {bin} {args:?} spawn error: {e}" );
			false
		}
	}
}

/// Like [`run`] but returns success without logging — for calls that are
/// expected to fail routinely (link retries, best-effort unlinks).
fn run_quiet( bin: &str, args: &[ &str ] ) -> bool
{
	Command::new( bin )
		.args( args )
		.output()
		.map( |o| o.status.success() )
		.unwrap_or( false )
}

pub fn env( key: &str, default: &str ) -> String
{
	std::env::var( key ).unwrap_or_else( |_| default.to_string() )
}

pub fn env_bool( key: &str, default: bool ) -> bool
{
	std::env::var( key )
		.ok()
		.map( |v| matches!( v.as_str(), "1" | "true" | "yes" | "on" ) )
		.unwrap_or( default )
}
