package sqlite import ( "context" "database/sql" "time" "chat-switchboard/models" "chat-switchboard/store" ) type PersonaGroupStore struct{} func NewPersonaGroupStore() *PersonaGroupStore { return &PersonaGroupStore{} } const personaGroupCols = `id, name, description, owner_id, scope, team_id, created_at, updated_at` func (s *PersonaGroupStore) List(ctx context.Context, ownerID string) ([]models.PersonaGroup, error) { rows, err := DB.QueryContext(ctx, ` SELECT `+personaGroupCols+` FROM persona_groups WHERE owner_id = ? ORDER BY name `, ownerID) if err != nil { return nil, err } defer rows.Close() groups := []models.PersonaGroup{} for rows.Next() { var g models.PersonaGroup if err := rows.Scan(&g.ID, &g.Name, &g.Description, &g.OwnerID, &g.Scope, &g.TeamID, st(&g.CreatedAt), st(&g.UpdatedAt)); err != nil { continue } groups = append(groups, g) } return groups, rows.Err() } func (s *PersonaGroupStore) Get(ctx context.Context, id, ownerID string) (*models.PersonaGroup, error) { var g models.PersonaGroup err := DB.QueryRowContext(ctx, ` SELECT `+personaGroupCols+` FROM persona_groups WHERE id = ? AND owner_id = ? `, id, ownerID).Scan(&g.ID, &g.Name, &g.Description, &g.OwnerID, &g.Scope, &g.TeamID, st(&g.CreatedAt), st(&g.UpdatedAt)) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } return &g, nil } func (s *PersonaGroupStore) Create(ctx context.Context, g *models.PersonaGroup) error { g.ID = store.NewID() now := time.Now().UTC() g.CreatedAt = now g.UpdatedAt = now _, err := DB.ExecContext(ctx, ` INSERT INTO persona_groups (id, name, description, owner_id, scope, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?) `, g.ID, g.Name, g.Description, g.OwnerID, g.Scope, now.Format(timeFmt), now.Format(timeFmt)) return err } func (s *PersonaGroupStore) Update(ctx context.Context, id string, fields map[string]interface{}) error { for k, v := range fields { _, err := DB.ExecContext(ctx, `UPDATE persona_groups SET `+k+` = ?, updated_at = datetime('now') WHERE id = ?`, v, id) if err != nil { return err } } return nil } func (s *PersonaGroupStore) Delete(ctx context.Context, id, ownerID string) (int64, error) { result, err := DB.ExecContext(ctx, `DELETE FROM persona_groups WHERE id = ? AND owner_id = ?`, id, ownerID) if err != nil { return 0, err } return result.RowsAffected() } func (s *PersonaGroupStore) GetOwnerID(ctx context.Context, id string) (string, error) { var ownerID string err := DB.QueryRowContext(ctx, `SELECT owner_id FROM persona_groups WHERE id = ?`, id).Scan(&ownerID) if err == sql.ErrNoRows { return "", nil } return ownerID, err } func (s *PersonaGroupStore) AddMember(ctx context.Context, groupID, personaID string, isLeader bool) error { if isLeader { _, _ = DB.ExecContext(ctx, `UPDATE persona_group_members SET is_leader = 0 WHERE group_id = ?`, groupID) } id := store.NewID() _, err := DB.ExecContext(ctx, ` INSERT INTO persona_group_members (id, group_id, persona_id, is_leader) VALUES (?, ?, ?, ?) ON CONFLICT (group_id, persona_id) DO UPDATE SET is_leader = excluded.is_leader `, id, groupID, personaID, isLeader) return err } func (s *PersonaGroupStore) RemoveMember(ctx context.Context, memberID, groupID string) error { _, err := DB.ExecContext(ctx, `DELETE FROM persona_group_members WHERE id = ? AND group_id = ?`, memberID, groupID) return err } func (s *PersonaGroupStore) ListMembers(ctx context.Context, groupID string) ([]models.PersonaGroupMember, error) { rows, err := DB.QueryContext(ctx, ` SELECT pgm.id, pgm.group_id, pgm.persona_id, pgm.is_leader, pgm.sort_order, COALESCE(p.name, '') AS persona_name, COALESCE(p.handle, '') AS persona_handle, COALESCE(p.avatar, '') AS persona_avatar FROM persona_group_members pgm LEFT JOIN personas p ON p.id = pgm.persona_id WHERE pgm.group_id = ? ORDER BY pgm.is_leader DESC, pgm.sort_order, pgm.id `, groupID) if err != nil { return []models.PersonaGroupMember{}, nil } defer rows.Close() members := []models.PersonaGroupMember{} for rows.Next() { var m models.PersonaGroupMember if err := rows.Scan(&m.ID, &m.GroupID, &m.PersonaID, &m.IsLeader, &m.SortOrder, &m.PersonaName, &m.PersonaHandle, &m.PersonaAvatar); err != nil { continue } members = append(members, m) } return members, rows.Err() }