#define _POSIX_C_SOURCE 200809L

#include "tools_internal.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.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 const char* find_d_tag_local(cJSON* tags) {
    if (!tags || !cJSON_IsArray(tags)) {
        return NULL;
    }

    int n = cJSON_GetArraySize(tags);
    for (int i = 0; i < n; i++) {
        cJSON* tag = cJSON_GetArrayItem(tags, i);
        if (!tag || !cJSON_IsArray(tag) || cJSON_GetArraySize(tag) < 2) {
            continue;
        }

        cJSON* key = cJSON_GetArrayItem(tag, 0);
        cJSON* value = cJSON_GetArrayItem(tag, 1);
        if (!key || !value || !cJSON_IsString(key) || !cJSON_IsString(value) ||
            !key->valuestring || !value->valuestring) {
            continue;
        }

        if (strcmp(key->valuestring, "d") == 0) {
            return value->valuestring;
        }
    }

    return NULL;
}

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

    cJSON* args = cJSON_Parse(args_json ? args_json : "{}");
    if (!args || !cJSON_IsObject(args)) {
        cJSON_Delete(args);
        return json_error_local("invalid arguments JSON");
    }

    int limit = 200;
    int timeout_ms = 8000;
    int kind_filter = -1;

    cJSON* limit_item = cJSON_GetObjectItemCaseSensitive(args, "limit");
    if (limit_item) {
        if (!cJSON_IsNumber(limit_item)) {
            cJSON_Delete(args);
            return json_error_local("limit must be an integer");
        }
        limit = (int)limit_item->valuedouble;
        if (limit < 1) limit = 1;
        if (limit > 2000) limit = 2000;
    }

    cJSON* timeout_item = cJSON_GetObjectItemCaseSensitive(args, "timeout_ms");
    if (timeout_item) {
        if (!cJSON_IsNumber(timeout_item)) {
            cJSON_Delete(args);
            return json_error_local("timeout_ms must be an integer");
        }
        timeout_ms = (int)timeout_item->valuedouble;
        if (timeout_ms < 1000) timeout_ms = 1000;
        if (timeout_ms > 60000) timeout_ms = 60000;
    }

    cJSON* kind_item = cJSON_GetObjectItemCaseSensitive(args, "kind");
    if (kind_item) {
        if (!cJSON_IsNumber(kind_item)) {
            cJSON_Delete(args);
            return json_error_local("kind must be an integer when provided");
        }
        kind_filter = (int)kind_item->valuedouble;
        if (kind_filter < 0) {
            cJSON_Delete(args);
            return json_error_local("kind must be >= 0");
        }
    }

    cJSON* filter = cJSON_CreateObject();
    cJSON* authors = cJSON_CreateArray();
    if (!filter || !authors) {
        cJSON_Delete(filter);
        cJSON_Delete(authors);
        cJSON_Delete(args);
        return json_error_local("failed to build filter");
    }

    cJSON_AddItemToArray(authors, cJSON_CreateString(ctx->cfg->keys.public_key_hex));
    cJSON_AddItemToObject(filter, "authors", authors);
    if (kind_filter >= 0) {
        cJSON* kinds = cJSON_CreateArray();
        if (!kinds) {
            cJSON_Delete(filter);
            cJSON_Delete(args);
            return json_error_local("failed to build kind filter");
        }
        cJSON_AddItemToArray(kinds, cJSON_CreateNumber(kind_filter));
        cJSON_AddItemToObject(filter, "kinds", kinds);
    }
    cJSON_AddNumberToObject(filter, "limit", limit);

    char* events_json = nostr_handler_query_json(filter, timeout_ms);
    cJSON_Delete(filter);
    cJSON_Delete(args);
    if (!events_json) {
        return json_error_local("nostr query failed");
    }

    cJSON* events = cJSON_Parse(events_json);
    free(events_json);
    if (!events || !cJSON_IsArray(events)) {
        cJSON_Delete(events);
        return json_error_local("nostr query returned invalid JSON");
    }

    cJSON* summarized = cJSON_CreateArray();
    cJSON* seen_event_ids = cJSON_CreateObject();
    if (!summarized || !seen_event_ids) {
        cJSON_Delete(events);
        cJSON_Delete(summarized);
        cJSON_Delete(seen_event_ids);
        return json_error_local("allocation failure");
    }

    int n = cJSON_GetArraySize(events);
    for (int i = 0; i < n; i++) {
        cJSON* ev = cJSON_GetArrayItem(events, i);
        if (!ev || !cJSON_IsObject(ev)) {
            continue;
        }

        cJSON* kind = cJSON_GetObjectItemCaseSensitive(ev, "kind");
        cJSON* id = cJSON_GetObjectItemCaseSensitive(ev, "id");
        cJSON* created_at = cJSON_GetObjectItemCaseSensitive(ev, "created_at");
        cJSON* tags = cJSON_GetObjectItemCaseSensitive(ev, "tags");

        if (!kind || !cJSON_IsNumber(kind) ||
            !id || !cJSON_IsString(id) || !id->valuestring ||
            !created_at || !cJSON_IsNumber(created_at)) {
            continue;
        }

        cJSON* already_seen = cJSON_GetObjectItemCaseSensitive(seen_event_ids, id->valuestring);
        if (already_seen) {
            continue;
        }
        cJSON_AddBoolToObject(seen_event_ids, id->valuestring, 1);

        cJSON* item = cJSON_CreateObject();
        if (!item) {
            continue;
        }

        cJSON_AddNumberToObject(item, "kind", (int)kind->valuedouble);
        cJSON_AddStringToObject(item, "event_id", id->valuestring);
        {
            char ts_buf[32];
            long long ts = (long long)created_at->valuedouble;
            snprintf(ts_buf, sizeof(ts_buf), "%lld", ts);
            cJSON_AddStringToObject(item, "timestamp", ts_buf);
        }

        const char* d_tag = find_d_tag_local(tags);
        if (d_tag && d_tag[0] != '\0') {
            cJSON_AddStringToObject(item, "d_tag", d_tag);
        } else {
            cJSON_AddNullToObject(item, "d_tag");
        }

        cJSON_AddBoolToObject(item,
                              "in_current_cache",
                              nostr_handler_is_event_in_self_cache(id->valuestring) ? 1 : 0);

        cJSON_AddItemToArray(summarized, item);
    }

    cJSON* out = cJSON_CreateObject();
    if (!out) {
        cJSON_Delete(events);
        cJSON_Delete(summarized);
        cJSON_Delete(seen_event_ids);
        return json_error_local("allocation failure");
    }

    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddNumberToObject(out, "count", cJSON_GetArraySize(summarized));
    cJSON_AddItemToObject(out, "events", summarized);

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