<?php
/**
 * Encrypted settings storage for MakePay credentials.
 */

namespace IPS\makepay\Storage;

if (!\defined('\IPS\SUITE_UNIQUE_KEY')) {
    exit;
}

class _Storage
{
    private const PREFIX = 'makepay_';
    private const CIPHER = 'aes-256-gcm';

    public static function get(string $key, ?string $default = null): ?string
    {
        $setting = self::PREFIX . $key;

        return isset(\IPS\Settings::i()->$setting)
            ? (string) \IPS\Settings::i()->$setting
            : $default;
    }

    public static function set(string $key, ?string $value): void
    {
        $setting = self::PREFIX . $key;
        \IPS\Db::i()->replace('core_sys_conf_settings', [
            'conf_key' => $setting,
            'conf_value' => $value ?? '',
            'conf_default' => '',
            'conf_app' => 'makepay',
        ]);
        unset(\IPS\Data\Store::i()->settings);
    }

    public static function setSecret(string $key, string $value): void
    {
        self::set($key, self::encrypt($value));
    }

    public static function getSecret(string $key): ?string
    {
        $value = self::get($key);

        return $value ? self::decrypt($value) : null;
    }

    public static function delete(string $key): void
    {
        \IPS\Db::i()->delete('core_sys_conf_settings', [
            'conf_key=?',
            self::PREFIX . $key,
        ]);
        unset(\IPS\Data\Store::i()->settings);
    }

    public static function encrypt(string $plainText): string
    {
        $iv = random_bytes(12);
        $tag = '';
        $cipherText = openssl_encrypt(
            $plainText,
            self::CIPHER,
            self::keyMaterial(),
            OPENSSL_RAW_DATA,
            $iv,
            $tag,
        );

        if ($cipherText === false) {
            throw new \RuntimeException('Unable to encrypt MakePay secret.');
        }

        return 'v1.' . base64_encode($iv . $tag . $cipherText);
    }

    public static function decrypt(string $payload): string
    {
        if (!str_starts_with($payload, 'v1.')) {
            return $payload;
        }

        $raw = base64_decode(substr($payload, 3), true);
        if ($raw === false || strlen($raw) < 28) {
            throw new \RuntimeException('Stored MakePay secret is invalid.');
        }

        $iv = substr($raw, 0, 12);
        $tag = substr($raw, 12, 16);
        $cipherText = substr($raw, 28);
        $plainText = openssl_decrypt(
            $cipherText,
            self::CIPHER,
            self::keyMaterial(),
            OPENSSL_RAW_DATA,
            $iv,
            $tag,
        );

        if ($plainText === false) {
            throw new \RuntimeException('Unable to decrypt MakePay secret.');
        }

        return $plainText;
    }

    private static function keyMaterial(): string
    {
        return hash('sha256', \IPS\SUITE_UNIQUE_KEY . ':makepay:invision', true);
    }
}
