package repository import ( "context" "sync" "apps/backend/internal/module/inspire/domain" ) type MemoryStore struct { mu sync.Mutex trends map[int64][]*domain.TrendItem elements map[string]*domain.Element sessions map[string]*domain.Session // by session id active map[int64]string // owner -> session id } func NewMemory() *MemoryStore { return &MemoryStore{ trends: map[int64][]*domain.TrendItem{}, elements: map[string]*domain.Element{}, sessions: map[string]*domain.Session{}, active: map[int64]string{}, } } func (s *MemoryStore) ListTrends(_ context.Context, ownerUID int64) ([]*domain.TrendItem, error) { s.mu.Lock() defer s.mu.Unlock() list := s.trends[ownerUID] out := make([]*domain.TrendItem, 0, len(list)) for _, t := range list { cp := *t if t.Keywords != nil { cp.Keywords = append([]string(nil), t.Keywords...) } if t.Samples != nil { cp.Samples = append([]string(nil), t.Samples...) } out = append(out, &cp) } return out, nil } func (s *MemoryStore) SaveTrends(_ context.Context, ownerUID int64, items []*domain.TrendItem) error { s.mu.Lock() defer s.mu.Unlock() cp := make([]*domain.TrendItem, 0, len(items)) for _, t := range items { x := *t if t.Keywords != nil { x.Keywords = append([]string(nil), t.Keywords...) } if t.Samples != nil { x.Samples = append([]string(nil), t.Samples...) } cp = append(cp, &x) } s.trends[ownerUID] = cp return nil } func (s *MemoryStore) ListElements(_ context.Context, ownerUID int64) ([]*domain.Element, error) { s.mu.Lock() defer s.mu.Unlock() var out []*domain.Element for _, e := range s.elements { if e.OwnerUID == ownerUID { cp := *e out = append(out, &cp) } } return out, nil } func (s *MemoryStore) SaveElement(_ context.Context, e *domain.Element) error { s.mu.Lock() defer s.mu.Unlock() cp := *e s.elements[e.ID] = &cp return nil } func (s *MemoryStore) DeleteElement(_ context.Context, id string) error { s.mu.Lock() defer s.mu.Unlock() if _, ok := s.elements[id]; !ok { return domain.ErrNotFound } delete(s.elements, id) return nil } func (s *MemoryStore) GetElement(_ context.Context, id string) (*domain.Element, error) { s.mu.Lock() defer s.mu.Unlock() e, ok := s.elements[id] if !ok { return nil, domain.ErrNotFound } cp := *e return &cp, nil } func (s *MemoryStore) ListSessions(_ context.Context, ownerUID int64) ([]*domain.Session, error) { s.mu.Lock() defer s.mu.Unlock() var out []*domain.Session for _, sess := range s.sessions { if sess.OwnerUID == ownerUID { out = append(out, copySession(sess)) } } // sort by updated_at desc for i := 0; i < len(out); i++ { for j := i + 1; j < len(out); j++ { if out[j].UpdatedAt > out[i].UpdatedAt { out[i], out[j] = out[j], out[i] } } } if len(out) > 50 { out = out[:50] } return out, nil } func (s *MemoryStore) GetSession(_ context.Context, ownerUID int64) (*domain.Session, error) { s.mu.Lock() defer s.mu.Unlock() if id, ok := s.active[ownerUID]; ok { if sess, ok2 := s.sessions[id]; ok2 && sess.OwnerUID == ownerUID { return copySession(sess), nil } } var best *domain.Session for _, sess := range s.sessions { if sess.OwnerUID != ownerUID { continue } if best == nil || sess.UpdatedAt > best.UpdatedAt { best = sess } } if best == nil { return nil, domain.ErrNotFound } s.active[ownerUID] = best.ID return copySession(best), nil } func (s *MemoryStore) GetSessionByID(_ context.Context, ownerUID int64, id string) (*domain.Session, error) { s.mu.Lock() defer s.mu.Unlock() sess, ok := s.sessions[id] if !ok || sess.OwnerUID != ownerUID { return nil, domain.ErrNotFound } return copySession(sess), nil } func (s *MemoryStore) SaveSession(_ context.Context, sess *domain.Session) error { s.mu.Lock() defer s.mu.Unlock() s.sessions[sess.ID] = copySession(sess) return nil } func (s *MemoryStore) DeleteSessionByID(_ context.Context, ownerUID int64, id string) error { s.mu.Lock() defer s.mu.Unlock() sess, ok := s.sessions[id] if !ok || sess.OwnerUID != ownerUID { return domain.ErrNotFound } delete(s.sessions, id) if s.active[ownerUID] == id { delete(s.active, ownerUID) } return nil } func (s *MemoryStore) GetActiveSessionID(_ context.Context, ownerUID int64) (string, error) { s.mu.Lock() defer s.mu.Unlock() id, ok := s.active[ownerUID] if !ok || id == "" { return "", domain.ErrNotFound } return id, nil } func (s *MemoryStore) SetActiveSessionID(_ context.Context, ownerUID int64, sessionID string) error { s.mu.Lock() defer s.mu.Unlock() s.active[ownerUID] = sessionID return nil } func copySession(sess *domain.Session) *domain.Session { cp := *sess if sess.Messages != nil { cp.Messages = append([]domain.ChatMessage(nil), sess.Messages...) for i := range cp.Messages { if sess.Messages[i].Draft != nil { d := *sess.Messages[i].Draft cp.Messages[i].Draft = &d } } } if sess.PinnedElementIDs != nil { cp.PinnedElementIDs = append([]string(nil), sess.PinnedElementIDs...) } return &cp } var _ domain.Repository = (*MemoryStore)(nil)