package database import ( "context" "errors" "time" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" "github.com/noteapp/backend/internal/domain/entities" ) // SpaceRepository implements the space repository interface type SpaceRepository struct { collection *mongo.Collection } // NewSpaceRepository creates a new space repository func NewSpaceRepository(db *mongo.Database) *SpaceRepository { return &SpaceRepository{ collection: db.Collection("spaces"), } } // CreateSpace creates a new space func (r *SpaceRepository) CreateSpace(ctx context.Context, space *entities.Space) error { space.ID = bson.NewObjectID() space.CreatedAt = time.Now() space.UpdatedAt = time.Now() _, err := r.collection.InsertOne(ctx, space) return err } // GetSpaceByID retrieves a space by ID func (r *SpaceRepository) GetSpaceByID(ctx context.Context, id bson.ObjectID) (*entities.Space, error) { var space entities.Space err := r.collection.FindOne(ctx, bson.M{"_id": id}).Decode(&space) if err != nil { if err == mongo.ErrNoDocuments { return nil, errors.New("space not found") } return nil, err } return &space, nil } // GetSpacesByUserID retrieves all spaces for a user (via memberships) func (r *SpaceRepository) GetSpacesByUserID(ctx context.Context, userID bson.ObjectID) ([]*entities.Space, error) { var spaces []*entities.Space // Query spaces where user is a member opts := options.Find().SetSort(bson.M{"created_at": -1}) cursor, err := r.collection.Find(ctx, bson.M{}, opts) if err != nil { return nil, err } defer cursor.Close(ctx) // This would typically be joined with membership collection // For now, returning all spaces - in production, filter by membership if err = cursor.All(ctx, &spaces); err != nil { return nil, err } return spaces, nil } // UpdateSpace updates a space func (r *SpaceRepository) UpdateSpace(ctx context.Context, space *entities.Space) error { space.UpdatedAt = time.Now() _, err := r.collection.ReplaceOne(ctx, bson.M{"_id": space.ID}, space) return err } // DeleteSpace deletes a space func (r *SpaceRepository) DeleteSpace(ctx context.Context, id bson.ObjectID) error { _, err := r.collection.DeleteOne(ctx, bson.M{"_id": id}) return err } // GetAllSpaces retrieves all spaces sorted by creation date descending func (r *SpaceRepository) GetAllSpaces(ctx context.Context) ([]*entities.Space, error) { opts := options.Find().SetSort(bson.D{bson.E{Key: "created_at", Value: -1}}) cursor, err := r.collection.Find(ctx, bson.M{}, opts) if err != nil { return nil, err } defer cursor.Close(ctx) var spaces []*entities.Space if err := cursor.All(ctx, &spaces); err != nil { return nil, err } return spaces, nil } // GetPublicSpaces retrieves all spaces marked as public func (r *SpaceRepository) GetPublicSpaces(ctx context.Context) ([]*entities.Space, error) { opts := options.Find().SetSort(bson.D{bson.E{Key: "created_at", Value: -1}}) cursor, err := r.collection.Find(ctx, bson.M{"is_public": true}, opts) if err != nil { return nil, err } defer cursor.Close(ctx) var spaces []*entities.Space if err := cursor.All(ctx, &spaces); err != nil { return nil, err } return spaces, nil } // EnsureIndexes creates necessary indexes func (r *SpaceRepository) EnsureIndexes(ctx context.Context) error { indexModel := []mongo.IndexModel{ { Keys: bson.D{bson.E{Key: "owner_id", Value: 1}}, }, { Keys: bson.D{bson.E{Key: "created_at", Value: -1}}, }, } _, err := r.collection.Indexes().CreateMany(ctx, indexModel) return err } // ========== MEMBERSHIP REPOSITORY ========== // MembershipRepository implements the membership repository interface type MembershipRepository struct { collection *mongo.Collection } // NewMembershipRepository creates a new membership repository func NewMembershipRepository(db *mongo.Database) *MembershipRepository { return &MembershipRepository{ collection: db.Collection("memberships"), } } // CreateMembership creates a new membership func (r *MembershipRepository) CreateMembership(ctx context.Context, membership *entities.Membership) error { membership.ID = bson.NewObjectID() membership.JoinedAt = time.Now() _, err := r.collection.InsertOne(ctx, membership) return err } // GetMembershipByID retrieves a membership by ID func (r *MembershipRepository) GetMembershipByID(ctx context.Context, id bson.ObjectID) (*entities.Membership, error) { var membership entities.Membership err := r.collection.FindOne(ctx, bson.M{"_id": id}).Decode(&membership) if err != nil { if err == mongo.ErrNoDocuments { return nil, errors.New("membership not found") } return nil, err } return &membership, nil } // GetUserMembership retrieves a membership for a user in a space func (r *MembershipRepository) GetUserMembership(ctx context.Context, userID, spaceID bson.ObjectID) (*entities.Membership, error) { var membership entities.Membership err := r.collection.FindOne(ctx, bson.M{ "user_id": userID, "space_id": spaceID, }).Decode(&membership) if err != nil { if err == mongo.ErrNoDocuments { return nil, errors.New("membership not found") } return nil, err } return &membership, nil } // GetSpaceMembers retrieves all members in a space func (r *MembershipRepository) GetSpaceMembers(ctx context.Context, spaceID bson.ObjectID) ([]*entities.Membership, error) { var memberships []*entities.Membership cursor, err := r.collection.Find(ctx, bson.M{"space_id": spaceID}) if err != nil { return nil, err } defer cursor.Close(ctx) if err = cursor.All(ctx, &memberships); err != nil { return nil, err } return memberships, nil } // GetUserMemberships retrieves all memberships for a user func (r *MembershipRepository) GetUserMemberships(ctx context.Context, userID bson.ObjectID) ([]*entities.Membership, error) { var memberships []*entities.Membership cursor, err := r.collection.Find(ctx, bson.M{"user_id": userID}) if err != nil { return nil, err } defer cursor.Close(ctx) if err = cursor.All(ctx, &memberships); err != nil { return nil, err } return memberships, nil } // UpdateMembership updates a membership func (r *MembershipRepository) UpdateMembership(ctx context.Context, membership *entities.Membership) error { _, err := r.collection.ReplaceOne(ctx, bson.M{"_id": membership.ID}, membership) return err } // DeleteMembership deletes a membership func (r *MembershipRepository) DeleteMembership(ctx context.Context, id bson.ObjectID) error { _, err := r.collection.DeleteOne(ctx, bson.M{"_id": id}) return err } // DeleteMembershipsBySpaceID deletes all memberships for a space func (r *MembershipRepository) DeleteMembershipsBySpaceID(ctx context.Context, spaceID bson.ObjectID) error { _, err := r.collection.DeleteMany(ctx, bson.M{"space_id": spaceID}) return err } // EnsureIndexes creates necessary indexes func (r *MembershipRepository) EnsureIndexes(ctx context.Context) error { indexModel := []mongo.IndexModel{ { Keys: bson.D{bson.E{Key: "user_id", Value: 1}, bson.E{Key: "space_id", Value: 1}}, Options: options.Index().SetUnique(true), }, { Keys: bson.D{bson.E{Key: "space_id", Value: 1}}, }, } _, err := r.collection.Indexes().CreateMany(ctx, indexModel) return err }