id = $id; $this->sessionId = $sessionId; $this->role = $role; $this->content = $content; $this->model = $model; $this->tokens = $tokens; $this->timing = $timing; $this->metadata = $metadata; $this->authorProfileId = $authorProfileId; $this->systemPromptId = $systemPromptId; $this->llmRequestId = $llmRequestId; $this->createdAt = $createdAt; } // Factory methods for domain logic public static function createUserMessage(int $sessionId, string $content, ?int $authorProfileId = null): self { return new self( null, $sessionId, MessageRole::USER, MessageContent::fromString($content), null, TokenCount::zero(), MessageTiming::none(), MessageMetadata::empty(), $authorProfileId, null, null, new \DateTimeImmutable() ); } public static function createAssistantMessage( int $sessionId, string $content, string $model, TokenCount $tokens, MessageTiming $timing ): self { return new self( null, $sessionId, MessageRole::ASSISTANT, MessageContent::fromStringOrEmpty($content), $model, $tokens, $timing, MessageMetadata::empty(), null, null, null, new \DateTimeImmutable() ); } public static function createSystemMessage(int $sessionId, string $content, ?int $systemPromptId = null): self { return new self( null, $sessionId, MessageRole::SYSTEM, MessageContent::fromStringOrEmpty($content), null, TokenCount::zero(), MessageTiming::none(), MessageMetadata::empty(), null, $systemPromptId, null, new \DateTimeImmutable() ); } // Reconstitution from persistence (used by Factory) public static function reconstituteFromPersistence( ?int $id, int $sessionId, MessageRole $role, MessageContent $content, ?string $model, TokenCount $tokens, MessageTiming $timing, MessageMetadata $metadata, ?int $authorProfileId, ?int $systemPromptId, ?int $llmRequestId, \DateTimeImmutable $createdAt ): self { return new self( $id, $sessionId, $role, $content, $model, $tokens, $timing, $metadata, $authorProfileId, $systemPromptId, $llmRequestId, $createdAt ); } // Essential getters public function id(): ?int { return $this->id; } public function sessionId(): int { return $this->sessionId; } public function role(): MessageRole { return $this->role; } public function content(): MessageContent { return $this->content; } public function model(): ?string { return $this->model; } public function tokens(): TokenCount { return $this->tokens; } public function timing(): MessageTiming { return $this->timing; } public function metadata(): MessageMetadata { return $this->metadata; } public function authorProfileId(): ?int { return $this->authorProfileId; } public function systemPromptId(): ?int { return $this->systemPromptId; } public function llmRequestId(): ?int { return $this->llmRequestId; } public function createdAt(): \DateTimeImmutable { return $this->createdAt; } // Business logic methods public function isUser(): bool { return $this->role->isUser(); } public function isAssistant(): bool { return $this->role->isAssistant(); } public function isSystem(): bool { return $this->role->isSystem(); } public function hasTokens(): bool { return $this->tokens->hasTokens(); } public function estimatedCostUsd(): float { return $this->tokens->estimatedCostUsd(); } public function durationMs(): ?float { return $this->timing->durationMs(); } // Mutation methods (return new instance for immutability) public function withId(int $id): self { $clone = clone $this; $clone->id = $id; return $clone; } public function withMetadata(MessageMetadata $metadata): self { $clone = clone $this; $clone->metadata = $metadata; return $clone; } public function withLlmRequestId(int $llmRequestId): self { $clone = clone $this; $clone->llmRequestId = $llmRequestId; return $clone; } public function withTiming(MessageTiming $timing): self { $clone = clone $this; $clone->timing = $timing; return $clone; } /** * Convert to array for persistence. * * @return array */ public function toArray(): array { return [ 'id' => $this->id, 'session_id' => $this->sessionId, 'role' => $this->role->value, 'content' => $this->content->value(), 'model' => $this->model, 'tokens_input' => $this->tokens->input() > 0 ? $this->tokens->input() : null, 'tokens_output' => $this->tokens->output() > 0 ? $this->tokens->output() : null, 'sources' => $this->metadata->sources() !== null ? json_encode($this->metadata->sources()) : null, 'start_microtime' => $this->timing->startMicrotime(), 'end_microtime' => $this->timing->endMicrotime(), 'author_profile_id' => $this->authorProfileId, 'system_prompt_id' => $this->systemPromptId, 'collections' => $this->metadata->collections() !== null ? json_encode($this->metadata->collections()) : null, 'context_limit' => $this->metadata->contextLimit(), 'chunks_used' => $this->metadata->chunksUsed() !== null ? json_encode($this->metadata->chunksUsed()) : null, 'llm_request_id' => $this->llmRequestId, 'created_at' => $this->createdAt->format('Y-m-d H:i:s'), ]; } }