//! MPRIS side effects: polling `playerctl` for the current track and issuing
//! Player controls over the session bus.

use std::thread;
use std::time::Duration;

use calloop::channel::Sender;
use tracing::info;

use super::MediaInfo;

pub(super) fn run_mpris_listener(sender: Sender<MediaInfo>) -> Result<(), Box<dyn std::error::Error>> {
    info!("MPRIS listener started");

    loop {
        let info = poll_playerctl();
        let _ = sender.send(info);
        thread::sleep(Duration::from_secs(3));
    }
}

fn poll_playerctl() -> MediaInfo {
    let output = std::process::Command::new("playerctl")
        .args([
            "metadata",
            "--format",
            "{{status}}\n{{title}}\n{{artist}}\n{{album}}\n{{mpris:artUrl}}",
        ])
        .output();

    match output {
        Ok(out) if out.status.success() => {
            let stdout = String::from_utf8_lossy(&out.stdout);
            let lines: Vec<&str> = stdout.lines().collect();
            if lines.len() >= 2 {
                let art_url = lines.get(4).and_then(|s| {
                    let s = s.trim();
                    if s.is_empty() || s == "{{mpris:artUrl}}" {
                        None
                    } else {
                        Some(s.to_string())
                    }
                });
                MediaInfo {
                    playing: lines.first().map_or(false, |s| *s == "Playing"),
                    title: lines.get(1).unwrap_or(&"").to_string(),
                    artist: lines.get(2).unwrap_or(&"").to_string(),
                    available: true,
                    art_url,
                }
            } else {
                MediaInfo::default()
            }
        }
        _ => MediaInfo::default(),
    }
}

/// Call a Player method on the first MPRIS player found on the session bus.
pub(super) fn mpris_player_call(method: &'static str) {
    let _ = thread::Builder::new()
        .name("forge-mpris-ctl".into())
        .spawn(move || {
            let Ok(conn) = zbus::blocking::Connection::session() else {
                return;
            };
            let Ok(reply) = conn.call_method(
                Some("org.freedesktop.DBus"),
                "/org/freedesktop/DBus",
                Some("org.freedesktop.DBus"),
                "ListNames",
                &(),
            ) else {
                return;
            };
            let Ok(names) = reply.body().deserialize::<Vec<String>>() else {
                return;
            };
            let Some(dest) = names.into_iter().find(|n| n.starts_with("org.mpris.MediaPlayer2.")) else {
                return;
            };
            let _ = conn.call_method(
                Some(dest.as_str()),
                "/org/mpris/MediaPlayer2",
                Some("org.mpris.MediaPlayer2.Player"),
                method,
                &(),
            );
        });
}
