<?php
declare(strict_types=1);

namespace App\Security;

use RuntimeException;

final class CryptoKeyring
{
    private array $config;

    public function __construct(?array $config = null)
    {
        $this->config = $config ?? require BASE_PATH . '/config/security_crypto.php';
    }

    public function currentVersion(): int
    {
        return (int)$this->config['key_version'];
    }

    public function current(string $purpose): string
    {
        return $this->derive($this->masterKey(), $purpose, $this->currentVersion());
    }

    /** @return list<array{version:int,key:string}> */
    public function candidates(string $purpose): array
    {
        $result = [['version' => $this->currentVersion(), 'key' => $this->current($purpose)]];
        $raw = trim((string)($_ENV[(string)$this->config['previous_master_keys_env']] ?? ''));
        if ($raw === '') return $result;
        foreach (array_values(array_filter(array_map('trim', explode(',', $raw)))) as $index => $encoded) {
            $master = $this->decodeMasterKey($encoded);
            $version = max(1, $this->currentVersion() - $index - 1);
            $result[] = ['version' => $version, 'key' => $this->derive($master, $purpose, $version)];
        }
        return $result;
    }

    private function masterKey(): string
    {
        $env = (string)$this->config['master_key_env'];
        $encoded = trim((string)($_ENV[$env] ?? getenv($env) ?: ''));
        if ($encoded === '') {
            throw new RuntimeException('Clé maître AXIOM absente. Définissez ' . $env . ' hors du projet.');
        }
        return $this->decodeMasterKey($encoded);
    }

    private function decodeMasterKey(string $encoded): string
    {
        $key = base64_decode($encoded, true);
        if ($key === false || strlen($key) !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
            throw new RuntimeException('Clé maître AXIOM invalide : 32 octets encodés en Base64 attendus.');
        }
        return $key;
    }

    private function derive(string $master, string $purpose, int $version): string
    {
        if (!function_exists('sodium_crypto_generichash')) {
            throw new RuntimeException('Extension Sodium obligatoire.');
        }
        return sodium_crypto_generichash('AXIOM|' . $purpose . '|v' . $version, $master, SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
    }
}
