package domain import ( "sync" pb "github.com/rwadurian/mpc-system/api/grpc/router/v1" ) // SessionEventBroadcaster manages session event subscriptions and broadcasting type SessionEventBroadcaster struct { subscribers map[string]chan *pb.SessionEvent // partyID -> event channel mu sync.RWMutex } // NewSessionEventBroadcaster creates a new session event broadcaster func NewSessionEventBroadcaster() *SessionEventBroadcaster { return &SessionEventBroadcaster{ subscribers: make(map[string]chan *pb.SessionEvent), } } // Subscribe subscribes a party to session events func (b *SessionEventBroadcaster) Subscribe(partyID string) <-chan *pb.SessionEvent { b.mu.Lock() defer b.mu.Unlock() // Create buffered channel for this subscriber ch := make(chan *pb.SessionEvent, 100) b.subscribers[partyID] = ch return ch } // Unsubscribe removes a party's subscription func (b *SessionEventBroadcaster) Unsubscribe(partyID string) { b.mu.Lock() defer b.mu.Unlock() if ch, exists := b.subscribers[partyID]; exists { close(ch) delete(b.subscribers, partyID) } } // Broadcast sends an event to all subscribers func (b *SessionEventBroadcaster) Broadcast(event *pb.SessionEvent) { b.mu.RLock() defer b.mu.RUnlock() for _, ch := range b.subscribers { // Non-blocking send to prevent slow subscribers from blocking select { case ch <- event: default: // Channel full, skip this subscriber } } } // BroadcastToParties sends an event to specific parties only func (b *SessionEventBroadcaster) BroadcastToParties(event *pb.SessionEvent, partyIDs []string) { b.mu.RLock() defer b.mu.RUnlock() for _, partyID := range partyIDs { if ch, exists := b.subscribers[partyID]; exists { // Non-blocking send select { case ch <- event: default: // Channel full, skip this subscriber } } } } // SubscriberCount returns the number of active subscribers func (b *SessionEventBroadcaster) SubscriberCount() int { b.mu.RLock() defer b.mu.RUnlock() return len(b.subscribers) }