use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use std::thread;
use std::time::Instant;

use calloop::LoopHandle;
use tracing::{error, info};

use crate::forge::Forge;

static BOOT_INSTANT: std::sync::OnceLock<Instant> = std::sync::OnceLock::new();
/// Shared last-activity timestamp in milliseconds since boot.
static LAST_ACTIVITY_MS: AtomicU64 = AtomicU64::new(0);
static NEXT_WATCH_ID: AtomicU32 = AtomicU32::new(1);

/// Update the last-activity timestamp. Called from input handling.
pub fn touch_activity() {
    let boot = BOOT_INSTANT.get_or_init(Instant::now);
    let ms = boot.elapsed().as_millis() as u64;
    LAST_ACTIVITY_MS.store(ms, Ordering::Relaxed);
}

/// Get current idle time in milliseconds. A held inhibition (video
/// playback) reports the session as active, so GetIdletime pollers
/// don't blank or lock either.
fn get_idle_time_ms() -> u64 {
    if crate::screensaver_dbus::inhibited() {
        return 0;
    }
    let boot = BOOT_INSTANT.get_or_init(Instant::now);
    let now_ms = boot.elapsed().as_millis() as u64;
    let last = LAST_ACTIVITY_MS.load(Ordering::Relaxed);
    now_ms.saturating_sub(last)
}

/// Start the IdleMonitor D-Bus server.
pub fn start_server(_event_loop: &LoopHandle<'static, Forge>) {
    // Initialize boot instant
    BOOT_INSTANT.get_or_init(Instant::now);
    touch_activity();

    thread::Builder::new()
        .name("forge-idle-monitor".into())
        .spawn(|| {
            if let Err(e) = run_idle_monitor_dbus() {
                error!("IdleMonitor D-Bus server failed: {e}");
            }
        })
        .expect("idle monitor thread");
}

fn run_idle_monitor_dbus() -> Result<(), Box<dyn std::error::Error>> {
    info!("IdleMonitor D-Bus server starting");

    let iface = IdleMonitorIface;

    let _conn = zbus::blocking::ConnectionBuilder::session()?
        .name("org.gnome.Mutter.IdleMonitor")?
        .serve_at("/org/gnome/Mutter/IdleMonitor/Core", iface)?
        .build()?;

    loop {
        std::thread::sleep(std::time::Duration::from_secs(60));
    }
}

struct IdleMonitorIface;

#[zbus::interface(name = "org.gnome.Mutter.IdleMonitor")]
impl IdleMonitorIface {
    fn get_idletime(&self) -> u64 {
        get_idle_time_ms()
    }

    fn add_idle_watch(&self, _interval_msec: u64) -> u32 {
        // Return a watch ID. The actual watch firing is not yet implemented
        // (would need a calloop timer integration). This satisfies API compatibility.
        NEXT_WATCH_ID.fetch_add(1, Ordering::Relaxed)
    }

    fn add_user_active_watch(&self) -> u32 {
        NEXT_WATCH_ID.fetch_add(1, Ordering::Relaxed)
    }

    fn remove_watch(&self, _id: u32) {
        // No-op for now
    }
}
