use std::path::{Path, PathBuf};
use std::sync::Arc;
use anyhow::Result;
use parking_lot::Mutex;
use super::store::OutputStore;
static ACTIVE: std::sync::OnceLock<Arc<Mutex<Option<(PathBuf, OutputStore)>>>> =
std::sync::OnceLock::new();
fn slot() -> Arc<Mutex<Option<(PathBuf, OutputStore)>>> {
ACTIVE.get_or_init(|| Arc::new(Mutex::new(None))).clone()
}
pub fn install(project_root: &Path) -> Result<OutputStore> {
let s = slot();
let mut g = s.lock();
if let Some((root, os)) = g.as_ref() {
if root == project_root {
return Ok(os.clone());
}
}
let os = OutputStore::open_for_project(project_root)?;
*g = Some((project_root.to_path_buf(), os.clone()));
Ok(os)
}
pub fn active() -> Option<OutputStore> {
slot().lock().as_ref().map(|(_, os)| os.clone())
}
pub fn uninstall() {
*slot().lock() = None;
}
pub fn emit(message: &super::Message) -> Option<uuid::Uuid> {
active().and_then(|s| s.emit(message).ok())
}
pub fn emit_task_complete(
task: &str,
summary: &str,
elapsed_secs: u64,
target: Option<uuid::Uuid>,
) -> Option<uuid::Uuid> {
use super::types::{kinds, ActionId, Lifetime, Message, Severity};
let mut meta = serde_json::json!({
"text": summary,
"task": task,
"elapsed_seconds": elapsed_secs,
"summary": summary,
});
if let (Some(obj), Some(id)) = (meta.as_object_mut(), target) {
obj.insert("target_paragraph".into(), serde_json::Value::String(id.to_string()));
}
let mut msg = Message::new(kinds::AI_TASK_COMPLETE, Severity::Info, Lifetime::Hours(12.0), meta)
.with_actions(vec![ActionId::Primary, ActionId::Dismiss, ActionId::Pin]);
if let Some(id) = target {
msg = msg.with_source_paragraph(id);
}
emit(&msg)
}