<?php

namespace App\Utils;

use App\Models\Game;

/**
 * Rsa密钥处理类
 *
 * Class Rsa
 * @package App\Utils
 */
class Rsa
{
    private $_config = [
        'public_key'  => '',
        'private_key' => '',
    ];

    // public function __construct($private_key_filepath, $public_key_filepath)
    // {
    //     $this->_config['private_key'] = $this->_getContents($private_key_filepath);
    //     $this->_config['public_key']  = $this->_getContents($public_key_filepath);
    // }

    public function __construct($appId)
    {
        [$this->_config['public_key'], $this->_config['private_key']] = $this->_getKeyData($appId);
    }

    /**
     * 获取密钥文件内容
     *
     * @param $file_path
     * @return false|string|void
     */
    private function _getKeyContents($file_path)
    {

        file_exists($file_path) or die ('密钥或公钥的文件路径错误');
        return file_get_contents($file_path);
    }

    /**
     * 获取密钥数据
     *
     * @param $appId
     * @return array
     */
    private function _getKeyData($appId): array
    {
        $gameObj = Game::select('id', 'app_rsa_pub_key', 'app_rsa_pri_key')
            ->where('id', $appId)
            ->first();

        if (!$gameObj) {
            throw new \RuntimeException('获取游戏信息失败!');
        }

        return [$gameObj->app_rsa_pub_key, $gameObj->app_rsa_pri_key];
    }

    /**
     * 获取私钥
     *
     * @return resource
     */
    private function _getPrivateKey()
    {
        $privateKey = $this->_config['private_key'];
        $resource   = openssl_pkey_get_private($privateKey);

        if (!$resource) {
            throw new \RuntimeException('私钥不可用!');
        }

        return $resource;
    }

    /**
     * 获取公钥
     *
     * @return resource
     */
    private function _getPublicKey()
    {
        $publicKey = $this->_config['public_key'];
        $resource = openssl_pkey_get_public($publicKey);
        if (!$resource) {
            throw new \RuntimeException('公钥不可用!');
        }

        return $resource;
    }

    /**
     * 公钥加密
     *
     * @param string $data
     * @return string
     */
    public function publicEncrypt(string $data = ''): string
    {
        $splitArr  = str_split($data, 117);
        $encrypted = '';

        foreach ($splitArr as $item) {
            $bool = openssl_public_encrypt($item, $encryptedData, $this->_getPublicKey());
            if (!$bool) {
                throw new \RuntimeException('公钥加密失败!');
            }

            //每次加密后的字符长度为128
            $encrypted .= $encryptedData;
        }

        return base64_encode($encrypted);
    }

    /**
     *  私钥加密
     *
     * @param string $data
     * @return string
     */
    public function privateEncrypt(string $data = ''): string
    {
        $splitArr     = str_split($data, 117);
        $encryptedStr = '';

        foreach ($splitArr as $item) {
            $bool = openssl_private_encrypt($item, $encryptedData, $this->_getPrivateKey());
            if (!$bool) {
                throw new \RuntimeException('私钥加密失败!');
            }

            //每次加密后的字符长度为128
            $encryptedStr .= $encryptedData;
        }

        return base64_encode($encryptedStr);
    }

    /**
     * 公钥解密
     *
     * @param string $data
     * @return string
     */
    public function publicDecrypt(string $data = ''): string
    {
        //密钥每次加密后的长度是128,所以base64_decode后分割长度为128
        $split  = str_split(base64_decode($data), 128);
        $crypto = '';

        foreach ($split as $chunk) {
            $bool = openssl_public_decrypt($chunk, $decryptData, $this->_getPublicKey());

            if (!$bool) {
                throw new \RuntimeException('公钥解密失败!');
            }

            $crypto .= $decryptData;
        }

        return $crypto;
    }

    /**
     * 私钥解密
     *
     * @param string $data
     * @return string
     */
    public function privateDecrypt(string $data = ''): string
    {
        //密钥每次加密后的长度是128,所以base64_decode后分割长度为128
        $split  = str_split(base64_decode($data), 128);
        $crypto = '';

        foreach ($split as $chunk) {
            $bool = openssl_private_decrypt($chunk, $decryptData, $this->_getPrivateKey());
            if (!$bool) {
                throw new \RuntimeException('私钥解密失败!');
            }

            $crypto .= $decryptData;
        }

        return $crypto;
    }
}