<?php
declare(strict_types=1);

namespace PSLauncher;

final class Crypto
{
    /**
     * Signe un payload (chaîne canonical UTF-8) avec la clé privée Ed25519.
     * Retourne la signature en base64.
     */
    public static function signEd25519(string $payload, string $privateKeyHex): string
    {
        if (strlen($privateKeyHex) !== 128) {
            throw new \InvalidArgumentException('private_key_hex must be 128 hex chars (sodium secret key)');
        }
        $sk = sodium_hex2bin($privateKeyHex);
        $sig = sodium_crypto_sign_detached($payload, $sk);
        return base64_encode($sig);
    }

    /**
     * Génère un nouveau keypair Ed25519 et retourne ['private_key_hex', 'public_key_hex'].
     */
    public static function generateKeypair(): array
    {
        $kp = sodium_crypto_sign_keypair();
        $sk = sodium_crypto_sign_secretkey($kp);
        $pk = sodium_crypto_sign_publickey($kp);
        return [
            'private_key_hex' => sodium_bin2hex($sk),
            'public_key_hex'  => sodium_bin2hex($pk),
        ];
    }

    /**
     * Encodage canonical d'un objet JSON pour la signature : on retire le champ
     * `signature` (s'il est là), on encode sans escapes inutiles, on signe ce blob.
     * Le client doit faire EXACTEMENT le même encodage avant verify.
     */
    public static function canonicalJson(array $data): string
    {
        unset($data['signature']);
        return json_encode(
            $data,
            JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION
        );
    }

    /**
     * Hash bcrypt-comparable d'une license_key (constant-time compare).
     */
    public static function constantTimeEquals(string $a, string $b): bool
    {
        return hash_equals($a, $b);
    }

    /**
     * HMAC-SHA-256 hex pour les URLs présignées (v0.6).
     */
    public static function hmacHex(string $message, string $secret): string
    {
        return hash_hmac('sha256', $message, $secret);
    }
}
