#define _POSIX_C_SOURCE 200809L

#include "tools_internal.h"

#include <stdlib.h>

#include "cjson/cJSON.h"
#include "../nostr_handler.h"

static char* json_error_local(const char* msg) {
    cJSON* root = cJSON_CreateObject();
    if (!root) return NULL;
    cJSON_AddBoolToObject(root, "success", 0);
    cJSON_AddStringToObject(root, "error", msg ? msg : "unknown error");
    char* out = cJSON_PrintUnformatted(root);
    cJSON_Delete(root);
    return out;
}

static cJSON* parse_args_local(const char* args_json) {
    const char* raw = args_json ? args_json : "{}";
    cJSON* args = cJSON_Parse(raw);
    if (!args || !cJSON_IsObject(args)) {
        cJSON_Delete(args);
        return NULL;
    }
    return args;
}

char* execute_nostr_relay_status(const char* args_json) {
    cJSON* args = parse_args_local(args_json);
    if (!args) return json_error_local("invalid arguments JSON");
    cJSON_Delete(args);

    char* status_json = nostr_handler_relay_status_json();
    if (!status_json) {
        return json_error_local("nostr_relay_status failed");
    }

    cJSON* status = cJSON_Parse(status_json);
    free(status_json);
    if (!status) {
        return json_error_local("nostr_relay_status returned invalid JSON");
    }

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        cJSON_Delete(status);
        return NULL;
    }

    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddItemToObject(out, "status", status);

    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

char* execute_nostr_relay_info(const char* args_json) {
    cJSON* args = parse_args_local(args_json);
    if (!args) return json_error_local("invalid arguments JSON");

    cJSON* relay_url = cJSON_GetObjectItemCaseSensitive(args, "relay_url");
    if (!relay_url || !cJSON_IsString(relay_url) || !relay_url->valuestring || relay_url->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("nostr_relay_info requires relay_url");
    }

    char* info_json = nostr_handler_relay_info_json(relay_url->valuestring);
    cJSON_Delete(args);
    if (!info_json) {
        return json_error_local("nostr_relay_info failed");
    }

    cJSON* info = cJSON_Parse(info_json);
    free(info_json);
    if (!info || !cJSON_IsObject(info)) {
        cJSON_Delete(info);
        return json_error_local("nostr_relay_info returned invalid JSON");
    }

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        cJSON_Delete(info);
        return NULL;
    }
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddItemToObject(out, "info", info);

    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

char* execute_nostr_subscription_status(const char* args_json) {
    cJSON* args = parse_args_local(args_json);
    if (!args) return json_error_local("invalid arguments JSON");
    cJSON_Delete(args);

    char* status_json = nostr_handler_subscription_status_json();
    if (!status_json) {
        return json_error_local("nostr_subscription_status failed");
    }

    cJSON* status = cJSON_Parse(status_json);
    free(status_json);
    if (!status || !cJSON_IsObject(status)) {
        cJSON_Delete(status);
        return json_error_local("nostr_subscription_status returned invalid JSON");
    }

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        cJSON_Delete(status);
        return NULL;
    }

    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddItemToObject(out, "status", status);

    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

char* execute_nostr_subscription_set(const char* args_json) {
    cJSON* args = parse_args_local(args_json);
    if (!args) return json_error_local("invalid arguments JSON");

    cJSON* name = cJSON_GetObjectItemCaseSensitive(args, "name");
    if (!name || !cJSON_IsString(name) || !name->valuestring || name->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("nostr_subscription_set requires non-empty string name");
    }

    cJSON* enabled_item = cJSON_GetObjectItemCaseSensitive(args, "enabled");
    int enabled_set = 0;
    int enabled = 0;
    if (enabled_item) {
        if (!cJSON_IsBool(enabled_item)) {
            cJSON_Delete(args);
            return json_error_local("nostr_subscription_set enabled must be boolean");
        }
        enabled_set = 1;
        enabled = cJSON_IsTrue(enabled_item) ? 1 : 0;
    }

    cJSON* filter_item = cJSON_GetObjectItemCaseSensitive(args, "filter");
    cJSON* filter_dup = NULL;
    if (filter_item) {
        if (!cJSON_IsObject(filter_item)) {
            cJSON_Delete(args);
            return json_error_local("nostr_subscription_set filter must be an object");
        }
        filter_dup = cJSON_Duplicate(filter_item, 1);
        if (!filter_dup) {
            cJSON_Delete(args);
            return json_error_local("nostr_subscription_set filter copy failed");
        }
    }

    int rc = nostr_handler_subscription_set_json(name->valuestring, filter_dup, enabled, enabled_set);
    cJSON_Delete(filter_dup);

    char* status_json = nostr_handler_subscription_status_json();
    cJSON_Delete(args);

    if (rc != 0) {
        free(status_json);
        return json_error_local("nostr_subscription_set failed");
    }

    cJSON* status = status_json ? cJSON_Parse(status_json) : NULL;
    free(status_json);

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        cJSON_Delete(status);
        return NULL;
    }

    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "name", name->valuestring);
    cJSON_AddBoolToObject(out, "updated", 1);
    if (enabled_set) {
        cJSON_AddBoolToObject(out, "enabled", enabled ? 1 : 0);
    }
    if (status) {
        cJSON_AddItemToObject(out, "status", status);
    }

    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}
