package database import ( "context" "errors" "strings" "time" "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) // PermissionGroupRepository implements permission group data access. type PermissionGroupRepository struct { collection *mongo.Collection } // NewPermissionGroupRepository creates a new group repository. func NewPermissionGroupRepository(db *mongo.Database) *PermissionGroupRepository { return &PermissionGroupRepository{collection: db.Collection("permission_groups")} } // CreateGroup creates a new permission group. func (r *PermissionGroupRepository) CreateGroup(ctx context.Context, group *entities.PermissionGroup) error { group.ID = bson.NewObjectID() group.Name = strings.TrimSpace(group.Name) group.NameKey = strings.ToLower(group.Name) group.CreatedAt = time.Now() group.UpdatedAt = time.Now() _, err := r.collection.InsertOne(ctx, group) return err } // GetGroupByID retrieves a group by ID. func (r *PermissionGroupRepository) GetGroupByID(ctx context.Context, id bson.ObjectID) (*entities.PermissionGroup, error) { var group entities.PermissionGroup err := r.collection.FindOne(ctx, bson.M{"_id": id}).Decode(&group) if err != nil { if err == mongo.ErrNoDocuments { return nil, errors.New("group not found") } return nil, err } return &group, nil } // GetGroupByName retrieves a group by case-insensitive name. func (r *PermissionGroupRepository) GetGroupByName(ctx context.Context, name string) (*entities.PermissionGroup, error) { normalized := strings.ToLower(strings.TrimSpace(name)) var group entities.PermissionGroup err := r.collection.FindOne(ctx, bson.M{"name_key": normalized}).Decode(&group) if err != nil { if err == mongo.ErrNoDocuments { return nil, errors.New("group not found") } return nil, err } return &group, nil } // GetGroupsByIDs retrieves groups by IDs. func (r *PermissionGroupRepository) GetGroupsByIDs(ctx context.Context, ids []bson.ObjectID) ([]*entities.PermissionGroup, error) { if len(ids) == 0 { return []*entities.PermissionGroup{}, nil } cursor, err := r.collection.Find(ctx, bson.M{"_id": bson.M{"$in": ids}}) if err != nil { return nil, err } defer cursor.Close(ctx) var groups []*entities.PermissionGroup if err := cursor.All(ctx, &groups); err != nil { return nil, err } return groups, nil } // ListGroups retrieves all groups sorted by name. func (r *PermissionGroupRepository) ListGroups(ctx context.Context) ([]*entities.PermissionGroup, error) { opts := options.Find().SetSort(bson.D{{Key: "name", Value: 1}}) cursor, err := r.collection.Find(ctx, bson.M{}, opts) if err != nil { return nil, err } defer cursor.Close(ctx) var groups []*entities.PermissionGroup if err := cursor.All(ctx, &groups); err != nil { return nil, err } return groups, nil } // UpdateGroup updates an existing group. func (r *PermissionGroupRepository) UpdateGroup(ctx context.Context, group *entities.PermissionGroup) error { group.UpdatedAt = time.Now() _, err := r.collection.UpdateOne(ctx, bson.M{"_id": group.ID}, bson.M{ "$set": bson.M{ "name": strings.TrimSpace(group.Name), "name_key": strings.ToLower(strings.TrimSpace(group.Name)), "description": group.Description, "permissions": group.Permissions, "is_system": group.IsSystem, "updated_at": group.UpdatedAt, }, }) return err } // DeleteGroup deletes a group. func (r *PermissionGroupRepository) DeleteGroup(ctx context.Context, id bson.ObjectID) error { _, err := r.collection.DeleteOne(ctx, bson.M{"_id": id}) return err } // EnsureIndexes creates indexes for the permission groups collection. func (r *PermissionGroupRepository) EnsureIndexes(ctx context.Context) error { _, err := r.collection.Indexes().CreateMany(ctx, []mongo.IndexModel{ { Keys: bson.D{{Key: "name_key", Value: 1}}, Options: options.Index().SetUnique(true), }, }) return err }