<?php
declare(strict_types=1);

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

final class StreamSubscriber
{
    public function __construct(
        private readonly string $subscriberId,
        private readonly string $topic,
        private readonly string $status = StreamSubscriberStatus::ACTIVE,
        private readonly int $offset = 0,
        private readonly int $received = 0,
        private readonly int $acknowledged = 0,
        private readonly int $failed = 0,
        private readonly string $lastMessageId = '',
        private readonly string $registeredAt = '',
        private readonly string $lastActivityAt = ''
    ) {}

    public static function register(string $subscriberId, string $topic): self
    {
        return new self($subscriberId, $topic, StreamSubscriberStatus::ACTIVE, 0, 0, 0, 0, '', date('c'), date('c'));
    }

    public static function fromArray(array $data): self
    {
        return new self(
            (string) ($data['subscriberId'] ?? ''),
            (string) ($data['topic'] ?? ''),
            (string) ($data['status'] ?? StreamSubscriberStatus::ACTIVE),
            (int) ($data['offset'] ?? 0),
            (int) ($data['received'] ?? 0),
            (int) ($data['acknowledged'] ?? 0),
            (int) ($data['failed'] ?? 0),
            (string) ($data['lastMessageId'] ?? ''),
            (string) ($data['registeredAt'] ?? ''),
            (string) ($data['lastActivityAt'] ?? '')
        );
    }

    public function subscriberId(): string { return $this->subscriberId; }
    public function topic(): string { return $this->topic; }
    public function status(): string { return $this->status; }
    public function offset(): int { return max(0, $this->offset); }
    public function active(): bool { return $this->status === StreamSubscriberStatus::ACTIVE; }

    public function receive(string $messageId): self
    {
        return new self(
            $this->subscriberId,
            $this->topic,
            $this->status,
            $this->offset() + 1,
            $this->received + 1,
            $this->acknowledged,
            $this->failed,
            $messageId,
            $this->registeredAt,
            date('c')
        );
    }

    public function acknowledge(?string $messageId = null): self
    {
        return new self(
            $this->subscriberId,
            $this->topic,
            $this->status,
            $this->offset(),
            $this->received,
            $this->acknowledged + 1,
            $this->failed,
            $messageId ?? $this->lastMessageId,
            $this->registeredAt,
            date('c')
        );
    }

    public function fail(?string $messageId = null): self
    {
        return new self(
            $this->subscriberId,
            $this->topic,
            $this->status,
            $this->offset(),
            $this->received,
            $this->acknowledged,
            $this->failed + 1,
            $messageId ?? $this->lastMessageId,
            $this->registeredAt,
            date('c')
        );
    }

    public function pause(): self
    {
        return new self($this->subscriberId, $this->topic, StreamSubscriberStatus::PAUSED, $this->offset(), $this->received, $this->acknowledged, $this->failed, $this->lastMessageId, $this->registeredAt, date('c'));
    }

    public function resume(): self
    {
        return new self($this->subscriberId, $this->topic, StreamSubscriberStatus::ACTIVE, $this->offset(), $this->received, $this->acknowledged, $this->failed, $this->lastMessageId, $this->registeredAt, date('c'));
    }

    public function toArray(): array
    {
        return [
            'subscriberId' => $this->subscriberId,
            'topic' => $this->topic,
            'status' => $this->status,
            'active' => $this->active(),
            'offset' => $this->offset(),
            'received' => $this->received,
            'acknowledged' => $this->acknowledged,
            'failed' => $this->failed,
            'lastMessageId' => $this->lastMessageId,
            'registeredAt' => $this->registeredAt !== '' ? $this->registeredAt : date('c'),
            'lastActivityAt' => $this->lastActivityAt !== '' ? $this->lastActivityAt : date('c'),
        ];
    }
}
