/*
 * NIP-44 Detailed Debug Test - Print every single intermediate step
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "../nostr_core/nostr_core.h"

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

static void bytes_to_hex(const unsigned char* bytes, size_t len, char* hex) {
    for (size_t i = 0; i < len; i++) {
        sprintf(hex + i * 2, "%02x", bytes[i]);
    }
    hex[len * 2] = '\0';
}

static void print_bytes(const char* label, const unsigned char* bytes, size_t len) {
    char hex[len * 2 + 1];
    bytes_to_hex(bytes, len, hex);
    printf("%s: %s\n", label, hex);
}

// Test NIP-44 padding calculation (replicate from our crypto.c)
static size_t calc_padded_len(size_t unpadded_len) {
    if (unpadded_len <= 32) {
        return 32;
    }
    
    size_t next_power = 1;
    while (next_power < unpadded_len) {
        next_power <<= 1;
    }
    
    size_t chunk = (next_power <= 256) ? 32 : (next_power / 8);
    return chunk * ((unpadded_len - 1) / chunk + 1);
}

// Test NIP-44 padding (replicate from our crypto.c)
static unsigned char* pad_plaintext_debug(const char* plaintext, size_t* padded_len) {
    size_t unpadded_len = strlen(plaintext);
    if (unpadded_len > 65535) {
        return NULL;
    }
    
    printf("🔍 PADDING DEBUG:\n");
    printf("   unpadded_len: %zu\n", unpadded_len);
    
    *padded_len = calc_padded_len(unpadded_len + 2); // +2 for length prefix
    printf("   calculated_padded_len: %zu\n", *padded_len);
    
    unsigned char* padded = malloc(*padded_len);
    if (!padded) return NULL;
    
    // Write length prefix (big-endian u16)
    padded[0] = (unpadded_len >> 8) & 0xFF;
    padded[1] = unpadded_len & 0xFF;
    printf("   length_prefix: %02x%02x (big-endian u16 = %zu)\n", padded[0], padded[1], unpadded_len);
    
    // Copy plaintext (if any)
    if (unpadded_len > 0) {
        memcpy(padded + 2, plaintext, unpadded_len);
    }
    
    // Zero-fill padding
    memset(padded + 2 + unpadded_len, 0, *padded_len - 2 - unpadded_len);
    
    print_bytes("   padded_plaintext", padded, *padded_len);
    
    return padded;
}

int main() {
    printf("🧪 NIP-44 DETAILED DEBUG TEST\n");
    printf("===============================\n\n");
    
    // Initialize the library
    if (nostr_init() != NOSTR_SUCCESS) {
        printf("❌ Failed to initialize NOSTR library\n");
        return 1;
    }
    
    printf("=== TESTING: Single char 'a' ===\n");
    
    // Step 1: Parse private keys
    unsigned char sec1[32], sec2[32];
    hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000001", sec1, 32);
    hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000002", sec2, 32);
    
    print_bytes("sec1", sec1, 32);
    print_bytes("sec2", sec2, 32);
    
    // Step 2: Generate public keys
    unsigned char pub1[32], pub2[32];
    nostr_ec_public_key_from_private_key(sec1, pub1);
    nostr_ec_public_key_from_private_key(sec2, pub2);
    
    print_bytes("pub1", pub1, 32);
    print_bytes("pub2", pub2, 32);
    
    // Step 3: ECDH shared secret
    unsigned char shared_secret[32];
    ecdh_shared_secret(sec1, pub2, shared_secret);
    print_bytes("ecdh_shared_secret", shared_secret, 32);
    
    // Step 4: HKDF Extract (conversation key)
    unsigned char conversation_key[32];
    const char* salt_str = "nip44-v2";
    nostr_hkdf_extract((const unsigned char*)salt_str, strlen(salt_str), shared_secret, 32, conversation_key);
    print_bytes("conversation_key", conversation_key, 32);
    
    // Step 5: Parse nonce
    unsigned char nonce[32];
    hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000001", nonce, 32);
    print_bytes("nonce", nonce, 32);
    
    // Step 6: HKDF Expand (message keys)
    unsigned char message_keys[76];
    nostr_hkdf_expand(conversation_key, 32, nonce, 32, message_keys, 76);
    
    print_bytes("chacha_key", message_keys, 32);
    print_bytes("chacha_nonce", message_keys + 32, 12);
    print_bytes("hmac_key", message_keys + 44, 32);
    
    // Step 7: Pad plaintext
    const char* plaintext = "a";
    printf("\n🔍 PLAINTEXT: \"%s\" (length: %zu)\n", plaintext, strlen(plaintext));
    
    size_t padded_len;
    unsigned char* padded_plaintext = pad_plaintext_debug(plaintext, &padded_len);
    if (!padded_plaintext) {
        printf("❌ Failed to pad plaintext\n");
        return 1;
    }
    
    // Step 8: ChaCha20 encrypt
    printf("\n🔍 CHACHA20 ENCRYPTION:\n");
    unsigned char* ciphertext = malloc(padded_len);
    if (!ciphertext) {
        printf("❌ Failed to allocate ciphertext buffer\n");
        free(padded_plaintext);
        return 1;
    }
    
    // Use our ChaCha20 function
    if (chacha20_encrypt(message_keys, 0, message_keys + 32, padded_plaintext, ciphertext, padded_len) != 0) {
        printf("❌ ChaCha20 encryption failed\n");
        free(padded_plaintext);
        free(ciphertext);
        return 1;
    }
    
    print_bytes("   ciphertext", ciphertext, padded_len);
    
    // Step 9: HMAC with AAD
    printf("\n🔍 HMAC CALCULATION:\n");
    unsigned char* aad_data = malloc(32 + padded_len);
    if (!aad_data) {
        printf("❌ Failed to allocate AAD buffer\n");
        free(padded_plaintext);
        free(ciphertext);
        return 1;
    }
    
    memcpy(aad_data, nonce, 32);
    memcpy(aad_data + 32, ciphertext, padded_len);
    print_bytes("   aad_data", aad_data, 32 + padded_len);
    
    unsigned char mac[32];
    nostr_hmac_sha256(message_keys + 44, 32, aad_data, 32 + padded_len, mac);
    print_bytes("   mac", mac, 32);
    
    // Step 10: Construct final payload
    printf("\n🔍 PAYLOAD CONSTRUCTION:\n");
    size_t payload_len = 1 + 32 + padded_len + 32;
    unsigned char* payload = malloc(payload_len);
    if (!payload) {
        printf("❌ Failed to allocate payload buffer\n");
        free(padded_plaintext);
        free(ciphertext);
        free(aad_data);
        return 1;
    }
    
    payload[0] = 0x02; // NIP-44 version 2
    memcpy(payload + 1, nonce, 32);
    memcpy(payload + 33, ciphertext, padded_len);
    memcpy(payload + 33 + padded_len, mac, 32);
    
    printf("   version: 0x%02x\n", payload[0]);
    print_bytes("   payload_nonce", payload + 1, 32);
    print_bytes("   payload_ciphertext", payload + 33, padded_len);
    print_bytes("   payload_mac", payload + 33 + padded_len, 32);
    print_bytes("   raw_payload", payload, payload_len);
    
    // Step 11: Base64 encode
    printf("\n🔍 BASE64 ENCODING:\n");
    size_t b64_len = ((payload_len + 2) / 3) * 4 + 1;
    char* base64_output = malloc(b64_len);
    if (!base64_output) {
        printf("❌ Failed to allocate base64 buffer\n");
        free(padded_plaintext);
        free(ciphertext);
        free(aad_data);
        free(payload);
        return 1;
    }
    
    // Use our internal base64_encode function from crypto.c
    extern size_t base64_encode(const unsigned char* data, size_t len, char* output, size_t output_size);
    size_t actual_b64_len = base64_encode(payload, payload_len, base64_output, b64_len);
    
    printf("   payload_length: %zu\n", payload_len);
    printf("   base64_length: %zu\n", actual_b64_len);
    printf("   our_base64: %s\n", base64_output);
    printf("   expected:   AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb\n");
    
    if (strcmp(base64_output, "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb") == 0) {
        printf("✅ PERFECT MATCH!\n");
    } else {
        printf("❌ MISMATCH - need to investigate!\n");
        
        // Let's also try our full encrypt function for comparison
        printf("\n🔍 FULL ENCRYPT FUNCTION TEST:\n");
        char full_encrypt_output[8192];
        int result = nostr_nip44_encrypt_with_nonce(sec1, pub2, plaintext, nonce, full_encrypt_output, sizeof(full_encrypt_output));
        if (result == NOSTR_SUCCESS) {
            printf("   full_encrypt: %s\n", full_encrypt_output);
        } else {
            printf("   full_encrypt failed with error: %d\n", result);
        }
    }
    
    // Cleanup
    free(padded_plaintext);
    free(ciphertext);
    free(aad_data);
    free(payload);
    free(base64_output);
    
    nostr_cleanup();
    printf("\n🏁 Detailed debug test completed\n");
    return 0;
}
