// Dark Broken Key C++ SDK — wraps the Dark Broken Key Client API v1 (POST /api/v1).
// Requires libcurl. Intentionally minimal: requests/responses are small JSON
// objects, parsed with tiny string helpers (swap in nlohmann/json if you like).
//
// Build: g++ -std=c++17 your_app.cpp -lcurl
// Setup: set BASE_URL, NAME, OWNERID below to the credentials shown in your
// dashboard (Overview -> Credentials).
#include <curl/curl.h>
#include <iostream>
#include <stdexcept>
#include <string>

static const std::string BASE_URL = "https://your-site.pages.dev"; // your Dark Broken Key deployment
static const std::string NAME = "MyApp";                           // your application name
static const std::string OWNERID = "YOUR_OWNER_ID";                // your developer owner ID
static const std::string VERSION = "1.0";                          // your application version

static size_t writeCb(void* ptr, size_t size, size_t nmemb, void* userdata) {
    static_cast<std::string*>(userdata)->append(static_cast<char*>(ptr), size * nmemb);
    return size * nmemb;
}

static std::string jsonEscape(const std::string& s) {
    std::string out;
    for (char c : s) {
        if (c == '"' || c == '\\') out += '\\';
        out += c;
    }
    return out;
}

// Extracts a top-level string value ("key": "value") — enough for this API.
static std::string jsonGetString(const std::string& json, const std::string& key) {
    std::string pat = "\"" + key + "\"";
    size_t p = json.find(pat);
    if (p == std::string::npos) return "";
    p = json.find(':', p + pat.size());
    if (p == std::string::npos) return "";
    p = json.find('"', p);
    if (p == std::string::npos) return "";
    std::string out;
    for (size_t i = p + 1; i < json.size(); i++) {
        if (json[i] == '\\' && i + 1 < json.size()) { out += json[++i]; continue; }
        if (json[i] == '"') break;
        out += json[i];
    }
    return out;
}

class DarkBrokenKey {
public:
    std::string sessionid;

    // fields: extra JSON pairs already formatted, e.g. "\"username\":\"x\""
    std::string call(const std::string& type, const std::string& fields = "") {
        std::string body = "{\"type\":\"" + type + "\",\"name\":\"" + jsonEscape(NAME) +
                           "\",\"ownerid\":\"" + jsonEscape(OWNERID) + "\"";
        if (!fields.empty()) body += "," + fields;
        body += "}";

        CURL* curl = curl_easy_init();
        if (!curl) throw std::runtime_error("curl init failed");
        std::string response;
        struct curl_slist* headers = nullptr;
        headers = curl_slist_append(headers, "Content-Type: application/json");
        curl_easy_setopt(curl, CURLOPT_URL, (BASE_URL + "/api/v1").c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCb);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L);
        CURLcode rc = curl_easy_perform(curl);
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
        if (rc != CURLE_OK) throw std::runtime_error("Network error");

        if (response.find("\"success\":true") == std::string::npos &&
            response.find("\"success\": true") == std::string::npos) {
            throw std::runtime_error(jsonGetString(response, "message"));
        }
        std::string sid = jsonGetString(response, "sessionid");
        if (!sid.empty()) sessionid = sid;
        return response;
    }

    std::string f(const std::string& k, const std::string& v) {
        return "\"" + k + "\":\"" + jsonEscape(v) + "\"";
    }

    // Handshake + version check. Call this before anything else.
    std::string init() { return call("init", f("version", VERSION)); }

    // Create an end-user account by consuming a license key.
    std::string reg(const std::string& u, const std::string& p, const std::string& key, const std::string& hwid = "") {
        return call("register", f("username", u) + "," + f("pass", p) + "," + f("key", key) + "," + f("hwid", hwid));
    }

    // Log in with username + password (HWID lock enforced).
    std::string login(const std::string& u, const std::string& p, const std::string& hwid = "") {
        return call("login", f("username", u) + "," + f("pass", p) + "," + f("hwid", hwid));
    }

    // Key-only login. Unused keys are activated on first use.
    std::string license(const std::string& key, const std::string& hwid = "") {
        return call("license", f("key", key) + "," + f("hwid", hwid));
    }

    // Validate the current session (also re-checks ban/expiry).
    std::string check() { return call("check", f("sessionid", sessionid)); }

    // Fetch a server-side variable by name (requires a valid session).
    std::string var(const std::string& varid) {
        std::string r = call("var", f("sessionid", sessionid) + "," + f("varid", varid));
        return jsonGetString(r, "value");
    }
};

// Example:
// int main() {
//     Dark Broken Key app;
//     app.init();
//     app.login("username", "password", "YOUR-HWID-HERE");
//     std::cout << app.var("welcome") << std::endl;
// }
