<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Service;

use RuntimeException;
use SodiumException;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

final class TokenEncryptor
{
    private readonly string $key;
    private readonly bool $configured;

    public function __construct(
        #[Autowire('%env(ENCRYPTION_KEY)%')]
        string $encryptionKey,
    ) {
        $key = $this->tryDecodeKey($encryptionKey);

        $this->key        = $key ?? '';
        $this->configured = null !== $key;
    }

    public function encrypt(string $plaintext): string
    {
        $this->assertConfigured();

        $nonce      = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $this->key);

        return sodium_bin2hex($nonce.$ciphertext);
    }

    public function decrypt(string $encrypted): string
    {
        $this->assertConfigured();

        $decoded    = sodium_hex2bin($encrypted);
        $nonce      = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
        $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');

        $plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->key);

        if (false === $plaintext) {
            throw new RuntimeException('Failed to decrypt token.');
        }

        return $plaintext;
    }

    private function tryDecodeKey(string $encryptionKey): ?string
    {
        if ('' === $encryptionKey) {
            return null;
        }

        try {
            $key = sodium_hex2bin($encryptionKey);
        } catch (SodiumException) {
            return null;
        }

        if (SODIUM_CRYPTO_SECRETBOX_KEYBYTES !== mb_strlen($key, '8bit')) {
            return null;
        }

        return $key;
    }

    private function assertConfigured(): void
    {
        if (!$this->configured) {
            throw new RuntimeException('Encryption is not configured. Please set a valid ENCRYPTION_KEY.');
        }
    }
}
