#define _POSIX_C_SOURCE 200809L

#include "tools_internal.h"

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

#include "cjson/cJSON.h"
#include "../nostr_handler.h"
#include "../../nostr_core_lib/nostr_core/nostr_core.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;
}

static int is_hex_string_len_local(const char* s, size_t expected_len) {
    if (!s) return 0;
    if (strlen(s) != expected_len) return 0;
    for (size_t i = 0; i < expected_len; i++) {
        if (!((s[i] >= '0' && s[i] <= '9') ||
              (s[i] >= 'a' && s[i] <= 'f') ||
              (s[i] >= 'A' && s[i] <= 'F'))) {
            return 0;
        }
    }
    return 1;
}

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

    cJSON* recipient = cJSON_GetObjectItemCaseSensitive(args, "recipient_pubkey");
    cJSON* message = cJSON_GetObjectItemCaseSensitive(args, "message");
    if (!recipient || !cJSON_IsString(recipient) || !recipient->valuestring || !is_hex_string_len_local(recipient->valuestring, 64U) ||
        !message || !cJSON_IsString(message) || !message->valuestring || message->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("nostr_dm_send requires recipient_pubkey hex and non-empty message");
    }

    int rc = nostr_handler_send_dm(recipient->valuestring, message->valuestring);
    cJSON* out = cJSON_CreateObject();
    if (!out) {
        cJSON_Delete(args);
        return NULL;
    }
    cJSON_AddBoolToObject(out, "success", rc == 0 ? 1 : 0);
    cJSON_AddStringToObject(out, "recipient_pubkey", recipient->valuestring);
    cJSON_AddNumberToObject(out, "message_length", (double)strlen(message->valuestring));
    cJSON_Delete(args);
    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

char* execute_nostr_encrypt(tools_context_t* ctx, const char* args_json) {
    if (!ctx || !ctx->cfg) return json_error_local("tool context unavailable");

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

    cJSON* recipient = cJSON_GetObjectItemCaseSensitive(args, "recipient_pubkey");
    cJSON* plaintext = cJSON_GetObjectItemCaseSensitive(args, "plaintext");
    if (!recipient || !cJSON_IsString(recipient) || !recipient->valuestring || !is_hex_string_len_local(recipient->valuestring, 64U) ||
        !plaintext || !cJSON_IsString(plaintext) || !plaintext->valuestring || plaintext->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("nostr_encrypt requires recipient_pubkey hex and plaintext");
    }

    unsigned char recipient_pubkey[32];
    if (nostr_hex_to_bytes(recipient->valuestring, recipient_pubkey, sizeof(recipient_pubkey)) != 0) {
        cJSON_Delete(args);
        return json_error_local("nostr_encrypt invalid recipient_pubkey");
    }

    size_t out_cap = (strlen(plaintext->valuestring) * 4U) + 1024U;
    char* ciphertext = (char*)malloc(out_cap);
    if (!ciphertext) {
        cJSON_Delete(args);
        return json_error_local("allocation failure");
    }

    int rc = nostr_nip44_encrypt(ctx->cfg->keys.private_key,
                                 recipient_pubkey,
                                 plaintext->valuestring,
                                 ciphertext,
                                 out_cap);
    if (rc != NOSTR_SUCCESS) {
        free(ciphertext);
        cJSON_Delete(args);
        return json_error_local("nostr_encrypt failed");
    }

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        free(ciphertext);
        cJSON_Delete(args);
        return NULL;
    }
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "recipient_pubkey", recipient->valuestring);
    cJSON_AddStringToObject(out, "ciphertext", ciphertext);

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

char* execute_nostr_decrypt(tools_context_t* ctx, const char* args_json) {
    if (!ctx || !ctx->cfg) return json_error_local("tool context unavailable");

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

    cJSON* sender = cJSON_GetObjectItemCaseSensitive(args, "sender_pubkey");
    cJSON* ciphertext = cJSON_GetObjectItemCaseSensitive(args, "ciphertext");
    if (!sender || !cJSON_IsString(sender) || !sender->valuestring || !is_hex_string_len_local(sender->valuestring, 64U) ||
        !ciphertext || !cJSON_IsString(ciphertext) || !ciphertext->valuestring || ciphertext->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("nostr_decrypt requires sender_pubkey hex and ciphertext");
    }

    unsigned char sender_pubkey[32];
    if (nostr_hex_to_bytes(sender->valuestring, sender_pubkey, sizeof(sender_pubkey)) != 0) {
        cJSON_Delete(args);
        return json_error_local("nostr_decrypt invalid sender_pubkey");
    }

    size_t out_cap = strlen(ciphertext->valuestring) + 1024U;
    char* plaintext = (char*)malloc(out_cap);
    if (!plaintext) {
        cJSON_Delete(args);
        return json_error_local("allocation failure");
    }

    int rc = nostr_nip44_decrypt(ctx->cfg->keys.private_key,
                                 sender_pubkey,
                                 ciphertext->valuestring,
                                 plaintext,
                                 out_cap);
    if (rc != NOSTR_SUCCESS) {
        free(plaintext);
        cJSON_Delete(args);
        return json_error_local("nostr_decrypt failed");
    }

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        free(plaintext);
        cJSON_Delete(args);
        return NULL;
    }
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "sender_pubkey", sender->valuestring);
    cJSON_AddStringToObject(out, "plaintext", plaintext);

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

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

    cJSON* recipient = cJSON_GetObjectItemCaseSensitive(args, "recipient_pubkey");
    cJSON* message = cJSON_GetObjectItemCaseSensitive(args, "message");
    cJSON* subject = cJSON_GetObjectItemCaseSensitive(args, "subject");

    if (!recipient || !cJSON_IsString(recipient) || !recipient->valuestring || !is_hex_string_len_local(recipient->valuestring, 64U) ||
        !message || !cJSON_IsString(message) || !message->valuestring || message->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("nostr_dm_send_nip17 requires recipient_pubkey hex and non-empty message");
    }
    if (subject && !cJSON_IsString(subject)) {
        cJSON_Delete(args);
        return json_error_local("nostr_dm_send_nip17 subject must be a string when provided");
    }

    int rc = nostr_handler_send_dm_nip17(recipient->valuestring,
                                         message->valuestring,
                                         (subject && subject->valuestring) ? subject->valuestring : NULL);

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        cJSON_Delete(args);
        return NULL;
    }
    cJSON_AddBoolToObject(out, "success", rc == 0 ? 1 : 0);
    cJSON_AddStringToObject(out, "recipient_pubkey", recipient->valuestring);
    cJSON_AddStringToObject(out, "protocol", "nip17");
    cJSON_AddNumberToObject(out, "message_length", (double)strlen(message->valuestring));

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