#define _POSIX_C_SOURCE 200809L

#include "tools_internal.h"

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

#include "cjson/cJSON.h"
#include "../cashu_wallet.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 char* wrap_wallet_json_result(int rc, cJSON* obj, const char* default_error) {
    if (!obj) {
        return json_error_local(default_error ? default_error : "cashu wallet operation failed");
    }

    if (rc != 0) {
        cJSON* success = cJSON_GetObjectItemCaseSensitive(obj, "success");
        if (!success) {
            cJSON_AddBoolToObject(obj, "success", 0);
        }
        if (!cJSON_GetObjectItemCaseSensitive(obj, "error")) {
            cJSON_AddStringToObject(obj,
                                    "error",
                                    default_error ? default_error : "cashu wallet operation failed");
        }
    }

    char* out = cJSON_PrintUnformatted(obj);
    cJSON_Delete(obj);
    if (!out) {
        return json_error_local(default_error ? default_error : "cashu wallet operation failed");
    }
    return out;
}

char* execute_cashu_wallet_balance(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* out = NULL;
    int rc = cashu_wallet_balance(&out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_balance failed");
}

char* execute_cashu_wallet_info(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* mint = cJSON_GetObjectItemCaseSensitive(args, "mint_url");
    const char* mint_url = (mint && cJSON_IsString(mint) && mint->valuestring) ? mint->valuestring : NULL;
    if ((!mint_url || mint_url[0] == '\0') && !cashu_wallet_has_default_mint()) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_info requires mint_url when no cashu_wallet.mint_urls are configured");
    }

    cJSON* out = NULL;
    int rc = cashu_wallet_info(mint_url, &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_info failed (check mint_url and mint connectivity)");
}

char* execute_cashu_wallet_mint_quote(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* amount = cJSON_GetObjectItemCaseSensitive(args, "amount");
    if (!amount || !cJSON_IsNumber(amount) || amount->valuedouble <= 0) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_mint_quote requires numeric amount > 0");
    }

    cJSON* mint = cJSON_GetObjectItemCaseSensitive(args, "mint_url");
    cJSON* unit = cJSON_GetObjectItemCaseSensitive(args, "unit");
    const char* mint_url = (mint && cJSON_IsString(mint) && mint->valuestring) ? mint->valuestring : NULL;
    const char* unit_s = (unit && cJSON_IsString(unit) && unit->valuestring) ? unit->valuestring : NULL;
    if ((!mint_url || mint_url[0] == '\0') && !cashu_wallet_has_default_mint()) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_mint_quote requires mint_url when no cashu_wallet.mint_urls are configured");
    }

    cJSON* out = NULL;
    int rc = cashu_wallet_mint_quote(mint_url, (uint64_t)amount->valuedouble, unit_s, &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_mint_quote failed");
}

char* execute_cashu_wallet_mint_check(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* quote_id = cJSON_GetObjectItemCaseSensitive(args, "quote_id");
    if (!quote_id || !cJSON_IsString(quote_id) || !quote_id->valuestring || quote_id->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_mint_check requires quote_id");
    }

    cJSON* mint = cJSON_GetObjectItemCaseSensitive(args, "mint_url");
    const char* mint_url = (mint && cJSON_IsString(mint) && mint->valuestring) ? mint->valuestring : NULL;
    if ((!mint_url || mint_url[0] == '\0') && !cashu_wallet_has_default_mint()) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_mint_check requires mint_url when no cashu_wallet.mint_urls are configured");
    }

    cJSON* out = NULL;
    int rc = cashu_wallet_mint_check(mint_url, quote_id->valuestring, &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_mint_check failed");
}

char* execute_cashu_wallet_mint_claim(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* quote_id = cJSON_GetObjectItemCaseSensitive(args, "quote_id");
    if (!quote_id || !cJSON_IsString(quote_id) || !quote_id->valuestring || quote_id->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_mint_claim requires quote_id");
    }

    uint64_t amount = 0;
    cJSON* amount_item = cJSON_GetObjectItemCaseSensitive(args, "amount");
    if (amount_item) {
        if (!cJSON_IsNumber(amount_item) || amount_item->valuedouble < 0) {
            cJSON_Delete(args);
            return json_error_local("cashu_wallet_mint_claim amount must be a non-negative number");
        }
        amount = (uint64_t)amount_item->valuedouble;
    }

    cJSON* mint = cJSON_GetObjectItemCaseSensitive(args, "mint_url");
    const char* mint_url = (mint && cJSON_IsString(mint) && mint->valuestring) ? mint->valuestring : NULL;
    if ((!mint_url || mint_url[0] == '\0') && !cashu_wallet_has_default_mint()) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_mint_claim requires mint_url when no cashu_wallet.mint_urls are configured");
    }

    cJSON* out = NULL;
    int rc = cashu_wallet_mint_claim(mint_url, quote_id->valuestring, amount, &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_mint_claim failed");
}

char* execute_cashu_wallet_melt_quote(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* payment_request = cJSON_GetObjectItemCaseSensitive(args, "payment_request");
    if (!payment_request || !cJSON_IsString(payment_request) || !payment_request->valuestring || payment_request->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_melt_quote requires payment_request");
    }

    cJSON* mint = cJSON_GetObjectItemCaseSensitive(args, "mint_url");
    cJSON* unit = cJSON_GetObjectItemCaseSensitive(args, "unit");
    const char* mint_url = (mint && cJSON_IsString(mint) && mint->valuestring) ? mint->valuestring : NULL;
    const char* unit_s = (unit && cJSON_IsString(unit) && unit->valuestring) ? unit->valuestring : NULL;
    if ((!mint_url || mint_url[0] == '\0') && !cashu_wallet_has_default_mint()) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_melt_quote requires mint_url when no cashu_wallet.mint_urls are configured");
    }

    cJSON* out = NULL;
    int rc = cashu_wallet_melt_quote(mint_url, payment_request->valuestring, unit_s, &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_melt_quote failed");
}

char* execute_cashu_wallet_melt_pay(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* quote_id = cJSON_GetObjectItemCaseSensitive(args, "quote_id");
    if (!quote_id || !cJSON_IsString(quote_id) || !quote_id->valuestring || quote_id->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_melt_pay requires quote_id");
    }

    cJSON* mint = cJSON_GetObjectItemCaseSensitive(args, "mint_url");
    const char* mint_url = (mint && cJSON_IsString(mint) && mint->valuestring) ? mint->valuestring : NULL;
    if ((!mint_url || mint_url[0] == '\0') && !cashu_wallet_has_default_mint()) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_melt_pay requires mint_url when no cashu_wallet.mint_urls are configured");
    }

    cJSON* out = NULL;
    int rc = cashu_wallet_melt_pay(mint_url, quote_id->valuestring, &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_melt_pay failed");
}

char* execute_cashu_wallet_check_proofs(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* mint = cJSON_GetObjectItemCaseSensitive(args, "mint_url");
    const char* mint_url = (mint && cJSON_IsString(mint) && mint->valuestring) ? mint->valuestring : NULL;
    if ((!mint_url || mint_url[0] == '\0') && !cashu_wallet_has_default_mint()) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_check_proofs requires mint_url when no cashu_wallet.mint_urls are configured");
    }

    cJSON* out = NULL;
    int rc = cashu_wallet_check_proofs(mint_url, &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_check_proofs failed");
}

char* execute_cashu_wallet_receive_token(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* token = cJSON_GetObjectItemCaseSensitive(args, "token");
    if (!token || !cJSON_IsString(token) || !token->valuestring || token->valuestring[0] == '\0') {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_receive_token requires token");
    }

    cJSON* mint = cJSON_GetObjectItemCaseSensitive(args, "mint_url");
    const char* mint_url = (mint && cJSON_IsString(mint) && mint->valuestring) ? mint->valuestring : NULL;

    cJSON* out = NULL;
    int rc = cashu_wallet_receive_token(token->valuestring, mint_url, &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_receive_token failed");
}

char* execute_cashu_wallet_send_token(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* amount = cJSON_GetObjectItemCaseSensitive(args, "amount");
    if (!amount || !cJSON_IsNumber(amount) || amount->valuedouble <= 0) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_send_token requires numeric amount > 0");
    }

    cJSON* mint = cJSON_GetObjectItemCaseSensitive(args, "mint_url");
    const char* mint_url = (mint && cJSON_IsString(mint) && mint->valuestring) ? mint->valuestring : NULL;
    if ((!mint_url || mint_url[0] == '\0') && !cashu_wallet_has_default_mint()) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_send_token requires mint_url when no cashu_wallet.mint_urls are configured");
    }

    cJSON* format = cJSON_GetObjectItemCaseSensitive(args, "format");
    const char* format_s = (format && cJSON_IsString(format) && format->valuestring) ? format->valuestring : NULL;

    cJSON* out = NULL;
    int rc = cashu_wallet_send_token(mint_url, (uint64_t)amount->valuedouble, format_s, &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_send_token failed");
}

char* execute_cashu_wallet_mints_get(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 = NULL;
    int rc = cashu_wallet_mints_get(&out);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_mints_get failed");
}

char* execute_cashu_wallet_mints_set(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* target = cJSON_GetObjectItemCaseSensitive(args, "target");
    const char* target_s = (target && cJSON_IsString(target) && target->valuestring) ? target->valuestring : "both";

    int set_wallet = 0;
    int set_public = 0;
    if (strcmp(target_s, "wallet") == 0) {
        set_wallet = 1;
    } else if (strcmp(target_s, "public") == 0) {
        set_public = 1;
    } else if (strcmp(target_s, "both") == 0) {
        set_wallet = 1;
        set_public = 1;
    } else {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_mints_set target must be wallet, public, or both");
    }

    cJSON* wallet_arr = cJSON_GetObjectItemCaseSensitive(args, "wallet_mints");
    cJSON* public_arr = cJSON_GetObjectItemCaseSensitive(args, "public_mints");

    const char* wallet_buf[32] = {0};
    int wallet_count = 0;
    if (wallet_arr) {
        if (!cJSON_IsArray(wallet_arr)) {
            cJSON_Delete(args);
            return json_error_local("wallet_mints must be an array of strings");
        }
        int n = cJSON_GetArraySize(wallet_arr);
        for (int i = 0; i < n && wallet_count < 32; i++) {
            cJSON* it = cJSON_GetArrayItem(wallet_arr, i);
            if (it && cJSON_IsString(it) && it->valuestring && it->valuestring[0] != '\0') {
                wallet_buf[wallet_count++] = it->valuestring;
            }
        }
    }

    const char* public_buf[32] = {0};
    int public_count = 0;
    if (public_arr) {
        if (!cJSON_IsArray(public_arr)) {
            cJSON_Delete(args);
            return json_error_local("public_mints must be an array of strings");
        }
        int n = cJSON_GetArraySize(public_arr);
        for (int i = 0; i < n && public_count < 32; i++) {
            cJSON* it = cJSON_GetArrayItem(public_arr, i);
            if (it && cJSON_IsString(it) && it->valuestring && it->valuestring[0] != '\0') {
                public_buf[public_count++] = it->valuestring;
            }
        }
    }

    if (set_wallet && wallet_count <= 0) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_mints_set requires wallet_mints when target includes wallet");
    }
    if (set_public && public_count <= 0) {
        cJSON_Delete(args);
        return json_error_local("cashu_wallet_mints_set requires public_mints when target includes public");
    }

    cJSON* out = NULL;
    int rc = cashu_wallet_mints_set(wallet_buf,
                                         wallet_count,
                                         public_buf,
                                         public_count,
                                         set_wallet,
                                         set_public,
                                         &out);
    cJSON_Delete(args);
    return wrap_wallet_json_result(rc, out, "cashu_wallet_mints_set failed");
}
