package comment import ( "context" "errors" "tercul/internal/domain" ) // CommentCommands contains the command handlers for the comment aggregate. type CommentCommands struct { repo domain.CommentRepository analyticsService AnalyticsService } // AnalyticsService defines the interface for analytics operations. type AnalyticsService interface { IncrementWorkComments(ctx context.Context, workID uint) error IncrementTranslationComments(ctx context.Context, translationID uint) error } // NewCommentCommands creates a new CommentCommands handler. func NewCommentCommands(repo domain.CommentRepository, analyticsService AnalyticsService) *CommentCommands { return &CommentCommands{ repo: repo, analyticsService: analyticsService, } } // CreateCommentInput represents the input for creating a new comment. type CreateCommentInput struct { Text string UserID uint WorkID *uint TranslationID *uint ParentID *uint } // CreateComment creates a new comment. func (c *CommentCommands) CreateComment(ctx context.Context, input CreateCommentInput) (*domain.Comment, error) { if input.Text == "" { return nil, errors.New("comment text cannot be empty") } if input.UserID == 0 { return nil, errors.New("user ID cannot be zero") } comment := &domain.Comment{ Text: input.Text, UserID: input.UserID, WorkID: input.WorkID, TranslationID: input.TranslationID, ParentID: input.ParentID, } err := c.repo.Create(ctx, comment) if err != nil { return nil, err } // Increment analytics if comment.WorkID != nil { c.analyticsService.IncrementWorkComments(ctx, *comment.WorkID) } if comment.TranslationID != nil { c.analyticsService.IncrementTranslationComments(ctx, *comment.TranslationID) } return comment, nil } // UpdateCommentInput represents the input for updating an existing comment. type UpdateCommentInput struct { ID uint Text string UserID uint // for authorization } // UpdateComment updates an existing comment. func (c *CommentCommands) UpdateComment(ctx context.Context, input UpdateCommentInput) (*domain.Comment, error) { if input.ID == 0 { return nil, errors.New("comment ID cannot be zero") } if input.Text == "" { return nil, errors.New("comment text cannot be empty") } // Fetch the existing comment comment, err := c.repo.GetByID(ctx, input.ID) if err != nil { return nil, err } if comment == nil { return nil, errors.New("comment not found") } // Check ownership if comment.UserID != input.UserID { return nil, errors.New("unauthorized") } // Update fields comment.Text = input.Text err = c.repo.Update(ctx, comment) if err != nil { return nil, err } return comment, nil } // DeleteCommentInput represents the input for deleting a comment. type DeleteCommentInput struct { ID uint UserID uint // for authorization } // DeleteComment deletes a comment by ID. func (c *CommentCommands) DeleteComment(ctx context.Context, input DeleteCommentInput) error { if input.ID == 0 { return errors.New("invalid comment ID") } // Fetch the existing comment comment, err := c.repo.GetByID(ctx, input.ID) if err != nil { return err } if comment == nil { return errors.New("comment not found") } // Check ownership if comment.UserID != input.UserID { return errors.New("unauthorized") } return c.repo.Delete(ctx, input.ID) }