/*
 * NOSTR Core Library Implementation - Relay Pool Management
 * 
 * This file contains:
 * - Relay Pool Management
 * - Pool connection management
 * - Subscription handling
 * - Event processing and dispatching
 * - Statistics and latency tracking
 * - Multi-relay query and publish functions
 */

#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L

#include "nostr_core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

// Our production-ready WebSocket implementation
#include "../nostr_websocket/nostr_websocket_tls.h"

// cJSON for JSON handling
#include "../cjson/cJSON.h"




// =============================================================================
// RELAY POOL MANAGEMENT
// =============================================================================

// Pool configuration constants
#define NOSTR_POOL_MAX_RELAYS 32
#define NOSTR_POOL_MAX_SUBSCRIPTIONS 64
#define NOSTR_POOL_MAX_SEEN_EVENTS 1000
#define NOSTR_POOL_DEFAULT_TIMEOUT 5000
#define NOSTR_POOL_SUBSCRIPTION_ID_SIZE 32
#define NOSTR_POOL_PING_INTERVAL 59  // 59 seconds - keeps connections alive
#define NOSTR_POOL_MAX_PENDING_SUBSCRIPTIONS 8  // Max concurrent subscription timings per relay
#define NOSTR_POOL_MAX_PENDING_PUBLISHES 32  // Max concurrent publish operations

// High-resolution timing helper
static double get_current_time_ms(void) {
    struct timespec ts;
    if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
        return ts.tv_sec * 1000.0 + ts.tv_nsec / 1000000.0;
    } else {
        // Fallback to lower resolution
        return (double)time(NULL) * 1000.0;
    }
}

// Subscription timing entry for multi-subscription support
typedef struct subscription_timing {
    char subscription_id[NOSTR_POOL_SUBSCRIPTION_ID_SIZE];
    double start_time_ms;
    int active;
} subscription_timing_t;

// Publish operation tracking for async callbacks
typedef struct publish_operation {
    char event_id[65];                      // Event ID being published
    publish_response_callback_t callback;   // User callback function
    void* user_data;                        // User data for callback
    time_t publish_time;                    // When publish was initiated
    int pending_relay_count;                // Number of relays still pending response
    char** pending_relay_urls;              // URLs of relays still pending
    int total_relay_count;                  // Total number of relays for this publish
} publish_operation_t;

// Internal structures
typedef struct relay_connection {
    char* url;
    nostr_ws_client_t* ws_client;
    nostr_pool_relay_status_t status;

    // Connection management
    time_t connect_time;
    nostr_relay_pool_t* pool; // Back reference to pool for config access

    // Reconnection management
    int reconnect_attempts;
    time_t last_reconnect_attempt;
    time_t next_reconnect_time;

    // Connection health monitoring (ping/pong)
    time_t last_ping_sent;
    time_t last_pong_received;
    int ping_pending;
    double pending_ping_start_ms;   // High-resolution timestamp for ping measurement

    // Multi-subscription latency tracking (REQ->first EVENT/EOSE)
    subscription_timing_t pending_subscriptions[NOSTR_POOL_MAX_PENDING_SUBSCRIPTIONS];
    int pending_subscription_count;

    // Error reporting for publish operations
    char last_publish_error[512];   // Last error message from relay publish response
    time_t last_publish_error_time; // When the error occurred

    // Error reporting for connection operations
    char last_connection_error[512];   // Last error message from relay connection
    time_t last_connection_error_time; // When the connection error occurred

    // Statistics
    nostr_relay_stats_t stats;
} relay_connection_t;

struct nostr_pool_subscription {
    char subscription_id[NOSTR_POOL_SUBSCRIPTION_ID_SIZE];
    cJSON* filter;

    // Relay-specific subscription tracking
    char** relay_urls;
    int relay_count;
    int* eose_received;  // Track EOSE from each relay

    // Callbacks
    void (*on_event)(cJSON* event, const char* relay_url, void* user_data);
    void (*on_eose)(cJSON** events, int event_count, void* user_data);
    void* user_data;

    int closed;
    int close_on_eose;  // Auto-close subscription when all relays send EOSE
    nostr_relay_pool_t* pool; // Back reference to pool

    // New subscription control parameters
    int enable_deduplication;                    // Per-subscription deduplication control
    nostr_pool_eose_result_mode_t result_mode;   // EOSE result selection mode
    int relay_timeout_seconds;                   // Timeout for individual relay operations
    int eose_timeout_seconds;                    // Timeout for waiting for EOSE completion
    time_t subscription_start_time;              // When subscription was created

    // Event collection for EOSE result modes
    cJSON** collected_events;
    int collected_event_count;
    int collected_events_capacity;

    // Per-relay timeout tracking
    time_t* relay_last_activity;                 // Last activity time per relay
};

struct nostr_relay_pool {
    relay_connection_t* relays[NOSTR_POOL_MAX_RELAYS];
    int relay_count;

    // Reconnection configuration
    nostr_pool_reconnect_config_t reconnect_config;

    // Event deduplication - simple hash table with linear probing
    char seen_event_ids[NOSTR_POOL_MAX_SEEN_EVENTS][65]; // 64 hex chars + null terminator
    int seen_count;
    int seen_next_index;

    // Active subscriptions
    nostr_pool_subscription_t* subscriptions[NOSTR_POOL_MAX_SUBSCRIPTIONS];
    int subscription_count;

    // Active publish operations for async callbacks
    publish_operation_t* publish_operations[NOSTR_POOL_MAX_PENDING_PUBLISHES];
    int publish_operation_count;

    // Pool-wide settings
    int default_timeout_ms;
};

// Helper function to generate unique subscription IDs
static void generate_subscription_id(char* id_buffer, size_t buffer_size) {
    static int counter = 0;
    snprintf(id_buffer, buffer_size, "pool_%d_%ld", ++counter, time(NULL));
}

// Helper function to find relay by URL
static relay_connection_t* find_relay_by_url(nostr_relay_pool_t* pool, const char* url) {
    if (!pool || !url) return NULL;
    
    for (int i = 0; i < pool->relay_count; i++) {
        if (pool->relays[i] && pool->relays[i]->url && 
            strcmp(pool->relays[i]->url, url) == 0) {
            return pool->relays[i];
        }
    }
    return NULL;
}

// Helper function to add subscription timing to relay
static int add_subscription_timing(relay_connection_t* relay, const char* subscription_id) {
    if (!relay || !subscription_id) return -1;
    
    // Check if we have space for another timing
    if (relay->pending_subscription_count >= NOSTR_POOL_MAX_PENDING_SUBSCRIPTIONS) {
        return -1; // No space available
    }
    
    // Add new timing entry
    int index = relay->pending_subscription_count;
    strncpy(relay->pending_subscriptions[index].subscription_id, subscription_id, 
           sizeof(relay->pending_subscriptions[index].subscription_id) - 1);
    relay->pending_subscriptions[index].subscription_id[sizeof(relay->pending_subscriptions[index].subscription_id) - 1] = '\0';
    relay->pending_subscriptions[index].start_time_ms = get_current_time_ms();
    relay->pending_subscriptions[index].active = 1;
    
    relay->pending_subscription_count++;
    return 0;
}

// Helper function to find and remove subscription timing
static double remove_subscription_timing(relay_connection_t* relay, const char* subscription_id) {
    if (!relay || !subscription_id) return -1.0;

    for (int i = 0; i < relay->pending_subscription_count; i++) {
        if (relay->pending_subscriptions[i].active &&
            strcmp(relay->pending_subscriptions[i].subscription_id, subscription_id) == 0) {

            // Calculate latency
            double current_time_ms = get_current_time_ms();
            double latency_ms = current_time_ms - relay->pending_subscriptions[i].start_time_ms;

            // Mark as inactive and remove by shifting remaining entries
            relay->pending_subscriptions[i].active = 0;
            for (int j = i; j < relay->pending_subscription_count - 1; j++) {
                relay->pending_subscriptions[j] = relay->pending_subscriptions[j + 1];
            }
            relay->pending_subscription_count--;

            return latency_ms;
        }
    }

    return -1.0; // Not found
}

// Helper functions for managing publish operations
static int add_publish_operation(nostr_relay_pool_t* pool, const char* event_id,
                                const char** relay_urls, int relay_count,
                                publish_response_callback_t callback, void* user_data) {
    if (!pool || !event_id || !relay_urls || relay_count <= 0) return -1;
    
    // Check if we have space for another operation
    if (pool->publish_operation_count >= NOSTR_POOL_MAX_PENDING_PUBLISHES) {
        return -1; // No space available
    }
    
    // Create new publish operation
    publish_operation_t* op = calloc(1, sizeof(publish_operation_t));
    if (!op) return -1;
    
    // Copy event ID
    strncpy(op->event_id, event_id, sizeof(op->event_id) - 1);
    op->event_id[sizeof(op->event_id) - 1] = '\0';
    
    // Set callback and user data
    op->callback = callback;
    op->user_data = user_data;
    op->publish_time = time(NULL);
    op->total_relay_count = relay_count;
    op->pending_relay_count = relay_count;
    
    // Copy relay URLs
    op->pending_relay_urls = malloc(relay_count * sizeof(char*));
    if (!op->pending_relay_urls) {
        free(op);
        return -1;
    }
    
    for (int i = 0; i < relay_count; i++) {
        op->pending_relay_urls[i] = strdup(relay_urls[i]);
        if (!op->pending_relay_urls[i]) {
            // Cleanup on failure
            for (int j = 0; j < i; j++) {
                free(op->pending_relay_urls[j]);
            }
            free(op->pending_relay_urls);
            free(op);
            return -1;
        }
    }
    
    // Add to pool
    pool->publish_operations[pool->publish_operation_count++] = op;
    return 0;
}

static publish_operation_t* find_publish_operation(nostr_relay_pool_t* pool, const char* event_id) {
    if (!pool || !event_id) return NULL;
    
    for (int i = 0; i < pool->publish_operation_count; i++) {
        if (pool->publish_operations[i] &&
            strcmp(pool->publish_operations[i]->event_id, event_id) == 0) {
            return pool->publish_operations[i];
        }
    }
    return NULL;
}

static void remove_publish_operation(nostr_relay_pool_t* pool, const char* event_id) {
    if (!pool || !event_id) return;
    
    for (int i = 0; i < pool->publish_operation_count; i++) {
        if (pool->publish_operations[i] &&
            strcmp(pool->publish_operations[i]->event_id, event_id) == 0) {
            
            publish_operation_t* op = pool->publish_operations[i];
            
            // Free relay URLs (only non-NULL ones)
            if (op->pending_relay_urls) {
                for (int j = 0; j < op->total_relay_count; j++) {
                    if (op->pending_relay_urls[j]) {
                        free(op->pending_relay_urls[j]);
                        op->pending_relay_urls[j] = NULL;
                    }
                }
                free(op->pending_relay_urls);
                op->pending_relay_urls = NULL;
            }
            free(op);
            
            // Shift remaining operations
            for (int j = i; j < pool->publish_operation_count - 1; j++) {
                pool->publish_operations[j] = pool->publish_operations[j + 1];
            }
            pool->publish_operations[--pool->publish_operation_count] = NULL;
            break;
        }
    }
}

static int remove_relay_from_publish_operation(publish_operation_t* op, const char* relay_url) {
    if (!op || !relay_url) return -1;
    
    for (int i = 0; i < op->pending_relay_count; i++) {
        if (op->pending_relay_urls[i] && strcmp(op->pending_relay_urls[i], relay_url) == 0) {
            // Free this relay URL
            free(op->pending_relay_urls[i]);
            op->pending_relay_urls[i] = NULL; // Mark as freed
            
            // Shift remaining URLs
            for (int j = i; j < op->pending_relay_count - 1; j++) {
                op->pending_relay_urls[j] = op->pending_relay_urls[j + 1];
            }
            op->pending_relay_urls[op->pending_relay_count - 1] = NULL; // Clear the last slot
            op->pending_relay_count--;
            return op->pending_relay_count; // Return remaining count
        }
    }
    return -1; // Relay not found
}

// Helper function to ensure relay connection
static int ensure_relay_connection(relay_connection_t* relay) {
    if (!relay) {
        return -1;
    }

    if (relay->ws_client && nostr_ws_get_state(relay->ws_client) == NOSTR_WS_CONNECTED) {
        relay->status = NOSTR_POOL_RELAY_CONNECTED;
        return 0; // Already connected
    }

    // Close existing connection if any
    if (relay->ws_client) {
        nostr_ws_close(relay->ws_client);
        relay->ws_client = NULL;
    }

    // Attempt connection
    relay->status = NOSTR_POOL_RELAY_CONNECTING;
    relay->stats.connection_attempts++;

    relay->ws_client = nostr_ws_connect(relay->url);

    if (!relay->ws_client) {
        relay->status = NOSTR_POOL_RELAY_ERROR;
        relay->reconnect_attempts++;
        relay->stats.connection_failures++;
        // Set connection error message
        snprintf(relay->last_connection_error, sizeof(relay->last_connection_error),
                "Failed to create WebSocket client for relay %s", relay->url);
        relay->last_connection_error_time = time(NULL);
        return -1;
    }

    nostr_ws_state_t state = nostr_ws_get_state(relay->ws_client);

    if (state == NOSTR_WS_CONNECTED) {
        relay->status = NOSTR_POOL_RELAY_CONNECTED;
        relay->connect_time = time(NULL);
        relay->reconnect_attempts = 0;

        // Initialize ping/pong monitoring on new connection
        relay->last_ping_sent = time(NULL);
        relay->last_pong_received = time(NULL);
        relay->ping_pending = 0;

        return 0;
    } else {
        relay->status = NOSTR_POOL_RELAY_ERROR;
        relay->reconnect_attempts++;
        relay->stats.connection_failures++;

        // Set connection error message
        snprintf(relay->last_connection_error, sizeof(relay->last_connection_error),
                "WebSocket connection failed for relay %s (state: %d)", relay->url, state);
        relay->last_connection_error_time = time(NULL);

        // Close the failed connection
        nostr_ws_close(relay->ws_client);
        relay->ws_client = NULL;

        return -1;
    }
}

// Reconnection helper functions
static int should_attempt_reconnect(relay_connection_t* relay) {
    if (!relay->pool->reconnect_config.enable_auto_reconnect) return 0;
    if (relay->status == NOSTR_POOL_RELAY_CONNECTED) return 0;
    if (relay->reconnect_attempts >= relay->pool->reconnect_config.max_reconnect_attempts) return 0;

    time_t now = time(NULL);
    return (now >= relay->next_reconnect_time);
}

static int calculate_reconnect_delay(relay_connection_t* relay) {
    int delay = relay->pool->reconnect_config.initial_reconnect_delay_ms;

    // Apply exponential backoff
    for (int i = 1; i < relay->reconnect_attempts; i++) {
        delay *= relay->pool->reconnect_config.reconnect_backoff_multiplier;
        if (delay > relay->pool->reconnect_config.max_reconnect_delay_ms) {
            delay = relay->pool->reconnect_config.max_reconnect_delay_ms;
            break;
        }
    }

    return delay;
}

static void restore_subscriptions_on_reconnect(relay_connection_t* relay) {
    // Find subscriptions that should be active on this relay
    for (int i = 0; i < relay->pool->subscription_count; i++) {
        nostr_pool_subscription_t* sub = relay->pool->subscriptions[i];
        if (!sub->closed) {
            // Check if this subscription should be on this relay
            for (int j = 0; j < sub->relay_count; j++) {
                if (strcmp(sub->relay_urls[j], relay->url) == 0) {
                    // Re-send the subscription
                    if (nostr_relay_send_req(relay->ws_client, sub->subscription_id, sub->filter) >= 0) {
                        // Add timing for latency measurement
                        add_subscription_timing(relay, sub->subscription_id);
                    }
                    break;
                }
            }
        }
    }
}

static void attempt_reconnect(relay_connection_t* relay) {
    relay->status = NOSTR_POOL_RELAY_CONNECTING;
    relay->last_reconnect_attempt = time(NULL);
    relay->reconnect_attempts++;

    if (ensure_relay_connection(relay) == 0) {
        // Success! Reset reconnection state
        relay->reconnect_attempts = 0;
        relay->next_reconnect_time = 0;

        // Restore subscriptions on reconnect
        restore_subscriptions_on_reconnect(relay);
    } else {
        // Failed - schedule next attempt with backoff
        int delay_ms = calculate_reconnect_delay(relay);
        relay->next_reconnect_time = time(NULL) + (delay_ms / 1000);
    }
}

// Connection health monitoring (ping/pong)
static void check_connection_health(relay_connection_t* relay) {
    time_t now = time(NULL);

    // Send ping if interval elapsed and ping is enabled
    if (relay->pool->reconnect_config.ping_interval_seconds > 0 &&
        now - relay->last_ping_sent >= relay->pool->reconnect_config.ping_interval_seconds &&
        !relay->ping_pending) {

        if (nostr_ws_ping(relay->ws_client) == 0) {
            relay->last_ping_sent = now;
            relay->ping_pending = 1;
            // Store high-resolution start time for latency measurement
            relay->pending_ping_start_ms = get_current_time_ms();
        }
    }

    // Check for pong timeout
    if (relay->ping_pending &&
        now - relay->last_ping_sent > relay->pool->reconnect_config.pong_timeout_seconds) {

        // No pong received - connection is dead
        relay->status = NOSTR_POOL_RELAY_DISCONNECTED;
        relay->ping_pending = 0;
    }
}

static void handle_pong_response(relay_connection_t* relay) {
    relay->last_pong_received = time(NULL);

    if (relay->ping_pending) {
        // Calculate ping latency
        double current_time_ms = get_current_time_ms();
        double ping_latency = current_time_ms - relay->pending_ping_start_ms;

        // Update ping statistics
        if (relay->stats.ping_samples == 0) {
            relay->stats.ping_latency_avg = ping_latency;
            relay->stats.ping_latency_min = ping_latency;
            relay->stats.ping_latency_max = ping_latency;
        } else {
            relay->stats.ping_latency_avg =
                (relay->stats.ping_latency_avg * relay->stats.ping_samples + ping_latency) /
                (relay->stats.ping_samples + 1);

            if (ping_latency < relay->stats.ping_latency_min) {
                relay->stats.ping_latency_min = ping_latency;
            }
            if (ping_latency > relay->stats.ping_latency_max) {
                relay->stats.ping_latency_max = ping_latency;
            }
        }

        relay->stats.ping_latency_current = ping_latency;
        relay->stats.ping_samples++;
        relay->ping_pending = 0;
    }
}

static int is_event_seen(nostr_relay_pool_t* pool, const char* event_id) {
    if (!pool || !event_id) return 0;

    for (int i = 0; i < pool->seen_count; i++) {
        if (strcmp(pool->seen_event_ids[i], event_id) == 0) {
            return 1;
        }
    }
    return 0;
}

// Helper function to mark event as seen
static void mark_event_seen(nostr_relay_pool_t* pool, const char* event_id) {
    if (!pool || !event_id) return;

    // Don't add duplicates
    if (is_event_seen(pool, event_id)) return;

    // Use circular buffer for seen events
    strncpy(pool->seen_event_ids[pool->seen_next_index], event_id, 64);
    pool->seen_event_ids[pool->seen_next_index][64] = '\0';

    pool->seen_next_index = (pool->seen_next_index + 1) % NOSTR_POOL_MAX_SEEN_EVENTS;
    if (pool->seen_count < NOSTR_POOL_MAX_SEEN_EVENTS) {
        pool->seen_count++;
    }
}

// Default configuration helper
nostr_pool_reconnect_config_t* nostr_pool_reconnect_config_default(void) {
    static nostr_pool_reconnect_config_t config = {
        .enable_auto_reconnect = 1,
        .max_reconnect_attempts = 10,
        .initial_reconnect_delay_ms = 1000,
        .max_reconnect_delay_ms = 30000,
        .reconnect_backoff_multiplier = 2,
        .ping_interval_seconds = 30,
        .pong_timeout_seconds = 10
    };
    return &config;
}

// Pool management functions
nostr_relay_pool_t* nostr_relay_pool_create(nostr_pool_reconnect_config_t* config) {
    if (!config) {
        config = nostr_pool_reconnect_config_default();
    }

    nostr_relay_pool_t* pool = calloc(1, sizeof(nostr_relay_pool_t));
    if (!pool) {
        return NULL;
    }

    // Copy configuration
    pool->reconnect_config = *config;
    pool->default_timeout_ms = NOSTR_POOL_DEFAULT_TIMEOUT;

    return pool;
}

// Compatibility wrapper for old API
nostr_relay_pool_t* nostr_relay_pool_create_compat(void) {
    return nostr_relay_pool_create(NULL);
}

int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url) {
    if (!pool || !relay_url || pool->relay_count >= NOSTR_POOL_MAX_RELAYS) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Check if relay already exists
    if (find_relay_by_url(pool, relay_url)) {
        return NOSTR_SUCCESS; // Already exists
    }
    
    // Create new relay connection
    relay_connection_t* relay = calloc(1, sizeof(relay_connection_t));
    if (!relay) {
        return NOSTR_ERROR_MEMORY_FAILED;
    }
    
    relay->url = strdup(relay_url);
    if (!relay->url) {
        free(relay);
        return NOSTR_ERROR_MEMORY_FAILED;
    }
    
    relay->status = NOSTR_POOL_RELAY_DISCONNECTED;
    relay->ws_client = NULL;
    relay->connect_time = 0;
    relay->pool = pool; // Set back reference

    // Initialize reconnection state
    relay->reconnect_attempts = 0;
    relay->last_reconnect_attempt = 0;
    relay->next_reconnect_time = 0;

    // Initialize ping/pong monitoring
    relay->last_ping_sent = 0;
    relay->last_pong_received = 0;
    relay->ping_pending = 0;
    relay->pending_ping_start_ms = 0.0;
    
    // Initialize statistics
    memset(&relay->stats, 0, sizeof(relay->stats));
    relay->stats.connection_uptime_start = time(NULL);
    relay->stats.ping_latency_min = -1.0;
    relay->stats.ping_latency_max = -1.0;
    
    pool->relays[pool->relay_count++] = relay;
    return NOSTR_SUCCESS;
}

int nostr_relay_pool_remove_relay(nostr_relay_pool_t* pool, const char* relay_url) {
    if (!pool || !relay_url) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    for (int i = 0; i < pool->relay_count; i++) {
        if (pool->relays[i] && pool->relays[i]->url && 
            strcmp(pool->relays[i]->url, relay_url) == 0) {
            
            // Close connection if active
            if (pool->relays[i]->ws_client) {
                nostr_ws_close(pool->relays[i]->ws_client);
            }
            
            // Free memory
            free(pool->relays[i]->url);
            free(pool->relays[i]);
            
            // Shift remaining relays
            for (int j = i; j < pool->relay_count - 1; j++) {
                pool->relays[j] = pool->relays[j + 1];
            }
            pool->relays[--pool->relay_count] = NULL;
            
            return NOSTR_SUCCESS;
        }
    }
    
    return NOSTR_ERROR_INVALID_INPUT; // Relay not found
}

void nostr_relay_pool_destroy(nostr_relay_pool_t* pool) {
    if (!pool) return;
    
    // Close all subscriptions
    for (int i = 0; i < pool->subscription_count; i++) {
        if (pool->subscriptions[i]) {
            nostr_pool_subscription_close(pool->subscriptions[i]);
        }
    }
    
    // Clean up all pending publish operations
    for (int i = 0; i < pool->publish_operation_count; i++) {
        if (pool->publish_operations[i]) {
            publish_operation_t* op = pool->publish_operations[i];
            
            // Free relay URLs
            for (int j = 0; j < op->total_relay_count; j++) {
                free(op->pending_relay_urls[j]);
            }
            free(op->pending_relay_urls);
            free(op);
        }
    }
    
    // Close all relay connections
    for (int i = 0; i < pool->relay_count; i++) {
        if (pool->relays[i]) {
            if (pool->relays[i]->ws_client) {
                nostr_ws_close(pool->relays[i]->ws_client);
            }
            free(pool->relays[i]->url);
            free(pool->relays[i]);
        }
    }
    
    free(pool);
}


// Subscription management
nostr_pool_subscription_t* nostr_relay_pool_subscribe(
    nostr_relay_pool_t* pool,
    const char** relay_urls,
    int relay_count,
    cJSON* filter,
    void (*on_event)(cJSON* event, const char* relay_url, void* user_data),
    void (*on_eose)(cJSON** events, int event_count, void* user_data),
    void* user_data,
    int close_on_eose,
    int enable_deduplication,
    nostr_pool_eose_result_mode_t result_mode,
    int relay_timeout_seconds,
    int eose_timeout_seconds) {
    
    if (!pool || !relay_urls || relay_count <= 0 || !filter || 
        pool->subscription_count >= NOSTR_POOL_MAX_SUBSCRIPTIONS) {
        return NULL;
    }
    
    // Create subscription
    nostr_pool_subscription_t* sub = calloc(1, sizeof(nostr_pool_subscription_t));
    if (!sub) {
        return NULL;
    }
    
    // Generate unique subscription ID
    generate_subscription_id(sub->subscription_id, sizeof(sub->subscription_id));
    
    // Copy filter
    sub->filter = cJSON_Duplicate(filter, 1);
    if (!sub->filter) {
        free(sub);
        return NULL;
    }
    
    // Copy relay URLs
    sub->relay_urls = malloc(relay_count * sizeof(char*));
    sub->eose_received = calloc(relay_count, sizeof(int));
    if (!sub->relay_urls || !sub->eose_received) {
        cJSON_Delete(sub->filter);
        free(sub->relay_urls);
        free(sub->eose_received);
        free(sub);
        return NULL;
    }
    
    sub->relay_count = relay_count;
    for (int i = 0; i < relay_count; i++) {
        sub->relay_urls[i] = strdup(relay_urls[i]);
        if (!sub->relay_urls[i]) {
            // Cleanup on failure
            for (int j = 0; j < i; j++) {
                free(sub->relay_urls[j]);
            }
            cJSON_Delete(sub->filter);
            free(sub->relay_urls);
            free(sub->eose_received);
            free(sub);
            return NULL;
        }
    }
    
    // Set callbacks
    sub->on_event = on_event;
    sub->on_eose = on_eose;
    sub->user_data = user_data;
    sub->closed = 0;
    sub->close_on_eose = close_on_eose;
    sub->pool = pool;

    // Set new subscription control parameters
    sub->enable_deduplication = enable_deduplication;
    sub->result_mode = result_mode;
    sub->relay_timeout_seconds = relay_timeout_seconds;
    sub->eose_timeout_seconds = eose_timeout_seconds;
    sub->subscription_start_time = time(NULL);

    // Initialize event collection arrays (only for EOSE result modes)
    if (result_mode != NOSTR_POOL_EOSE_FIRST) {
        sub->collected_events_capacity = 10; // Initial capacity
        sub->collected_events = calloc(sub->collected_events_capacity, sizeof(cJSON*));
        if (!sub->collected_events) {
            // Cleanup on failure
            cJSON_Delete(sub->filter);
            for (int j = 0; j < relay_count; j++) {
                free(sub->relay_urls[j]);
            }
            free(sub->relay_urls);
            free(sub->eose_received);
            free(sub);
            return NULL;
        }
        sub->collected_event_count = 0;
    } else {
        sub->collected_events = NULL;
        sub->collected_event_count = 0;
        sub->collected_events_capacity = 0;
    }

    // Initialize per-relay activity tracking
    sub->relay_last_activity = calloc(relay_count, sizeof(time_t));
    if (!sub->relay_last_activity) {
        // Cleanup on failure
        if (sub->collected_events) free(sub->collected_events);
        cJSON_Delete(sub->filter);
        for (int j = 0; j < relay_count; j++) {
            free(sub->relay_urls[j]);
        }
        free(sub->relay_urls);
        free(sub->eose_received);
        free(sub);
        return NULL;
    }
    // Initialize all relay activity times to current time
    time_t now = time(NULL);
    for (int i = 0; i < relay_count; i++) {
        sub->relay_last_activity[i] = now;
    }
    
    // Add to pool
    pool->subscriptions[pool->subscription_count++] = sub;
    
    // Send subscription to all specified relays
    for (int i = 0; i < relay_count; i++) {
        relay_connection_t* relay = find_relay_by_url(pool, relay_urls[i]);
        if (!relay) {
            // Add relay if it doesn't exist
            if (nostr_relay_pool_add_relay(pool, relay_urls[i]) == NOSTR_SUCCESS) {
                relay = find_relay_by_url(pool, relay_urls[i]);
            }
        }
        
        if (relay && ensure_relay_connection(relay) == 0) {
            // Add subscription timing for latency measurement
            add_subscription_timing(relay, sub->subscription_id);
            
            // Send REQ message
            if (nostr_relay_send_req(relay->ws_client, sub->subscription_id, sub->filter) < 0) {
                // Remove timing if send failed
                remove_subscription_timing(relay, sub->subscription_id);
            }
        }
    }
    
    return sub;
}

int nostr_pool_subscription_close(nostr_pool_subscription_t* subscription) {
    if (!subscription || subscription->closed) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    subscription->closed = 1;
    
    // Send CLOSE messages to all relays
    for (int i = 0; i < subscription->relay_count; i++) {
        relay_connection_t* relay = find_relay_by_url(subscription->pool, subscription->relay_urls[i]);
        if (relay && relay->ws_client) {
            nostr_relay_send_close(relay->ws_client, subscription->subscription_id);
        }
    }
    
    // Remove from pool
    nostr_relay_pool_t* pool = subscription->pool;
    for (int i = 0; i < pool->subscription_count; i++) {
        if (pool->subscriptions[i] == subscription) {
            // Shift remaining subscriptions
            for (int j = i; j < pool->subscription_count - 1; j++) {
                pool->subscriptions[j] = pool->subscriptions[j + 1];
            }
            pool->subscriptions[--pool->subscription_count] = NULL;
            break;
        }
    }
    
    // Cleanup subscription
    cJSON_Delete(subscription->filter);
    for (int i = 0; i < subscription->relay_count; i++) {
        free(subscription->relay_urls[i]);
    }
    free(subscription->relay_urls);
    free(subscription->eose_received);
    free(subscription);
    
    return NOSTR_SUCCESS;
}

// Event processing
static void process_relay_message(nostr_relay_pool_t* pool, relay_connection_t* relay, const char* message) {
    if (!pool || !relay || !message) {
        return;
    }
    
    char* msg_type = NULL;
    cJSON* parsed = NULL;
    
    if (nostr_parse_relay_message(message, &msg_type, &parsed) != 0) {
        return;
    }
    
    relay->stats.last_event_time = time(NULL);

    if (strcmp(msg_type, "EVENT") == 0) {
        // Handle EVENT message: ["EVENT", subscription_id, event]
        if (cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 3) {
            cJSON* sub_id_json = cJSON_GetArrayItem(parsed, 1);
            cJSON* event = cJSON_GetArrayItem(parsed, 2);
            
            if (cJSON_IsString(sub_id_json) && event) {
                const char* subscription_id = cJSON_GetStringValue(sub_id_json);
                cJSON* event_id_json = cJSON_GetObjectItem(event, "id");
                
                if (event_id_json && cJSON_IsString(event_id_json)) {
                    const char* event_id = cJSON_GetStringValue(event_id_json);
                    
                    // Find subscription first
                    nostr_pool_subscription_t* sub = NULL;
                    for (int i = 0; i < pool->subscription_count; i++) {
                        if (pool->subscriptions[i] && !pool->subscriptions[i]->closed &&
                            strcmp(pool->subscriptions[i]->subscription_id, subscription_id) == 0) {
                            sub = pool->subscriptions[i];
                            break;
                        }
                    }

                    if (sub) {
                        // Check for duplicate (per-subscription deduplication)
                        int is_duplicate = 0;
                        if (sub->enable_deduplication) {
                            if (is_event_seen(pool, event_id)) {
                                is_duplicate = 1;
                            } else {
                                mark_event_seen(pool, event_id);
                            }
                        }

                        if (!is_duplicate) {
                            relay->stats.events_received++;

                            // Measure query latency (first event response)
                            double latency_ms = remove_subscription_timing(relay, subscription_id);
                            if (latency_ms > 0.0) {
                                // Update query latency statistics
                                if (relay->stats.query_samples == 0) {
                                    relay->stats.query_latency_avg = latency_ms;
                                    relay->stats.query_latency_min = latency_ms;
                                    relay->stats.query_latency_max = latency_ms;
                                } else {
                                    relay->stats.query_latency_avg =
                                        (relay->stats.query_latency_avg * relay->stats.query_samples + latency_ms) /
                                        (relay->stats.query_samples + 1);

                                    if (latency_ms < relay->stats.query_latency_min) {
                                        relay->stats.query_latency_min = latency_ms;
                                    }
                                    if (latency_ms > relay->stats.query_latency_max) {
                                        relay->stats.query_latency_max = latency_ms;
                                    }
                                }
                                relay->stats.query_samples++;
                            }

                            // Call event callback
                            if (sub->on_event) {
                                sub->on_event(event, relay->url, sub->user_data);
                            }
                        }
                    }
                }
            }
        }
    } else if (strcmp(msg_type, "EOSE") == 0) {
        // Handle End Of Stored Events: ["EOSE", subscription_id]
        if (cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 2) {
            cJSON* sub_id_json = cJSON_GetArrayItem(parsed, 1);
            
            if (cJSON_IsString(sub_id_json)) {
                const char* subscription_id = cJSON_GetStringValue(sub_id_json);
                
                // Find subscription and mark EOSE received for this relay
                for (int i = 0; i < pool->subscription_count; i++) {
                    nostr_pool_subscription_t* sub = pool->subscriptions[i];
                    if (sub && !sub->closed && 
                        strcmp(sub->subscription_id, subscription_id) == 0) {
                        
                        // Find relay index and mark EOSE received
                        for (int j = 0; j < sub->relay_count; j++) {
                            if (strcmp(sub->relay_urls[j], relay->url) == 0) {
                                sub->eose_received[j] = 1;
                                break;
                            }
                        }
                        
                        // Check if all relays have sent EOSE
                        int all_eose = 1;
                        for (int j = 0; j < sub->relay_count; j++) {
                            if (!sub->eose_received[j]) {
                                all_eose = 0;
                                break;
                            }
                        }
                        
                        if (all_eose) {
                            if (sub->on_eose) {
                                // Pass collected events based on result mode
                                if (sub->result_mode == NOSTR_POOL_EOSE_FIRST) {
                                    // FIRST mode: no events collected, pass NULL/0
                                    sub->on_eose(NULL, 0, sub->user_data);
                                } else {
                                    // FULL_SET or MOST_RECENT: pass collected events
                                    sub->on_eose(sub->collected_events, sub->collected_event_count, sub->user_data);
                                }
                            }

                            // Auto-close subscription if close_on_eose is enabled
                            if (sub->close_on_eose && !sub->closed) {
                                nostr_pool_subscription_close(sub);
                            }
                        }
                        break;
                    }
                }
            }
        }
    } else if (strcmp(msg_type, "OK") == 0) {
        // Handle OK response: ["OK", event_id, true/false, message]
        if (cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 3) {
            cJSON* event_id_json = cJSON_GetArrayItem(parsed, 1);
            cJSON* success_flag = cJSON_GetArrayItem(parsed, 2);
            
            if (cJSON_IsString(event_id_json) && cJSON_IsBool(success_flag)) {
                const char* event_id = cJSON_GetStringValue(event_id_json);
                int success = cJSON_IsTrue(success_flag);
                const char* error_message = NULL;
                
                // Extract error message if available
                if (!success && cJSON_GetArraySize(parsed) >= 4) {
                    cJSON* error_msg = cJSON_GetArrayItem(parsed, 3);
                    if (cJSON_IsString(error_msg)) {
                        error_message = cJSON_GetStringValue(error_msg);
                    }
                }
                
                // Update relay statistics
                if (success) {
                    relay->stats.events_published_ok++;
                } else {
                    relay->stats.events_published_failed++;
                    // Store error message for legacy API
                    if (error_message) {
                        strncpy(relay->last_publish_error, error_message,
                               sizeof(relay->last_publish_error) - 1);
                        relay->last_publish_error[sizeof(relay->last_publish_error) - 1] = '\0';
                        relay->last_publish_error_time = time(NULL);
                    }
                }
                
                // Check for async publish operation callback
                publish_operation_t* op = find_publish_operation(pool, event_id);
                if (op && op->callback) {
                    // Call the user's callback
                    op->callback(relay->url, event_id, success, error_message, op->user_data);
                    
                    // Remove this relay from the pending list
                    int remaining = remove_relay_from_publish_operation(op, relay->url);
                    
                    // If no more relays pending, remove the operation
                    if (remaining == 0) {
                        remove_publish_operation(pool, event_id);
                    }
                }
            }
        }
    } else if (strcmp(msg_type, "PONG") == 0) {
        // Handle PONG response for connection health monitoring
        handle_pong_response(relay);
    }
    
    if (msg_type) free(msg_type);
    if (parsed) cJSON_Delete(parsed);
}

// Query functions
cJSON** nostr_relay_pool_query_sync(
    nostr_relay_pool_t* pool,
    const char** relay_urls,
    int relay_count, 
    cJSON* filter,
    int* event_count,
    int timeout_ms) {
    
    if (!pool || !relay_urls || relay_count <= 0 || !filter || !event_count) {
        if (event_count) *event_count = 0;
        return NULL;
    }
    
    cJSON** events = NULL;
    *event_count = 0;
    int events_capacity = 0;
    
    // Generate unique subscription ID
    char subscription_id[NOSTR_POOL_SUBSCRIPTION_ID_SIZE];
    generate_subscription_id(subscription_id, sizeof(subscription_id));
    
    // Connect to relays and send REQ
    relay_connection_t* connected_relays[NOSTR_POOL_MAX_RELAYS];
    int connected_count = 0;
    
    for (int i = 0; i < relay_count && connected_count < NOSTR_POOL_MAX_RELAYS; i++) {
        relay_connection_t* relay = find_relay_by_url(pool, relay_urls[i]);
        if (!relay) {
            // Add relay if it doesn't exist
            if (nostr_relay_pool_add_relay(pool, relay_urls[i]) == NOSTR_SUCCESS) {
                relay = find_relay_by_url(pool, relay_urls[i]);
            }
        }
        
        if (relay && ensure_relay_connection(relay) == 0) {
            connected_relays[connected_count++] = relay;
            
            // Add subscription timing for latency measurement
            add_subscription_timing(relay, subscription_id);
            
            // Send REQ message
            if (nostr_relay_send_req(relay->ws_client, subscription_id, filter) < 0) {
                // Remove timing if send failed
                remove_subscription_timing(relay, subscription_id);
                connected_count--; // Don't count failed connections
            }
        }
    }
    
    if (connected_count == 0) {
        return NULL;
    }
    
    // Wait for responses
    time_t start_time = time(NULL);
    int eose_count = 0;
    
    while (time(NULL) - start_time < (timeout_ms / 1000) && eose_count < connected_count) {
        for (int i = 0; i < connected_count; i++) {
            relay_connection_t* relay = connected_relays[i];
            
            char buffer[8192];
            int len = nostr_ws_receive(relay->ws_client, buffer, sizeof(buffer) - 1, 100);
            if (len > 0) {
                buffer[len] = '\0';

                // Check if this is a pong message (WebSocket library prefixes pong messages)
                if (strncmp(buffer, "__PONG__", 8) == 0) {
                    // Handle pong response for connection health monitoring
                    handle_pong_response(relay);
                } else {
                    // Process as regular NOSTR message
                    char* msg_type = NULL;
                    cJSON* parsed = NULL;
                    if (nostr_parse_relay_message(buffer, &msg_type, &parsed) == 0) {
                        if (msg_type && strcmp(msg_type, "EVENT") == 0) {
                        // Handle EVENT message
                        if (cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 3) {
                            cJSON* sub_id_json = cJSON_GetArrayItem(parsed, 1);
                            cJSON* event = cJSON_GetArrayItem(parsed, 2);
                            
                            if (cJSON_IsString(sub_id_json) && event &&
                                strcmp(cJSON_GetStringValue(sub_id_json), subscription_id) == 0) {
                                
                                cJSON* event_id_json = cJSON_GetObjectItem(event, "id");
                                if (event_id_json && cJSON_IsString(event_id_json)) {
                                    const char* event_id = cJSON_GetStringValue(event_id_json);
                                    
                                    // Check for duplicate
                                    if (!is_event_seen(pool, event_id)) {
                                        mark_event_seen(pool, event_id);
                                        relay->stats.events_received++;
                                        
                                        // Add to results array
                                        if (*event_count >= events_capacity) {
                                            events_capacity = events_capacity == 0 ? 10 : events_capacity * 2;
                                            events = realloc(events, events_capacity * sizeof(cJSON*));
                                            if (!events) {
                                                *event_count = 0;
                                                break;
                                            }
                                        }
                                        
                                        events[(*event_count)++] = cJSON_Duplicate(event, 1);
                                    }
                                }
                            }
                        }
                    } else if (msg_type && strcmp(msg_type, "EOSE") == 0) {
                        // Handle EOSE message
                        if (cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 2) {
                            cJSON* sub_id_json = cJSON_GetArrayItem(parsed, 1);
                            if (cJSON_IsString(sub_id_json) &&
                                strcmp(cJSON_GetStringValue(sub_id_json), subscription_id) == 0) {
                                eose_count++;
                            }
                        }
                    }
                    if (msg_type) free(msg_type);
                    if (parsed) cJSON_Delete(parsed);
                    }
                }
            }
        }
    }
    
    // Send CLOSE messages
    for (int i = 0; i < connected_count; i++) {
        relay_connection_t* relay = connected_relays[i];
        if (relay->ws_client) {
            nostr_relay_send_close(relay->ws_client, subscription_id);
        }
    }
    
    return events;
}

cJSON* nostr_relay_pool_get_event(
    nostr_relay_pool_t* pool,
    const char** relay_urls,
    int relay_count,
    cJSON* filter,
    int timeout_ms) {
    
    int event_count = 0;
    cJSON** events = nostr_relay_pool_query_sync(pool, relay_urls, relay_count, filter, &event_count, timeout_ms);
    
    if (!events || event_count == 0) {
        return NULL;
    }
    
    // Return the most recent event (highest created_at)
    cJSON* most_recent = events[0];
    time_t most_recent_time = 0;
    
    for (int i = 0; i < event_count; i++) {
        cJSON* created_at = cJSON_GetObjectItem(events[i], "created_at");
        if (created_at && cJSON_IsNumber(created_at)) {
            time_t event_time = (time_t)cJSON_GetNumberValue(created_at);
            if (event_time > most_recent_time) {
                most_recent_time = event_time;
                most_recent = events[i];
            }
        }
    }
    
    // Duplicate the most recent event
    cJSON* result = cJSON_Duplicate(most_recent, 1);
    
    // Free all events
    for (int i = 0; i < event_count; i++) {
        cJSON_Delete(events[i]);
    }
    free(events);
    
    return result;
}


int nostr_relay_pool_publish_async(
    nostr_relay_pool_t* pool,
    const char** relay_urls,
    int relay_count,
    cJSON* event,
    publish_response_callback_t callback,
    void* user_data) {
    
    if (!pool || !relay_urls || relay_count <= 0 || !event) {
        return -1;
    }
    
    // Extract event ID for tracking
    cJSON* event_id_json = cJSON_GetObjectItem(event, "id");
    if (!event_id_json || !cJSON_IsString(event_id_json)) {
        return -1; // Event must have an ID
    }
    const char* event_id = cJSON_GetStringValue(event_id_json);
    
    // Add publish operation for tracking (only if callback provided)
    publish_operation_t* op = NULL;
    if (callback) {
        if (add_publish_operation(pool, event_id, relay_urls, relay_count, callback, user_data) != 0) {
            return -1; // Failed to add operation
        }
        op = find_publish_operation(pool, event_id);
    }
    
    int success_count = 0;
    
    for (int i = 0; i < relay_count; i++) {
        relay_connection_t* relay = find_relay_by_url(pool, relay_urls[i]);
        if (!relay) {
            // Add relay if it doesn't exist
            if (nostr_relay_pool_add_relay(pool, relay_urls[i]) == NOSTR_SUCCESS) {
                relay = find_relay_by_url(pool, relay_urls[i]);
            }
        }
        
        if (relay && ensure_relay_connection(relay) == 0) {
            // Send EVENT message
            if (nostr_relay_send_event(relay->ws_client, event) >= 0) {
                relay->stats.events_published++;
                success_count++;
            } else {
                // If send failed and we have a callback, notify immediately
                if (callback && op) {
                    callback(relay_urls[i], event_id, 0, "Failed to send event to relay", user_data);
                    
                    // Remove this relay from the pending operation
                    int remaining = remove_relay_from_publish_operation(op, relay_urls[i]);
                    if (remaining == 0) {
                        remove_publish_operation(pool, event_id);
                        op = NULL; // Mark as removed to prevent double-free
                    }
                }
            }
        } else {
            // Connection failed - notify callback immediately if provided
            if (callback && op) {
                callback(relay_urls[i], event_id, 0, "Failed to connect to relay", user_data);
                
                // Remove this relay from the pending operation
                int remaining = remove_relay_from_publish_operation(op, relay_urls[i]);
                if (remaining == 0) {
                    remove_publish_operation(pool, event_id);
                    op = NULL; // Mark as removed to prevent double-free
                }
            }
        }
    }
    
    return success_count;
}

// Status and statistics functions
nostr_pool_relay_status_t nostr_relay_pool_get_relay_status(
    nostr_relay_pool_t* pool, 
    const char* relay_url) {
    
    if (!pool || !relay_url) {
        return NOSTR_POOL_RELAY_ERROR;
    }
    
    relay_connection_t* relay = find_relay_by_url(pool, relay_url);
    if (!relay) {
        return NOSTR_POOL_RELAY_DISCONNECTED;
    }
    
    return relay->status;
}

int nostr_relay_pool_list_relays(
    nostr_relay_pool_t* pool,
    char*** relay_urls,
    nostr_pool_relay_status_t** statuses) {
    
    if (!pool || !relay_urls || !statuses) {
        return -1;
    }
    
    if (pool->relay_count == 0) {
        *relay_urls = NULL;
        *statuses = NULL;
        return 0;
    }
    
    *relay_urls = malloc(pool->relay_count * sizeof(char*));
    *statuses = malloc(pool->relay_count * sizeof(nostr_pool_relay_status_t));
    
    if (!*relay_urls || !*statuses) {
        free(*relay_urls);
        free(*statuses);
        return -1;
    }
    
    for (int i = 0; i < pool->relay_count; i++) {
        (*relay_urls)[i] = strdup(pool->relays[i]->url);
        (*statuses)[i] = pool->relays[i]->status;
    }
    
    return pool->relay_count;
}

const nostr_relay_stats_t* nostr_relay_pool_get_relay_stats(
    nostr_relay_pool_t* pool, 
    const char* relay_url) {
    
    if (!pool || !relay_url) {
        return NULL;
    }
    
    relay_connection_t* relay = find_relay_by_url(pool, relay_url);
    if (!relay) {
        return NULL;
    }
    
    return &relay->stats;
}

int nostr_relay_pool_reset_relay_stats(
    nostr_relay_pool_t* pool, 
    const char* relay_url) {
    
    if (!pool || !relay_url) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    relay_connection_t* relay = find_relay_by_url(pool, relay_url);
    if (!relay) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    memset(&relay->stats, 0, sizeof(relay->stats));
    relay->stats.connection_uptime_start = time(NULL);
    relay->stats.ping_latency_min = -1.0;
    relay->stats.ping_latency_max = -1.0;
    
    return NOSTR_SUCCESS;
}

double nostr_relay_pool_get_relay_ping_latency(
    nostr_relay_pool_t* pool, 
    const char* relay_url) {
    
    if (!pool || !relay_url) {
        return -1.0;
    }
    
    relay_connection_t* relay = find_relay_by_url(pool, relay_url);
    if (!relay) {
        return -1.0;
    }
    
    return relay->stats.ping_latency_current;
}

double nostr_relay_pool_get_relay_query_latency(
    nostr_relay_pool_t* pool,
    const char* relay_url) {

    if (!pool || !relay_url) {
        return -1.0;
    }

    relay_connection_t* relay = find_relay_by_url(pool, relay_url);
    if (!relay) {
        return -1.0;
    }

    return relay->stats.query_latency_avg;
}

const char* nostr_relay_pool_get_relay_last_publish_error(
    nostr_relay_pool_t* pool,
    const char* relay_url) {

    if (!pool || !relay_url) {
        return NULL;
    }

    relay_connection_t* relay = find_relay_by_url(pool, relay_url);
    if (!relay) {
        return NULL;
    }

    // Return error message only if it's recent (within last 60 seconds)
    if (relay->last_publish_error_time > 0 &&
        time(NULL) - relay->last_publish_error_time < 60) {
        return relay->last_publish_error[0] != '\0' ? relay->last_publish_error : NULL;
    }

    return NULL;
}

const char* nostr_relay_pool_get_relay_last_connection_error(
    nostr_relay_pool_t* pool,
    const char* relay_url) {

    if (!pool || !relay_url) {
        return NULL;
    }

    relay_connection_t* relay = find_relay_by_url(pool, relay_url);
    if (!relay) {
        return NULL;
    }

    // Return error message only if it's recent (within last 60 seconds)
    if (relay->last_connection_error_time > 0 &&
        time(NULL) - relay->last_connection_error_time < 60) {
        return relay->last_connection_error[0] != '\0' ? relay->last_connection_error : NULL;
    }

    return NULL;
}

int nostr_relay_pool_ping_relay(
    nostr_relay_pool_t* pool,
    const char* relay_url) {

    // PING FUNCTIONALITY DISABLED
    /* COMMENTED OUT - PING FUNCTIONALITY DISABLED
    if (!pool || !relay_url) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    relay_connection_t* relay = find_relay_by_url(pool, relay_url);
    if (!relay || !relay->ws_client) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    if (ensure_relay_connection(relay) != 0) {
        return NOSTR_ERROR_NETWORK_FAILED;
    }

    time_t current_time = time(NULL);
    relay->pending_ping_start_ms = get_current_time_ms();
    relay->ping_pending = 1;
    relay->last_ping_sent = current_time;
    relay->next_ping_time = current_time + NOSTR_POOL_PING_INTERVAL;

    if (nostr_ws_send_ping(relay->ws_client, "ping", 4) < 0) {
        relay->ping_pending = 0;
        return NOSTR_ERROR_NETWORK_FAILED;
    }

    return NOSTR_SUCCESS;
    */

    (void)pool; // Suppress unused parameter warning
    (void)relay_url; // Suppress unused parameter warning
    return NOSTR_ERROR_INVALID_INPUT; // Function disabled
}

int nostr_relay_pool_ping_relay_sync(
    nostr_relay_pool_t* pool,
    const char* relay_url,
    int timeout_seconds) {

    // PING FUNCTIONALITY DISABLED
    /* COMMENTED OUT - PING FUNCTIONALITY DISABLED
    if (!pool || !relay_url) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    relay_connection_t* relay = find_relay_by_url(pool, relay_url);
    if (!relay || !relay->ws_client) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    if (ensure_relay_connection(relay) != 0) {
        return NOSTR_ERROR_NETWORK_FAILED;
    }

    if (timeout_seconds <= 0) {
        timeout_seconds = 5;
    }

    time_t current_time = time(NULL);
    relay->pending_ping_start_ms = get_current_time_ms();
    relay->ping_pending = 1;
    relay->last_ping_sent = current_time;
    relay->next_ping_time = current_time + NOSTR_POOL_PING_INTERVAL;

    if (nostr_ws_send_ping(relay->ws_client, "ping", 4) < 0) {
        relay->ping_pending = 0;
        return NOSTR_ERROR_NETWORK_FAILED;
    }

    // Wait for PONG response
    time_t wait_start = time(NULL);
    while (time(NULL) - wait_start < timeout_seconds && relay->ping_pending) {
        char buffer[1024];
        int len = nostr_ws_receive(relay->ws_client, buffer, sizeof(buffer) - 1, 1000);
        if (len > 0) {
            buffer[len] = '\0';

            char* msg_type = NULL;
            cJSON* parsed = NULL;
            if (nostr_parse_relay_message(buffer, &msg_type, &parsed) == 0) {
                if (msg_type && strcmp(msg_type, "PONG") == 0) {
                    // Handle PONG - this would be processed in process_relay_message
                    // but we're calling this directly for sync operation
                    if (relay->ping_pending) {
                        double current_time_ms = get_current_time_ms();
                        double ping_latency = current_time_ms - relay->pending_ping_start_ms;

                        // Update ping statistics
                        if (relay->stats.ping_samples == 0) {
                            relay->stats.ping_latency_avg = ping_latency;
                            relay->stats.ping_latency_min = ping_latency;
                            relay->stats.ping_latency_max = ping_latency;
                        } else {
                            relay->stats.ping_latency_avg =
                                (relay->stats.ping_latency_avg * relay->stats.ping_samples + ping_latency) /
                                (relay->stats.ping_samples + 1);

                            if (ping_latency < relay->stats.ping_latency_min) {
                                relay->stats.ping_latency_min = ping_latency;
                            }
                            if (ping_latency > relay->stats.ping_latency_max) {
                                relay->stats.ping_latency_max = ping_latency;
                            }
                        }

                        relay->stats.ping_latency_current = ping_latency;
                        relay->stats.ping_samples++;
                        relay->ping_pending = 0;

                        if (msg_type) free(msg_type);
                        if (parsed) cJSON_Delete(parsed);
                        return NOSTR_SUCCESS;
                    }
                }
                if (msg_type) free(msg_type);
                if (parsed) cJSON_Delete(parsed);
            }
        }
    }

    // Timeout
    relay->ping_pending = 0;
    return NOSTR_ERROR_NETWORK_FAILED;
    */

    (void)pool; // Suppress unused parameter warning
    (void)relay_url; // Suppress unused parameter warning
    (void)timeout_seconds; // Suppress unused parameter warning
    return NOSTR_ERROR_INVALID_INPUT; // Function disabled
}

// Event processing functions
int nostr_relay_pool_run(nostr_relay_pool_t* pool, int timeout_ms) {
    if (!pool) {
        return -1;
    }
    
    struct timespec start_time;
    clock_gettime(CLOCK_MONOTONIC, &start_time);
    int total_events = 0;
    
    // Convert timeout_ms to nanoseconds for proper comparison
    long timeout_ns = timeout_ms * 1000000L;
    
    while (1) {
        // Check if timeout has been reached (only if timeout is specified)
        if (timeout_ms > 0) {
            struct timespec current_time;
            clock_gettime(CLOCK_MONOTONIC, &current_time);
            long elapsed_ns = (current_time.tv_sec - start_time.tv_sec) * 1000000000L + 
                             (current_time.tv_nsec - start_time.tv_nsec);
            if (elapsed_ns >= timeout_ns) {
                break;
            }
        }
        
        int events_processed = nostr_relay_pool_poll(pool, 100);
        
        if (events_processed < 0) {
            return events_processed; // Error
        }
        total_events += events_processed;
        
        // Small delay to prevent busy waiting
        if (events_processed == 0) {
            usleep(10000); // 10ms
        }
    }
    
    return total_events;
}

int nostr_relay_pool_poll(nostr_relay_pool_t* pool, int timeout_ms) {
    if (!pool) {
        return -1;
    }

    int events_processed = 0;

    for (int i = 0; i < pool->relay_count; i++) {
        relay_connection_t* relay = pool->relays[i];
        if (!relay) {
            continue;
        }

        // Check if reconnection is needed
        if (should_attempt_reconnect(relay)) {
            attempt_reconnect(relay);
        }

        // Skip if no WebSocket client
        if (!relay->ws_client) {
            continue;
        }

        // Check connection state
        nostr_ws_state_t state = nostr_ws_get_state(relay->ws_client);

        if (state != NOSTR_WS_CONNECTED) {
            relay->status = (state == NOSTR_WS_ERROR) ? NOSTR_POOL_RELAY_ERROR : NOSTR_POOL_RELAY_DISCONNECTED;
            continue;
        }

        relay->status = NOSTR_POOL_RELAY_CONNECTED;

        // Connection health monitoring (ping/pong)
        check_connection_health(relay);

        // Process incoming messages
        char buffer[8192];
        int timeout_per_relay = timeout_ms / pool->relay_count;

        int len = nostr_ws_receive(relay->ws_client, buffer, sizeof(buffer) - 1, timeout_per_relay);

        if (len > 0) {
            buffer[len] = '\0';

            // Check if this is a pong message (WebSocket library prefixes pong messages)
            if (strncmp(buffer, "__PONG__", 8) == 0) {
                // Handle pong response for connection health monitoring
                handle_pong_response(relay);
            } else {
                // Process as regular NOSTR message
                process_relay_message(pool, relay, buffer);
            }
            events_processed++;
        }
    }

    return events_processed;
}
