#define _POSIX_C_SOURCE 200809L

#include "tools_internal.h"

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

#include <sys/stat.h>
#include <unistd.h>

#include "cjson/cJSON.h"
#include "../../nostr_core_lib/nostr_core/blossom_client.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_safe_relative_path_local(const char* path) {
    if (!path || path[0] == '\0') return 0;
    if (path[0] == '/') return 0;
    if (strstr(path, "..") != NULL) return 0;
    if (strchr(path, '\\') != NULL) return 0;
    return 1;
}

static int build_tool_path_local(tools_context_t* ctx, const char* rel_path, char* out, size_t out_size) {
    if (!ctx || !ctx->cfg || !rel_path || !out || out_size == 0) return -1;
    if (!is_safe_relative_path_local(rel_path)) return -1;
    const char* cwd = ctx->cfg->tools.shell.working_directory[0] != '\0' ? ctx->cfg->tools.shell.working_directory : ".";
    int n = (strcmp(cwd, ".") == 0) ? snprintf(out, out_size, "%s", rel_path)
                                      : snprintf(out, out_size, "%s/%s", cwd, rel_path);
    if (n < 0 || (size_t)n >= out_size) return -1;
    return 0;
}

static int is_https_server_local(const char* server) {
    return server && strncmp(server, "https://", 8) == 0;
}

static int file_exists_local(const char* path) {
    if (!path || path[0] == '\0') return 0;
    return access(path, F_OK) == 0;
}

static int file_size_local(const char* path, size_t* out_size) {
    if (!path || !out_size) return -1;
    struct stat st;
    if (stat(path, &st) != 0) return -1;
    if (st.st_size < 0) return -1;
    *out_size = (size_t)st.st_size;
    return 0;
}

static void descriptor_to_json(cJSON* obj, const blossom_blob_descriptor_t* d) {
    if (!obj || !d) return;
    cJSON_AddStringToObject(obj, "sha256", d->sha256);
    cJSON_AddStringToObject(obj, "url", d->url);
    cJSON_AddNumberToObject(obj, "size", d->size);
    cJSON_AddStringToObject(obj, "content_type", d->content_type);
    cJSON_AddNumberToObject(obj, "created", d->created);
}

char* execute_blossom_upload(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* server = cJSON_GetObjectItemCaseSensitive(args, "server");
    cJSON* file_path = cJSON_GetObjectItemCaseSensitive(args, "file_path");
    cJSON* content_type = cJSON_GetObjectItemCaseSensitive(args, "content_type");

    if (!server || !cJSON_IsString(server) || !server->valuestring || server->valuestring[0] == '\0' ||
        !file_path || !cJSON_IsString(file_path) || !file_path->valuestring || file_path->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("blossom_upload requires server and file_path");
    }

    if (!is_https_server_local(server->valuestring)) {
        cJSON_Delete(args);
        return json_error_local("blossom_upload requires an https:// server URL");
    }

    char full_path[PATH_MAX];
    if (build_tool_path_local(ctx, file_path->valuestring, full_path, sizeof(full_path)) != 0) {
        cJSON_Delete(args);
        return json_error_local("blossom_upload file_path is not allowed");
    }

    size_t input_size = 0;
    if (file_size_local(full_path, &input_size) != 0) {
        cJSON_Delete(args);
        return json_error_local("blossom_upload could not stat file_path");
    }

    int upload_max = ctx->cfg->tools.blossom_max_upload_bytes > 0 ? ctx->cfg->tools.blossom_max_upload_bytes : (16 * 1024 * 1024);
    if (input_size > (size_t)upload_max) {
        cJSON_Delete(args);
        return json_error_local("blossom_upload file exceeds configured max upload size");
    }

    blossom_blob_descriptor_t d;
    int rc = blossom_upload_file(server->valuestring,
                                 full_path,
                                 (content_type && cJSON_IsString(content_type) && content_type->valuestring) ? content_type->valuestring : NULL,
                                 ctx->cfg->keys.private_key,
                                 30,
                                 &d);
    cJSON_Delete(args);
    if (rc != NOSTR_SUCCESS) return json_error_local("blossom_upload failed");

    cJSON* out = cJSON_CreateObject();
    if (!out) return NULL;
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "server", server->valuestring);
    descriptor_to_json(out, &d);
    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

char* execute_blossom_download(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* server = cJSON_GetObjectItemCaseSensitive(args, "server");
    cJSON* sha = cJSON_GetObjectItemCaseSensitive(args, "sha256");
    cJSON* output_path = cJSON_GetObjectItemCaseSensitive(args, "output_path");

    if (!server || !cJSON_IsString(server) || !server->valuestring || server->valuestring[0] == '\0' ||
        !sha || !cJSON_IsString(sha) || !sha->valuestring ||
        !output_path || !cJSON_IsString(output_path) || !output_path->valuestring) {
        cJSON_Delete(args);
        return json_error_local("blossom_download requires server, sha256, and output_path");
    }

    if (!is_https_server_local(server->valuestring)) {
        cJSON_Delete(args);
        return json_error_local("blossom_download requires an https:// server URL");
    }

    char full_path[PATH_MAX];
    if (build_tool_path_local(ctx, output_path->valuestring, full_path, sizeof(full_path)) != 0) {
        cJSON_Delete(args);
        return json_error_local("blossom_download output_path is not allowed");
    }

    cJSON* overwrite = cJSON_GetObjectItemCaseSensitive(args, "overwrite");
    int allow_overwrite = (overwrite && cJSON_IsBool(overwrite) && cJSON_IsTrue(overwrite)) ? 1 : 0;
    if (!allow_overwrite && file_exists_local(full_path)) {
        cJSON_Delete(args);
        return json_error_local("blossom_download output_path exists (set overwrite=true to replace)");
    }

    int download_max = ctx->cfg->tools.blossom_max_download_bytes > 0 ? ctx->cfg->tools.blossom_max_download_bytes : (16 * 1024 * 1024);
    blossom_blob_descriptor_t d;
    int rc = blossom_download_to_file(server->valuestring, sha->valuestring, full_path, 30, (size_t)download_max, &d);
    cJSON_Delete(args);
    if (rc != NOSTR_SUCCESS) return json_error_local("blossom_download failed");

    cJSON* out = cJSON_CreateObject();
    if (!out) return NULL;
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "output_path", full_path);
    descriptor_to_json(out, &d);
    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

char* execute_blossom_head(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* server = cJSON_GetObjectItemCaseSensitive(args, "server");
    cJSON* sha = cJSON_GetObjectItemCaseSensitive(args, "sha256");
    if (!server || !cJSON_IsString(server) || !server->valuestring || !sha || !cJSON_IsString(sha) || !sha->valuestring) {
        cJSON_Delete(args);
        return json_error_local("blossom_head requires server and sha256");
    }

    if (!is_https_server_local(server->valuestring)) {
        cJSON_Delete(args);
        return json_error_local("blossom_head requires an https:// server URL");
    }

    blossom_blob_descriptor_t d;
    int rc = blossom_head(server->valuestring, sha->valuestring, 15, &d);
    cJSON_Delete(args);
    if (rc != NOSTR_SUCCESS) return json_error_local("blossom_head failed");

    cJSON* out = cJSON_CreateObject();
    if (!out) return NULL;
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddBoolToObject(out, "exists", 1);
    descriptor_to_json(out, &d);
    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

char* execute_blossom_delete(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* server = cJSON_GetObjectItemCaseSensitive(args, "server");
    cJSON* sha = cJSON_GetObjectItemCaseSensitive(args, "sha256");
    if (!server || !cJSON_IsString(server) || !server->valuestring || !sha || !cJSON_IsString(sha) || !sha->valuestring) {
        cJSON_Delete(args);
        return json_error_local("blossom_delete requires server and sha256");
    }

    if (!is_https_server_local(server->valuestring)) {
        cJSON_Delete(args);
        return json_error_local("blossom_delete requires an https:// server URL");
    }

    int rc = blossom_delete(server->valuestring, sha->valuestring, ctx->cfg->keys.private_key, 15);
    cJSON_Delete(args);
    if (rc != NOSTR_SUCCESS) return json_error_local("blossom_delete failed");

    cJSON* out = cJSON_CreateObject();
    if (!out) return NULL;
    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddBoolToObject(out, "deleted", 1);
    cJSON_AddStringToObject(out, "sha256", sha->valuestring);
    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}

char* execute_blossom_list(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* server = cJSON_GetObjectItemCaseSensitive(args, "server");
    cJSON* pubkey = cJSON_GetObjectItemCaseSensitive(args, "pubkey");
    if (!server || !cJSON_IsString(server) || !server->valuestring) {
        cJSON_Delete(args);
        return json_error_local("blossom_list requires server");
    }

    if (!is_https_server_local(server->valuestring)) {
        cJSON_Delete(args);
        return json_error_local("blossom_list requires an https:// server URL");
    }

    const char* pk = (pubkey && cJSON_IsString(pubkey) && pubkey->valuestring && pubkey->valuestring[0] != '\0')
                         ? pubkey->valuestring
                         : ctx->cfg->keys.public_key_hex;

    blossom_blob_descriptor_t* items = NULL;
    int count = 0;
    int rc = blossom_list(server->valuestring, pk, 20, &items, &count);
    cJSON_Delete(args);
    if (rc != NOSTR_SUCCESS) return json_error_local("blossom_list failed");

    cJSON* out = cJSON_CreateObject();
    cJSON* arr = cJSON_CreateArray();
    if (!out || !arr) {
        free(items);
        cJSON_Delete(out);
        cJSON_Delete(arr);
        return NULL;
    }

    cJSON_AddBoolToObject(out, "success", 1);
    cJSON_AddStringToObject(out, "pubkey", pk);
    cJSON_AddNumberToObject(out, "count", count);

    for (int i = 0; i < count; i++) {
        cJSON* it = cJSON_CreateObject();
        if (!it) continue;
        descriptor_to_json(it, &items[i]);
        cJSON_AddItemToArray(arr, it);
    }
    free(items);

    cJSON_AddItemToObject(out, "blobs", arr);
    char* json = cJSON_PrintUnformatted(out);
    cJSON_Delete(out);
    return json;
}
