package live import ( "context" "sort" "sync" "time" bizlive "kra/internal/biz/live" ) type streamState struct { stream *bizlive.Stream users map[uint]struct{} } type streamRepo struct { mu sync.RWMutex streams map[string]*streamState } func NewStreamRepo() bizlive.StreamRepo { return &streamRepo{streams: make(map[string]*streamState)} } func (r *streamRepo) Get(_ context.Context, streamID string) (*bizlive.Stream, error) { r.mu.RLock() defer r.mu.RUnlock() state := r.streams[streamID] if state == nil { return &bizlive.Stream{StreamID: streamID, UpdatedAt: time.Now()}, nil } return clone(state.stream), nil } func (r *streamRepo) List(_ context.Context) ([]*bizlive.Stream, error) { r.mu.RLock() defer r.mu.RUnlock() items := make([]*bizlive.Stream, 0, len(r.streams)) for _, state := range r.streams { if state.stream.PublisherOnline { items = append(items, clone(state.stream)) } } sort.Slice(items, func(i, j int) bool { return items[i].StreamID < items[j].StreamID }) return items, nil } func (r *streamRepo) RegisterPublisher(_ context.Context, streamID string, userID uint) (*bizlive.Stream, error) { r.mu.Lock() defer r.mu.Unlock() state := r.streams[streamID] if state == nil { if len(r.streams) >= bizlive.MaxStreams { return nil, bizlive.ErrMaxStreams } state = &streamState{stream: &bizlive.Stream{StreamID: streamID}, users: make(map[uint]struct{})} r.streams[streamID] = state } if state.stream.PublisherOnline { return nil, bizlive.ErrPublisherExists } state.stream.PublisherID = userID state.stream.PublisherOnline = true state.stream.UpdatedAt = time.Now() return clone(state.stream), nil } func (r *streamRepo) UnregisterPublisher(_ context.Context, streamID string, userID uint) error { r.mu.Lock() defer r.mu.Unlock() state := r.streams[streamID] if state == nil || !state.stream.PublisherOnline || state.stream.PublisherID != userID { return bizlive.ErrPublisherNotFound } state.stream.PublisherID = 0 state.stream.PublisherOnline = false state.stream.UpdatedAt = time.Now() if len(state.users) == 0 { delete(r.streams, streamID) } return nil } func (r *streamRepo) RegisterSubscriber(_ context.Context, streamID string, userID uint) (*bizlive.Stream, error) { r.mu.Lock() defer r.mu.Unlock() state := r.streams[streamID] if state == nil { if len(r.streams) >= bizlive.MaxStreams { return nil, bizlive.ErrMaxStreams } state = &streamState{stream: &bizlive.Stream{StreamID: streamID}, users: make(map[uint]struct{})} r.streams[streamID] = state } if _, exists := state.users[userID]; exists { return nil, bizlive.ErrSubscriberExists } state.users[userID] = struct{}{} state.stream.SubscriberCount = len(state.users) state.stream.UpdatedAt = time.Now() return clone(state.stream), nil } func (r *streamRepo) UnregisterSubscriber(_ context.Context, streamID string, userID uint) error { r.mu.Lock() defer r.mu.Unlock() state := r.streams[streamID] if state == nil { return bizlive.ErrSubscriberNotFound } if _, exists := state.users[userID]; !exists { return bizlive.ErrSubscriberNotFound } delete(state.users, userID) state.stream.SubscriberCount = len(state.users) state.stream.UpdatedAt = time.Now() if !state.stream.PublisherOnline && len(state.users) == 0 { delete(r.streams, streamID) } return nil } func clone(stream *bizlive.Stream) *bizlive.Stream { if stream == nil { return nil } value := *stream return &value }