/*
 * NIP-13 Proof of Work Test Suite
 * Tests PoW generation, difficulty calculation, nonce extraction, and validation
 * 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 <unistd.h>
#include "../nostr_core/nip013.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);
    }
}

// Test 1: Difficulty calculation with NIP-13 example
int test_difficulty_calculation_reference(void) {
    print_test_header("Difficulty Calculation - NIP-13 Reference");
    
    // Event ID from NIP-13 spec: 000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358
    // This should have ~20 leading zero bits as stated in the spec
    const char* event_id = "000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358";
    
    printf("Testing event ID: %s\n", event_id);
    printf("Expected: ~20 leading zero bits\n");
    
    int difficulty = nostr_calculate_pow_difficulty(event_id);
    printf("Actual: %d leading zero bits\n", difficulty);
    
    if (difficulty < 0) {
        printf("❌ Error calculating difficulty: %d (%s)\n", difficulty, nostr_strerror(difficulty));
        return 0;
    }
    
    // The NIP-13 spec example should have around 20 bits
    if (difficulty >= 19 && difficulty <= 21) {
        printf("✅ Difficulty calculation matches expected range (19-21 bits)\n");
        return 1;
    } else {
        printf("❌ Difficulty %d is outside expected range (19-21 bits)\n", difficulty);
        return 0;
    }
}

// Test 2: Extract nonce info from NIP-13 reference event
int test_nonce_extraction_reference(void) {
    print_test_header("Nonce Extraction - NIP-13 Reference Event");
    
    // Complete event from NIP-13 spec
    const char* event_json = "{"
        "\"id\":\"000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358\","
        "\"pubkey\":\"a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243\","
        "\"created_at\":1651794653,"
        "\"kind\":1,"
        "\"tags\":[[\"nonce\",\"776797\",\"20\"]],"
        "\"content\":\"It's just me mining my own business\","
        "\"sig\":\"284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977\""
    "}";
    
    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;
    }
    
    uint64_t nonce_value = 0;
    int target_difficulty = -1;
    int result = nostr_extract_nonce_info(event, &nonce_value, &target_difficulty);
    
    printf("Extraction result: %d (%s)\n", result, nostr_strerror(result));
    printf("Expected nonce: 776797\n");
    printf("Actual nonce: %llu\n", (unsigned long long)nonce_value);
    printf("Expected target: 20\n");
    printf("Actual target: %d\n", target_difficulty);
    
    cJSON_Delete(event);
    
    if (result != NOSTR_SUCCESS) {
        printf("❌ Failed to extract nonce info\n");
        return 0;
    }
    
    if (nonce_value == 776797 && target_difficulty == 20) {
        printf("✅ Nonce extraction successful\n");
        return 1;
    } else {
        printf("❌ Extracted values don't match expected\n");
        return 0;
    }
}

// Test 3: PoW validation with reference event
int test_pow_validation_reference(void) {
    print_test_header("PoW Validation - NIP-13 Reference Event");
    
    const char* event_json = "{"
        "\"id\":\"000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358\","
        "\"pubkey\":\"a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243\","
        "\"created_at\":1651794653,"
        "\"kind\":1,"
        "\"tags\":[[\"nonce\",\"776797\",\"20\"]],"
        "\"content\":\"It's just me mining my own business\","
        "\"sig\":\"284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977\""
    "}";
    
    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;
    }
    
    // Test basic validation (no minimum difficulty)
    nostr_pow_result_t result_info;
    int validation_result = nostr_validate_pow(event, 0, NOSTR_POW_VALIDATE_BASIC, &result_info);
    
    printf("Validation result: %d (%s)\n", validation_result, nostr_strerror(validation_result));
    printf("Actual difficulty: %d\n", result_info.actual_difficulty);
    printf("Committed target: %d\n", result_info.committed_target);
    printf("Nonce value: %llu\n", (unsigned long long)result_info.nonce_value);
    printf("Has nonce tag: %d\n", result_info.has_nonce_tag);
    printf("Error detail: %s\n", result_info.error_detail);
    
    cJSON_Delete(event);
    return (validation_result == NOSTR_SUCCESS && result_info.has_nonce_tag == 1);
}

// Test 4: Generate PoW with our library and validate it
int test_pow_generation_and_validation(void) {
    print_test_header("PoW Generation and Validation - Our Library");
    
    // Create a test event for mining
    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();
    
    printf("Creating base event...\n");
    cJSON* event = nostr_create_and_sign_event(1, "Testing PoW generation", tags, private_key, time(NULL));
    
    if (!event) {
        printf("❌ Event creation failed\n");
        cJSON_Delete(tags);
        return 0;
    }
    
    char* original_event_str = cJSON_Print(event);
    printf("Original event (no PoW):\n%s\n\n", original_event_str);
    free(original_event_str);
    
    // Add PoW with difficulty 8 (reasonable for testing)
    printf("Adding PoW with difficulty 8...\n");
    int pow_result = nostr_add_proof_of_work(event, private_key, 8, 100000, 10000, 10000, NULL, NULL);
    
    if (pow_result != NOSTR_SUCCESS) {
        printf("❌ PoW generation failed: %d (%s)\n", pow_result, nostr_strerror(pow_result));
        cJSON_Delete(event);
        return 0;
    }
    
    char* pow_event_str = cJSON_Print(event);
    printf("Event with PoW:\n%s\n\n", pow_event_str);
    free(pow_event_str);
    
    // Now validate the PoW
    printf("Validating generated PoW...\n");
    nostr_pow_result_t result_info;
    int validation_result = nostr_validate_pow(event, 8, NOSTR_POW_VALIDATE_FULL, &result_info);
    
    printf("Validation result: %d (%s)\n", validation_result, nostr_strerror(validation_result));
    printf("Actual difficulty: %d\n", result_info.actual_difficulty);
    printf("Committed target: %d\n", result_info.committed_target);
    printf("Has nonce tag: %d\n", result_info.has_nonce_tag);
    printf("Error detail: %s\n", result_info.error_detail);
    
    cJSON_Delete(event);
    
    if (validation_result == NOSTR_SUCCESS && result_info.actual_difficulty >= 8) {
        printf("✅ PoW generation and validation successful\n");
        return 1;
    } else {
        printf("❌ PoW validation failed\n");
        return 0;
    }
}

// Test 5: Test various validation flag combinations
int test_validation_flags(void) {
    print_test_header("Validation Flags - Various Combinations");
    
    // Use NIP-13 reference event
    const char* event_json = "{"
        "\"id\":\"000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358\","
        "\"pubkey\":\"a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243\","
        "\"created_at\":1651794653,"
        "\"kind\":1,"
        "\"tags\":[[\"nonce\",\"776797\",\"20\"]],"
        "\"content\":\"It's just me mining my own business\","
        "\"sig\":\"284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977\""
    "}";
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ JSON Parse Error\n");
        return 0;
    }
    
    int all_passed = 1;
    nostr_pow_result_t result_info;
    
    // Test 1: Basic validation
    printf("\nSubtest 1: NOSTR_POW_VALIDATE_BASIC\n");
    int result = nostr_validate_pow(event, 0, NOSTR_POW_VALIDATE_BASIC, &result_info);
    printf("Result: %d (%s)\n", result, nostr_strerror(result));
    if (result != NOSTR_SUCCESS) all_passed = 0;
    
    // Test 2: Full validation
    printf("\nSubtest 2: NOSTR_POW_VALIDATE_FULL\n");
    result = nostr_validate_pow(event, 0, NOSTR_POW_VALIDATE_FULL, &result_info);
    printf("Result: %d (%s)\n", result, nostr_strerror(result));
    if (result != NOSTR_SUCCESS) all_passed = 0;
    
    // Test 3: Anti-spam validation (should pass since committed target matches actual)
    printf("\nSubtest 3: NOSTR_POW_VALIDATE_ANTI_SPAM\n");
    result = nostr_validate_pow(event, 0, NOSTR_POW_VALIDATE_ANTI_SPAM, &result_info);
    printf("Result: %d (%s)\n", result, nostr_strerror(result));
    if (result != NOSTR_SUCCESS) all_passed = 0;
    
    // Test 4: Minimum difficulty requirement (15 bits - should pass)
    printf("\nSubtest 4: Minimum difficulty 15 bits\n");
    result = nostr_validate_pow(event, 15, NOSTR_POW_VALIDATE_BASIC, &result_info);
    printf("Result: %d (%s)\n", result, nostr_strerror(result));
    if (result != NOSTR_SUCCESS) all_passed = 0;
    
    // Test 5: Too high minimum difficulty (30 bits - should fail)
    printf("\nSubtest 5: Minimum difficulty 30 bits (should fail)\n");
    result = nostr_validate_pow(event, 30, NOSTR_POW_VALIDATE_BASIC, &result_info);
    printf("Result: %d (%s)\n", result, nostr_strerror(result));
    if (result == NOSTR_SUCCESS) {
        printf("❌ Should have failed but didn't\n");
        all_passed = 0;
    } else if (result == NOSTR_ERROR_NIP13_INSUFFICIENT) {
        printf("✅ Correctly failed with insufficient difficulty\n");
    } else {
        printf("❌ Failed with unexpected error\n");
        all_passed = 0;
    }
    
    cJSON_Delete(event);
    return all_passed;
}

// Test 6: Error conditions and edge cases
int test_error_conditions(void) {
    print_test_header("Error Conditions and Edge Cases");
    
    int all_passed = 1;
    
    // Test 1: NULL event
    printf("\nSubtest 1: NULL event\n");
    int result = nostr_validate_pow(NULL, 0, NOSTR_POW_VALIDATE_BASIC, NULL);
    printf("Expected: NOSTR_ERROR_INVALID_INPUT (-1)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    if (result != NOSTR_ERROR_INVALID_INPUT) all_passed = 0;
    
    // Test 2: Event without ID
    printf("\nSubtest 2: Event without ID\n");
    const char* no_id_json = "{\"kind\":1,\"content\":\"test\",\"tags\":[]}";
    cJSON* no_id_event = cJSON_Parse(no_id_json);
    result = nostr_validate_pow(no_id_event, 0, NOSTR_POW_VALIDATE_BASIC, NULL);
    printf("Expected: NOSTR_ERROR_EVENT_INVALID_ID (-31)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    if (result != NOSTR_ERROR_EVENT_INVALID_ID) all_passed = 0;
    cJSON_Delete(no_id_event);
    
    // Test 3: Event without nonce tag when required
    printf("\nSubtest 3: Event without nonce tag when required\n");
    const char* no_nonce_json = "{"
        "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\","
        "\"kind\":1,\"content\":\"test\",\"tags\":[]"
    "}";
    cJSON* no_nonce_event = cJSON_Parse(no_nonce_json);
    result = nostr_validate_pow(no_nonce_event, 0, NOSTR_POW_VALIDATE_NONCE_TAG, NULL);
    printf("Expected: NOSTR_ERROR_NIP13_NO_NONCE_TAG (-101)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    if (result != NOSTR_ERROR_NIP13_NO_NONCE_TAG) all_passed = 0;
    cJSON_Delete(no_nonce_event);
    
    // Test 4: Invalid hex ID
    printf("\nSubtest 4: Invalid hex ID\n");
    result = nostr_calculate_pow_difficulty("invalid_hex_string");
    printf("Expected: NOSTR_ERROR_NIP13_CALCULATION (-104)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    if (result != NOSTR_ERROR_NIP13_CALCULATION) all_passed = 0;
    
    // Test 5: Wrong length hex ID
    printf("\nSubtest 5: Wrong length hex ID\n");
    result = nostr_calculate_pow_difficulty("f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffa"); // 63 chars instead of 64
    printf("Expected: NOSTR_ERROR_NIP13_CALCULATION (-104)\n");
    printf("Actual: %d (%s)\n", result, nostr_strerror(result));
    if (result != NOSTR_ERROR_NIP13_CALCULATION) all_passed = 0;
    
    return all_passed;
}

// Test 7: Integration with nak-generated PoW events
int test_nak_integration(void) {
    print_test_header("Integration with nak-generated PoW events");
    
    // Generate a test event with nak and PoW difficulty 8
    printf("Generating PoW event with nak (difficulty 8)...\n");
    
    // Create a temporary key for this test
    char temp_key[] = "/tmp/nip13_test_key_XXXXXX";
    int fd = mkstemp(temp_key);
    if (fd == -1) {
        printf("❌ Failed to create temporary key file\n");
        return 0;
    }
    
    // Write test key to file (same as used in other tests)
    const char* test_key = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
    ssize_t bytes_written = write(fd, test_key, strlen(test_key));
    close(fd);
    
    if (bytes_written != (ssize_t)strlen(test_key)) {
        printf("❌ Failed to write test key to temporary file\n");
        unlink(temp_key);
        return 0;
    }
    
    // Generate event with PoW using nak
    char cmd[1024];
    snprintf(cmd, sizeof(cmd), "nak event --sec %s -c \"PoW test from nak\" --pow 8 --ts %ld", 
             test_key, (long)time(NULL));
    
    printf("Executing: %s\n", cmd);
    FILE* pipe = popen(cmd, "r");
    if (!pipe) {
        printf("❌ Failed to execute nak command\n");
        unlink(temp_key);
        return 0;
    }
    
    char event_json[4096] = {0};
    if (!fgets(event_json, sizeof(event_json), pipe)) {
        printf("❌ Failed to read nak output\n");
        pclose(pipe);
        unlink(temp_key);
        return 0;
    }
    pclose(pipe);
    unlink(temp_key);
    
    // Remove trailing newline
    event_json[strcspn(event_json, "\n")] = 0;
    
    printf("nak-generated event:\n%s\n\n", event_json);
    
    // Parse and validate the event
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        printf("❌ Failed to parse nak-generated JSON\n");
        return 0;
    }
    
    // Validate the PoW
    nostr_pow_result_t result_info;
    int validation_result = nostr_validate_pow(event, 8, NOSTR_POW_VALIDATE_FULL, &result_info);
    
    printf("Validation result: %d (%s)\n", validation_result, nostr_strerror(validation_result));
    printf("Actual difficulty: %d\n", result_info.actual_difficulty);
    printf("Committed target: %d\n", result_info.committed_target);
    printf("Has nonce tag: %d\n", result_info.has_nonce_tag);
    printf("Error detail: %s\n", result_info.error_detail);
    
    cJSON_Delete(event);
    
    if (validation_result == NOSTR_SUCCESS && result_info.actual_difficulty >= 8) {
        printf("✅ nak-generated PoW event validates successfully\n");
        return 1;
    } else {
        printf("❌ nak-generated PoW event validation failed\n");
        return 0;
    }
}

int main(void) {
    printf("=== NIP-13 Proof of Work Test Suite ===\n");
    printf("Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events\n");
    printf("Tests both PoW generation and validation functionality\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;
    
    // Test 1: Basic difficulty calculation
    test_result = test_difficulty_calculation_reference();
    print_test_result(test_result, "Difficulty Calculation - NIP-13 Reference");
    if (!test_result) all_passed = 0;
    
    // Test 2: Nonce extraction
    test_result = test_nonce_extraction_reference();
    print_test_result(test_result, "Nonce Extraction - NIP-13 Reference Event");
    if (!test_result) all_passed = 0;
    
    // Test 3: Basic PoW validation
    test_result = test_pow_validation_reference();
    print_test_result(test_result, "PoW Validation - NIP-13 Reference Event");
    if (!test_result) all_passed = 0;
    
    // Test 4: PoW generation and validation
    test_result = test_pow_generation_and_validation();
    print_test_result(test_result, "PoW Generation and Validation - Our Library");
    if (!test_result) all_passed = 0;
    
    // Test 5: Validation flags
    test_result = test_validation_flags();
    print_test_result(test_result, "Validation Flags - Various Combinations");
    if (!test_result) all_passed = 0;
    
    // Test 6: Error conditions
    test_result = test_error_conditions();
    print_test_result(test_result, "Error Conditions and Edge Cases");
    if (!test_result) all_passed = 0;
    
    // Test 7: nak integration (optional - may fail if nak not available)
    printf("\n=== Optional nak Integration Test ===\n");
    test_result = test_nak_integration();
    if (test_result) {
        print_test_result(test_result, "Integration with nak-generated PoW events");
    } else {
        printf("⚠️  SKIP: Integration with nak-generated PoW events (nak may not be available)\n");
        // Don't fail the entire test suite for this optional test
    }
    
    // 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! NIP-13 PoW implementation is working correctly.\n");
        printf("✅ PoW generation works\n");
        printf("✅ PoW difficulty calculation works\n");
        printf("✅ Nonce extraction works\n");
        printf("✅ PoW validation works\n");
        printf("✅ Error handling works\n");
    } else {
        printf("❌ SOME TESTS FAILED. Please review the output above.\n");
    }
    
    nostr_cleanup();
    return all_passed ? 0 : 1;
}