<?php

namespace App\Services;

class Encryption
{
    private $pubkey = '';
    private $privkey = '';

    //Block size for encryption block cipher
    private $ENCRYPT_BLOCK_SIZE = 200;// this for 2048 bit key for example, leaving some room

    //Block size for decryption block cipher
    private $DECRYPT_BLOCK_SIZE = 256;// this again for 2048 bit key

    public function __construct()
    {
       // $this->pubkey = file_get_contents(config('keys.public_key'));
       // $this->privkey = file_get_contents(config('keys.private_key'));
    }

    public function encryptByPublicKey($plaintext)
    {
        $plaintext = gzcompress($plaintext);

// Get the public Key of the recipient
        $publicKey = openssl_pkey_get_public('file://' . config('keys.public_key'));
        $a_key = openssl_pkey_get_details($publicKey);

// Encrypt the data in small chunks and then combine and send it.
        $chunkSize = ceil($a_key['bits'] / 8) - 11;
        $output = '';

        while ($plaintext) {
            $chunk = substr($plaintext, 0, $chunkSize);
            $plaintext = substr($plaintext, $chunkSize);
            $encrypted = '';
            if (!openssl_public_encrypt($chunk, $encrypted, $publicKey)) {
                die('Failed to encrypt data');
            }
            $output .= $encrypted;
        }
        openssl_free_key($publicKey);

// This is the final encrypted data to be sent to the recipient
        return bin2hex($output);
    }

    public function decryptByPrivateKey($encrypted)
    {
        if (!$privateKey = openssl_pkey_get_private('file://' . config('keys.private_key'))) {
            die('Private Key failed');
        }
        $encrypted = hex2bin($encrypted);
        $a_key = openssl_pkey_get_details($privateKey);

// Decrypt the data in the small chunks
        $chunkSize = ceil($a_key['bits'] / 8);
        $output = '';

        while ($encrypted) {
            $chunk = substr($encrypted, 0, $chunkSize);
            $encrypted = substr($encrypted, $chunkSize);
            $decrypted = '';
            if (!openssl_private_decrypt($chunk, $decrypted, $privateKey)) {
                die('Failed to decrypt data');
            }
            $output .= $decrypted;
        }
        openssl_free_key($privateKey);

        // Uncompress the unencrypted data.
        return gzuncompress($output);
    }


    public function decryptByPublicKey($data)
    {
        $decrypted = '';

        //decode must be done before spliting for getting the binary String
        $data = str_split(base64_decode($data), $this->DECRYPT_BLOCK_SIZE);

        foreach ($data as $chunk) {
            $partial = '';

            //be sure to match padding
            $decryptionOK = openssl_public_decrypt($chunk, $partial, $this->pubkey, OPENSSL_PKCS1_PADDING);

            if ($decryptionOK === false) {
                return false;
            }//here also processed errors in decryption. If too big this will be false
            $decrypted .= $partial;
        }
        return $decrypted;
    }

    public function encryptByPrivateKey($plainData)
    {
        $encrypted = '';
        $plainData = str_split($plainData, $this->ENCRYPT_BLOCK_SIZE);
        foreach ($plainData as $chunk) {
            $partialEncrypted = '';

            //using for example OPENSSL_PKCS1_PADDING as padding
            $encryptionOk = openssl_private_encrypt($chunk, $partialEncrypted, $this->privkey, OPENSSL_PKCS1_PADDING);

            if ($encryptionOk === false) {
                return false;
            }//also you can return and error. If too big this will be false
            $encrypted .= $partialEncrypted;
        }
        return base64_encode($encrypted);//encoding the whole binary String as MIME base 64
    }


    public function generateKeys()
    {
        if(!is_dir(storage_path("app/public/keys"))){
            mkdir(storage_path("app/public/keys"));
        }

        $config = array(
            "config"=>storage_path("app/openssl.cnf"),
            "private_key_bits" => 2048,
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        );
        $privateKey = openssl_pkey_new($config);

        // Save the private key to private.key file. Never share this file with anyone.
        //openssl_pkey_export_to_file($privateKey, storage_path("app/public/keys/oauth-private1.key"));
        // Get private key
        openssl_pkey_export($privateKey, $privKey,NULL,$config);
        file_put_contents(storage_path("app/public/keys/oauth-private.key"), $privKey);

        // Generate the public key for the private key
        $a_key = openssl_pkey_get_details($privateKey);
        // Save the public key in public.key file. Send this file to anyone who want to send you the encrypted data.
        file_put_contents(storage_path("app/public/keys/oauth-public.key"), $a_key['key']);
        //uploadFile(config('keys.public_key'),'oauth-public.key', $a_key['key']);
        // Free the private Key.
        //dd(openssl_free_key($privateKey),$privateKey);
    }
}
