<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use AppCorexceptionsIntegrationConfigException;

final class IntegrationSecretCipher
{
    public function __construct(private readonly string $secret)
    {
    }

    public function encrypt(string $plainText): ?string
    {
        $value = trim($plainText);
        if ($value === '') {
            return null;
        }
        if ($this->secret === '') {
            throw new IntegrationConfigException('Brak INTEGRATIONS_SECRET do szyfrowania danych integracji.');
        }

        $encryptionKey = hash('sha256', 'enc|' . $this->secret, true);
        $hmacKey = hash('sha256', 'auth|' . $this->secret, true);
        $iv = random_bytes(16);
        $cipherRaw = openssl_encrypt($value, 'AES-256-CBC', $encryptionKey, OPENSSL_RAW_DATA, $iv);
        if ($cipherRaw === false) {
            throw new IntegrationConfigException('Nie udalo sie zaszyfrowac danych integracji.');
        }

        $mac = hash_hmac('sha256', $iv . $cipherRaw, $hmacKey, true);
        return 'v1:' . base64_encode($iv . $mac . $cipherRaw);
    }

    public function decrypt(string $encrypted): ?string
    {
        if ($this->secret === '') {
            throw new IntegrationConfigException('Brak INTEGRATIONS_SECRET do odszyfrowania danych integracji.');
        }

        if (!str_starts_with($encrypted, 'v1:')) {
            return null;
        }

        $raw = base64_decode(substr($encrypted, 3), true);
        if ($raw === false || strlen($raw) < 48) {
            return null;
        }

        $encryptionKey = hash('sha256', 'enc|' . $this->secret, true);
        $hmacKey = hash('sha256', 'auth|' . $this->secret, true);
        $iv = substr($raw, 0, 16);
        $mac = substr($raw, 16, 32);
        $cipherRaw = substr($raw, 48);

        $expectedMac = hash_hmac('sha256', $iv . $cipherRaw, $hmacKey, true);
        if (!hash_equals($expectedMac, $mac)) {
            return null;
        }

        $decrypted = openssl_decrypt($cipherRaw, 'AES-256-CBC', $encryptionKey, OPENSSL_RAW_DATA, $iv);
        return $decrypted !== false ? $decrypted : null;
    }
}
