/*
 * pq_sign_example.c — connect to a running n_signer over its abstract UNIX
 * socket and demonstrate post-quantum signing with ML-DSA-65.
 *
 * The example:
 *   1. Sends a get_public_key request for an ML-DSA-65 role ("pq_sig").
 *   2. Prints the structured public key (algorithm, public_key, key_id).
 *   3. Sends a sign_data request with a test message.
 *   4. Prints the signature (hex) and algorithm.
 *
 * Prerequisites:
 *   - n_signer must be running with a role configured for purpose=pq-sig,
 *     curve=ml-dsa-65, named "pq_sig" (or pass the role name as the 2nd arg).
 *   - A mnemonic must be loaded in the signer.
 *
 * Usage: ./pq_sign_example [socket_name] [role_name]
 *
 * Default socket_name: nsigner
 * Default role_name:   pq_sig
 */

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

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

static int get_structured_pubkey(nsigner_client_t *client, const char *role,
                                  cJSON **out_obj) {
    cJSON *params = NULL;
    cJSON *opts = NULL;
    cJSON *result = NULL;
    cJSON *parsed = NULL;
    int rc = -1;

    *out_obj = NULL;

    params = cJSON_CreateArray();
    if (params == NULL) return -1;

    opts = cJSON_CreateObject();
    if (opts == NULL) {
        cJSON_Delete(params);
        return -1;
    }
    cJSON_AddStringToObject(opts, "role", role);
    cJSON_AddItemToArray(params, opts);
    opts = NULL;

    if (nsigner_client_call(client, "get_public_key", params, &result) != NOSTR_SUCCESS) {
        cJSON_Delete(params);
        return -1;
    }
    params = NULL;

    /* result is a cJSON string containing the serialized structured object. */
    if (cJSON_IsString(result)) {
        parsed = cJSON_Parse(result->valuestring);
        if (parsed != NULL && cJSON_IsObject(parsed)) {
            *out_obj = parsed;
            parsed = NULL;
            rc = 0;
        }
    }

    cJSON_Delete(parsed);
    cJSON_Delete(result);
    cJSON_Delete(params);
    return rc;
}

static char *sign_data(nsigner_client_t *client, const char *role,
                        const char *msg_hex) {
    cJSON *params = NULL;
    cJSON *opts = NULL;
    cJSON *result = NULL;
    char *sig_hex = NULL;

    params = cJSON_CreateArray();
    if (params == NULL) return NULL;

    cJSON_AddItemToArray(params, cJSON_CreateString(msg_hex));

    opts = cJSON_CreateObject();
    if (opts == NULL) {
        cJSON_Delete(params);
        return NULL;
    }
    cJSON_AddStringToObject(opts, "role", role);
    cJSON_AddItemToArray(params, opts);
    opts = NULL;

    if (nsigner_client_call(client, "sign_data", params, &result) != NOSTR_SUCCESS) {
        cJSON_Delete(params);
        return NULL;
    }
    params = NULL;

    /* result is a string containing {"signature":"<hex>","algorithm":"<alg>"} */
    if (cJSON_IsString(result)) {
        cJSON *parsed = cJSON_Parse(result->valuestring);
        if (parsed != NULL) {
            cJSON *sig_item = cJSON_GetObjectItemCaseSensitive(parsed, "signature");
            if (cJSON_IsString(sig_item)) {
                sig_hex = strdup(sig_item->valuestring);
            }
            cJSON_Delete(parsed);
        }
    }

    cJSON_Delete(result);
    cJSON_Delete(params);
    return sig_hex;
}

int main(int argc, char **argv) {
    const char *socket_name = "nsigner";
    const char *role = "pq_sig";
    /* "hello post-quantum world" in hex */
    const char *msg_hex = "68656c6c6f20706f73742d7175616e74756d20776f726c64";
    nsigner_transport_t *transport = NULL;
    nsigner_client_t *client = NULL;
    cJSON *pubkey_obj = NULL;
    char *sig_hex = NULL;
    int rc = 1;

    if (argc > 1 && argv[1] != NULL && argv[1][0] != '\0') {
        socket_name = argv[1];
    }
    if (argc > 2 && argv[2] != NULL && argv[2][0] != '\0') {
        role = argv[2];
    }

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

    transport = nsigner_transport_open_unix(socket_name, 10000);
    if (transport == NULL) {
        fprintf(stderr, "connect failed: cannot open unix transport @%s\n", socket_name);
        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;

    printf("=== PQ Sign Example (ML-DSA-65) ===\n");
    printf("socket: %s\n", socket_name);
    printf("role:   %s\n", role);
    printf("\n");

    /* 1. Get the structured public key. */
    if (get_structured_pubkey(client, role, &pubkey_obj) != 0 || pubkey_obj == NULL) {
        fprintf(stderr, "get_public_key failed: %s\n",
                nsigner_client_last_error(client));
        goto cleanup;
    }

    {
        cJSON *alg_item = cJSON_GetObjectItemCaseSensitive(pubkey_obj, "algorithm");
        cJSON *pk_item = cJSON_GetObjectItemCaseSensitive(pubkey_obj, "public_key");
        cJSON *kid_item = cJSON_GetObjectItemCaseSensitive(pubkey_obj, "key_id");

        printf("Public Key:\n");
        printf("  algorithm: %s\n",
               (cJSON_IsString(alg_item)) ? alg_item->valuestring : "?");
        printf("  key_id:    %s\n",
               (cJSON_IsString(kid_item)) ? kid_item->valuestring : "?");
        if (cJSON_IsString(pk_item)) {
            /* ML-DSA-65 public key is 3904 hex chars — print length + prefix. */
            printf("  pub_len:   %zu hex chars (%zu bytes)\n",
                   strlen(pk_item->valuestring), strlen(pk_item->valuestring) / 2);
            printf("  pub_head:  %.64s...\n", pk_item->valuestring);
        } else {
            printf("  public_key: (missing)\n");
        }
    }
    printf("\n");

    /* 2. Sign a test message. */
    printf("Signing message (hex): %s\n", msg_hex);
    sig_hex = sign_data(client, role, msg_hex);
    if (sig_hex == NULL) {
        fprintf(stderr, "sign_data failed: %s\n",
                nsigner_client_last_error(client));
        goto cleanup;
    }

    printf("Signature:\n");
    printf("  sig_len: %zu hex chars (%zu bytes)\n",
           strlen(sig_hex), strlen(sig_hex) / 2);
    printf("  sig_head: %.64s...\n", sig_hex);
    printf("\n");

    printf("To verify externally, use ML-DSA-65 (FIPS 204) verify with the\n");
    printf("public key above, the message, and this signature.\n");

    rc = 0;

cleanup:
    free(sig_hex);
    cJSON_Delete(pubkey_obj);
    nsigner_client_free(client);
    nostr_cleanup();
    return rc;
}
