<?php
declare(strict_types=1);

namespace SafeTun\Backend\Service;

use SafeTun\Backend\Config\ValidatedConfig;
use SafeTun\Backend\Crypto\KeyLoader;
use SafeTun\Backend\Crypto\ReleaseManifest;
use SafeTun\Backend\Exception\PublicApiException;

final class ReleaseService
{
    public const DOMAIN = ReleaseManifest::DOMAIN;

    public function __construct(private readonly ValidatedConfig $validated)
    {
    }

    /** @return array<string,mixed> */
    public function manifest(string $channel): array
    {
        $config = $this->validated->values['release'];
        if (!hash_equals($config['channel'], $channel)) {
            throw new PublicApiException('NOT_FOUND', 404, 'Release channel is unavailable.');
        }
        $raw = @file_get_contents($config['manifest_path']);
        $signatureFile = @file_get_contents($config['manifest_signature_path']);
        if (!is_string($raw) || !is_string($signatureFile) || strlen($raw) > 1024 * 1024) {
            throw new PublicApiException('SERVICE_UNAVAILABLE', 503, 'Release manifest state is unavailable.');
        }
        try {
            $decoded = json_decode($raw, true, 32, JSON_THROW_ON_ERROR);
            if (!is_array($decoded) || array_is_list($decoded) || ($decoded['channel'] ?? null) !== $channel
                || ($decoded['release_key_id'] ?? null) !== $config['active_key_id']
                || !is_string($decoded['version'] ?? null) || !is_array($decoded['artifacts'] ?? null)) {
                throw new \InvalidArgumentException('Manifest contract is invalid.');
            }
            $signature = ReleaseManifest::decodeSignature($signatureFile);
            ReleaseManifest::verifyDetached(
                $raw,
                $signature,
                KeyLoader::public($config['release_public_key_path']),
            );
            return ReleaseManifest::wrap($config['active_key_id'], $raw, $signature);
        } catch (\Throwable $exception) {
            throw new PublicApiException('SERVICE_UNAVAILABLE', 503, 'Release manifest verification failed.');
        }
    }

}
