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

use crate::forge::Forge;

/// Events from hostname1 D-Bus.
#[derive(Debug)]
pub enum HostnameEvent {
    /// Hostname resolved.
    HostnameResolved(String),
}

/// Start the hostname1 D-Bus listener.
pub fn start_listener(event_loop: &LoopHandle<'static, Forge>) {
    crate::services::dbus_worker::spawn_adapter(
        event_loop,
        "forge-hostname",
        run_hostname_listener,
        |ev, forge| match ev {
            HostnameEvent::HostnameResolved(name) => {
                forge.shell.sys_state.hostname = name;
            }
        },
    );
}

fn run_hostname_listener(
    sender: Sender<HostnameEvent>,
) -> Result<(), Box<dyn std::error::Error>> {
    info!("Hostname D-Bus listener starting");

    let conn = zbus::blocking::Connection::system()?;

    let hostname = get_hostname(&conn);
    let _ = sender.send(HostnameEvent::HostnameResolved(hostname));

    // Hostname rarely changes, poll infrequently
    loop {
        std::thread::sleep(std::time::Duration::from_secs(300));
        let hostname = get_hostname(&conn);
        let _ = sender.send(HostnameEvent::HostnameResolved(hostname));
    }
}

fn get_hostname(conn: &zbus::blocking::Connection) -> String {
    let reply = conn.call_method(
        Some("org.freedesktop.hostname1"),
        "/org/freedesktop/hostname1",
        Some("org.freedesktop.DBus.Properties"),
        "Get",
        &("org.freedesktop.hostname1", "Hostname"),
    );

    match reply {
        Ok(msg) => msg
            .body()
            .deserialize::<zbus::zvariant::Value>()
            .ok()
            .and_then(|v| match v {
                zbus::zvariant::Value::Str(s) => Some(s.to_string()),
                _ => None,
            })
            .unwrap_or_else(|| gethostname_fallback()),
        Err(_) => gethostname_fallback(),
    }
}

fn gethostname_fallback() -> String {
    std::fs::read_to_string("/etc/hostname")
        .unwrap_or_else(|_| "localhost".to_string())
        .trim()
        .to_string()
}
