#define _GNU_SOURCE

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             NIP-42 AUTHENTICATION FUNCTIONS
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
#include <pthread.h>
#include "debug.h"
#include <cjson/cJSON.h>
#include <libwebsockets.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "websockets.h"


// Forward declaration for notice message function
void send_notice_message(struct lws* wsi, const char* message);

// Forward declarations for NIP-42 functions from request_validator.c
int nostr_nip42_generate_challenge(char *challenge_buffer, size_t buffer_size);
int nostr_nip42_verify_auth_event(cJSON *event, const char *challenge_id,
                                  const char *relay_url, int time_tolerance_seconds);

// Forward declaration for per_session_data struct (defined in websockets.h)


// Send NIP-42 authentication challenge to client
void send_nip42_auth_challenge(struct lws* wsi, struct per_session_data* pss) {
    if (!wsi || !pss) return;
    
    // Generate challenge using existing request_validator function
    char challenge[65];
    if (nostr_nip42_generate_challenge(challenge, sizeof(challenge)) != 0) {
        DEBUG_ERROR("Failed to generate NIP-42 challenge");
        send_notice_message(wsi, "Authentication temporarily unavailable");
        return;
    }
    
    // Store challenge in session
    pthread_mutex_lock(&pss->session_lock);
    strncpy(pss->active_challenge, challenge, sizeof(pss->active_challenge) - 1);
    pss->active_challenge[sizeof(pss->active_challenge) - 1] = '\0';
    pss->challenge_created = time(NULL);
    pss->challenge_expires = pss->challenge_created + 600; // 10 minutes
    pss->auth_challenge_sent = 1;
    pthread_mutex_unlock(&pss->session_lock);
    
    // Send AUTH challenge message: ["AUTH", <challenge>]
    cJSON* auth_msg = cJSON_CreateArray();
    cJSON_AddItemToArray(auth_msg, cJSON_CreateString("AUTH"));
    cJSON_AddItemToArray(auth_msg, cJSON_CreateString(challenge));
    
    char* msg_str = cJSON_Print(auth_msg);
    if (msg_str) {
        size_t msg_len = strlen(msg_str);
        // Use proper message queue system instead of direct lws_write
        if (queue_message(wsi, pss, msg_str, msg_len, LWS_WRITE_TEXT) != 0) {
            DEBUG_ERROR("Failed to queue AUTH challenge message");
        }
        free(msg_str);
    }
    cJSON_Delete(auth_msg);
}

// Handle NIP-42 signed authentication event from client
void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* pss, cJSON* auth_event) {
    if (!wsi || !pss || !auth_event) return;
    
    // Serialize event for validation
    char* event_json = cJSON_Print(auth_event);
    if (!event_json) {
        send_notice_message(wsi, "Invalid authentication event format");
        return;
    }
    
    pthread_mutex_lock(&pss->session_lock);
    char challenge_copy[65];
    strncpy(challenge_copy, pss->active_challenge, sizeof(challenge_copy) - 1);
    challenge_copy[sizeof(challenge_copy) - 1] = '\0';
    time_t challenge_expires = pss->challenge_expires;
    pthread_mutex_unlock(&pss->session_lock);
    
    // Check if challenge has expired
    time_t current_time = time(NULL);
    if (current_time > challenge_expires) {
        free(event_json);
        send_notice_message(wsi, "Authentication challenge expired, please retry");
        DEBUG_WARN("NIP-42 authentication failed: challenge expired");
        return;
    }
    
    // Verify authentication using existing request_validator function
    // Note: nostr_nip42_verify_auth_event doesn't extract pubkey, we need to do that separately
    int result = nostr_nip42_verify_auth_event(auth_event, challenge_copy,
                                               "ws://localhost:8888", 600); // 10 minutes tolerance
    
    char authenticated_pubkey[65] = {0};
    if (result == 0) {
        // Extract pubkey from the auth event
        cJSON* pubkey_json = cJSON_GetObjectItem(auth_event, "pubkey");
        if (pubkey_json && cJSON_IsString(pubkey_json)) {
            const char* pubkey_str = cJSON_GetStringValue(pubkey_json);
            if (pubkey_str && strlen(pubkey_str) == 64) {
                strncpy(authenticated_pubkey, pubkey_str, sizeof(authenticated_pubkey) - 1);
                authenticated_pubkey[sizeof(authenticated_pubkey) - 1] = '\0';
            } else {
                result = -1; // Invalid pubkey format
            }
        } else {
            result = -1; // Missing pubkey
        }
    }
    
    free(event_json);
    
    if (result == 0) {
        // Authentication successful
        pthread_mutex_lock(&pss->session_lock);
        pss->authenticated = 1;
        strncpy(pss->authenticated_pubkey, authenticated_pubkey, sizeof(pss->authenticated_pubkey) - 1);
        pss->authenticated_pubkey[sizeof(pss->authenticated_pubkey) - 1] = '\0';
        // Clear challenge
        memset(pss->active_challenge, 0, sizeof(pss->active_challenge));
        pss->challenge_expires = 0;
        pss->auth_challenge_sent = 0;
        pthread_mutex_unlock(&pss->session_lock);
        
        send_notice_message(wsi, "NIP-42 authentication successful");
    } else {
        // Authentication failed
        char error_msg[256];
        snprintf(error_msg, sizeof(error_msg),
                 "NIP-42 authentication failed (error code: %d)", result);
        DEBUG_WARN(error_msg);
        
        send_notice_message(wsi, "NIP-42 authentication failed - invalid signature or challenge");
    }
}

// Handle challenge response (not typically used in NIP-42, but included for completeness)
void handle_nip42_auth_challenge_response(struct lws* wsi, struct per_session_data* pss, const char* challenge) {
    (void)wsi; (void)pss; (void)challenge; // Mark as intentionally unused
    
    // NIP-42 doesn't typically use challenge responses from client to server
    // This is reserved for potential future use or protocol extensions
    DEBUG_WARN("Received unexpected challenge response from client (not part of standard NIP-42 flow)");
    send_notice_message(wsi, "Challenge responses are not supported - please send signed authentication event");
}
