use std::thread;

use calloop::channel::{self, Sender};
use calloop::LoopHandle;
use tracing::{error, info};

use crate::forge::Forge;

/// Event sent when a USB storage device is detected.
#[derive(Debug, Clone)]
pub struct MountEvent {
    pub device: String,
    pub label: String,
}

/// Start monitoring UDisks2 for new USB storage devices.
pub fn start_monitor(event_loop: &LoopHandle<'static, Forge>) {
    let (sender, channel) = channel::channel::<MountEvent>();

    event_loop
        .insert_source(channel, |event, _, forge| {
            if let channel::Event::Msg(mount_evt) = event {
                tracing::info!("USB device detected: {} ({})", mount_evt.label, mount_evt.device);
                forge.request_redraw();
            }
        })
        .expect("mount monitor channel");

    thread::Builder::new()
        .name("forge-mount-monitor".into())
        .spawn(move || {
            if let Err(e) = run_udisks2_monitor(sender) {
                error!("UDisks2 mount monitor failed: {e}");
            }
        })
        .expect("mount monitor thread");
}

fn run_udisks2_monitor(sender: Sender<MountEvent>) -> Result<(), Box<dyn std::error::Error>> {
    info!("UDisks2 mount monitor starting");

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

    // Subscribe to InterfacesAdded signals from UDisks2 ObjectManager
    let proxy = zbus::blocking::fdo::ObjectManagerProxy::builder(&conn)
        .destination("org.freedesktop.UDisks2")?
        .path("/org/freedesktop/UDisks2")?
        .build()?;

    // Poll for InterfacesAdded signals
    let iter = proxy.receive_interfaces_added()?;

    for signal in iter {
        let args = match signal.args() {
            Ok(a) => a,
            Err(_) => continue,
        };

        let path = args.object_path().to_string();

        // Only interested in block devices
        if !path.contains("/block_devices/") {
            continue;
        }

        // Check if it has a filesystem interface (mountable)
        let interfaces = args.interfaces_and_properties();
        if !interfaces.contains_key("org.freedesktop.UDisks2.Filesystem") {
            continue;
        }

        // Try to get the device label
        let label = interfaces
            .get("org.freedesktop.UDisks2.Block")
            .and_then(|props| props.get("IdLabel"))
            .and_then(|v| match v {
                zbus::zvariant::Value::Str(s) => Some(s.to_string()),
                _ => None,
            })
            .unwrap_or_else(|| "Unknown".to_string());

        let device = path.rsplit('/').next().unwrap_or("unknown").to_string();

        let _ = sender.send(MountEvent { device, label });
    }

    Ok(())
}
