//! SSE endpoint streaming case events to the web UI.
//!
//! One long-lived connection per browser tab. Non-admins only see events
//! for their own user_slug; admins see events across all users
//! (useful for support/monitoring). A 15s keep-alive comment keeps idle
//! connections alive through reverse proxies / NAT.

use std::convert::Infallible;
use std::time::Duration;

use axum::extract::State;
use axum::response::sse::{Event, KeepAlive, Sse};
use futures_util::stream::Stream;
use tokio_stream::StreamExt;
use tokio_stream::wrappers::BroadcastStream;
use tokio_stream::wrappers::errors::BroadcastStreamRecvError;

use crate::auth::AuthenticatedWebUser;
use crate::events::EventSender;

/// `GET /web/events` — `text/event-stream`, one connection per tab.
pub async fn handle_events(
    user: AuthenticatedWebUser,
    State(events_tx): State<EventSender>,
) -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
    let rx = events_tx.subscribe();
    let is_admin = user.is_admin();
    let slug = user.slug.clone();

    let stream = BroadcastStream::new(rx).filter_map(move |res| match res {
        Ok(evt) => {
            // Non-admins only see events for their own user_slug.
            if !is_admin && evt.user_slug != slug {
                return None;
            }
            // Serialization of `CaseEvent` is infallible in practice (plain
            // struct with String + enum fields); on the unexpected error
            // path we drop the event rather than tear down the stream.
            let json = serde_json::to_string(&evt).ok()?;
            Some(Ok(Event::default().event("case").data(json)))
        }
        // Lagged subscribers: skip silently. The next real event or the
        // user's next navigation will resync state from the filesystem.
        Err(BroadcastStreamRecvError::Lagged(n)) => {
            tracing::debug!(skipped = n, "sse subscriber lagged");
            None
        }
    });

    Sse::new(stream).keep_alive(
        KeepAlive::new()
            .interval(Duration::from_secs(15))
            .text("keepalive"),
    )
}
