<?php

declare(strict_types=1);

namespace PhpWA\Crypto\Signal;

use InvalidArgumentException;
use PhpWA\Exception\SignalException;

final class PreKeyBundle
{
    /** @var string */ private $identityKey;
    /** @var int */ private $registrationId;
    /** @var int */ private $signedPreKeyId;
    /** @var string */ private $signedPreKey;
    /** @var string */ private $signedPreKeySignature;
    /** @var int|null */ private $preKeyId;
    /** @var string|null */ private $preKey;

    public function __construct(
        string $identityKey,
        int $registrationId,
        int $signedPreKeyId,
        string $signedPreKey,
        string $signedPreKeySignature,
        ?int $preKeyId = null,
        ?string $preKey = null
    ) {
        SignalCurve::assertPublicKey($identityKey);
        SignalCurve::assertPublicKey($signedPreKey);
        if (strlen($signedPreKeySignature) !== 64 || $registrationId < 0 || $registrationId > 0xffffffff || $signedPreKeyId < 0 || $signedPreKeyId > 0xffffff) {
            throw new InvalidArgumentException('Signal pre-key bundle metadata is invalid.');
        }
        if (($preKeyId === null) !== ($preKey === null)) {
            throw new InvalidArgumentException('Signal one-time pre-key ID and value must be present together.');
        }
        if ($preKeyId !== null) {
            if ($preKeyId < 0 || $preKeyId > 0xffffff) { throw new InvalidArgumentException('Signal one-time pre-key ID is invalid.'); }
            SignalCurve::assertPublicKey((string) $preKey);
        }
        $this->identityKey = $identityKey;
        $this->registrationId = $registrationId;
        $this->signedPreKeyId = $signedPreKeyId;
        $this->signedPreKey = $signedPreKey;
        $this->signedPreKeySignature = $signedPreKeySignature;
        $this->preKeyId = $preKeyId;
        $this->preKey = $preKey;
    }

    public function assertSignature(SignalCurve $curve): void
    {
        if (!$curve->verify($this->identityKey, $this->signedPreKey, $this->signedPreKeySignature)) {
            throw new SignalException('Remote signed pre-key signature verification failed.');
        }
    }

    public function identityKey(): string { return $this->identityKey; }
    public function registrationId(): int { return $this->registrationId; }
    public function signedPreKeyId(): int { return $this->signedPreKeyId; }
    public function signedPreKey(): string { return $this->signedPreKey; }
    public function signedPreKeySignature(): string { return $this->signedPreKeySignature; }
    public function preKeyId(): ?int { return $this->preKeyId; }
    public function preKey(): ?string { return $this->preKey; }
}
