/*
 * NIP-01 Event Validation Test Suite
 * Tests event structure validation and cryptographic verification
 * Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events
 */

#define _GNU_SOURCE  // For strdup on Linux
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../nostr_core/nip001.h"
#include "../nostr_core/nostr_common.h"
#include "../nostr_core/utils.h"
#include "../cjson/cJSON.h"

// Ensure strdup is declared
#ifndef strdup
extern char *strdup(const char *s);
#endif

// Test counter for tracking progress
static int test_count = 0;
static int passed_tests = 0;

void print_test_header(const char* test_name) {
    test_count++;
    printf("\n=== TEST %d: %s ===\n", test_count, test_name);
}

void print_test_result(int passed, const char* test_name) {
    if (passed) {
        passed_tests++;
        printf("✅ PASS: %s\n", test_name);
    } else {
        printf("❌ FAIL: %s\n", test_name);
    }
}

void print_json_comparison(const char* label, cJSON* expected, cJSON* actual) {
    char* expected_str = cJSON_Print(expected);
    char* actual_str;
    
    if (actual) {
        actual_str = cJSON_Print(actual);
    } else {
        actual_str = strdup("NULL");
    }
    
    printf("%s Expected JSON:\n%s\n", label, expected_str ? expected_str : "NULL");
    printf("%s Actual JSON:\n%s\n", label, actual_str ? actual_str : "NULL");
    
    if (expected_str) free(expected_str);
    if (actual_str) free(actual_str);
}

// Test vector 1: Valid event from nak (should pass all validation)
int test_valid_event_1(void) {
    print_test_header("Valid Event from nak - Basic");
    
    // Generated with: nak event --sec nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99 -c "Test event 1" -k 1
    const char* event_json = "{"
        "\"kind\":1,"
        "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
        "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
        "\"created_at\":1755599338,"
        "\"tags\":[],"
        "\"content\":\"Test event 1\","
        "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
    "}";
    
    printf("Input Event JSON:\n%s\n\n", event_json);
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    printf("Testing Structure Validation...\n");
    int structure_result = nostr_validate_event_structure(event);
    printf("Expected: NOSTR_SUCCESS (0)\n");
    printf("Actual: %d (%s)\n", structure_result, nostr_strerror(structure_result));
    
    if (structure_result != NOSTR_SUCCESS) {
        printf("❌ Structure validation failed\n");
        cJSON_Delete(event);
        return 0;
    }
    printf("✅ Structure validation passed\n\n");
    
    printf("Testing Cryptographic Verification...\n");
    int crypto_result = nostr_verify_event_signature(event);
    printf("Expected: NOSTR_SUCCESS (0)\n");
    printf("Actual: %d (%s)\n", crypto_result, nostr_strerror(crypto_result));
    
    if (crypto_result != NOSTR_SUCCESS) {
        printf("❌ Cryptographic verification failed\n");
        cJSON_Delete(event);
        return 0;
    }
    printf("✅ Cryptographic verification passed\n\n");
    
    printf("Testing Complete Validation...\n");
    int complete_result = nostr_validate_event(event);
    printf("Expected: NOSTR_SUCCESS (0)\n");
    printf("Actual: %d (%s)\n", complete_result, nostr_strerror(complete_result));
    
    cJSON_Delete(event);
    return (complete_result == NOSTR_SUCCESS);
}

// Test vector 2: Valid event with tags (should pass all validation)
int test_valid_event_with_tags(void) {
    print_test_header("Valid Event with Tags");
    
    // Generated with: nak event --sec nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99 -c "Test event with tags" -k 1 -t "hello=world" -t "test=value"
    const char* event_json = "{"
        "\"kind\":1,"
        "\"id\":\"781bf3185479315350c4039f719c3a8859a1ebb21a4b04e0e649addf0ae4665b\","
        "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
        "\"created_at\":1755599345,"
        "\"tags\":[[\"hello\",\"world\"],[\"test\",\"value\"]],"
        "\"content\":\"Test event with tags\","
        "\"sig\":\"3a390ece9d746bd576cb8fbba6f9ed59730099781d0d414d1194f832ca072f23e7764935451a025399202e02f28a59fde2d4c25399e241f3de2ad0b3a4830d6a\""
    "}";
    
    printf("Input Event JSON:\n%s\n\n", event_json);
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    int result = nostr_validate_event(event);
    printf("Expected: NOSTR_SUCCESS (0)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    
    cJSON_Delete(event);
    return (result == NOSTR_SUCCESS);
}

// Test vector 3: Valid metadata event (kind 0)
int test_valid_metadata_event(void) {
    print_test_header("Valid Metadata Event (Kind 0)");
    
    // Generated with: nak event --sec nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99 -c "" -k 0 --ts 1640995200
    const char* event_json = "{"
        "\"kind\":0,"
        "\"id\":\"a2ed5175224f6597bc3a553e91d6fcea2e13f199e56afc90ccecca1c890b308a\","
        "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
        "\"created_at\":1640995200,"
        "\"tags\":[],"
        "\"content\":\"\","
        "\"sig\":\"b9b2859ded73bbc010331145e2d297b30205d2b94d80ef0619077b687d53756b8d9eb3954ef1483b814059cd879c992153ca9d7b50dcf7053a3d530cd82fb884\""
    "}";
    
    printf("Input Event JSON:\n%s\n\n", event_json);
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    int result = nostr_validate_event(event);
    printf("Expected: NOSTR_SUCCESS (0)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    
    cJSON_Delete(event);
    return (result == NOSTR_SUCCESS);
}

// Test 4: Missing required field (id)
int test_missing_id_field(void) {
    print_test_header("Missing Required Field - ID");
    
    const char* event_json = "{"
        "\"kind\":1,"
        "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
        "\"created_at\":1755599338,"
        "\"tags\":[],"
        "\"content\":\"Test event 1\","
        "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
    "}";
    
    printf("Input Event JSON (missing 'id' field):\n%s\n\n", event_json);
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    int result = nostr_validate_event_structure(event);
    printf("Expected: NOSTR_ERROR_EVENT_INVALID_STRUCTURE (-30)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    
    cJSON_Delete(event);
    return (result == NOSTR_ERROR_EVENT_INVALID_STRUCTURE);
}

// Test 5: Invalid hex string length (pubkey too short)
int test_invalid_pubkey_length(void) {
    print_test_header("Invalid Pubkey Length");
    
    const char* event_json = "{"
        "\"kind\":1,"
        "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
        "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7\","  // Too short (63 chars instead of 64)
        "\"created_at\":1755599338,"
        "\"tags\":[],"
        "\"content\":\"Test event 1\","
        "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
    "}";
    
    printf("Input Event JSON (pubkey too short - 63 chars instead of 64):\n%s\n\n", event_json);
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    int result = nostr_validate_event_structure(event);
    printf("Expected: NOSTR_ERROR_EVENT_INVALID_PUBKEY (-32)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    
    cJSON_Delete(event);
    return (result == NOSTR_ERROR_EVENT_INVALID_PUBKEY);
}

// Test 6: Invalid kind (negative)
int test_invalid_kind_negative(void) {
    print_test_header("Invalid Kind - Negative Value");
    
    const char* event_json = "{"
        "\"kind\":-1,"  // Invalid negative kind
        "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
        "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
        "\"created_at\":1755599338,"
        "\"tags\":[],"
        "\"content\":\"Test event 1\","
        "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
    "}";
    
    printf("Input Event JSON (kind = -1):\n%s\n\n", event_json);
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    int result = nostr_validate_event_structure(event);
    printf("Expected: NOSTR_ERROR_EVENT_INVALID_KIND (-35)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    
    cJSON_Delete(event);
    return (result == NOSTR_ERROR_EVENT_INVALID_KIND);
}

// Test 7: Invalid tags (not an array)
int test_invalid_tags_not_array(void) {
    print_test_header("Invalid Tags - Not Array");
    
    const char* event_json = "{"
        "\"kind\":1,"
        "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
        "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
        "\"created_at\":1755599338,"
        "\"tags\":\"not an array\","  // Should be array
        "\"content\":\"Test event 1\","
        "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
    "}";
    
    printf("Input Event JSON (tags is string instead of array):\n%s\n\n", event_json);
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    int result = nostr_validate_event_structure(event);
    printf("Expected: NOSTR_ERROR_EVENT_INVALID_TAGS (-36)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    
    cJSON_Delete(event);
    return (result == NOSTR_ERROR_EVENT_INVALID_TAGS);
}

// Test 8: Wrong event ID (cryptographic test)
int test_wrong_event_id(void) {
    print_test_header("Wrong Event ID - Cryptographic Test");
    
    // Take valid event but change the ID to something incorrect
    const char* event_json = "{"
        "\"kind\":1,"
        "\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\","  // Wrong ID
        "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
        "\"created_at\":1755599338,"
        "\"tags\":[],"
        "\"content\":\"Test event 1\","
        "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\""
    "}";
    
    printf("Input Event JSON (ID changed to all zeros):\n%s\n\n", event_json);
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    // Structure should pass
    int structure_result = nostr_validate_event_structure(event);
    printf("Structure validation - Expected: NOSTR_SUCCESS (0)\n");
    printf("Structure validation - Actual: %d (%s)\n", structure_result, nostr_strerror(structure_result));
    
    if (structure_result != NOSTR_SUCCESS) {
        printf("❌ Unexpected structure validation failure\n");
        cJSON_Delete(event);
        return 0;
    }
    
    // Crypto should fail
    int crypto_result = nostr_verify_event_signature(event);
    printf("Crypto verification - Expected: NOSTR_ERROR_EVENT_INVALID_ID (-31)\n");
    printf("Crypto verification - Actual: %d (%s)\n", crypto_result, nostr_strerror(crypto_result));
    
    cJSON_Delete(event);
    return (crypto_result == NOSTR_ERROR_EVENT_INVALID_ID);
}

// Test 9: Invalid signature (cryptographic test)
int test_invalid_signature(void) {
    print_test_header("Invalid Signature - Cryptographic Test");
    
    // Take valid event but change the signature to a structurally valid but cryptographically wrong signature
    const char* event_json = "{"
        "\"kind\":1,"
        "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
        "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\","
        "\"created_at\":1755599338,"
        "\"tags\":[],"
        "\"content\":\"Test event 1\","
        "\"sig\":\"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\""  // Wrong but valid hex
    "}";
    
    printf("Input Event JSON (signature changed to invalid hex):\n%s\n\n", event_json);
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    // Structure should pass
    int structure_result = nostr_validate_event_structure(event);
    printf("Structure validation - Expected: NOSTR_SUCCESS (0)\n");
    printf("Structure validation - Actual: %d (%s)\n", structure_result, nostr_strerror(structure_result));
    
    if (structure_result != NOSTR_SUCCESS) {
        printf("❌ Unexpected structure validation failure\n");
        cJSON_Delete(event);
        return 0;
    }
    
    // Crypto should fail
    int crypto_result = nostr_verify_event_signature(event);
    printf("Crypto verification - Expected: NOSTR_ERROR_EVENT_INVALID_SIGNATURE (-33)\n");
    printf("Crypto verification - Actual: %d (%s)\n", crypto_result, nostr_strerror(crypto_result));
    
    cJSON_Delete(event);
    return (crypto_result == NOSTR_ERROR_EVENT_INVALID_SIGNATURE);
}

// Test 10: Integration test with our own event creation
int test_integration_with_event_creation(void) {
    print_test_header("Integration Test - Validate Our Own Created Event");
    
    // Create a test event using our existing function
    const char* private_key_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
    unsigned char private_key[32];
    nostr_hex_to_bytes(private_key_hex, private_key, 32);
    
    cJSON* tags = cJSON_CreateArray();
    cJSON* tag = cJSON_CreateArray();
    cJSON_AddItemToArray(tag, cJSON_CreateString("test"));
    cJSON_AddItemToArray(tag, cJSON_CreateString("integration"));
    cJSON_AddItemToArray(tags, tag);
    
    printf("Creating event with our library...\n");
    cJSON* created_event = nostr_create_and_sign_event(1, "Integration test message", tags, private_key, time(NULL));
    
    if (!created_event) {
        printf("❌ Event creation failed\n");
        cJSON_Delete(tags);
        return 0;
    }
    
    char* event_str = cJSON_Print(created_event);
    printf("Created Event JSON:\n%s\n\n", event_str);
    free(event_str);
    
    printf("Validating our created event...\n");
    int result = nostr_validate_event(created_event);
    printf("Expected: NOSTR_SUCCESS (0)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    
    if (result == NOSTR_SUCCESS) {
        printf("✅ Our library creates valid events that pass validation\n");
    } else {
        printf("❌ Our own created event failed validation\n");
    }
    
    cJSON_Delete(created_event);
    return (result == NOSTR_SUCCESS);
}

int main(void) {
    printf("=== NIP-01 Event Validation Test Suite ===\n");
    printf("Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events\n");
    
    // Initialize crypto library
    if (nostr_init() != NOSTR_SUCCESS) {
        printf("❌ Failed to initialize nostr library\n");
        return 1;
    }
    
    int all_passed = 1;
    int test_result;
    
    // Valid event tests
    test_result = test_valid_event_1();
    print_test_result(test_result, "Valid Event from nak - Basic");
    if (!test_result) all_passed = 0;
    
    test_result = test_valid_event_with_tags();
    print_test_result(test_result, "Valid Event with Tags");
    if (!test_result) all_passed = 0;
    
    test_result = test_valid_metadata_event();
    print_test_result(test_result, "Valid Metadata Event (Kind 0)");
    if (!test_result) all_passed = 0;
    
    // Invalid structure tests
    test_result = test_missing_id_field();
    print_test_result(test_result, "Missing Required Field - ID");
    if (!test_result) all_passed = 0;
    
    test_result = test_invalid_pubkey_length();
    print_test_result(test_result, "Invalid Pubkey Length");
    if (!test_result) all_passed = 0;
    
    test_result = test_invalid_kind_negative();
    print_test_result(test_result, "Invalid Kind - Negative Value");
    if (!test_result) all_passed = 0;
    
    test_result = test_invalid_tags_not_array();
    print_test_result(test_result, "Invalid Tags - Not Array");
    if (!test_result) all_passed = 0;
    
    // Invalid cryptography tests
    test_result = test_wrong_event_id();
    print_test_result(test_result, "Wrong Event ID - Cryptographic Test");
    if (!test_result) all_passed = 0;
    
    test_result = test_invalid_signature();
    print_test_result(test_result, "Invalid Signature - Cryptographic Test");
    if (!test_result) all_passed = 0;
    
    // Integration test
    test_result = test_integration_with_event_creation();
    print_test_result(test_result, "Integration Test - Validate Our Own Created Event");
    if (!test_result) all_passed = 0;
    
    // Summary
    printf("\n=== TEST SUMMARY ===\n");
    printf("Total tests: %d\n", test_count);
    printf("Passed: %d\n", passed_tests);
    printf("Failed: %d\n", test_count - passed_tests);
    
    if (all_passed) {
        printf("🎉 ALL TESTS PASSED! Event validation implementation is working correctly.\n");
    } else {
        printf("❌ SOME TESTS FAILED. Please review the output above.\n");
    }
    
    nostr_cleanup();
    return all_passed ? 0 : 1;
}
