#define _POSIX_C_SOURCE 200809L

#include "tools_internal.h"

#include <stdlib.h>
#include <string.h>

#include "cjson/cJSON.h"
#include "../nostr_block_list.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_block_list(tools_context_t* ctx, const char* args_json) {
    (void)ctx;
    cJSON* args = parse_args_local(args_json);
    if (!args) return json_error_local("invalid arguments JSON");

    cJSON* type = cJSON_GetObjectItemCaseSensitive(args, "type");
    const char* which = (type && cJSON_IsString(type) && type->valuestring) ? type->valuestring : "all";

    char* raw = nostr_block_list_json();
    cJSON_Delete(args);
    if (!raw) return json_error_local("nostr_block_list unavailable");

    cJSON* parsed = cJSON_Parse(raw);
    free(raw);
    if (!parsed || !cJSON_IsObject(parsed)) {
        cJSON_Delete(parsed);
        return json_error_local("nostr_block_list returned invalid JSON");
    }

    if (strcmp(which, "all") != 0 && strcmp(which, "p") != 0 && strcmp(which, "e") != 0 && strcmp(which, "t") != 0) {
        cJSON_Delete(parsed);
        return json_error_local("type must be one of: all, p, e, t");
    }

    if (strcmp(which, "all") == 0) {
        char* out = cJSON_PrintUnformatted(parsed);
        cJSON_Delete(parsed);
        return out;
    }

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

    cJSON* success = cJSON_GetObjectItemCaseSensitive(parsed, "success");
    cJSON_AddBoolToObject(out, "success", success && cJSON_IsBool(success) ? cJSON_IsTrue(success) : 1);

    if (strcmp(which, "p") == 0) {
        cJSON* arr = cJSON_Duplicate(cJSON_GetObjectItemCaseSensitive(parsed, "blocked_pubkeys"), 1);
        if (!arr) arr = cJSON_CreateArray();
        cJSON_AddItemToObject(out, "blocked_pubkeys", arr);
    } else if (strcmp(which, "e") == 0) {
        cJSON* arr = cJSON_Duplicate(cJSON_GetObjectItemCaseSensitive(parsed, "blocked_events"), 1);
        if (!arr) arr = cJSON_CreateArray();
        cJSON_AddItemToObject(out, "blocked_events", arr);
    } else {
        cJSON* arr = cJSON_Duplicate(cJSON_GetObjectItemCaseSensitive(parsed, "blocked_hashtags"), 1);
        if (!arr) arr = cJSON_CreateArray();
        cJSON_AddItemToObject(out, "blocked_hashtags", arr);
    }

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

char* execute_nostr_block_edit(tools_context_t* ctx, const char* args_json) {
    (void)ctx;
    cJSON* args = parse_args_local(args_json);
    if (!args) return json_error_local("invalid arguments JSON");

    cJSON* action = cJSON_GetObjectItemCaseSensitive(args, "action");
    cJSON* items = cJSON_GetObjectItemCaseSensitive(args, "items");
    cJSON* is_public = cJSON_GetObjectItemCaseSensitive(args, "public");

    if (!action || !cJSON_IsString(action) || !action->valuestring ||
        !items || !cJSON_IsArray(items)) {
        cJSON_Delete(args);
        return json_error_local("nostr_block_edit requires action and items");
    }

    int add_mode = strcmp(action->valuestring, "add") == 0;
    int remove_mode = strcmp(action->valuestring, "remove") == 0;
    if (!add_mode && !remove_mode) {
        cJSON_Delete(args);
        return json_error_local("action must be add or remove");
    }

    int public_mode = (is_public && cJSON_IsBool(is_public) && cJSON_IsTrue(is_public)) ? 1 : 0;

    int affected = 0;
    int count = cJSON_GetArraySize(items);
    for (int i = 0; i < count; i++) {
        cJSON* tuple = cJSON_GetArrayItem(items, i);
        if (!tuple || !cJSON_IsArray(tuple) || cJSON_GetArraySize(tuple) < 2) {
            continue;
        }

        cJSON* k = cJSON_GetArrayItem(tuple, 0);
        cJSON* v = cJSON_GetArrayItem(tuple, 1);
        if (!k || !v || !cJSON_IsString(k) || !cJSON_IsString(v) || !k->valuestring || !v->valuestring) {
            continue;
        }

        int rc = add_mode
            ? nostr_block_list_add(k->valuestring, v->valuestring, public_mode)
            : nostr_block_list_remove(k->valuestring, v->valuestring);
        if (rc == 0) {
            affected++;
        }
    }

    cJSON_Delete(args);

    cJSON* out = cJSON_CreateObject();
    if (!out) return NULL;
    cJSON_AddBoolToObject(out, "success", affected > 0 ? 1 : 0);
    cJSON_AddStringToObject(out, "action", add_mode ? "add" : "remove");
    cJSON_AddBoolToObject(out, "public", public_mode ? 1 : 0);
    cJSON_AddNumberToObject(out, "items_affected", affected);

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