<?php

namespace WPD\Security;

use WPD\Application\Config;

final class CredentialVault
{
    private $config;

    public function __construct(Config $config)
    {
        $this->config = $config;
    }

    public function seal(array $credentials, $activationId)
    {
        if (!function_exists('openssl_encrypt') || !preg_match('/^[a-f0-9]{32}$/', (string) $activationId)) {
            return null;
        }
        try {
            $nonce = random_bytes(12);
        } catch (\Throwable $error) {
            return null;
        }
        $plaintext = json_encode($credentials, JSON_UNESCAPED_SLASHES);
        if (!is_string($plaintext)) {
            return null;
        }
        $tag = '';
        $ciphertext = openssl_encrypt($plaintext, 'aes-256-gcm', $this->key($activationId), OPENSSL_RAW_DATA, $nonce, $tag, (string) $activationId);
        if (!is_string($ciphertext) || strlen($tag) !== 16) {
            return null;
        }
        return array(
            'schema' => 1,
            'nonce' => base64_encode($nonce),
            'tag' => base64_encode($tag),
            'ciphertext' => base64_encode($ciphertext),
        );
    }

    public function open(array $sealed, $activationId)
    {
        if (!function_exists('openssl_decrypt')
            || !isset($sealed['schema'], $sealed['nonce'], $sealed['tag'], $sealed['ciphertext'])
            || (int) $sealed['schema'] !== 1
            || !preg_match('/^[a-f0-9]{32}$/', (string) $activationId)) {
            return null;
        }
        $nonce = base64_decode((string) $sealed['nonce'], true);
        $tag = base64_decode((string) $sealed['tag'], true);
        $ciphertext = base64_decode((string) $sealed['ciphertext'], true);
        if (!is_string($nonce) || strlen($nonce) !== 12 || !is_string($tag) || strlen($tag) !== 16 || !is_string($ciphertext)) {
            return null;
        }
        $plaintext = openssl_decrypt($ciphertext, 'aes-256-gcm', $this->key($activationId), OPENSSL_RAW_DATA, $nonce, $tag, (string) $activationId);
        if (!is_string($plaintext)) {
            return null;
        }
        $credentials = json_decode($plaintext, true);
        return is_array($credentials) ? $credentials : null;
    }

    private function key($activationId)
    {
        return hash_hmac('sha256', 'wpd-manual-database|' . (string) $activationId, (string) $this->config->get('access_token'), true);
    }
}

