/*
 * get_pubkey_tcp.c — connect to a running n_signer over TCP and call
 * get_public_key for nostr_index 0 and 1, printing both hex pubkey and
 * bech32 npub for each.
 *
 * This is a cross-qube test client for Qubes OS: the signer runs in the
 * nostr_signer qube listening on tcp:[::]:8080, and this client runs in
 * a different qube connecting to the signer's FIPS address.
 *
 * Usage:
 *   ./get_pubkey_tcp <host> <port>
 *   ./get_pubkey_tcp npub1xxx...fips 8080
 *
 * If no arguments are given, defaults to localhost:8080.
 *
 * Output: for each index, prints:
 *   index 0: hex=<64 hex chars>  npub=npub1...
 */

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

#include "nostr_common.h"
#include "nsigner_transport.h"
#include "nsigner_client.h"
#include "nip019.h"
#include "../cjson/cJSON.h"

/* Demo caller key (32 bytes). Replace with your stable caller key in real use. */
static const unsigned char DEMO_PRIVKEY[32] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
    17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
};

static int hex_to_bytes(const char *hex, unsigned char *out, size_t out_len) {
    size_t len = strlen(hex);
    if (len != out_len * 2) {
        return -1;
    }
    for (size_t i = 0; i < out_len; i++) {
        unsigned int byte;
        if (sscanf(hex + 2 * i, "%2x", &byte) != 1) {
            return -1;
        }
        out[i] = (unsigned char)byte;
    }
    return 0;
}

static int query_pubkey(const char *host, int port, int nostr_index,
                        char *out_hex, size_t hex_size, char *out_npub, size_t npub_size) {
    (void)npub_size; /* npub buffer size is enforced by nostr_key_to_bech32 output */
    nsigner_transport_t *transport = NULL;
    nsigner_client_t *client = NULL;
    cJSON *params = NULL;
    cJSON *opts = NULL;
    cJSON *result = NULL;
    const char *hex_pubkey = NULL;
    unsigned char pubkey_bytes[32];
    int rc = -1;

    transport = nsigner_transport_open_tcp(host, port, 10000);
    if (transport == NULL) {
        fprintf(stderr, "connect failed: cannot open TCP transport to %s:%d\n", host, port);
        goto cleanup;
    }

    client = nsigner_client_new(transport);
    if (client == NULL) {
        fprintf(stderr, "connect failed: cannot create nsigner client\n");
        transport->close(transport);
        goto cleanup;
    }
    transport = NULL; /* owned by client now */

    /* Set auth envelope — required for TCP listeners */
    if (nsigner_client_set_auth(client, DEMO_PRIVKEY, "get_pubkey_tcp") != NOSTR_SUCCESS) {
        fprintf(stderr, "failed to set auth envelope\n");
        goto cleanup;
    }

    /* params: [{"nostr_index": N}] */
    params = cJSON_CreateArray();
    if (params == NULL) {
        fprintf(stderr, "out of memory\n");
        goto cleanup;
    }
    opts = cJSON_CreateObject();
    if (opts == NULL) {
        fprintf(stderr, "out of memory\n");
        goto cleanup;
    }
    cJSON_AddNumberToObject(opts, "nostr_index", nostr_index);
    cJSON_AddItemToArray(params, opts);
    opts = NULL;

    if (nsigner_client_call(client, "get_public_key", params, &result) != NOSTR_SUCCESS) {
        fprintf(stderr, "request failed for index %d: %s\n", nostr_index,
                nsigner_client_last_error(client));
        params = NULL; /* nsigner_client_call took ownership even on failure */
        goto cleanup;
    }
    params = NULL; /* nsigner_client_call took ownership */

    if (!cJSON_IsString(result)) {
        fprintf(stderr, "index %d: unexpected result type\n", nostr_index);
        goto cleanup;
    }

    hex_pubkey = result->valuestring;
    if (strlen(hex_pubkey) != 64) {
        fprintf(stderr, "index %d: unexpected pubkey length: %zu\n", nostr_index, strlen(hex_pubkey));
        goto cleanup;
    }

    strncpy(out_hex, hex_pubkey, hex_size - 1);
    out_hex[hex_size - 1] = '\0';

    /* Convert hex pubkey to npub (bech32) */
    if (hex_to_bytes(hex_pubkey, pubkey_bytes, 32) != 0) {
        fprintf(stderr, "index %d: failed to parse hex pubkey\n", nostr_index);
        goto cleanup;
    }
    if (nostr_key_to_bech32(pubkey_bytes, "npub", out_npub) != NOSTR_SUCCESS) {
        fprintf(stderr, "index %d: failed to convert to npub\n", nostr_index);
        goto cleanup;
    }

    rc = 0;

cleanup:
    cJSON_Delete(opts);
    cJSON_Delete(params);
    cJSON_Delete(result);
    nsigner_client_free(client);
    return rc;
}

int main(int argc, char **argv) {
    const char *host = "127.0.0.1";
    int port = 8080;
    char hex0[65], npub0[128];
    char hex1[65], npub1[128];
    int failures = 0;

    if (argc > 1) {
        host = argv[1];
    }
    if (argc > 2) {
        port = atoi(argv[2]);
        if (port <= 0 || port > 65535) {
            fprintf(stderr, "Invalid port: %s\n", argv[2]);
            return 1;
        }
    }

    if (nostr_init() != NOSTR_SUCCESS) {
        fprintf(stderr, "failed to initialize crypto subsystem\n");
        return 1;
    }

    printf("Connecting to n_signer at %s:%d\n", host, port);
    printf("Querying get_public_key for nostr_index 0 and 1...\n\n");

    /* Query index 0 */
    hex0[0] = '\0';
    npub0[0] = '\0';
    if (query_pubkey(host, port, 0, hex0, sizeof(hex0), npub0, sizeof(npub0)) == 0) {
        printf("index 0: hex=%s  npub=%s\n", hex0, npub0);
    } else {
        printf("index 0: FAILED\n");
        failures++;
    }

    /* Query index 1 */
    hex1[0] = '\0';
    npub1[0] = '\0';
    if (query_pubkey(host, port, 1, hex1, sizeof(hex1), npub1, sizeof(npub1)) == 0) {
        printf("index 1: hex=%s  npub=%s\n", hex1, npub1);
    } else {
        printf("index 1: FAILED\n");
        failures++;
    }

    nostr_cleanup();

    if (failures > 0) {
        printf("\n%d query(s) failed\n", failures);
        return 1;
    }

    printf("\nAll queries succeeded\n");
    return 0;
}
