<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Encryption\Encrypter;
use RuntimeException;

/**
 * Encrypts the TOTP secret at rest under a key derived from a dedicated plain-text secret
 * (MFA_ENCRYPTION_SECRET) rather than the application key - Rotating APP_KEY must never
 * invalidate MFA enrollments, and a leaked application key alone must not expose second factors.
 */
class MfaEncrypted implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes)
    {
        return $value === null ? null : static::encrypter()->decryptString($value);
    }

    public function set($model, string $key, $value, array $attributes)
    {
        return $value === null ? null : static::encrypter()->encryptString($value);
    }

    private static function encrypter()
    {
        // Derivation is deliberately expensive, so the result is kept for the life of the process.
        static $encrypter = null;

        if($encrypter !== null) {
            return $encrypter;
        }

        $secret = config('security.mfa_encryption_secret');

        if(!$secret) {
            // Fail loudly - Silently falling back to the application key would defeat the point.
            throw new RuntimeException('MFA_ENCRYPTION_SECRET is not configured.');
        }

        $key = hash_pbkdf2('sha256', $secret, 'creatia-mfa-totp-at-rest', 600000, 32, true);

        return $encrypter = new Encrypter($key, config('app.cipher'));
    }
}
