#ifndef CPPHTTPLIB_OPENSSL_SUPPORT
#define CPPHTTPLIB_OPENSSL_SUPPORT
#endif

#include "GetbinClient.hpp"
#include "httplib.hpp"
#include <fstream>
#include <sstream>
#include <nlohmann/json.hpp>
#include <string>

using json = nlohmann::json;

static constexpr const char* SERVER_HOST = "tools.dropshell.app";

GetbinClient::GetbinClient() {}

bool GetbinClient::download(const std::string& toolName, const std::string& arch, const std::string& outPath) {
    httplib::SSLClient cli(SERVER_HOST, 443);
    std::string object_path = "/object/" + toolName + ":" + arch;
    auto res = cli.Get(object_path.c_str());
    if (!res || res->status != 200) {
        std::cerr << "[GetbinClient::download] HTTP request failed (no response)." << std::endl;
        return false;
    }
    std::ofstream ofs(outPath, std::ios::binary);
    if (!ofs) return false;
    ofs.write(res->body.data(), res->body.size());
    return ofs.good();
}

bool GetbinClient::upload(const std::string& archivePath, std::string& outUrl, std::string& outHash, const std::string& token) {
    httplib::SSLClient cli(SERVER_HOST,443);
    httplib::MultipartFormDataItems items;
    // Read file
    std::ifstream ifs(archivePath, std::ios::binary);
    if (!ifs) {
        std::cerr << "[GetbinClient::upload] Failed to open archive file: " << archivePath << std::endl;
        return false;
    }
    std::string file_content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
    // Compose metadata (minimal, can be extended)
    json metadata = { {"labeltags", json::array()} };
    // Try to extract tool:arch from filename
    std::string filename = archivePath.substr(archivePath.find_last_of("/\\") + 1);
    size_t dot = filename.find('.');
    std::string labeltag = dot != std::string::npos ? filename.substr(0, dot) : filename;
    metadata["labeltags"].push_back(labeltag);
    items.push_back({"file", file_content, filename, "application/gzip"});
    items.push_back({"metadata", metadata.dump(), "", "application/json"});
    httplib::Headers headers = { {"Authorization", "Bearer " + token} };

    //cli.enable_server_certificate_verification(false); // TEMPORARY
    cli.set_ca_cert_path("/etc/ssl/certs/ca-certificates.crt");
    auto res = cli.Put("/upload", headers, items);
    if (!res) {
        std::cerr << "[GetbinClient::upload] HTTP request failed (no response)." << std::endl;
        return false;
    }
    if (res->status != 200 && res->status != 201) {
        std::cerr << "[GetbinClient::upload] HTTP error: status code " << res->status << std::endl;
        std::cerr << "[GetbinClient::upload] Response body: " << res->body << std::endl;
        return false;
    }
    // Parse response for URL/hash
    try {
        auto resp_json = json::parse(res->body);
        if (resp_json.contains("url")) outUrl = resp_json["url"].get<std::string>();
        if (resp_json.contains("hash")) outHash = resp_json["hash"].get<std::string>();
    } catch (const std::exception& e) {
        std::cerr << "[GetbinClient::upload] Failed to parse JSON response: " << e.what() << std::endl;
        std::cerr << "[GetbinClient::upload] Response body: " << res->body << std::endl;
        return false;
    } catch (...) {
        std::cerr << "[GetbinClient::upload] Unknown error while parsing JSON response." << std::endl;
        std::cerr << "[GetbinClient::upload] Response body: " << res->body << std::endl;
        return false;
    }
    return true;
}

bool GetbinClient::getHash(const std::string& toolName, const std::string& arch, std::string& outHash) {
    httplib::SSLClient cli(SERVER_HOST, 443);
    std::string exists_path = "/exists/" + toolName + ":" + arch;
    auto res = cli.Get(exists_path.c_str());
    if (!res || res->status != 200) return false;
    // Try to parse hash from response body (assume plain text or JSON)
    try {
        // Try JSON
        auto resp_json = json::parse(res->body);
        if (resp_json.contains("hash")) {
            outHash = resp_json["hash"].get<std::string>();
            return true;
        }
    } catch (...) {
        // Not JSON, treat as plain text
        outHash = res->body;
        return !outHash.empty();
    }
    return false;
} 