<?php

declare(strict_types=1);

namespace PhpWA\Protocol;

use Google\Protobuf\Internal\Message;
use InvalidArgumentException;
use PhpWA\Exception\ProtocolException;

final class ProtobufCodec
{
    /** @var int */ private $maxMessageBytes;

    public function __construct(int $maxMessageBytes = 33554432)
    {
        if ($maxMessageBytes < 1 || $maxMessageBytes > 268435456) {
            throw new InvalidArgumentException('Protobuf message limit is out of range.');
        }
        $this->maxMessageBytes = $maxMessageBytes;
    }

    public function encode(Message $message): string
    {
        try {
            $bytes = $message->serializeToString();
        } catch (\Throwable $error) {
            throw new ProtocolException('Unable to encode protobuf message.', 0, $error);
        }
        if (!is_string($bytes) || strlen($bytes) > $this->maxMessageBytes) {
            throw new ProtocolException('Encoded protobuf message exceeds configured limit.');
        }
        return $bytes;
    }

    /**
     * @template T of Message
     * @param class-string<T> $messageClass
     * @return T
     */
    public function decode(string $messageClass, string $bytes): Message
    {
        if (strlen($bytes) > $this->maxMessageBytes) {
            throw new ProtocolException('Incoming protobuf message exceeds configured limit.');
        }
        if (!is_a($messageClass, Message::class, true)) {
            throw new InvalidArgumentException('Protobuf target must extend Google protobuf Message.');
        }
        try {
            /** @var T $message */
            $message = new $messageClass();
            $message->mergeFromString($bytes);
            return $message;
        } catch (\Throwable $error) {
            throw new ProtocolException('Unable to decode protobuf message.', 0, $error);
        }
    }
}
