/*
 * NOSTR Core Library - NIP-001: Basic Protocol Flow
 * 
 * Event creation, signing, serialization and core protocol functions
 */

#include "nip001.h"
#include "utils.h"
#include "../cjson/cJSON.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../nostr_core/nostr_common.h"

// Forward declarations for crypto functions (private API)
// These functions are implemented in crypto/ but not exposed through public headers
typedef struct {
    unsigned char data[64];
} nostr_secp256k1_xonly_pubkey;

int nostr_secp256k1_xonly_pubkey_parse(nostr_secp256k1_xonly_pubkey* pubkey, const unsigned char* input32);
int nostr_secp256k1_schnorrsig_verify(const unsigned char* sig64, const unsigned char* msg32, const nostr_secp256k1_xonly_pubkey* pubkey);

// Declare utility functions
void nostr_bytes_to_hex(const unsigned char* bytes, size_t len, char* hex);
int nostr_hex_to_bytes(const char* hex, unsigned char* bytes, size_t len);
int nostr_sha256(const unsigned char* data, size_t len, unsigned char* hash);
int nostr_ec_public_key_from_private_key(const unsigned char* private_key, unsigned char* public_key);
int nostr_ec_sign(const unsigned char* private_key, const unsigned char* hash, unsigned char* signature);

/**
 * Create and sign a NOSTR event
 */
cJSON* nostr_create_and_sign_event(int kind, const char* content, cJSON* tags, const unsigned char* private_key, time_t timestamp) {
    if (!private_key) {
        return NULL;
    }
    
    if (!content) {
        content = ""; // Default to empty content
    }
    
    // Convert private key to public key
    unsigned char public_key[32];
    if (nostr_ec_public_key_from_private_key(private_key, public_key) != 0) {
        return NULL;
    }
    
    // Convert public key to hex
    char pubkey_hex[65];
    nostr_bytes_to_hex(public_key, 32, pubkey_hex);
    
    // Create event structure
    cJSON* event = cJSON_CreateObject();
    if (!event) {
        return NULL;
    }
    
    // Use provided timestamp or current time if timestamp is 0
    time_t event_time = (timestamp == 0) ? time(NULL) : timestamp;
    
    cJSON_AddStringToObject(event, "pubkey", pubkey_hex);
    cJSON_AddNumberToObject(event, "created_at", (double)event_time);
    cJSON_AddNumberToObject(event, "kind", kind);
    
    // Add tags (copy provided tags or create empty array)
    if (tags) {
        cJSON_AddItemToObject(event, "tags", cJSON_Duplicate(tags, 1));
    } else {
        cJSON_AddItemToObject(event, "tags", cJSON_CreateArray());
    }
    
    cJSON_AddStringToObject(event, "content", content);
    
    // ============================================================================
    // INLINE SERIALIZATION AND SIGNING LOGIC 
    // ============================================================================
    
    // Get event fields for serialization
    cJSON* pubkey_item = cJSON_GetObjectItem(event, "pubkey");
    cJSON* created_at_item = cJSON_GetObjectItem(event, "created_at");
    cJSON* kind_item = cJSON_GetObjectItem(event, "kind");
    cJSON* tags_item = cJSON_GetObjectItem(event, "tags");
    cJSON* content_item = cJSON_GetObjectItem(event, "content");
    
    if (!pubkey_item || !created_at_item || !kind_item || !tags_item || !content_item) {
        cJSON_Delete(event);
        return NULL;
    }
    
    // Create serialization array: [0, pubkey, created_at, kind, tags, content]
    cJSON* serialize_array = cJSON_CreateArray();
    if (!serialize_array) {
        cJSON_Delete(event);
        return NULL;
    }
    
    cJSON_AddItemToArray(serialize_array, cJSON_CreateNumber(0));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(pubkey_item, 1));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(created_at_item, 1));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(kind_item, 1));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(tags_item, 1));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(content_item, 1));
    
    char* serialize_string = cJSON_PrintUnformatted(serialize_array);
    cJSON_Delete(serialize_array);
    
    if (!serialize_string) {
        cJSON_Delete(event);
        return NULL;
    }
    
    // Hash the serialized event
    unsigned char event_hash[32];
    if (nostr_sha256((const unsigned char*)serialize_string, strlen(serialize_string), event_hash) != 0) {
        free(serialize_string);
        cJSON_Delete(event);
        return NULL;
    }
    
    // Convert hash to hex for event ID
    char event_id[65];
    nostr_bytes_to_hex(event_hash, 32, event_id);
    
    // Sign the hash using ECDSA
    unsigned char signature[64];
    if (nostr_ec_sign(private_key, event_hash, signature) != 0) {
        free(serialize_string);
        cJSON_Delete(event);
        return NULL;
    }
    
    // Convert signature to hex
    char sig_hex[129];
    nostr_bytes_to_hex(signature, 64, sig_hex);
    
    // Add ID and signature to the event
    cJSON_AddStringToObject(event, "id", event_id);
    cJSON_AddStringToObject(event, "sig", sig_hex);
    
    free(serialize_string);
    
    return event;
}

/**
 * Validate the structure of a NOSTR event
 * Checks required fields, types, and basic format validation
 */
int nostr_validate_event_structure(cJSON* event) {
    if (!event || !cJSON_IsObject(event)) {
        return NOSTR_ERROR_EVENT_INVALID_STRUCTURE;
    }
    
    // Check required fields exist
    cJSON* id_item = cJSON_GetObjectItem(event, "id");
    cJSON* pubkey_item = cJSON_GetObjectItem(event, "pubkey");
    cJSON* created_at_item = cJSON_GetObjectItem(event, "created_at");
    cJSON* kind_item = cJSON_GetObjectItem(event, "kind");
    cJSON* tags_item = cJSON_GetObjectItem(event, "tags");
    cJSON* content_item = cJSON_GetObjectItem(event, "content");
    cJSON* sig_item = cJSON_GetObjectItem(event, "sig");
    
    if (!id_item || !pubkey_item || !created_at_item || !kind_item || 
        !tags_item || !content_item || !sig_item) {
        return NOSTR_ERROR_EVENT_INVALID_STRUCTURE;
    }
    
    // Validate field types
    if (!cJSON_IsString(id_item)) return NOSTR_ERROR_EVENT_INVALID_ID;
    if (!cJSON_IsString(pubkey_item)) return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
    if (!cJSON_IsNumber(created_at_item)) return NOSTR_ERROR_EVENT_INVALID_CREATED_AT;
    if (!cJSON_IsNumber(kind_item)) return NOSTR_ERROR_EVENT_INVALID_KIND;
    if (!cJSON_IsArray(tags_item)) return NOSTR_ERROR_EVENT_INVALID_TAGS;
    if (!cJSON_IsString(content_item)) return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    if (!cJSON_IsString(sig_item)) return NOSTR_ERROR_EVENT_INVALID_SIGNATURE;
    
    // Validate hex string lengths
    const char* id_str = cJSON_GetStringValue(id_item);
    const char* pubkey_str = cJSON_GetStringValue(pubkey_item);
    const char* sig_str = cJSON_GetStringValue(sig_item);
    
    if (!id_str || strlen(id_str) != 64) return NOSTR_ERROR_EVENT_INVALID_ID;
    if (!pubkey_str || strlen(pubkey_str) != 64) return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
    if (!sig_str || strlen(sig_str) != 128) return NOSTR_ERROR_EVENT_INVALID_SIGNATURE;
    
    // Validate hex characters (lowercase)
    for (int i = 0; i < 64; i++) {
        char c = id_str[i];
        if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
            return NOSTR_ERROR_EVENT_INVALID_ID;
        }
        c = pubkey_str[i];
        if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
            return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
        }
    }
    
    // Validate signature hex characters (lowercase) - 128 characters
    for (int i = 0; i < 128; i++) {
        char c = sig_str[i];
        if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
            return NOSTR_ERROR_EVENT_INVALID_SIGNATURE;
        }
    }
    
    // Validate created_at is a valid timestamp (positive number)
    double created_at = cJSON_GetNumberValue(created_at_item);
    if (created_at < 0) return NOSTR_ERROR_EVENT_INVALID_CREATED_AT;
    
    // Validate kind is valid (0-65535)
    double kind = cJSON_GetNumberValue(kind_item);
    if (kind < 0 || kind > 65535 || kind != (int)kind) {
        return NOSTR_ERROR_EVENT_INVALID_KIND;
    }
    
    // Validate tags array structure (array of arrays of strings)
    cJSON* tag_item;
    cJSON_ArrayForEach(tag_item, tags_item) {
        if (!cJSON_IsArray(tag_item)) {
            return NOSTR_ERROR_EVENT_INVALID_TAGS;
        }
        cJSON* tag_element;
        cJSON_ArrayForEach(tag_element, tag_item) {
            if (!cJSON_IsString(tag_element)) {
                return NOSTR_ERROR_EVENT_INVALID_TAGS;
            }
        }
    }
    
    return NOSTR_SUCCESS;
}

/**
 * Verify the cryptographic signature of a NOSTR event
 * Validates event ID and signature according to NIP-01
 */
int nostr_verify_event_signature(cJSON* event) {
    if (!event) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Get event fields
    cJSON* id_item = cJSON_GetObjectItem(event, "id");
    cJSON* pubkey_item = cJSON_GetObjectItem(event, "pubkey");
    cJSON* created_at_item = cJSON_GetObjectItem(event, "created_at");
    cJSON* kind_item = cJSON_GetObjectItem(event, "kind");
    cJSON* tags_item = cJSON_GetObjectItem(event, "tags");
    cJSON* content_item = cJSON_GetObjectItem(event, "content");
    cJSON* sig_item = cJSON_GetObjectItem(event, "sig");
    
    if (!id_item || !pubkey_item || !created_at_item || !kind_item || 
        !tags_item || !content_item || !sig_item) {
        return NOSTR_ERROR_EVENT_INVALID_STRUCTURE;
    }
    
    // Create serialization array: [0, pubkey, created_at, kind, tags, content]
    cJSON* serialize_array = cJSON_CreateArray();
    if (!serialize_array) {
        return NOSTR_ERROR_MEMORY_FAILED;
    }
    
    cJSON_AddItemToArray(serialize_array, cJSON_CreateNumber(0));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(pubkey_item, 1));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(created_at_item, 1));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(kind_item, 1));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(tags_item, 1));
    cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(content_item, 1));
    
    char* serialize_string = cJSON_PrintUnformatted(serialize_array);
    cJSON_Delete(serialize_array);
    
    if (!serialize_string) {
        return NOSTR_ERROR_MEMORY_FAILED;
    }
    
    // Hash the serialized event
    unsigned char event_hash[32];
    if (nostr_sha256((const unsigned char*)serialize_string, strlen(serialize_string), event_hash) != 0) {
        free(serialize_string);
        return NOSTR_ERROR_CRYPTO_FAILED;
    }
    
    // Convert hash to hex for event ID verification
    char calculated_id[65];
    nostr_bytes_to_hex(event_hash, 32, calculated_id);
    
    // Compare with provided event ID
    const char* provided_id = cJSON_GetStringValue(id_item);
    if (!provided_id || strcmp(calculated_id, provided_id) != 0) {
        free(serialize_string);
        return NOSTR_ERROR_EVENT_INVALID_ID;
    }
    
    // Verify signature
    const char* pubkey_str = cJSON_GetStringValue(pubkey_item);
    const char* sig_str = cJSON_GetStringValue(sig_item);
    
    if (!pubkey_str || !sig_str) {
        free(serialize_string);
        return NOSTR_ERROR_EVENT_INVALID_STRUCTURE;
    }
    
    // Convert hex strings to bytes
    unsigned char pubkey_bytes[32];
    unsigned char sig_bytes[64];
    
    if (nostr_hex_to_bytes(pubkey_str, pubkey_bytes, 32) != 0 ||
        nostr_hex_to_bytes(sig_str, sig_bytes, 64) != 0) {
        free(serialize_string);
        return NOSTR_ERROR_CRYPTO_FAILED;
    }
    
    // Parse the public key into secp256k1 format
    nostr_secp256k1_xonly_pubkey xonly_pubkey;
    if (!nostr_secp256k1_xonly_pubkey_parse(&xonly_pubkey, pubkey_bytes)) {
        free(serialize_string);
        return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
    }
    
    // Verify Schnorr signature
    if (!nostr_secp256k1_schnorrsig_verify(sig_bytes, event_hash, &xonly_pubkey)) {
        free(serialize_string);
        return NOSTR_ERROR_EVENT_INVALID_SIGNATURE;
    }
    
    free(serialize_string);
    return NOSTR_SUCCESS;
}

/**
 * Complete validation of a NOSTR event
 * Performs both structure and cryptographic validation
 */
int nostr_validate_event(cJSON* event) {
    // First validate structure (fast check)
    int structure_result = nostr_validate_event_structure(event);
    if (structure_result != NOSTR_SUCCESS) {
        return structure_result;
    }
    
    // Then verify signature (expensive check)
    return nostr_verify_event_signature(event);
}
