//! Events I/O wrappers — the ONLY place in `service/events/` that may perform
//! side effects such as reading the system clock or spawning async tasks.

use crate::io::watcher::WatcherEvent;
use tokio::sync::broadcast;

use super::buffer::{EventBuffer, StoredEvent};

/// Returns the current Unix timestamp in milliseconds.
pub(super) fn now_ms() -> u64 {
    std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_millis() as u64)
        .unwrap_or(0)
}

/// Spawn a background task that consumes [`WatcherEvent`] broadcasts and
/// stores relevant events in `buffer`.
///
/// Only [`WatcherEvent::WorkItem`] (with a known `from_stage`),
/// [`WatcherEvent::MergeFailure`], and [`WatcherEvent::StoryBlocked`]
/// variants are stored.  All other variants are silently ignored.
pub fn subscribe_to_watcher(buffer: EventBuffer, mut rx: broadcast::Receiver<WatcherEvent>) {
    tokio::spawn(async move {
        loop {
            match rx.recv().await {
                Ok(WatcherEvent::WorkItem {
                    stage,
                    item_id,
                    from_stage,
                    ..
                }) => {
                    if let Some(from) = from_stage {
                        buffer.push(StoredEvent::StageTransition {
                            story_id: item_id,
                            from_stage: from,
                            to_stage: stage,
                            timestamp_ms: now_ms(),
                        });
                    }
                }
                Ok(WatcherEvent::MergeFailure { story_id, reason }) => {
                    buffer.push(StoredEvent::MergeFailure {
                        story_id,
                        reason,
                        timestamp_ms: now_ms(),
                    });
                }
                Ok(WatcherEvent::StoryBlocked { story_id, reason }) => {
                    buffer.push(StoredEvent::StoryBlocked {
                        story_id,
                        reason,
                        timestamp_ms: now_ms(),
                    });
                }
                Ok(_) => {}
                Err(broadcast::error::RecvError::Lagged(n)) => {
                    crate::slog!("[events] Subscriber lagged, skipped {n} events");
                }
                Err(broadcast::error::RecvError::Closed) => {
                    crate::slog!("[events] Watcher channel closed; stopping event subscriber");
                    break;
                }
            }
        }
    });
}
