validate(); } public static function create(string $stepName, string $description, ?array $metadata = null): self { return new self( stepName: $stepName, description: $description, metadata: $metadata ); } public static function completed(string $stepName, string $description, ?array $metadata = null): self { return new self( stepName: $stepName, description: $description, completed: true, completedAt: date('Y-m-d H:i:s'), metadata: $metadata ); } public function markAsCompleted(?array $additionalMetadata = null): self { $metadata = $this->metadata ?? []; if ($additionalMetadata) { $metadata = array_merge($metadata, $additionalMetadata); } return new self( stepName: $this->stepName, description: $this->description, completed: true, completedAt: date('Y-m-d H:i:s'), metadata: $metadata ); } public function withMetadata(array $metadata): self { return new self( stepName: $this->stepName, description: $this->description, completed: $this->completed, completedAt: $this->completedAt, metadata: array_merge($this->metadata ?? [], $metadata) ); } public function getDisplayName(): string { return $this->stepName; } public function getStatus(): string { return $this->completed ? 'completed' : 'pending'; } public function toArray(): array { return [ 'step_name' => $this->stepName, 'description' => $this->description, 'completed' => $this->completed, 'completed_at' => $this->completedAt, 'status' => $this->getStatus(), 'metadata' => $this->metadata ]; } private function validate(): void { if (empty(trim($this->stepName))) { throw new \InvalidArgumentException('Step name cannot be empty'); } if (empty(trim($this->description))) { throw new \InvalidArgumentException('Step description cannot be empty'); } if ($this->completed && !$this->completedAt) { throw new \InvalidArgumentException('Completed steps must have a completion timestamp'); } } }