<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Streaming\Dlq;

final class StreamDlqMessage
{
    public function __construct(
        private readonly string $dlqId,
        private readonly string $messageId,
        private readonly string $topic,
        private readonly string $type,
        private readonly array $payload = [],
        private readonly array $headers = [],
        private readonly string $reason = '',
        private readonly int $attempts = 0,
        private readonly string $status = StreamDlqStatus::DEAD,
        private readonly string $availableAt = '',
        private readonly string $createdAt = '',
        private readonly string $updatedAt = ''
    ) {}

    public static function dead(array $message, string $reason = 'delivery failed'): self
    {
        $messageId = (string) ($message['messageId'] ?? ('msg_' . substr(hash('sha256', json_encode($message) . microtime(true)), 0, 24)));

        return new self(
            'dlq_' . substr(hash('sha256', $messageId . $reason), 0, 24),
            $messageId,
            (string) ($message['topic'] ?? ''),
            (string) ($message['type'] ?? ''),
            (array) ($message['payload'] ?? []),
            (array) ($message['headers'] ?? []),
            $reason,
            0,
            StreamDlqStatus::DEAD,
            date('c'),
            date('c'),
            date('c')
        );
    }

    public static function fromArray(array $data): self
    {
        return new self(
            (string) ($data['dlqId'] ?? ''),
            (string) ($data['messageId'] ?? ''),
            (string) ($data['topic'] ?? ''),
            (string) ($data['type'] ?? ''),
            (array) ($data['payload'] ?? []),
            (array) ($data['headers'] ?? []),
            (string) ($data['reason'] ?? ''),
            (int) ($data['attempts'] ?? 0),
            (string) ($data['status'] ?? StreamDlqStatus::DEAD),
            (string) ($data['availableAt'] ?? ''),
            (string) ($data['createdAt'] ?? ''),
            (string) ($data['updatedAt'] ?? '')
        );
    }

    public function dlqId(): string { return $this->dlqId; }
    public function messageId(): string { return $this->messageId; }
    public function topic(): string { return $this->topic; }
    public function type(): string { return $this->type; }
    public function payload(): array { return $this->payload; }
    public function headers(): array { return $this->headers; }
    public function attempts(): int { return max(0, $this->attempts); }
    public function status(): string { return $this->status; }

    public function markRetryPending(StreamRetryPolicy $policy): self
    {
        $attempts = $this->attempts() + 1;
        return new self(
            $this->dlqId,
            $this->messageId,
            $this->topic,
            $this->type,
            $this->payload,
            $this->headers,
            $this->reason,
            $attempts,
            StreamDlqStatus::RETRY_PENDING,
            date('c', time() + $policy->delayForAttempt($attempts)),
            $this->createdAt,
            date('c')
        );
    }

    public function markRetried(): self
    {
        return $this->withStatus(StreamDlqStatus::RETRIED);
    }

    public function markReplayed(): self
    {
        return $this->withStatus(StreamDlqStatus::REPLAYED);
    }

    public function markCleared(): self
    {
        return $this->withStatus(StreamDlqStatus::CLEARED);
    }

    private function withStatus(string $status): self
    {
        return new self(
            $this->dlqId,
            $this->messageId,
            $this->topic,
            $this->type,
            $this->payload,
            $this->headers,
            $this->reason,
            $this->attempts,
            $status,
            $this->availableAt,
            $this->createdAt,
            date('c')
        );
    }

    public function toArray(): array
    {
        return [
            'dlqId' => $this->dlqId,
            'messageId' => $this->messageId,
            'topic' => $this->topic,
            'type' => $this->type,
            'payload' => $this->payload,
            'headers' => $this->headers,
            'reason' => $this->reason,
            'attempts' => $this->attempts(),
            'status' => $this->status,
            'availableAt' => $this->availableAt !== '' ? $this->availableAt : date('c'),
            'createdAt' => $this->createdAt !== '' ? $this->createdAt : date('c'),
            'updatedAt' => $this->updatedAt !== '' ? $this->updatedAt : date('c'),
        ];
    }
}
