package work import ( "context" "errors" "fmt" "tercul/internal/app/authz" "tercul/internal/domain" "tercul/internal/domain/search" "tercul/internal/domain/work" platform_auth "tercul/internal/platform/auth" "gorm.io/gorm" ) // WorkCommands contains the command handlers for the work aggregate. type WorkCommands struct { repo work.WorkRepository searchClient search.SearchClient authzSvc *authz.Service } // NewWorkCommands creates a new WorkCommands handler. func NewWorkCommands(repo work.WorkRepository, searchClient search.SearchClient, authzSvc *authz.Service) *WorkCommands { return &WorkCommands{ repo: repo, searchClient: searchClient, authzSvc: authzSvc, } } // CreateWork creates a new work. func (c *WorkCommands) CreateWork(ctx context.Context, work *work.Work) (*work.Work, error) { if work == nil { return nil, errors.New("work cannot be nil") } if work.Title == "" { return nil, errors.New("work title cannot be empty") } if work.Language == "" { return nil, errors.New("work language cannot be empty") } err := c.repo.Create(ctx, work) if err != nil { return nil, err } // Index the work in the search client err = c.searchClient.IndexWork(ctx, work, "") if err != nil { // Log the error but don't fail the operation } return work, nil } // UpdateWork updates an existing work after performing an authorization check. func (c *WorkCommands) UpdateWork(ctx context.Context, work *work.Work) error { if work == nil { return fmt.Errorf("%w: work cannot be nil", domain.ErrValidation) } if work.ID == 0 { return fmt.Errorf("%w: work ID cannot be zero", domain.ErrValidation) } userID, ok := platform_auth.GetUserIDFromContext(ctx) if !ok { return domain.ErrUnauthorized } existingWork, err := c.repo.GetByID(ctx, work.ID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("%w: work with id %d not found", domain.ErrNotFound, work.ID) } return fmt.Errorf("failed to get work for authorization: %w", err) } can, err := c.authzSvc.CanEditWork(ctx, userID, existingWork) if err != nil { return err } if !can { return domain.ErrForbidden } if work.Title == "" { return fmt.Errorf("%w: work title cannot be empty", domain.ErrValidation) } if work.Language == "" { return fmt.Errorf("%w: work language cannot be empty", domain.ErrValidation) } err = c.repo.Update(ctx, work) if err != nil { return err } // Index the work in the search client return c.searchClient.IndexWork(ctx, work, "") } // DeleteWork deletes a work by ID after performing an authorization check. func (c *WorkCommands) DeleteWork(ctx context.Context, id uint) error { if id == 0 { return fmt.Errorf("%w: invalid work ID", domain.ErrValidation) } userID, ok := platform_auth.GetUserIDFromContext(ctx) if !ok { return domain.ErrUnauthorized } existingWork, err := c.repo.GetByID(ctx, id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("%w: work with id %d not found", domain.ErrNotFound, id) } return fmt.Errorf("failed to get work for authorization: %w", err) } can, err := c.authzSvc.CanEditWork(ctx, userID, existingWork) // Re-using CanEditWork for deletion for now if err != nil { return err } if !can { return domain.ErrForbidden } return c.repo.Delete(ctx, id) } // AnalyzeWork performs linguistic analysis on a work. func (c *WorkCommands) AnalyzeWork(ctx context.Context, workID uint) error { // TODO: implement this return nil }