/*
 * httpclient_web.cpp — emscripten_fetch backend.
 *
 * emscripten_fetch is async and fires onsuccess/onerror on the main thread.
 * We attach a small heap-owned context to userData; the trampolines invoke
 * the user callback and free the context.
 *
 * http_pump() is a no-op because the runtime drives completion delivery.
 */
#ifdef __EMSCRIPTEN__

#include "httpclient.h"

#include <emscripten/fetch.h>
#include <stdlib.h>
#include <string.h>

namespace {

struct ctx_t {
    http_callback_t cb;
    void *user;
};

void deliver(emscripten_fetch_t *fetch, int status)
{
    ctx_t *c = static_cast<ctx_t *>(fetch->userData);
    http_response_t resp;
    resp.status = status;
    resp.body   = fetch->data ? fetch->data : "";
    resp.len    = (size_t)fetch->numBytes;
    c->cb(&resp, c->user);
    emscripten_fetch_close(fetch);
    delete c;
}

void on_success(emscripten_fetch_t *fetch)
{
    deliver(fetch, (int)fetch->status);
}

void on_error(emscripten_fetch_t *fetch)
{
    // Distinguish: a non-2xx with body (e.g. 500 + JSON) reports its status;
    // a true transport failure (no connection, CORS reject) reports 0.
    int status = (fetch->status >= 100 && fetch->status < 600)
                    ? (int)fetch->status : 0;
    deliver(fetch, status);
}

void issue(const char *method, const char *url, const char *body,
           http_callback_t cb, void *user)
{
    emscripten_fetch_attr_t attr;
    emscripten_fetch_attr_init(&attr);
    strncpy(attr.requestMethod, method, sizeof(attr.requestMethod) - 1);
    attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
    attr.onsuccess  = on_success;
    attr.onerror    = on_error;

    static const char *headers[] = {
        "Content-Type", "application/json", nullptr
    };
    if(body)
    {
        attr.requestHeaders = headers;
        attr.requestData    = body;
        attr.requestDataSize = strlen(body);
    }

    auto *c = new ctx_t{cb, user};
    attr.userData = c;
    emscripten_fetch(&attr, url);
}

} // namespace

extern "C" void http_init(void)     {}
extern "C" void http_shutdown(void) {}
extern "C" void http_pump(void)     {}

extern "C" void http_post_json(const char *url, const char *body,
                               http_callback_t cb, void *user)
{
    if(!cb) return;
    issue("POST", url, body ? body : "", cb, user);
}

extern "C" void http_get(const char *url, http_callback_t cb, void *user)
{
    if(!cb) return;
    issue("GET", url, nullptr, cb, user);
}

#endif /* __EMSCRIPTEN__ */
