#include "rpc.h"
#include "http.h"

#include <stdio.h>
#include <string.h>

void rpc_init(RpcClient *c, const char *host, int port, const char *user, const char *pass)
{
    memset(c, 0, sizeof(*c));
    snprintf(c->host, sizeof(c->host), "%s", host);
    c->port = port;
    if (user)
        snprintf(c->user, sizeof(c->user), "%s", user);
    if (pass)
        snprintf(c->pass, sizeof(c->pass), "%s", pass);
}

int rpc_call(RpcClient *c, const char *method, cJSON *arguments,
             cJSON **out_args, char *err, size_t errlen)
{
    if (out_args)
        *out_args = NULL;

    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "method", method);
    if (arguments)
        cJSON_AddItemToObject(root, "arguments", arguments);

    char *body = cJSON_PrintUnformatted(root);
    size_t body_len = strlen(body);

    c->last_http_status = 0;
    int rc = -1;
    for (int attempt = 0; attempt < 2; attempt++) {
        HttpResponse resp;
        int hrc = http_post_json(c->host, c->port, "/transmission/rpc",
                                  c->user[0] ? c->user : NULL, c->pass,
                                  c->session_id[0] ? c->session_id : NULL,
                                  body, body_len, &resp, err, errlen);
        if (hrc != 0) {
            rc = -1;
            break;
        }
        c->last_http_status = resp.status;

        if (resp.session_id[0])
            snprintf(c->session_id, sizeof(c->session_id), "%s", resp.session_id);

        if (resp.status == 409 && attempt == 0) {
            /* CSRF handshake: retry once now that we have a session id. */
            http_response_free(&resp);
            continue;
        }

        if (resp.status != 200) {
            snprintf(err, errlen, "HTTP error %d from server", resp.status);
            http_response_free(&resp);
            rc = -1;
            break;
        }

        cJSON *respjson = cJSON_ParseWithLength(resp.body, resp.body_len);
        http_response_free(&resp);
        if (!respjson) {
            snprintf(err, errlen, "could not parse JSON response");
            rc = -1;
            break;
        }

        cJSON *result = cJSON_GetObjectItemCaseSensitive(respjson, "result");
        if (!cJSON_IsString(result) || strcmp(result->valuestring, "success") != 0) {
            snprintf(err, errlen, "RPC error: %s",
                     cJSON_IsString(result) ? result->valuestring : "unknown error");
            cJSON_Delete(respjson);
            rc = -1;
            break;
        }

        if (out_args) {
            cJSON *args = cJSON_DetachItemFromObjectCaseSensitive(respjson, "arguments");
            *out_args = args;
        }
        cJSON_Delete(respjson);
        rc = 0;
        break;
    }

    cJSON_free(body);
    cJSON_Delete(root);
    return rc;
}
