/*
 * NIP-44 Encryption/Decryption Test
 * 
 * Test suite for NIP-44 versioned encryption functionality
 * Uses known test vectors and cross-implementation compatibility tests
 */

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

// Test vectors for NIP-44 with proper key pairs
typedef struct {
    const char* name;
    const char* sender_private_key_hex;
    const char* recipient_private_key_hex;  // FIX: Need proper private key, not public key
    const char* plaintext;
    const char* expected_encrypted; // Optional - for known test vectors
} nip44_test_vector_t;

// Additional test vectors for edge cases (converted to round-trip tests with new 32-bit padding)

// Round-trip test vectors with proper key pairs
static nip44_test_vector_t test_vectors[] = {
    {
        "Basic short message",
        "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe",  // Working keys from simple_nip44_test
        "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220",
        "Hello, NIP-44!",
        NULL
    },
    {
        "Unicode message",
        "1111111111111111111111111111111111111111111111111111111111111111",
        "2222222222222222222222222222222222222222222222222222222222222222",
        "Hello 🌍 World! 🚀",
        NULL
    },
    {
        "Empty message",
        "3333333333333333333333333333333333333333333333333333333333333333",
        "4444444444444444444444444444444444444444444444444444444444444444",
        "",
        NULL
    },
    {
        "64KB payload test",
        "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe",  // Same keys as basic test
        "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220",
        NULL,  // Will be generated dynamically
        NULL
    }
};

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 int test_nip44_round_trip(const nip44_test_vector_t* tv) {
    printf("Test: %s\n", tv->name);

    // Parse keys - both private keys
    unsigned char sender_private_key[32];
    unsigned char recipient_private_key[32];

    if (hex_to_bytes(tv->sender_private_key_hex, sender_private_key, 32) != 0) {
        printf("  FAIL: Failed to parse sender private key\n");
        return -1;
    }

    if (hex_to_bytes(tv->recipient_private_key_hex, recipient_private_key, 32) != 0) {
        printf("  FAIL: Failed to parse recipient private key\n");
        return -1;
    }

    // Generate the public keys from the private keys
    unsigned char sender_public_key[32];
    unsigned char recipient_public_key[32];

    if (nostr_ec_public_key_from_private_key(sender_private_key, sender_public_key) != 0) {
        printf("  FAIL: Failed to derive sender public key\n");
        return -1;
    }

    if (nostr_ec_public_key_from_private_key(recipient_private_key, recipient_public_key) != 0) {
        printf("  FAIL: Failed to derive recipient public key\n");
        return -1;
    }

    // Special handling for large payload tests
    char* test_plaintext;
    if (strcmp(tv->name, "64KB payload test") == 0) {
        // Generate exactly 64KB (65,535 bytes) of predictable content - max NIP-44 size
        const size_t payload_size = 65535;
        test_plaintext = malloc(payload_size + 1);
        if (!test_plaintext) {
            printf("  FAIL: Memory allocation failed for 64KB test payload\n");
            return -1;
        }

        // Fill with a predictable pattern: "ABCDEFGH01234567" repeated
        const char* pattern = "ABCDEFGH01234567"; // 16 bytes
        const size_t pattern_len = 16;

        for (size_t i = 0; i < payload_size; i += pattern_len) {
            size_t copy_len = (i + pattern_len <= payload_size) ? pattern_len : payload_size - i;
            memcpy(test_plaintext + i, pattern, copy_len);
        }
        test_plaintext[payload_size] = '\0';

        printf("  Generated 64KB test payload (%zu bytes)\n", payload_size);
        printf("  Pattern: \"%s\" repeated\n", pattern);
        printf("  First 64 chars: \"%.64s...\"\n", test_plaintext);
        printf("  Last 64 chars:  \"...%.64s\"\n", test_plaintext + payload_size - 64);
    } else {
        test_plaintext = (char*)tv->plaintext;
    }

    // Debug: Check plaintext length
    size_t plaintext_len = strlen(test_plaintext);
    printf("  Plaintext length: %zu bytes\n", plaintext_len);
    printf("  Output buffer size: %zu bytes\n", (size_t)10485760);

    // Test encryption - use larger buffer for 1MB+ payloads (10MB for NIP-44 overhead)
    char* encrypted = malloc(10485760); // 10MB buffer for large payloads
    if (!encrypted) {
        printf("  FAIL: Memory allocation failed for encrypted buffer\n");
        if (strcmp(tv->name, "0.5MB payload test") == 0) free(test_plaintext);
        return -1;
    }

    // For large payloads, use _with_nonce to avoid random generation issues
    unsigned char fixed_nonce[32] = {0};
    int encrypt_result = nostr_nip44_encrypt_with_nonce(
        sender_private_key,
        recipient_public_key,
        test_plaintext,
        fixed_nonce,
        encrypted,
        10485760
    );

    if (encrypt_result != NOSTR_SUCCESS) {
        printf("  FAIL: Encryption - Expected: %d, Actual: %d\n", NOSTR_SUCCESS, encrypt_result);
        if (strcmp(tv->name, "1MB payload test") == 0) free(test_plaintext);
        free(encrypted);
        return -1;
    }

    // Test decryption - use recipient private key + sender public key
    char* decrypted = malloc(65536 + 1); // 64KB + 1 for null terminator
    if (!decrypted) {
        printf("  FAIL: Memory allocation failed for decrypted buffer\n");
        if (strcmp(tv->name, "64KB payload test") == 0) free(test_plaintext);
        free(encrypted);
        return -1;
    }
    int decrypt_result = nostr_nip44_decrypt(
        recipient_private_key,
        sender_public_key,
        encrypted,
        decrypted,
        65536 + 1
    );

    if (decrypt_result != NOSTR_SUCCESS) {
        printf("  FAIL: Decryption - Expected: %d, Actual: %d\n", NOSTR_SUCCESS, decrypt_result);
        if (strcmp(tv->name, "1MB payload test") == 0) free(test_plaintext);
        free(encrypted);
        free(decrypted);
        return -1;
    }

    // Verify round-trip
    if (strcmp(test_plaintext, decrypted) != 0) {
        printf("  FAIL: Round-trip mismatch\n");
        printf("  Expected: \"%s\"\n", test_plaintext);
        printf("  Actual:   \"%s\"\n", decrypted);
        if (strcmp(tv->name, "1MB payload test") == 0) free(test_plaintext);
        free(encrypted);
        free(decrypted);
        return -1;
    }

    if (strcmp(tv->name, "64KB payload test") == 0) {
        printf("  ✅ 64KB payload round-trip: PASS\n");
        printf("  ✅ Content verification: All %zu bytes match perfectly!\n", strlen(test_plaintext));
        printf("  Encrypted length: %zu bytes\n", strlen(encrypted));
        printf("  🎉 64KB NIP-44 STRESS TEST COMPLETED SUCCESSFULLY! 🎉\n");
    } else {
        printf("  PASS: Expected: \"%s\", Actual: \"%s\"\n", test_plaintext, decrypted);
        printf("  Encrypted output: %s\n", encrypted);
    }

    if (strcmp(tv->name, "64KB payload test") == 0) free(test_plaintext);
    free(encrypted);
    free(decrypted);

    return 0;
}

static int test_nip44_error_conditions() {
    printf("Test: NIP-44 error conditions\n");
    
    // Use proper valid secp256k1 private keys
    unsigned char valid_sender_key[32];
    unsigned char valid_recipient_key[32];
    unsigned char valid_recipient_pubkey[32];
    
    hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000001", valid_sender_key, 32);
    hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000002", valid_recipient_key, 32);
    
    // Generate the recipient's public key
    if (nostr_ec_public_key_from_private_key(valid_recipient_key, valid_recipient_pubkey) != 0) {
        printf("  FAIL: Failed to generate recipient public key\n");
        return -1;
    }
    
    char output[1024];
    
    // Test NULL parameters
    int result = nostr_nip44_encrypt(NULL, valid_recipient_pubkey, "test", output, sizeof(output));
    if (result != NOSTR_ERROR_INVALID_INPUT) {
        printf("  FAIL: NULL sender key - Expected: %d, Actual: %d\n", NOSTR_ERROR_INVALID_INPUT, result);
        return -1;
    }
    
    result = nostr_nip44_encrypt(valid_sender_key, NULL, "test", output, sizeof(output));
    if (result != NOSTR_ERROR_INVALID_INPUT) {
        printf("  FAIL: NULL recipient key - Expected: %d, Actual: %d\n", NOSTR_ERROR_INVALID_INPUT, result);
        return -1;
    }
    
    result = nostr_nip44_encrypt(valid_sender_key, valid_recipient_pubkey, NULL, output, sizeof(output));
    if (result != NOSTR_ERROR_INVALID_INPUT) {
        printf("  FAIL: NULL plaintext - Expected: %d, Actual: %d\n", NOSTR_ERROR_INVALID_INPUT, result);
        return -1;
    }
    
    result = nostr_nip44_encrypt(valid_sender_key, valid_recipient_pubkey, "test", NULL, sizeof(output));
    if (result != NOSTR_ERROR_INVALID_INPUT) {
        printf("  FAIL: NULL output buffer - Expected: %d, Actual: %d\n", NOSTR_ERROR_INVALID_INPUT, result);
        return -1;
    }
    
    // Test buffer too small
    char small_buffer[10];
    result = nostr_nip44_encrypt(valid_sender_key, valid_recipient_pubkey, "test message", small_buffer, sizeof(small_buffer));
    if (result != NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL) {
        printf("  FAIL: Buffer too small - Expected: %d, Actual: %d\n", NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL, result);
        return -1;
    }
    
    printf("  PASS: All error conditions handled correctly\n");
    return 0;
}


static int test_nip44_encryption_variability() {
    printf("Test: NIP-44 encryption variability (non-deterministic)\n");
    
    const char* test_message = "Test message for variability";
    unsigned char sender_key[32], recipient_key[32];
    
    // Use fixed test keys
    hex_to_bytes("1111111111111111111111111111111111111111111111111111111111111111", sender_key, 32);
    hex_to_bytes("2222222222222222222222222222222222222222222222222222222222222222", recipient_key, 32);
    
    // Generate recipient public key
    unsigned char recipient_pubkey[32];
    if (nostr_ec_public_key_from_private_key(recipient_key, recipient_pubkey) != 0) {
        printf("  FAIL: Failed to generate recipient public key\n");
        return -1;
    }
    
    // Encrypt the same message multiple times
    char* encrypted1 = malloc(2097152); // 2MB buffer
    char* encrypted2 = malloc(2097152);
    char* encrypted3 = malloc(2097152);
    if (!encrypted1 || !encrypted2 || !encrypted3) {
        printf("  FAIL: Memory allocation failed for encrypted buffers\n");
        free(encrypted1);
        free(encrypted2);
        free(encrypted3);
        return -1;
    }
    
    int result1 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted1, 2097152);
    int result2 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted2, 2097152);
    int result3 = nostr_nip44_encrypt(sender_key, recipient_pubkey, test_message, encrypted3, 2097152);
    
    if (result1 != NOSTR_SUCCESS || result2 != NOSTR_SUCCESS || result3 != NOSTR_SUCCESS) {
        printf("  FAIL: Encryption failed - Results: %d, %d, %d\n", result1, result2, result3);
        return -1;
    }
    
    // Verify all ciphertexts are different (non-deterministic)
    if (strcmp(encrypted1, encrypted2) == 0 || strcmp(encrypted1, encrypted3) == 0 || strcmp(encrypted2, encrypted3) == 0) {
        printf("  FAIL: NIP-44 encryption should produce different ciphertext each time\n");
        printf("  Encryption 1: %.50s...\n", encrypted1);
        printf("  Encryption 2: %.50s...\n", encrypted2);
        printf("  Encryption 3: %.50s...\n", encrypted3);
        free(encrypted1);
        free(encrypted2);
        free(encrypted3);
        return -1;
    }
    
    // Verify all decrypt to the same plaintext
    unsigned char sender_pubkey[32];
    if (nostr_ec_public_key_from_private_key(sender_key, sender_pubkey) != 0) {
        printf("  FAIL: Failed to generate sender public key\n");
        return -1;
    }
    
    char* decrypted1 = malloc(1048576 + 1);
    char* decrypted2 = malloc(1048576 + 1);
    char* decrypted3 = malloc(1048576 + 1);
    if (!decrypted1 || !decrypted2 || !decrypted3) {
        printf("  FAIL: Memory allocation failed for decrypted buffers\n");
        free(encrypted1);
        free(encrypted2);
        free(encrypted3);
        free(decrypted1);
        free(decrypted2);
        free(decrypted3);
        return -1;
    }

    int decrypt1 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted1, decrypted1, 1048576 + 1);
    int decrypt2 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted2, decrypted2, 1048576 + 1);
    int decrypt3 = nostr_nip44_decrypt(recipient_key, sender_pubkey, encrypted3, decrypted3, 1048576 + 1);
    
    if (decrypt1 != NOSTR_SUCCESS || decrypt2 != NOSTR_SUCCESS || decrypt3 != NOSTR_SUCCESS) {
        printf("  FAIL: Decryption failed - Results: %d, %d, %d\n", decrypt1, decrypt2, decrypt3);
        return -1;
    }
    
    if (strcmp(decrypted1, test_message) != 0 || strcmp(decrypted2, test_message) != 0 || strcmp(decrypted3, test_message) != 0) {
        printf("  FAIL: Decryption mismatch\n");
        printf("  Expected: \"%s\"\n", test_message);
        printf("  Decrypted1: \"%s\"\n", decrypted1);
        printf("  Decrypted2: \"%s\"\n", decrypted2);
        printf("  Decrypted3: \"%s\"\n", decrypted3);
        free(encrypted1);
        free(encrypted2);
        free(encrypted3);
        free(decrypted1);
        free(decrypted2);
        free(decrypted3);
        return -1;
    }

    printf("  PASS: All encryptions different, all decrypt to: \"%s\"\n", test_message);
    printf("  Sample ciphertext lengths: %zu, %zu, %zu bytes\n", strlen(encrypted1), strlen(encrypted2), strlen(encrypted3));

    free(encrypted1);
    free(encrypted2);
    free(encrypted3);
    free(decrypted1);
    free(decrypted2);
    free(decrypted3);

    return 0;
}


int main() {
    printf("NIP-44 Encryption Test Suite\n");
    printf("=============================\n");
    
    // Initialize the library
    if (nostr_init() != NOSTR_SUCCESS) {
        printf("FAIL: Failed to initialize NOSTR library\n");
        return 1;
    }
    
    int total_tests = 0;
    int passed_tests = 0;
    
    // Test all vectors
    size_t num_vectors = sizeof(test_vectors) / sizeof(test_vectors[0]);
    for (size_t i = 0; i < num_vectors; i++) {
        total_tests++;
        printf("Test #%d\n", total_tests);
        if (test_nip44_round_trip(&test_vectors[i]) == 0) {
            passed_tests++;
        }
        printf("\n");
    }
    
    // Additional edge case tests (converted to round-trip tests with new 32-bit padding)
    // These test the same plaintexts as the old decryption vectors but with our new format
    static nip44_test_vector_t edge_case_test_vectors[] = {
        {
            "Edge case: single char 'a'",
            "0000000000000000000000000000000000000000000000000000000000000001",  // sec1
            "0000000000000000000000000000000000000000000000000000000000000002",  // sec2
            "a",
            NULL
        },
        {
            "Edge case: emoji",
            "0000000000000000000000000000000000000000000000000000000000000002",  // sec1
            "0000000000000000000000000000000000000000000000000000000000000001",  // sec2
            "🍕🫃",
            NULL
        },
        {
            "Edge case: wide unicode",
            "5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a",  // sec1
            "4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d",  // sec2
            "表ポあA鷗ŒéＢ逍Üßªąñ丂㐀𠀀",
            NULL
        }
    };

    size_t num_edge_case_vectors = sizeof(edge_case_test_vectors) / sizeof(edge_case_test_vectors[0]);
    for (size_t i = 0; i < num_edge_case_vectors; i++) {
        total_tests++;
        printf("Test #%d\n", total_tests);
        if (test_nip44_round_trip(&edge_case_test_vectors[i]) == 0) {
            passed_tests++;
        }
        printf("\n");
    }
    
    // Test encryption variability (NIP-44 non-deterministic behavior)
    total_tests++;
    printf("Test #%d\n", total_tests);
    if (test_nip44_encryption_variability() == 0) {
        passed_tests++;
    }
    printf("\n");
    
    // Test error conditions
    total_tests++;
    printf("Test #%d\n", total_tests);
    if (test_nip44_error_conditions() == 0) {
        passed_tests++;
    }
    printf("\n");
    
    // Final results
    printf("\nTest Results: %d/%d passed\n", passed_tests, total_tests);
    
    if (passed_tests == total_tests) {
        printf("All NIP-44 tests PASSED\n");
        nostr_cleanup();
        return 0;
    } else {
        printf("Some tests FAILED\n");
        nostr_cleanup();
        return 1;
    }
}
