#define _POSIX_C_SOURCE 200809L

#include "tools_internal.h"

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

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

char* execute_message_current(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_Delete(args);

    cJSON* out = cJSON_CreateObject();
    if (!out) return NULL;
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "content", ctx->template_current_user_message ? ctx->template_current_user_message : "");
    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

char* execute_agent_identity(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_Delete(args);

    char npub[128] = {0};
    if (nostr_key_to_bech32(ctx->cfg->keys.public_key, "npub", npub) != NOSTR_SUCCESS) {
        strcpy(npub, "unknown");
    }

    cJSON* out = cJSON_CreateObject();
    if (!out) return NULL;
    cJSON_AddBoolToObject(out, "success", 1);

    char content[384];
    snprintf(content,
             sizeof(content),
             "## Agent Identity\n\nYour pubkey (hex): %s\nYour npub: %s",
             ctx->cfg->keys.public_key_hex[0] ? ctx->cfg->keys.public_key_hex : "unknown",
             npub);
    cJSON_AddStringToObject(out, "content", content);

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

char* execute_nostr_agent_profile(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_Delete(args);

    char* kind0 = nostr_handler_get_agent_kind0_context();
    const char* profile_json = (kind0 && kind0[0]) ? kind0 : "{}";

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        free(kind0);
        return NULL;
    }

    cJSON_AddBoolToObject(out, "success", 1);

    size_t content_len = strlen("## Agent Kind 0 Profile (source: nostr kind 0)\n\nAgent kind 0 profile content (JSON): ") + strlen(profile_json) + 1U;
    char* content = (char*)malloc(content_len);
    if (!content) {
        cJSON_Delete(out);
        free(kind0);
        return NULL;
    }
    snprintf(content,
             content_len,
             "## Agent Kind 0 Profile (source: nostr kind 0)\n\nAgent kind 0 profile content (JSON): %s",
             profile_json);
    cJSON_AddStringToObject(out, "content", content);
    free(content);

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

char* execute_nostr_agent_contacts(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_Delete(args);

    char* kind3 = nostr_handler_get_agent_kind3_context();
    const char* contacts_json = (kind3 && kind3[0]) ? kind3 : "[]";

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        free(kind3);
        return NULL;
    }

    cJSON_AddBoolToObject(out, "success", 1);

    size_t content_len = strlen("## Agent Kind 3 Contacts (source: nostr kind 3)\n\nAgent contacts (JSON): ") + strlen(contacts_json) + 1U;
    char* content = (char*)malloc(content_len);
    if (!content) {
        cJSON_Delete(out);
        free(kind3);
        return NULL;
    }
    snprintf(content,
             content_len,
             "## Agent Kind 3 Contacts (source: nostr kind 3)\n\nAgent contacts (JSON): %s",
             contacts_json);
    cJSON_AddStringToObject(out, "content", content);
    free(content);

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

char* execute_nostr_agent_relays(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_Delete(args);

    char* kind10002 = nostr_handler_get_agent_kind10002_context();
    const char* relays_json = (kind10002 && kind10002[0]) ? kind10002 : "[]";

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        free(kind10002);
        return NULL;
    }

    cJSON_AddBoolToObject(out, "success", 1);

    size_t content_len = strlen("## Agent Kind 10002 Relays (source: nostr kind 10002)\n\nAgent relay list (JSON): ") + strlen(relays_json) + 1U;
    char* content = (char*)malloc(content_len);
    if (!content) {
        cJSON_Delete(out);
        free(kind10002);
        return NULL;
    }
    snprintf(content,
             content_len,
             "## Agent Kind 10002 Relays (source: nostr kind 10002)\n\nAgent relay list (JSON): %s",
             relays_json);
    cJSON_AddStringToObject(out, "content", content);
    free(content);

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

char* execute_nostr_agent_notes(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_Delete(args);

    char* notes = nostr_handler_get_agent_kind1_notes_context();
    const char* notes_text = (notes && notes[0]) ? notes : "";

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        free(notes);
        return NULL;
    }

    cJSON_AddBoolToObject(out, "success", 1);

    size_t content_len = strlen("## Agent Recent Kind 1 Notes\n\n") + strlen(notes_text) + 1U;
    char* content = (char*)malloc(content_len);
    if (!content) {
        cJSON_Delete(out);
        free(notes);
        return NULL;
    }
    snprintf(content,
             content_len,
             "## Agent Recent Kind 1 Notes\n\n%s",
             notes_text);
    cJSON_AddStringToObject(out, "content", content);
    free(content);

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

char* execute_adopted_skills(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_Delete(args);

    cJSON* filter = cJSON_CreateObject();
    cJSON* kinds = cJSON_CreateArray();
    cJSON* authors = cJSON_CreateArray();
    if (!filter || !kinds || !authors) {
        cJSON_Delete(filter);
        cJSON_Delete(kinds);
        cJSON_Delete(authors);
        return NULL;
    }

    cJSON_AddItemToArray(kinds, cJSON_CreateNumber(10123));
    cJSON_AddItemToObject(filter, "kinds", kinds);
    cJSON_AddItemToArray(authors, cJSON_CreateString(ctx->cfg->keys.public_key_hex));
    cJSON_AddItemToObject(filter, "authors", authors);
    cJSON_AddNumberToObject(filter, "limit", 1);

    char* events_json = nostr_handler_query_json(filter, 3000);
    cJSON_Delete(filter);

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        free(events_json);
        return NULL;
    }
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "content", (events_json && events_json[0]) ? events_json : "[]");

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

char* execute_trigger_event(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");

    const char* trigger_json = (ctx->template_trigger_event_json && ctx->template_trigger_event_json[0] != '\0')
                                   ? ctx->template_trigger_event_json
                                   : NULL;

    if (!trigger_json) {
        cJSON* arg_json = cJSON_GetObjectItemCaseSensitive(args, "trigger_event_json");
        if (arg_json && cJSON_IsString(arg_json) && arg_json->valuestring && arg_json->valuestring[0] != '\0') {
            trigger_json = arg_json->valuestring;
        }
    }

    cJSON_Delete(args);

    if (!trigger_json) {
        trigger_json = "{}";
    }

    cJSON* out = cJSON_CreateObject();
    if (!out) return NULL;
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "content", trigger_json);
    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

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

    cJSON* out = cJSON_CreateObject();
    if (!out) return NULL;

    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "name", DIDACTYL_NAME);
    cJSON_AddStringToObject(out, "description", DIDACTYL_DESCRIPTION);
    cJSON_AddStringToObject(out, "software", DIDACTYL_SOFTWARE);
    cJSON_AddStringToObject(out, "version", DIDACTYL_VERSION);
    cJSON_AddNumberToObject(out, "version_major", DIDACTYL_VERSION_MAJOR);
    cJSON_AddNumberToObject(out, "version_minor", DIDACTYL_VERSION_MINOR);
    cJSON_AddNumberToObject(out, "version_patch", DIDACTYL_VERSION_PATCH);

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