/*
 * demo_c99.c — comprehensive C99 demo for connecting to a running n_signer
 * via Qubes qrexec and performing all three core operations:
 *
 *   1. get_public_key  — retrieve a Nostr public key by nostr_index
 *   2. sign_event      — sign a Nostr event (kind 1 text note)
 *   3. nip44_encrypt   — encrypt a message to a peer (and decrypt it back)
 *
 * Note: mine_event (NIP-13 PoW) is also available via the JSON-RPC interface.
 * See demo_javascript.js and demo_python.py for mine_event usage examples.
 * The high-level nostr_signer API does not yet wrap mine_event.
 *
 * This uses the high-level nostr_signer API from nostr_core_lib:
 *   - nostr_signer_nsigner_qrexec()     — qrexec transport (no network)
 *   - nostr_signer_nsigner_set_nostr_index() — select key by NIP-06 index
 *   - nostr_signer_get_public_key()     — get pubkey
 *   - nostr_signer_sign_event()         — sign an event
 *   - nostr_signer_nip44_encrypt()      — encrypt
 *   - nostr_signer_nip44_decrypt()      — decrypt
 *
 * Prerequisites:
 *   - n_signer running in the target qube with --bridge-source-trusted
 *   - qubes.NsignerRpc service installed in the target qube
 *   - dom0 qrexec policy allowing this qube to call the service
 *
 * Build (from n_signer repo root):
 *   make examples
 *
 * Usage:
 *   ./build/demo_c99 <target_qube> [nostr_index]
 *   ./build/demo_c99 nostr_signer 1
 *
 * If no nostr_index is given, defaults to 0.
 */

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

#include "nostr_common.h"
#include "nostr_signer.h"
#include "nip019.h"
#include "../cjson/cJSON.h"

/* Helper: convert 32-byte hex pubkey to bech32 npub */
static int hex_to_npub(const char *hex, char *out_npub, size_t out_sz) {
    unsigned char bytes[32];
    int i;

    if (strlen(hex) != 64) {
        return -1;
    }

    for (i = 0; i < 32; i++) {
        unsigned int byte;
        if (sscanf(hex + 2 * i, "%2x", &byte) != 1) {
            return -1;
        }
        bytes[i] = (unsigned char)byte;
    }

    return nostr_key_to_bech32(bytes, "npub", out_npub);
}

/* Helper: print an error with a human-readable description */
static void print_error(const char *operation, int rc) {
    const char *desc = "unknown error";
    switch (rc) {
        case NOSTR_ERROR_INVALID_INPUT:
            desc = "invalid input";
            break;
        case NOSTR_ERROR_CRYPTO_FAILED:
            desc = "crypto operation failed";
            break;
        case NOSTR_ERROR_IO_FAILED:
            desc = "I/O failed (transport error)";
            break;
        case NOSTR_ERROR_NETWORK_FAILED:
            desc = "network failed";
            break;
        case NOSTR_ERROR_NSIGNER_POLICY_DENIED:
            desc = "policy denied (caller not approved at signer terminal)";
            break;
        case NOSTR_ERROR_NSIGNER_INDEX_NOT_ALLOWED:
            desc = "index not in signer's whitelist";
            break;
        default:
            /* Try to print the numeric code */
            fprintf(stderr, "  %s failed: error code %d\n", operation, rc);
            return;
    }
    fprintf(stderr, "  %s failed: %s (code %d)\n", operation, desc, rc);
}

/*
 * Demo 1: Get a public key by nostr_index.
 * Returns the hex pubkey in `out_hex` (must be 65 bytes).
 */
static int demo_get_public_key(nostr_signer_t *signer, int nostr_index,
                                char *out_hex, size_t hex_sz) {
    char npub[128];
    int rc;

    printf("\n=== Demo 1: get_public_key (nostr_index=%d) ===\n", nostr_index);

    rc = nostr_signer_get_public_key(signer, out_hex);
    if (rc != NOSTR_SUCCESS) {
        print_error("get_public_key", rc);
        return rc;
    }

    if (hex_to_npub(out_hex, npub, sizeof(npub)) == 0) {
        printf("  pubkey hex: %s\n", out_hex);
        printf("  npub:       %s\n", npub);
    } else {
        printf("  pubkey hex: %s\n", out_hex);
        printf("  (npub conversion failed)\n");
    }

    return NOSTR_SUCCESS;
}

/*
 * Demo 2: Sign a Nostr event (kind 1 text note).
 * The signed event JSON is printed.
 */
static int demo_sign_event(nostr_signer_t *signer, const char *pubkey_hex) {
    cJSON *unsigned_event = NULL;
    cJSON *signed_event = NULL;
    char *signed_json = NULL;
    int rc;

    printf("\n=== Demo 2: sign_event (kind 1 text note) ===\n");

    /* Build an unsigned Nostr event (kind 1 text note) */
    unsigned_event = cJSON_CreateObject();
    if (unsigned_event == NULL) {
        fprintf(stderr, "  failed to create event JSON\n");
        return NOSTR_ERROR_MEMORY_FAILED;
    }

    cJSON_AddNumberToObject(unsigned_event, "kind", 1);
    cJSON_AddStringToObject(unsigned_event, "content", "Hello from n_signer C99 demo!");
    cJSON_AddNumberToObject(unsigned_event, "created_at", (int)time(NULL));

    /* tags: empty array */
    cJSON_AddItemToObject(unsigned_event, "tags", cJSON_CreateArray());

    /* pubkey: the signer will fill this in, but we include it for completeness */
    cJSON_AddStringToObject(unsigned_event, "pubkey", pubkey_hex);

    printf("  Unsigned event:\n");
    {
        char *tmp = cJSON_PrintUnformatted(unsigned_event);
        if (tmp) {
            printf("    %s\n", tmp);
            free(tmp);
        }
    }

    /* Sign it */
    rc = nostr_signer_sign_event(signer, unsigned_event, &signed_event);
    if (rc != NOSTR_SUCCESS) {
        print_error("sign_event", rc);
        cJSON_Delete(unsigned_event);
        return rc;
    }

    /* Print the signed event */
    signed_json = cJSON_Print(signed_event);
    if (signed_json) {
        printf("  Signed event:\n");
        printf("    %s\n", signed_json);
        free(signed_json);
    }

    /* Extract and show the signature and event id */
    {
        cJSON *id = cJSON_GetObjectItemCaseSensitive(signed_event, "id");
        cJSON *sig = cJSON_GetObjectItemCaseSensitive(signed_event, "sig");
        if (id && cJSON_IsString(id)) {
            printf("  event id: %s\n", id->valuestring);
        }
        if (sig && cJSON_IsString(sig)) {
            printf("  signature: %s\n", sig->valuestring);
        }
    }

    cJSON_Delete(signed_event);
    cJSON_Delete(unsigned_event);
    return NOSTR_SUCCESS;
}

/*
 * Demo 3: NIP-44 encrypt and decrypt.
 * Encrypts a message to ourselves (using our own pubkey as the peer),
 * then decrypts it to verify round-trip.
 */
static int demo_nip44(nostr_signer_t *signer, const char *pubkey_hex) {
    const char *plaintext = "Secret message from n_signer C99 demo!";
    char *ciphertext = NULL;
    char *decrypted = NULL;
    int rc;

    printf("\n=== Demo 3: nip44_encrypt / nip44_decrypt ===\n");
    printf("  plaintext:  \"%s\"\n", plaintext);
    printf("  peer pubkey: %s (self)\n", pubkey_hex);

    /* Encrypt */
    rc = nostr_signer_nip44_encrypt(signer, pubkey_hex, plaintext, &ciphertext);
    if (rc != NOSTR_SUCCESS) {
        print_error("nip44_encrypt", rc);
        return rc;
    }

    printf("  ciphertext: %s\n", ciphertext);

    /* Decrypt (using our own pubkey as the sender) */
    rc = nostr_signer_nip44_decrypt(signer, pubkey_hex, ciphertext, &decrypted);
    if (rc != NOSTR_SUCCESS) {
        print_error("nip44_decrypt", rc);
        free(ciphertext);
        return rc;
    }

    printf("  decrypted:  \"%s\"\n", decrypted);

    /* Verify round-trip */
    if (strcmp(plaintext, decrypted) == 0) {
        printf("  ✓ Round-trip verified: plaintext matches decrypted\n");
    } else {
        printf("  ✗ Round-trip FAILED: plaintext does not match decrypted\n");
        rc = NOSTR_ERROR_CRYPTO_FAILED;
    }

    free(ciphertext);
    free(decrypted);
    return rc;
}

int main(int argc, char **argv) {
    const char *target_qube;
    const char *service_name = "qubes.NsignerRpc";
    int nostr_index = 0;
    nostr_signer_t *signer = NULL;
    char pubkey_hex[65];
    int rc;

    /* Ignore SIGPIPE — qrexec subprocess may close pipes abruptly */
    (void)signal(SIGPIPE, SIG_IGN);

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <target_qube> [nostr_index]\n", argv[0]);
        fprintf(stderr, "Example: %s nostr_signer 1\n", argv[0]);
        return 1;
    }
    target_qube = argv[1];
    if (argc > 2) {
        nostr_index = atoi(argv[2]);
    }

    /* Initialize the crypto subsystem */
    if (nostr_init() != NOSTR_SUCCESS) {
        fprintf(stderr, "Failed to initialize crypto subsystem\n");
        return 1;
    }

    printf("=== n_signer C99 Demo ===\n");
    printf("Target qube: %s\n", target_qube);
    printf("Service:     %s\n", service_name);
    printf("nostr_index: %d\n", nostr_index);
    printf("\n");

    /* Create a high-level signer backed by qrexec transport */
    printf("Connecting to n_signer via qrexec...\n");
    signer = nostr_signer_nsigner_qrexec(target_qube, service_name, NULL, 30000);
    if (signer == NULL) {
        fprintf(stderr, "Failed to create qrexec signer.\n");
        fprintf(stderr, "Is qrexec-client-vm available? Is the service installed?\n");
        nostr_cleanup();
        return 1;
    }
    printf("Connected.\n");

    /* Select key by nostr_index (NIP-06 m/44'/1237'/N'/0/0) */
    rc = nostr_signer_nsigner_set_nostr_index(signer, nostr_index);
    if (rc != NOSTR_SUCCESS) {
        print_error("set_nostr_index", rc);
        nostr_signer_free(signer);
        nostr_cleanup();
        return 1;
    }

    /* Demo 1: Get public key */
    rc = demo_get_public_key(signer, nostr_index, pubkey_hex, sizeof(pubkey_hex));
    if (rc != NOSTR_SUCCESS) {
        goto cleanup;
    }

    /* Demo 2: Sign an event */
    rc = demo_sign_event(signer, pubkey_hex);
    if (rc != NOSTR_SUCCESS) {
        goto cleanup;
    }

    /* Demo 3: NIP-44 encrypt/decrypt */
    rc = demo_nip44(signer, pubkey_hex);

cleanup:
    printf("\n=== Summary ===\n");
    if (rc == NOSTR_SUCCESS) {
        printf("All demos completed successfully.\n");
    } else {
        printf("Demo failed with error code %d.\n", rc);
    }

    nostr_signer_free(signer);
    nostr_cleanup();
    return (rc == NOSTR_SUCCESS) ? 0 : 1;
}
