use std::thread;

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

use crate::forge::Forge;

/// org.gnome.ScreenSaver D-Bus server.
/// Many GNOME apps use this interface to lock the screen.

#[derive(Debug)]
pub enum ScreenSaverEvent {
    Lock,
}

pub fn start_server(event_loop: &LoopHandle<'static, Forge>) {
    let (sender, channel) = channel::channel::<ScreenSaverEvent>();

    event_loop
        .insert_source(channel, |event, _, forge| {
            if let channel::Event::Msg(ev) = event {
                match ev {
                    ScreenSaverEvent::Lock => {
                        forge.lock_session();
                        forge.request_redraw();
                    }
                }
            }
        })
        .expect("screensaver channel");

    thread::Builder::new()
        .name("forge-screensaver".into())
        .spawn(move || {
            if let Err(e) = run_screensaver_dbus(sender) {
                error!("ScreenSaver D-Bus server failed: {e}");
            }
        })
        .expect("screensaver thread");
}

fn run_screensaver_dbus(sender: Sender<ScreenSaverEvent>) -> Result<(), Box<dyn std::error::Error>> {
    info!("ScreenSaver D-Bus server starting");

    let _conn = zbus::blocking::ConnectionBuilder::session()?
        .name("org.gnome.ScreenSaver")?
        .serve_at("/org/gnome/ScreenSaver", ScreenSaverIface { sender })?
        .build()?;

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

struct ScreenSaverIface {
    sender: Sender<ScreenSaverEvent>,
}

#[zbus::interface(name = "org.gnome.ScreenSaver")]
impl ScreenSaverIface {
    /// Lock the screen.
    fn lock(&self) {
        let _ = self.sender.send(ScreenSaverEvent::Lock);
    }

    /// Returns true if the screen is currently locked.
    fn get_active(&self) -> bool {
        // We can't easily query the main thread synchronously,
        // so return false. Apps mostly use Lock(), not GetActive().
        false
    }

    /// Simulate activity to reset idle timers.
    fn simulate_user_activity(&self) {
        // No-op; idle is handled by swayidle externally
    }

    /// Set the screensaver active state.
    fn set_active(&self, active: bool) {
        if active {
            let _ = self.sender.send(ScreenSaverEvent::Lock);
        }
    }

    #[zbus(signal)]
    async fn active_changed(ctxt: &zbus::SignalContext<'_>, active: bool) -> zbus::Result<()>;
}
