/*
 * RC4/authcode compatible SDK for networkValide.
 *
 * Build example:
 *   g++ cpp-rc4-sdk.cpp -lssl -lcrypto
 */

#include <openssl/md5.h>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>

static std::string md5Hex(const std::string& input) {
    unsigned char digest[MD5_DIGEST_LENGTH];
    MD5(reinterpret_cast<const unsigned char*>(input.data()), input.size(), digest);
    std::ostringstream ss;
    for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
        ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(digest[i]);
    }
    return ss.str();
}

static const char* B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static std::string base64Encode(const std::string& in) {
    std::string out;
    int val = 0, valb = -6;
    for (unsigned char c : in) {
        val = (val << 8) + c;
        valb += 8;
        while (valb >= 0) {
            out.push_back(B64[(val >> valb) & 0x3f]);
            valb -= 6;
        }
    }
    if (valb > -6) out.push_back(B64[((val << 8) >> (valb + 8)) & 0x3f]);
    while (out.size() % 4) out.push_back('=');
    return out;
}

static std::string base64Decode(std::string in) {
    std::vector<int> t(256, -1);
    for (int i = 0; i < 64; i++) t[B64[i]] = i;
    std::string out;
    int val = 0, valb = -8;
    for (unsigned char c : in) {
        if (t[c] == -1) break;
        val = (val << 6) + t[c];
        valb += 6;
        if (valb >= 0) {
            out.push_back(char((val >> valb) & 0xff));
            valb -= 8;
        }
    }
    return out;
}

static std::string rc4(const std::string& data, const std::string& key) {
    int box[256];
    for (int i = 0; i < 256; ++i) box[i] = i;
    int j = 0;
    for (int i = 0; i < 256; ++i) {
        j = (j + box[i] + static_cast<unsigned char>(key[i % key.size()])) & 255;
        std::swap(box[i], box[j]);
    }
    std::string out(data.size(), '\0');
    int a = 0; j = 0;
    for (size_t i = 0; i < data.size(); ++i) {
        a = (a + 1) & 255;
        j = (j + box[a]) & 255;
        std::swap(box[a], box[j]);
        out[i] = data[i] ^ box[(box[a] + box[j]) & 255];
    }
    return out;
}

std::string nvRc4Encode(const std::string& rawData, const std::string& rc4Key) {
    std::string key = md5Hex(rc4Key);
    std::string keya = md5Hex(key.substr(0, 16));
    std::string keyb = md5Hex(key.substr(16, 16));
    std::string keyc = md5Hex(std::to_string(std::time(nullptr))).substr(28, 4);
    std::string cryptkey = keya + md5Hex(keya + keyc);
    std::string payload = "0000000000" + md5Hex(rawData + keyb).substr(0, 16) + rawData;
    std::string encoded = base64Encode(rc4(payload, cryptkey));
    encoded.erase(std::remove(encoded.begin(), encoded.end(), '='), encoded.end());
    return keyc + encoded;
}

std::string nvRc4Decode(const std::string& msg, const std::string& rc4Key) {
    std::string key = md5Hex(rc4Key);
    std::string keya = md5Hex(key.substr(0, 16));
    std::string keyb = md5Hex(key.substr(16, 16));
    std::string keyc = msg.substr(0, 4);
    std::string body = msg.substr(4);
    while (body.size() % 4) body.push_back('=');
    std::string result = rc4(base64Decode(body), keya + md5Hex(keya + keyc));
    if (result.size() < 26) return "";
    std::string content = result.substr(26);
    return result.substr(10, 16) == md5Hex(content + keyb).substr(0, 16) ? content : "";
}

std::string nvMakeSign(const std::string& data, const std::string& appKey) {
    return md5Hex(data + appKey);
}
