<?php

/**
 * Key Manager
 *
 * @package _121Digital\Connect
 */

namespace _121Digital\Connect\Security;

/**
 * Key Manager Class
 *
 * Handles RSA key generation, encryption, and decryption
 */
class KeyManager
{
    /**
     * Generate a random alphanumeric string
     *
     * @param int $length Length of the string
     * @return string
     */
    public function generateRandomString(int $length = 25): string
    {
        $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $id = '';
        for ($i = 0; $i < $length; $i++) {
            $id .= $chars[random_int(0, strlen($chars) - 1)];
        }
        return $id;
    }

    /**
     * Generate RSA keypair
     *
     * Generates an RSA keypair, encrypts the private key using AUTH_KEY, and returns both keys.
     *
     * @return array
     */
    public function generateKeypair(): array
    {
        $config = [
            'private_key_bits' => 2048,
            'private_key_type' => \OPENSSL_KEYTYPE_RSA,
        ];

        $res = openssl_pkey_new($config);
        if (!$res) {
            return [
                'success' => false,
                'message' => 'Key generation failed: ' . openssl_error_string(),
            ];
        }

        openssl_pkey_export($res, $privateKey);
        $publicKeyDetails = openssl_pkey_get_details($res);
        $publicKey = $publicKeyDetails['key'];

        $encryptionKey = hash('sha256', AUTH_KEY, true);
        $iv = openssl_random_pseudo_bytes(16);

        $privateKeyEncrypted = openssl_encrypt(
            $privateKey,
            'aes-256-cbc',
            $encryptionKey,
            \OPENSSL_RAW_DATA,
            $iv
        );

        return [
            'success' => true,
            'public_key' => $publicKey,
            'private_key_encrypted' => base64_encode($privateKeyEncrypted),
            'iv' => base64_encode($iv),
        ];
    }

    /**
     * Decrypt the encrypted private key
     *
     * @param string $encryptedPrivateKey Base64-encoded encrypted private key
     * @param string $iv Base64-encoded initialization vector
     * @return string Decrypted private key (PEM format)
     */
    public function decryptPrivateKey(string $encryptedPrivateKey, string $iv): string
    {
        $encryptionKey = hash('sha256', AUTH_KEY, true);
        $encryptedPrivateKey = base64_decode($encryptedPrivateKey);

        $privateKey = openssl_decrypt(
            $encryptedPrivateKey,
            'aes-256-cbc',
            $encryptionKey,
            \OPENSSL_RAW_DATA,
            base64_decode($iv)
        );

        return $privateKey;
    }

    /**
     * Decrypt data using the stored private key
     *
     * @param string $encryptedData Base64-encoded encrypted data
     * @return string Decrypted plaintext data
     * @throws \Exception if decryption fails or private key is invalid
     */
    public function decrypt(string $encryptedData): string
    {
        $encryptedPrivateKey = get_option('sc_private_key');
        $iv = get_option('sc_private_key_iv');
        $privateKey = $this->decryptPrivateKey($encryptedPrivateKey, $iv);
        $privateKeyResource = openssl_pkey_get_private($privateKey);

        if ($privateKeyResource === false) {
            throw new \Exception('Invalid private key.');
        }

        $encryptedData = base64_decode($encryptedData);
        $decryptedData = '';

        $success = openssl_private_decrypt($encryptedData, $decryptedData, $privateKeyResource);

        if (!$success) {
            throw new \Exception('Decryption failed.');
        }

        openssl_free_key($privateKeyResource);

        return $decryptedData;
    }

    /**
     * Encrypt data using the stored public key
     *
     * @param string $data Plaintext data to encrypt
     * @return string Base64-encoded encrypted data
     * @throws \Exception if encryption fails or public key is not found
     */
    public function encrypt(string $data): string
    {
        $publicKey = get_option('sc_public_key');
        if (!$publicKey) {
            throw new \Exception('Public key not found.');
        }

        $publicKeyResource = openssl_pkey_get_public($publicKey);
        if ($publicKeyResource === false) {
            throw new \Exception('Invalid public key.');
        }

        $encrypted = '';
        $success = openssl_public_encrypt($data, $encrypted, $publicKeyResource);

        if (!$success) {
            throw new \Exception('Encryption failed.');
        }

        openssl_free_key($publicKeyResource);

        return base64_encode($encrypted);
    }

    /**
     * Register this component (required for Plugin initialization)
     *
     * @return void
     */
    public function register(): void
    {
        // No hooks to register for key manager
    }
}
