/*
 * Ginxsom Relay Client Implementation
 *
 * Manages connections to Nostr relays, publishes events, and subscribes to admin commands.
 */

#include "relay_client.h"
#include "admin_commands.h"
#include "../nostr_core_lib/nostr_core/nostr_core.h"
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>

// Forward declare app_log to avoid including ginxsom.h (which has typedef conflicts)
typedef enum {
    LOG_DEBUG = 0,
    LOG_INFO = 1,
    LOG_WARN = 2,
    LOG_ERROR = 3
} log_level_t;

void app_log(log_level_t level, const char* format, ...);

// Maximum number of relays to connect to
#define MAX_RELAYS 10

// Reconnection settings
#define RECONNECT_DELAY_SECONDS 30
#define MAX_RECONNECT_ATTEMPTS 5

// Global state
static struct {
    int enabled;
    int initialized;
    int running;
    char db_path[512];
    nostr_relay_pool_t* pool;
    char** relay_urls;
    int relay_count;
    nostr_pool_subscription_t* admin_subscription;
    pthread_t management_thread;
    pthread_mutex_t state_mutex;
} g_relay_state = {0};

// External globals from main.c
extern char g_blossom_seckey[65];
extern char g_blossom_pubkey[65];
extern char g_admin_pubkey[65];

// Forward declarations
static void *relay_management_thread(void *arg);
static int load_config_from_db(void);
static int parse_relay_urls(const char *json_array);
static int subscribe_to_admin_commands(void);
static void on_publish_response(const char* relay_url, const char* event_id, int success, const char* message, void* user_data);
static void on_admin_command_event(cJSON* event, const char* relay_url, void* user_data);
static void on_admin_subscription_eose(cJSON** events, int event_count, void* user_data);

// Initialize relay client system
int relay_client_init(const char *db_path) {
    if (g_relay_state.initialized) {
        app_log(LOG_WARN, "Relay client already initialized");
        return 0;
    }
    
    app_log(LOG_INFO, "Initializing relay client system...");
    
    // Store database path
    strncpy(g_relay_state.db_path, db_path, sizeof(g_relay_state.db_path) - 1);
    
    // Initialize mutex
    if (pthread_mutex_init(&g_relay_state.state_mutex, NULL) != 0) {
        app_log(LOG_ERROR, "Failed to initialize relay state mutex");
        return -1;
    }
    
    // Load configuration from database
    if (load_config_from_db() != 0) {
        app_log(LOG_ERROR, "Failed to load relay configuration from database");
        pthread_mutex_destroy(&g_relay_state.state_mutex);
        return -1;
    }
    
    // Create relay pool if enabled
    if (g_relay_state.enabled) {
        // Use default reconnection config (don't free - it's a static structure)
        nostr_pool_reconnect_config_t* config = nostr_pool_reconnect_config_default();
        g_relay_state.pool = nostr_relay_pool_create(config);
        if (!g_relay_state.pool) {
            app_log(LOG_ERROR, "Failed to create relay pool");
            pthread_mutex_destroy(&g_relay_state.state_mutex);
            return -1;
        }
        
        // Add all relays to pool
        for (int i = 0; i < g_relay_state.relay_count; i++) {
            if (nostr_relay_pool_add_relay(g_relay_state.pool, g_relay_state.relay_urls[i]) != NOSTR_SUCCESS) {
                app_log(LOG_WARN, "Failed to add relay to pool: %s", g_relay_state.relay_urls[i]);
            }
        }
        
        // Trigger initial connection attempts by creating a dummy subscription
        // This forces ensure_relay_connection() to be called for each relay
        app_log(LOG_INFO, "Initiating relay connections...");
        cJSON* dummy_filter = cJSON_CreateObject();
        cJSON* kinds = cJSON_CreateArray();
        cJSON_AddItemToArray(kinds, cJSON_CreateNumber(0));  // Kind 0 (will match nothing)
        cJSON_AddItemToObject(dummy_filter, "kinds", kinds);
        cJSON_AddNumberToObject(dummy_filter, "limit", 0);  // Limit 0 = no results
        
        nostr_pool_subscription_t* dummy_sub = nostr_relay_pool_subscribe(
            g_relay_state.pool,
            (const char**)g_relay_state.relay_urls,
            g_relay_state.relay_count,
            dummy_filter,
            NULL,  // No event callback
            NULL,  // No EOSE callback
            NULL,  // No user data
            1,     // close_on_eose
            1,     // enable_deduplication
            NOSTR_POOL_EOSE_FIRST,  // result_mode
            30,    // relay_timeout_seconds
            30     // eose_timeout_seconds
        );
        
        cJSON_Delete(dummy_filter);
        
        // Immediately close the dummy subscription
        if (dummy_sub) {
            nostr_pool_subscription_close(dummy_sub);
            app_log(LOG_INFO, "Connection attempts initiated for %d relays", g_relay_state.relay_count);
        } else {
            app_log(LOG_WARN, "Failed to initiate connection attempts");
        }
    }
    
    g_relay_state.initialized = 1;
    app_log(LOG_INFO, "Relay client initialized (enabled: %d, relays: %d)",
            g_relay_state.enabled, g_relay_state.relay_count);
    
    return 0;
}

// Load configuration from database
static int load_config_from_db(void) {
    sqlite3 *db;
    sqlite3_stmt *stmt;
    int rc;
    
    rc = sqlite3_open_v2(g_relay_state.db_path, &db, SQLITE_OPEN_READONLY, NULL);
    if (rc != SQLITE_OK) {
        app_log(LOG_ERROR, "Cannot open database: %s", sqlite3_errmsg(db));
        return -1;
    }
    
    // Load enable_relay_connect
    const char *sql = "SELECT value FROM config WHERE key = ?";
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        app_log(LOG_ERROR, "Failed to prepare statement: %s", sqlite3_errmsg(db));
        sqlite3_close(db);
        return -1;
    }
    
    sqlite3_bind_text(stmt, 1, "enable_relay_connect", -1, SQLITE_STATIC);
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        const char *value = (const char *)sqlite3_column_text(stmt, 0);
        g_relay_state.enabled = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
    } else {
        g_relay_state.enabled = 0;
    }
    sqlite3_finalize(stmt);
    
    // If not enabled, skip loading relay URLs
    if (!g_relay_state.enabled) {
        sqlite3_close(db);
        return 0;
    }
    
    // Load kind_10002_tags (relay URLs)
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        app_log(LOG_ERROR, "Failed to prepare statement: %s", sqlite3_errmsg(db));
        sqlite3_close(db);
        return -1;
    }
    
    sqlite3_bind_text(stmt, 1, "kind_10002_tags", -1, SQLITE_STATIC);
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        const char *json_array = (const char *)sqlite3_column_text(stmt, 0);
        if (parse_relay_urls(json_array) != 0) {
            app_log(LOG_ERROR, "Failed to parse relay URLs from config");
            sqlite3_finalize(stmt);
            sqlite3_close(db);
            return -1;
        }
    } else {
        app_log(LOG_WARN, "No relay URLs configured in kind_10002_tags");
    }
    sqlite3_finalize(stmt);
    
    sqlite3_close(db);
    return 0;
}

// Parse relay URLs from JSON array
static int parse_relay_urls(const char *json_array) {
    cJSON *root = cJSON_Parse(json_array);
    if (!root || !cJSON_IsArray(root)) {
        app_log(LOG_ERROR, "Invalid JSON array for relay URLs");
        if (root) cJSON_Delete(root);
        return -1;
    }
    
    int count = cJSON_GetArraySize(root);
    if (count > MAX_RELAYS) {
        app_log(LOG_WARN, "Too many relays configured (%d), limiting to %d", count, MAX_RELAYS);
        count = MAX_RELAYS;
    }
    
    // Allocate relay URLs array
    g_relay_state.relay_urls = malloc(count * sizeof(char*));
    if (!g_relay_state.relay_urls) {
        cJSON_Delete(root);
        return -1;
    }
    
    g_relay_state.relay_count = 0;
    for (int i = 0; i < count; i++) {
        cJSON *item = cJSON_GetArrayItem(root, i);
        if (cJSON_IsString(item) && item->valuestring) {
            g_relay_state.relay_urls[g_relay_state.relay_count] = strdup(item->valuestring);
            if (!g_relay_state.relay_urls[g_relay_state.relay_count]) {
                // Cleanup on failure
                for (int j = 0; j < g_relay_state.relay_count; j++) {
                    free(g_relay_state.relay_urls[j]);
                }
                free(g_relay_state.relay_urls);
                cJSON_Delete(root);
                return -1;
            }
            g_relay_state.relay_count++;
        }
    }
    
    cJSON_Delete(root);
    app_log(LOG_INFO, "Parsed %d relay URLs from configuration", g_relay_state.relay_count);
    return 0;
}

// Start relay connections
int relay_client_start(void) {
    if (!g_relay_state.initialized) {
        app_log(LOG_ERROR, "Relay client not initialized");
        return -1;
    }
    
    if (!g_relay_state.enabled) {
        app_log(LOG_INFO, "Relay client disabled in configuration");
        return 0;
    }
    
    if (g_relay_state.running) {
        app_log(LOG_WARN, "Relay client already running");
        return 0;
    }
    
    app_log(LOG_INFO, "Starting relay client...");
    
    // Start management thread
    g_relay_state.running = 1;
    if (pthread_create(&g_relay_state.management_thread, NULL, relay_management_thread, NULL) != 0) {
        app_log(LOG_ERROR, "Failed to create relay management thread");
        g_relay_state.running = 0;
        return -1;
    }
    
    app_log(LOG_INFO, "Relay client started successfully");
    return 0;
}

// Relay management thread
static void *relay_management_thread(void *arg) {
    (void)arg;
    
    app_log(LOG_INFO, "Relay management thread started");
    
    // Wait for at least one relay to connect (max 30 seconds)
    int connected = 0;
    for (int i = 0; i < 30 && !connected; i++) {
        sleep(1);
        
        // Poll to process connection attempts
        nostr_relay_pool_poll(g_relay_state.pool, 100);
        
        // Check if any relay is connected
        for (int j = 0; j < g_relay_state.relay_count; j++) {
            nostr_pool_relay_status_t status = nostr_relay_pool_get_relay_status(
                g_relay_state.pool,
                g_relay_state.relay_urls[j]
            );
            if (status == NOSTR_POOL_RELAY_CONNECTED) {
                connected = 1;
                app_log(LOG_INFO, "Relay connected: %s", g_relay_state.relay_urls[j]);
                break;
            }
        }
    }
    
    if (!connected) {
        app_log(LOG_WARN, "No relays connected after 30 seconds, continuing anyway");
    }
    
    // Publish initial events
    relay_client_publish_kind0();
    relay_client_publish_kind10002();
    
    // Subscribe to admin commands
    subscribe_to_admin_commands();
    
    // Main loop: poll the relay pool for incoming messages
    while (g_relay_state.running) {
        // Poll with 1000ms timeout
        int events_processed = nostr_relay_pool_poll(g_relay_state.pool, 1000);
        
        if (events_processed < 0) {
            app_log(LOG_ERROR, "Error polling relay pool");
            sleep(1);
        }
        // Pool handles all connection management, reconnection, and message processing
    }
    
    app_log(LOG_INFO, "Relay management thread stopping");
    return NULL;
}

// Stop relay connections
void relay_client_stop(void) {
    if (!g_relay_state.running) {
        return;
    }
    
    app_log(LOG_INFO, "Stopping relay client...");
    
    g_relay_state.running = 0;
    
    // Wait for management thread to finish
    pthread_join(g_relay_state.management_thread, NULL);
    
    // Close admin subscription
    if (g_relay_state.admin_subscription) {
        nostr_pool_subscription_close(g_relay_state.admin_subscription);
        g_relay_state.admin_subscription = NULL;
    }
    
    // Destroy relay pool (automatically disconnects all relays)
    if (g_relay_state.pool) {
        nostr_relay_pool_destroy(g_relay_state.pool);
        g_relay_state.pool = NULL;
    }
    
    // Free relay URLs
    if (g_relay_state.relay_urls) {
        for (int i = 0; i < g_relay_state.relay_count; i++) {
            free(g_relay_state.relay_urls[i]);
        }
        free(g_relay_state.relay_urls);
        g_relay_state.relay_urls = NULL;
    }
    
    pthread_mutex_destroy(&g_relay_state.state_mutex);
    
    app_log(LOG_INFO, "Relay client stopped");
}

// Check if relay client is enabled
int relay_client_is_enabled(void) {
    return g_relay_state.enabled;
}

// Publish Kind 0 profile event
int relay_client_publish_kind0(void) {
    if (!g_relay_state.enabled || !g_relay_state.running || !g_relay_state.pool) {
        return -1;
    }
    
    app_log(LOG_INFO, "Publishing Kind 0 profile event...");
    
    // Load kind_0_content from database
    sqlite3 *db;
    sqlite3_stmt *stmt;
    int rc;
    
    rc = sqlite3_open_v2(g_relay_state.db_path, &db, SQLITE_OPEN_READONLY, NULL);
    if (rc != SQLITE_OK) {
        app_log(LOG_ERROR, "Cannot open database: %s", sqlite3_errmsg(db));
        return -1;
    }
    
    const char *sql = "SELECT value FROM config WHERE key = 'kind_0_content'";
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        app_log(LOG_ERROR, "Failed to prepare statement: %s", sqlite3_errmsg(db));
        sqlite3_close(db);
        return -1;
    }
    
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_ROW) {
        app_log(LOG_WARN, "No kind_0_content found in config");
        sqlite3_finalize(stmt);
        sqlite3_close(db);
        return -1;
    }
    
    const char *content = (const char *)sqlite3_column_text(stmt, 0);
    
    // Convert private key from hex to bytes
    unsigned char privkey_bytes[32];
    if (nostr_hex_to_bytes(g_blossom_seckey, privkey_bytes, 32) != 0) {
        app_log(LOG_ERROR, "Failed to convert private key from hex");
        sqlite3_finalize(stmt);
        sqlite3_close(db);
        return -1;
    }
    
    // Create and sign Kind 0 event using nostr_core_lib
    cJSON* event = nostr_create_and_sign_event(
        0,                    // kind
        content,              // content
        NULL,                 // tags (empty for Kind 0)
        privkey_bytes,        // private key
        time(NULL)            // created_at
    );
    
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    
    if (!event) {
        app_log(LOG_ERROR, "Failed to create Kind 0 event");
        return -1;
    }
    
    // Publish to all relays using async pool API
    int result = nostr_relay_pool_publish_async(
        g_relay_state.pool,
        (const char**)g_relay_state.relay_urls,
        g_relay_state.relay_count,
        event,
        on_publish_response,
        (void*)"Kind 0"  // user_data to identify event type
    );
    
    cJSON_Delete(event);
    
    if (result == 0) {
        app_log(LOG_INFO, "Kind 0 profile event publish initiated");
        return 0;
    } else {
        app_log(LOG_ERROR, "Failed to initiate Kind 0 profile event publish");
        return -1;
    }
}

// Publish Kind 10002 relay list event
int relay_client_publish_kind10002(void) {
    if (!g_relay_state.enabled || !g_relay_state.running || !g_relay_state.pool) {
        return -1;
    }
    
    app_log(LOG_INFO, "Publishing Kind 10002 relay list event...");
    
    // Build tags array from configured relays
    cJSON* tags = cJSON_CreateArray();
    for (int i = 0; i < g_relay_state.relay_count; i++) {
        cJSON* tag = cJSON_CreateArray();
        cJSON_AddItemToArray(tag, cJSON_CreateString("r"));
        cJSON_AddItemToArray(tag, cJSON_CreateString(g_relay_state.relay_urls[i]));
        cJSON_AddItemToArray(tags, tag);
    }
    
    // Convert private key from hex to bytes
    unsigned char privkey_bytes[32];
    if (nostr_hex_to_bytes(g_blossom_seckey, privkey_bytes, 32) != 0) {
        app_log(LOG_ERROR, "Failed to convert private key from hex");
        cJSON_Delete(tags);
        return -1;
    }
    
    // Create and sign Kind 10002 event
    cJSON* event = nostr_create_and_sign_event(
        10002,                // kind
        "",                   // content (empty for Kind 10002)
        tags,                 // tags
        privkey_bytes,        // private key
        time(NULL)            // created_at
    );
    
    cJSON_Delete(tags);
    
    if (!event) {
        app_log(LOG_ERROR, "Failed to create Kind 10002 event");
        return -1;
    }
    
    // Publish to all relays using async pool API
    int result = nostr_relay_pool_publish_async(
        g_relay_state.pool,
        (const char**)g_relay_state.relay_urls,
        g_relay_state.relay_count,
        event,
        on_publish_response,
        (void*)"Kind 10002"  // user_data to identify event type
    );
    
    cJSON_Delete(event);
    
    if (result == 0) {
        app_log(LOG_INFO, "Kind 10002 relay list event publish initiated");
        return 0;
    } else {
        app_log(LOG_ERROR, "Failed to initiate Kind 10002 relay list event publish");
        return -1;
    }
}

// Send Kind 23459 admin response event
int relay_client_send_admin_response(const char *recipient_pubkey, const char *response_content) {
    if (!g_relay_state.enabled || !g_relay_state.running || !g_relay_state.pool) {
        return -1;
    }
    
    if (!recipient_pubkey || !response_content) {
        return -1;
    }
    
    app_log(LOG_INFO, "Sending Kind 23459 admin response to %s", recipient_pubkey);
    
    // TODO: Encrypt response_content using NIP-44
    // For now, use plaintext (stub implementation)
    const char *encrypted_content = response_content;
    
    // Build tags array
    cJSON* tags = cJSON_CreateArray();
    cJSON* p_tag = cJSON_CreateArray();
    cJSON_AddItemToArray(p_tag, cJSON_CreateString("p"));
    cJSON_AddItemToArray(p_tag, cJSON_CreateString(recipient_pubkey));
    cJSON_AddItemToArray(tags, p_tag);
    
    // Convert private key from hex to bytes
    unsigned char privkey_bytes[32];
    if (nostr_hex_to_bytes(g_blossom_seckey, privkey_bytes, 32) != 0) {
        app_log(LOG_ERROR, "Failed to convert private key from hex");
        cJSON_Delete(tags);
        return -1;
    }
    
    // Create and sign Kind 23459 event
    cJSON* event = nostr_create_and_sign_event(
        23459,                // kind
        encrypted_content,    // content
        tags,                 // tags
        privkey_bytes,        // private key
        time(NULL)            // created_at
    );
    
    cJSON_Delete(tags);
    
    if (!event) {
        app_log(LOG_ERROR, "Failed to create Kind 23459 event");
        return -1;
    }
    
    // Publish to all relays using async pool API
    int result = nostr_relay_pool_publish_async(
        g_relay_state.pool,
        (const char**)g_relay_state.relay_urls,
        g_relay_state.relay_count,
        event,
        on_publish_response,
        (void*)"Kind 23459"  // user_data to identify event type
    );
    
    cJSON_Delete(event);
    
    if (result == 0) {
        app_log(LOG_INFO, "Kind 23459 admin response publish initiated");
        return 0;
    } else {
        app_log(LOG_ERROR, "Failed to initiate Kind 23459 admin response publish");
        return -1;
    }
}

// Callback for publish responses
static void on_publish_response(const char* relay_url, const char* event_id, int success, const char* message, void* user_data) {
    const char* event_type = (const char*)user_data;
    
    if (success) {
        app_log(LOG_INFO, "%s event published successfully to %s (ID: %s)",
                event_type, relay_url, event_id);
    } else {
        app_log(LOG_WARN, "%s event rejected by %s: %s",
                event_type, relay_url, message ? message : "unknown error");
    }
}

// Callback for received Kind 23458 admin command events
static void on_admin_command_event(cJSON* event, const char* relay_url, void* user_data) {
    (void)user_data;
    
    app_log(LOG_INFO, "Received Kind 23458 admin command from relay: %s", relay_url);
    
    // Extract event fields
    cJSON* kind_json = cJSON_GetObjectItem(event, "kind");
    cJSON* pubkey_json = cJSON_GetObjectItem(event, "pubkey");
    cJSON* content_json = cJSON_GetObjectItem(event, "content");
    cJSON* id_json = cJSON_GetObjectItem(event, "id");
    
    if (!kind_json || !pubkey_json || !content_json || !id_json) {
        app_log(LOG_ERROR, "Invalid event structure");
        return;
    }
    
    int kind = cJSON_GetNumberValue(kind_json);
    const char* sender_pubkey = cJSON_GetStringValue(pubkey_json);
    const char* encrypted_content = cJSON_GetStringValue(content_json);
    const char* event_id = cJSON_GetStringValue(id_json);
    
    if (kind != 23458) {
        app_log(LOG_WARN, "Unexpected event kind: %d", kind);
        return;
    }
    
    // Verify sender is admin
    if (strcmp(sender_pubkey, g_admin_pubkey) != 0) {
        app_log(LOG_WARN, "Ignoring command from non-admin pubkey: %s", sender_pubkey);
        return;
    }
    
    app_log(LOG_INFO, "Processing admin command (event ID: %s)", event_id);
    
    // Convert keys from hex to bytes
    unsigned char server_privkey[32];
    unsigned char admin_pubkey_bytes[32];
    
    if (nostr_hex_to_bytes(g_blossom_seckey, server_privkey, 32) != 0) {
        app_log(LOG_ERROR, "Failed to convert server private key from hex");
        return;
    }
    
    if (nostr_hex_to_bytes(sender_pubkey, admin_pubkey_bytes, 32) != 0) {
        app_log(LOG_ERROR, "Failed to convert admin public key from hex");
        return;
    }
    
    // Decrypt command content using NIP-44
    char decrypted_command[4096];
    if (admin_decrypt_command(server_privkey, admin_pubkey_bytes, encrypted_content,
                             decrypted_command, sizeof(decrypted_command)) != 0) {
        app_log(LOG_ERROR, "Failed to decrypt admin command");
        
        // Send error response
        cJSON* error_response = cJSON_CreateObject();
        cJSON_AddStringToObject(error_response, "status", "error");
        cJSON_AddStringToObject(error_response, "message", "Failed to decrypt command");
        char* error_json = cJSON_PrintUnformatted(error_response);
        cJSON_Delete(error_response);
        
        char encrypted_response[4096];
        if (admin_encrypt_response(server_privkey, admin_pubkey_bytes, error_json,
                                   encrypted_response, sizeof(encrypted_response)) == 0) {
            relay_client_send_admin_response(sender_pubkey, encrypted_response);
        }
        free(error_json);
        return;
    }
    
    app_log(LOG_DEBUG, "Decrypted command: %s", decrypted_command);
    
    // Parse command JSON
    cJSON* command_json = cJSON_Parse(decrypted_command);
    if (!command_json) {
        app_log(LOG_ERROR, "Failed to parse command JSON");
        
        cJSON* error_response = cJSON_CreateObject();
        cJSON_AddStringToObject(error_response, "status", "error");
        cJSON_AddStringToObject(error_response, "message", "Invalid JSON format");
        char* error_json = cJSON_PrintUnformatted(error_response);
        cJSON_Delete(error_response);
        
        char encrypted_response[4096];
        if (admin_encrypt_response(server_privkey, admin_pubkey_bytes, error_json,
                                   encrypted_response, sizeof(encrypted_response)) == 0) {
            relay_client_send_admin_response(sender_pubkey, encrypted_response);
        }
        free(error_json);
        return;
    }
    
    // Process command and get response
    cJSON* response_json = admin_commands_process(command_json, event_id);
    cJSON_Delete(command_json);
    
    if (!response_json) {
        app_log(LOG_ERROR, "Failed to process admin command");
        response_json = cJSON_CreateObject();
        cJSON_AddStringToObject(response_json, "status", "error");
        cJSON_AddStringToObject(response_json, "message", "Failed to process command");
    }
    
    // Convert response to JSON string
    char* response_str = cJSON_PrintUnformatted(response_json);
    cJSON_Delete(response_json);
    
    if (!response_str) {
        app_log(LOG_ERROR, "Failed to serialize response JSON");
        return;
    }
    
    // Encrypt and send response
    char encrypted_response[4096];
    if (admin_encrypt_response(server_privkey, admin_pubkey_bytes, response_str,
                               encrypted_response, sizeof(encrypted_response)) != 0) {
        app_log(LOG_ERROR, "Failed to encrypt admin response");
        free(response_str);
        return;
    }
    
    free(response_str);
    
    if (relay_client_send_admin_response(sender_pubkey, encrypted_response) != 0) {
        app_log(LOG_ERROR, "Failed to send admin response");
    }
}

// Callback for EOSE (End Of Stored Events) - new signature
static void on_admin_subscription_eose(cJSON** events, int event_count, void* user_data) {
    (void)events;
    (void)event_count;
    (void)user_data;
    app_log(LOG_INFO, "Received EOSE for admin command subscription");
}

// Subscribe to admin commands (Kind 23458)
static int subscribe_to_admin_commands(void) {
    if (!g_relay_state.pool) {
        return -1;
    }
    
    app_log(LOG_INFO, "Subscribing to Kind 23458 admin commands...");
    
    // Create subscription filter for Kind 23458 events addressed to us
    cJSON* filter = cJSON_CreateObject();
    cJSON* kinds = cJSON_CreateArray();
    cJSON_AddItemToArray(kinds, cJSON_CreateNumber(23458));
    cJSON_AddItemToObject(filter, "kinds", kinds);
    
    cJSON* p_tags = cJSON_CreateArray();
    cJSON_AddItemToArray(p_tags, cJSON_CreateString(g_blossom_pubkey));
    cJSON_AddItemToObject(filter, "#p", p_tags);
    
    cJSON_AddNumberToObject(filter, "since", (double)time(NULL));
    
    // Subscribe using pool with new API signature
    g_relay_state.admin_subscription = nostr_relay_pool_subscribe(
        g_relay_state.pool,
        (const char**)g_relay_state.relay_urls,
        g_relay_state.relay_count,
        filter,
        on_admin_command_event,
        on_admin_subscription_eose,
        NULL,  // user_data
        0,     // close_on_eose (keep subscription open)
        1,     // enable_deduplication
        NOSTR_POOL_EOSE_FULL_SET,  // result_mode
        30,    // relay_timeout_seconds
        30     // eose_timeout_seconds
    );
    
    cJSON_Delete(filter);
    
    if (!g_relay_state.admin_subscription) {
        app_log(LOG_ERROR, "Failed to create admin command subscription");
        return -1;
    }
    
    app_log(LOG_INFO, "Successfully subscribed to admin commands");
    return 0;
}

// Get current relay connection status
char *relay_client_get_status(void) {
    if (!g_relay_state.pool) {
        return strdup("[]");
    }
    
    cJSON *root = cJSON_CreateArray();
    
    pthread_mutex_lock(&g_relay_state.state_mutex);
    for (int i = 0; i < g_relay_state.relay_count; i++) {
        cJSON *relay_obj = cJSON_CreateObject();
        cJSON_AddStringToObject(relay_obj, "url", g_relay_state.relay_urls[i]);
        
        // Get status from pool
        nostr_pool_relay_status_t status = nostr_relay_pool_get_relay_status(
            g_relay_state.pool,
            g_relay_state.relay_urls[i]
        );
        
        const char *state_str;
        switch (status) {
            case NOSTR_POOL_RELAY_CONNECTED: state_str = "connected"; break;
            case NOSTR_POOL_RELAY_CONNECTING: state_str = "connecting"; break;
            case NOSTR_POOL_RELAY_ERROR: state_str = "error"; break;
            default: state_str = "disconnected"; break;
        }
        cJSON_AddStringToObject(relay_obj, "state", state_str);
        
        // Get statistics from pool
        const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(
            g_relay_state.pool,
            g_relay_state.relay_urls[i]
        );
        
        if (stats) {
            cJSON_AddNumberToObject(relay_obj, "events_received", stats->events_received);
            cJSON_AddNumberToObject(relay_obj, "events_published", stats->events_published);
            cJSON_AddNumberToObject(relay_obj, "connection_attempts", stats->connection_attempts);
            cJSON_AddNumberToObject(relay_obj, "connection_failures", stats->connection_failures);
            
            if (stats->query_latency_avg > 0) {
                cJSON_AddNumberToObject(relay_obj, "query_latency_ms", stats->query_latency_avg);
            }
        }
        
        cJSON_AddItemToArray(root, relay_obj);
    }
    pthread_mutex_unlock(&g_relay_state.state_mutex);
    
    char *json_str = cJSON_PrintUnformatted(root);
    cJSON_Delete(root);
    
    return json_str;
}

// Force reconnection to all relays
int relay_client_reconnect(void) {
    if (!g_relay_state.enabled || !g_relay_state.running || !g_relay_state.pool) {
        return -1;
    }
    
    app_log(LOG_INFO, "Forcing reconnection to all relays...");
    
    // Remove and re-add all relays to force reconnection
    pthread_mutex_lock(&g_relay_state.state_mutex);
    for (int i = 0; i < g_relay_state.relay_count; i++) {
        nostr_relay_pool_remove_relay(g_relay_state.pool, g_relay_state.relay_urls[i]);
        nostr_relay_pool_add_relay(g_relay_state.pool, g_relay_state.relay_urls[i]);
    }
    pthread_mutex_unlock(&g_relay_state.state_mutex);
    
    app_log(LOG_INFO, "Reconnection initiated for all relays");
    return 0;
}