package service import ( "context" "fmt" "time" "github.com/google/uuid" "github.com/orchard9/rdev/internal/domain" "github.com/orchard9/rdev/internal/logging" "github.com/orchard9/rdev/internal/port" ) // BlueprintService orchestrates blueprint operations. type BlueprintService struct { repo port.BlueprintRepository } // NewBlueprintService creates a new blueprint service. func NewBlueprintService(repo port.BlueprintRepository) *BlueprintService { return &BlueprintService{repo: repo} } // CreateBlueprint creates a new blueprint. func (s *BlueprintService) CreateBlueprint(ctx context.Context, projectID, name, description string, spec map[string]any) (*domain.Blueprint, error) { if projectID == "" { return nil, fmt.Errorf("project_id is required") } if name == "" { return nil, fmt.Errorf("name is required") } if spec == nil { spec = make(map[string]any) } blueprint := &domain.Blueprint{ ID: domain.BlueprintID(uuid.New().String()), ProjectID: projectID, Name: name, Description: description, Spec: spec, } startTime := time.Now() if err := s.repo.CreateBlueprint(ctx, blueprint); err != nil { return nil, err } log := logging.FromContext(ctx) log.Info("blueprint created", "blueprint_id", blueprint.ID, logging.FieldProjectID, projectID, logging.FieldOperation, "create_blueprint", logging.FieldDuration, time.Since(startTime).Milliseconds(), "name", name, "has_spec", len(spec) > 0, ) return blueprint, nil } // GetBlueprint retrieves a blueprint by ID. func (s *BlueprintService) GetBlueprint(ctx context.Context, id domain.BlueprintID) (*domain.Blueprint, error) { return s.repo.GetBlueprint(ctx, id) } // ListBlueprints returns all blueprints for a project. func (s *BlueprintService) ListBlueprints(ctx context.Context, projectID string) ([]*domain.Blueprint, error) { return s.repo.ListBlueprints(ctx, projectID) } // UpdateBlueprint updates a blueprint's metadata and spec. func (s *BlueprintService) UpdateBlueprint(ctx context.Context, id domain.BlueprintID, name, description string, spec map[string]any) error { // Get existing blueprint first blueprint, err := s.repo.GetBlueprint(ctx, id) if err != nil { return err } // Update fields if name != "" { blueprint.Name = name } blueprint.Description = description if spec != nil { blueprint.Spec = spec } startTime := time.Now() if err := s.repo.UpdateBlueprint(ctx, blueprint); err != nil { return err } log := logging.FromContext(ctx) log.Info("blueprint updated", "blueprint_id", id, logging.FieldProjectID, blueprint.ProjectID, logging.FieldOperation, "update_blueprint", logging.FieldDuration, time.Since(startTime).Milliseconds(), ) return nil } // DeleteBlueprint deletes a blueprint. func (s *BlueprintService) DeleteBlueprint(ctx context.Context, id domain.BlueprintID) error { startTime := time.Now() if err := s.repo.DeleteBlueprint(ctx, id); err != nil { return err } log := logging.FromContext(ctx) log.Info("blueprint deleted", "blueprint_id", id, logging.FieldOperation, "delete_blueprint", logging.FieldDuration, time.Since(startTime).Milliseconds(), ) return nil }