#include "engine_crypto.h"

#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
#include <fstream>
#include <iostream>
#include <cstring>

namespace node669427 {

thread_local std::mt19937 CryptoEngine::rng_{std::random_device{}()};

CryptoEngine::CryptoEngine() = default;
CryptoEngine::~CryptoEngine() {
    std::lock_guard<std::mutex> lock(key_mutex_);
    OPENSSL_cleanse(&hmac_key_[0], hmac_key_.size());
}

bool CryptoEngine::loadKey(const std::string& key_path) {
    std::ifstream f(key_path);
    if (!f.is_open()) {
        std::cerr << "[Crypto] Cannot open key file " << key_path << std::endl;
        return false;
    }
    std::lock_guard<std::mutex> lock(key_mutex_);
    std::getline(f, hmac_key_);
    return !hmac_key_.empty();
}

bool CryptoEngine::loadKeyFromBuffer(const std::string& key_data) {
    std::lock_guard<std::mutex> lock(key_mutex_);
    hmac_key_ = key_data;
    return !hmac_key_.empty();
}

std::string CryptoEngine::hmacSha256(const std::string& data) const {
    std::lock_guard<std::mutex> lock(key_mutex_);
    return hmacSha256(hmac_key_, data);
}

std::string CryptoEngine::hmacSha256(const std::string& key, const std::string& data) {
    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int length = 0;
    HMAC(EVP_sha256(), key.data(), static_cast<int>(key.size()),
         reinterpret_cast<const unsigned char*>(data.data()), data.size(),
         hash, &length);
    std::ostringstream ss;
    for (unsigned int i = 0; i < length; ++i) {
        ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
    }
    return ss.str();
}

std::string CryptoEngine::sha256(const std::string& data) {
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(reinterpret_cast<const unsigned char*>(data.data()), data.size(), hash);
    std::ostringstream ss;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
        ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
    }
    return ss.str();
}

std::string CryptoEngine::generateNonce(size_t len) {
    static const char hex_chars[] = "0123456789abcdef";
    std::uniform_int_distribution<int> dis(0, 15);
    std::ostringstream ss;
    for (size_t i = 0; i < len; ++i) {
        ss << hex_chars[dis(rng_)];
    }
    return ss.str();
}

std::string CryptoEngine::base64Encode(const std::string& data) {
    BIO* bio = BIO_new(BIO_s_mem());
    BIO* b64 = BIO_new(BIO_f_base64());
    bio = BIO_push(b64, bio);
    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    BIO_write(bio, data.data(), static_cast<int>(data.size()));
    BIO_flush(bio);
    BUF_MEM* buffer_ptr;
    BIO_get_mem_ptr(bio, &buffer_ptr);
    std::string result(buffer_ptr->data, buffer_ptr->length);
    BIO_free_all(bio);
    return result;
}

std::string CryptoEngine::base64Decode(const std::string& data) {
    BIO* bio = BIO_new_mem_buf(data.data(), static_cast<int>(data.size()));
    BIO* b64 = BIO_new(BIO_f_base64());
    bio = BIO_push(b64, bio);
    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    std::string result(data.size(), '\0');
    int decoded = BIO_read(bio, &result[0], static_cast<int>(data.size()));
    BIO_free_all(bio);
    if (decoded > 0) {
        result.resize(decoded);
    } else {
        result.clear();
    }
    return result;
}

std::string CryptoEngine::bytesToHex(const std::vector<uint8_t>& bytes) {
    std::ostringstream ss;
    for (auto b : bytes) {
        ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(b);
    }
    return ss.str();
}

std::vector<uint8_t> CryptoEngine::hexToBytes(const std::string& hex) {
    std::vector<uint8_t> bytes;
    for (size_t i = 0; i + 1 < hex.size(); i += 2) {
        std::string byte_str = hex.substr(i, 2);
        bytes.push_back(static_cast<uint8_t>(std::stoi(byte_str, nullptr, 16)));
    }
    return bytes;
}

bool CryptoEngine::verifySignature(const std::string& data, const std::string& signature) const {
    return hmacSha256(data) == signature;
}

std::string CryptoEngine::signRequest(const std::string& timestamp,
                                      const std::string& nonce,
                                      const std::string& method,
                                      const std::string& path,
                                      const std::string& payload) const {
    // CRY-02 canonical: HMAC-SHA256( ts.nonce.METHOD.path.body )
    return hmacSha256(timestamp + "." + nonce + "." + method + "." + path + "." + payload);
}

} // namespace node669427
