use std::sync::{Arc, RwLock, Weak};

/// Wake contract implemented by the SessionManager.  The notifier holds only
/// a weak reference so the coordinator can be dropped independently and no
/// release cycle is formed.
#[async_trait::async_trait]
pub trait AgentInboxWakeTarget: Send + Sync {
    async fn wake_agent_inbox(&self, session_id: &str, revision: i64);
}

/// Late-bound wake bus between the AgentCoordinator and Session workers.
/// Event commits never wait for a wake; the durable inbox is the source of
/// truth and a lost wake only delays the next claim until the periodic
/// re-check.
pub struct AgentInboxNotifier {
    target: RwLock<Option<Weak<dyn AgentInboxWakeTarget + Send + Sync>>>,
}

impl Default for AgentInboxNotifier {
    fn default() -> Self {
        Self {
            target: RwLock::new(None),
        }
    }
}

impl AgentInboxNotifier {
    pub fn new() -> Arc<Self> {
        Arc::new(Self::default())
    }

    pub fn bind(&self, target: Weak<dyn AgentInboxWakeTarget + Send + Sync>) {
        *self
            .target
            .write()
            .unwrap_or_else(|poisoned| poisoned.into_inner()) = Some(target);
    }

    /// Best-effort wake.  A missing or dead target is not an error; the
    /// periodic scanner will claim the event later.
    pub async fn notify(&self, session_id: &str, revision: i64) {
        let target = self
            .target
            .read()
            .unwrap_or_else(|poisoned| poisoned.into_inner())
            .as_ref()
            .and_then(|weak| weak.upgrade());
        if let Some(target) = target {
            target.wake_agent_inbox(session_id, revision).await;
        }
    }
}
