#include <openssl/evp.h>
#include <openssl/sm2.h>
#include <openssl/rand.h>
#include <iostream>
#include <string>

EVP_PKEY* generate_sm2_key_pair() {
    EVP_PKEY_CTX *ctx;
    EVP_PKEY *pkey = nullptr;

    // 创建EVP_PKEY_CTX
    ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
    if (!ctx) {
        std::cerr << "Error creating EVP_PKEY_CTX for SM2 key generation" << std::endl;
        return nullptr;
    }

    // 初始化SM2密钥对生成
    if (EVP_PKEY_keygen_init(ctx) <= 0 ||
        EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_sm2) <= 0 ||
        EVP_PKEY_keygen(ctx, &pkey) <= 0) {
        std::cerr << "Error generating SM2 key pair" << std::endl;
        EVP_PKEY_CTX_free(ctx);
        return nullptr;
    }

    // 释放资源
    EVP_PKEY_CTX_free(ctx);
    return pkey;
}

std::string sm2_encrypt(const std::string &plaintext, EVP_PKEY *pubkey) {
      EVP_PKEY_CTX *ctx;
    unsigned char *ciphertext = nullptr;
    size_t ciphertext_len;

    // 创建EVP_PKEY_CTX
    ctx = EVP_PKEY_CTX_new(pubkey, nullptr);
    if (!ctx) {
        std::cerr << "Error creating EVP_PKEY_CTX for encryption" << std::endl;
        return "";
    }

    // 初始化加密操作
    if (EVP_PKEY_encrypt_init(ctx) <= 0) {
        std::cerr << "Error initializing encryption operation" << std::endl;
        EVP_PKEY_CTX_free(ctx);
        return "";
    }

    // 设置加密参数（这里可以设置一些参数，例如填充方式）
    // EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING);

    // 计算加密后的长度
    if (EVP_PKEY_encrypt(ctx, nullptr, &ciphertext_len, reinterpret_cast<const unsigned char*>(plaintext.c_str()), plaintext.length()) <= 0) {
        std::cerr << "Error computing ciphertext length" << std::endl;
        EVP_PKEY_CTX_free(ctx);
        return "";
    }

    // 分配内存并执行加密操作
    ciphertext = new unsigned char[ciphertext_len];
    if (EVP_PKEY_encrypt(ctx, ciphertext, &ciphertext_len, reinterpret_cast<const unsigned char*>(plaintext.c_str()), plaintext.length()) <= 0) {
        std::cerr << "Error encrypting data" << std::endl;
        delete[] ciphertext;
        EVP_PKEY_CTX_free(ctx);
        return "";
    }

    // 释放资源
    EVP_PKEY_CTX_free(ctx);

    // 返回加密后的数据（Base64编码）
    std::string encoded_ciphertext(reinterpret_cast<char*>(ciphertext), ciphertext_len);
    delete[] ciphertext;
    return encoded_ciphertext;
}

std::string sm2_decrypt(const std::string &ciphertext, EVP_PKEY *privkey) {
     EVP_PKEY_CTX *ctx;
    unsigned char *plaintext = nullptr;
    size_t plaintext_len;

    // 创建EVP_PKEY_CTX
    ctx = EVP_PKEY_CTX_new(privkey, nullptr);
    if (!ctx) {
        std::cerr << "Error creating EVP_PKEY_CTX for decryption" << std::endl;
        return "";
    }

    // 初始化解密操作
    if (EVP_PKEY_decrypt_init(ctx) <= 0) {
        std::cerr << "Error initializing decryption operation" << std::endl;
        EVP_PKEY_CTX_free(ctx);
        return "";
    }

    // 计算解密后的长度
    if (EVP_PKEY_decrypt(ctx, nullptr, &plaintext_len, reinterpret_cast<const unsigned char*>(ciphertext.c_str()), ciphertext.length()) <= 0) {
        std::cerr << "Error computing plaintext length" << std::endl;
        EVP_PKEY_CTX_free(ctx);
        return "";
    }

    // 分配内存并执行解密操作
    plaintext = new unsigned char[plaintext_len];
    if (EVP_PKEY_decrypt(ctx, plaintext, &plaintext_len, reinterpret_cast<const unsigned char*>(ciphertext.c_str()), ciphertext.length()) <= 0) {
        std::cerr << "Error decrypting data" << std::endl;
        delete[] plaintext;
        EVP_PKEY_CTX_free(ctx);
        return "";
    }

    // 释放资源
    EVP_PKEY_CTX_free(ctx);

    // 返回解密后的数据
    std::string decrypted_plaintext(reinterpret_cast<char*>(plaintext), plaintext_len);
    delete[] plaintext;
    return decrypted_plaintext;
}

int main() {
    EVP_PKEY *pubkey = generate_sm2_key_pair();
    EVP_PKEY *privkey = pubkey;  // 在实际应用中，公钥和私钥应该分开

    if (!pubkey) {
        std::cerr << "Error generating SM2 key pair" << std::endl;
        return 1;
    }

    std::string plaintext = "Hello, SM2!";
    std::string ciphertext = sm2_encrypt(plaintext, pubkey);
    std::string decrypted_text = sm2_decrypt(ciphertext, privkey);

    std::cout << "Original Text: " << plaintext << std::endl;
    std::cout << "Encrypted Text: " << ciphertext << std::endl;
    std::cout << "Decrypted Text: " << decrypted_text << std::endl;

    EVP_PKEY_free(pubkey);

    return 0;
}
