* SPDX-License-Identifier: AGPL-3.0-or-later */ namespace KTXM\MailManager\Queue; use DateTimeImmutable; use KTXF\Mail\Entity\IMessageMutable; use KTXF\Mail\Queue\SendOptions; /** * Mail Job * * Represents a queued mail job with metadata and message content. * * @since 2025.05.01 */ class MailJob { public function __construct( public readonly string $id, public readonly string $tenantId, public readonly string $providerId, public readonly string|int $serviceId, public readonly IMessageMutable $message, public readonly SendOptions $options, public JobStatus $status = JobStatus::Pending, public int $attempts = 0, public ?string $lastError = null, public ?string $messageId = null, public ?DateTimeImmutable $created = null, public ?DateTimeImmutable $scheduled = null, public ?DateTimeImmutable $lastAttempt = null, public ?DateTimeImmutable $completed = null, ) { $this->created = $this->created ?? new DateTimeImmutable(); $this->scheduled = $this->scheduled ?? $this->calculateScheduledTime(); } /** * Calculate when this job should be processed * * @return DateTimeImmutable */ private function calculateScheduledTime(): DateTimeImmutable { $scheduled = $this->created ?? new DateTimeImmutable(); if ($this->options->delaySeconds !== null && $this->options->delaySeconds > 0) { $scheduled = $scheduled->modify("+{$this->options->delaySeconds} seconds"); } return $scheduled; } /** * Check if the job is ready to be processed * * @return bool */ public function isReady(): bool { if ($this->status !== JobStatus::Pending) { return false; } return $this->scheduled === null || $this->scheduled <= new DateTimeImmutable(); } /** * Check if the job can be retried * * @return bool */ public function canRetry(): bool { return $this->attempts < $this->options->retryCount; } /** * Get retry delay in seconds based on attempt count (exponential backoff) * * @return int */ public function getRetryDelay(): int { // Exponential backoff: 30s, 60s, 120s, 240s, ... return min(30 * (2 ** $this->attempts), 3600); } /** * Serialize job metadata for storage * * @return array */ public function toMetaArray(): array { return [ 'id' => $this->id, 'tenantId' => $this->tenantId, 'providerId' => $this->providerId, 'serviceId' => $this->serviceId, 'options' => $this->options->jsonSerialize(), 'status' => $this->status->value, 'attempts' => $this->attempts, 'lastError' => $this->lastError, 'messageId' => $this->messageId, 'created' => $this->created?->format('c'), 'scheduled' => $this->scheduled?->format('c'), 'lastAttempt' => $this->lastAttempt?->format('c'), 'completed' => $this->completed?->format('c'), ]; } }