const STALE_TIMEOUT_MS = 60_000;
export function createSSEPresenceAdapter() {
    const connections = new Map();
    const subscribers = new Map();
    function broadcast(documentId) {
        const users = Array.from(connections.get(documentId)?.values() ?? []);
        const subs = subscribers.get(documentId);
        if (subs) {
            for (const cb of subs) {
                try {
                    cb(users);
                }
                catch { }
            }
        }
    }
    function expireStale(documentId) {
        const docConns = connections.get(documentId);
        if (!docConns)
            return;
        const now = Date.now();
        let changed = false;
        for (const [uid, user] of docConns) {
            if (now - user.lastSeen.getTime() > STALE_TIMEOUT_MS) {
                docConns.delete(uid);
                changed = true;
            }
        }
        if (changed)
            broadcast(documentId);
    }
    const provider = {
        async join(documentId, user) {
            if (!connections.has(documentId))
                connections.set(documentId, new Map());
            const now = new Date();
            connections.get(documentId).set(user.userId, { ...user, connectedAt: now, lastSeen: now });
            broadcast(documentId);
        },
        async leave(documentId, userId) {
            connections.get(documentId)?.delete(userId);
            broadcast(documentId);
        },
        async getPresent(documentId) {
            expireStale(documentId);
            return Array.from(connections.get(documentId)?.values() ?? []);
        },
        async heartbeat(documentId, userId) {
            const user = connections.get(documentId)?.get(userId);
            if (user) {
                user.lastSeen = new Date();
            }
        },
        onUpdate(documentId, callback) {
            if (!subscribers.has(documentId))
                subscribers.set(documentId, new Set());
            subscribers.get(documentId).add(callback);
            return () => {
                subscribers.get(documentId)?.delete(callback);
            };
        },
    };
    function handleSSE(request, documentId, user) {
        let unsubscribe = null;
        let interval = null;
        const stream = new ReadableStream({
            start(controller) {
                provider.join(documentId, { userId: user.userId, name: user.name, documentId });
                const send = (users) => {
                    try {
                        const data = JSON.stringify(users.map((u) => ({ userId: u.userId, name: u.name, connectedAt: u.connectedAt })));
                        controller.enqueue(new TextEncoder().encode(`event: presence\ndata: ${data}\n\n`));
                    }
                    catch { }
                };
                provider.getPresent(documentId).then(send);
                unsubscribe = provider.onUpdate(documentId, send);
                interval = setInterval(() => expireStale(documentId), 30_000);
            },
            cancel() {
                provider.leave(documentId, user.userId);
                if (unsubscribe)
                    unsubscribe();
                if (interval)
                    clearInterval(interval);
            },
        });
        return new Response(stream, {
            headers: {
                'Content-Type': 'text/event-stream',
                'Cache-Control': 'no-cache',
                Connection: 'keep-alive',
                'X-Accel-Buffering': 'no',
            },
        });
    }
    return { provider, handleSSE };
}
//# sourceMappingURL=index.js.map