#define _GNU_SOURCE
#include "config.h"
#include "debug.h"
#include "default_config_event.h"
#include "dm_admin.h"

// Undefine VERSION macros before including nostr_core.h to avoid redefinition warnings
// This must come AFTER default_config_event.h so that RELAY_VERSION macro expansion works correctly
#ifdef VERSION
#undef VERSION
#endif
#ifdef VERSION_MAJOR
#undef VERSION_MAJOR
#endif
#ifdef VERSION_MINOR
#undef VERSION_MINOR
#endif
#ifdef VERSION_PATCH
#undef VERSION_PATCH
#endif

#include "../nostr_core_lib/nostr_core/nostr_core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <libwebsockets.h>

// External database connection (from main.c)
extern sqlite3* g_db;

// External shutdown flag (from main.c)
extern volatile sig_atomic_t g_shutdown_flag;

// Database path for event-based config
char g_database_path[512] = {0};

// ================================
// NEW ADMIN API STRUCTURES
// ================================

// Migration state management
typedef enum {
    MIGRATION_NOT_NEEDED,
    MIGRATION_NEEDED,
    MIGRATION_IN_PROGRESS,
    MIGRATION_COMPLETED,
    MIGRATION_FAILED
} migration_state_t;

typedef struct {
    migration_state_t state;
    int event_config_count;
    int table_config_count;
    int migration_errors;
    time_t migration_started;
    time_t migration_completed;
    char error_message[512];
} migration_status_t;

static migration_status_t g_migration_status = {0};

// Configuration source type
typedef enum {
    CONFIG_SOURCE_EVENT,    // Current event-based system
    CONFIG_SOURCE_TABLE,    // New table-based system
    CONFIG_SOURCE_HYBRID    // During migration
} config_source_t;

// Forward declarations for new admin API functions
int populate_default_config_values(void);
int populate_all_config_values_atomic(const char* admin_pubkey, const char* relay_pubkey);
int process_admin_config_event(cJSON* event, char* error_message, size_t error_size);

// Forward declaration for relay info initialization
void init_relay_info(void);
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
                              const char* pattern_value);
int remove_auth_rule_from_config(const char* rule_type, const char* pattern_type,
                                const char* pattern_value);
int is_config_table_ready(void);
int migrate_config_from_events_to_table(void);
int populate_config_table_from_event(const cJSON* event);
int handle_config_query_unified(cJSON* event, const char* query_type, char* error_message, size_t error_size, struct lws* wsi);
int handle_config_set_unified(cJSON* event, const char* config_key, const char* config_value, char* error_message, size_t error_size, struct lws* wsi);
int handle_create_relay_event_unified(cJSON* event, const char* kind_str, const char* event_data_json, char* error_message, size_t error_size, struct lws* wsi);

// Forward declarations for tag parsing utilities
const char* get_first_tag_name(cJSON* event);
const char* get_tag_value(cJSON* event, const char* tag_name, int value_index);
int parse_auth_query_parameters(cJSON* event, char** query_type, char** pattern_value);
int handle_config_update_unified(cJSON* event, char* error_message, size_t error_size, struct lws* wsi);
int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_size, struct lws* wsi);
int handle_sql_query_unified(cJSON* event, const char* query, char* error_message, size_t error_size, struct lws* wsi);


// Current configuration cache
static cJSON* g_current_config = NULL;

// Cache for initial configuration event (before database is initialized)
static cJSON* g_pending_config_event = NULL;

// Temporary storage for relay private key during first-time setup
static char g_temp_relay_privkey[65] = {0};

// ================================
// UTILITY FUNCTIONS
// ================================

char** find_existing_db_files(void) {
    DIR *dir;
    struct dirent *entry;
    char **files = NULL;
    int count = 0;
    
    dir = opendir(".");
    if (dir == NULL) {
        return NULL;
    }
    
    // Count .db files (excluding auxiliary files like .db-wal, .db-shm, .db-journal)
    while ((entry = readdir(dir)) != NULL) {
        size_t len = strlen(entry->d_name);
        // Only count files that end with exactly ".db" (not .db-wal, .db-shm, etc.)
        if (len >= 3 && strcmp(entry->d_name + len - 3, ".db") == 0) {
            count++;
        }
    }
    rewinddir(dir);
    
    if (count == 0) {
        closedir(dir);
        return NULL;
    }
    
    // Allocate array for filenames
    files = malloc((count + 1) * sizeof(char*));
    if (!files) {
        closedir(dir);
        return NULL;
    }
    
    // Store filenames (only files ending with exactly ".db")
    int i = 0;
    while ((entry = readdir(dir)) != NULL && i < count) {
        size_t len = strlen(entry->d_name);
        // Only accept files that end with exactly ".db"
        if (len >= 3 && strcmp(entry->d_name + len - 3, ".db") == 0) {
            files[i] = malloc(strlen(entry->d_name) + 1);
            if (files[i]) {
                strcpy(files[i], entry->d_name);
                i++;
            }
        }
    }
    files[i] = NULL;  // Null terminate
    
    closedir(dir);
    return files;
}

char* extract_pubkey_from_filename(const char* filename) {
    if (!filename) return NULL;
    
    // Find .db extension
    const char* dot = strstr(filename, ".db");
    if (!dot) return NULL;
    
    // Calculate pubkey length
    size_t pubkey_len = dot - filename;
    if (pubkey_len != 64) return NULL;  // Invalid pubkey length
    
    // Allocate and copy pubkey
    char* pubkey = malloc(pubkey_len + 1);
    if (!pubkey) return NULL;
    
    strncpy(pubkey, filename, pubkey_len);
    pubkey[pubkey_len] = '\0';
    
    return pubkey;
}

char* get_database_name_from_relay_pubkey(const char* relay_pubkey) {
    if (!relay_pubkey || strlen(relay_pubkey) != 64) {
        return NULL;
    }
    
    char* db_name = malloc(strlen(relay_pubkey) + 4);  // +4 for ".db\0"
    if (!db_name) return NULL;

    sprintf(db_name, "%s.db", relay_pubkey);
    return db_name;
}

// ================================
// DATABASE FUNCTIONS
// ================================

int create_database_with_relay_pubkey(const char* relay_pubkey) {
    char* db_name = get_database_name_from_relay_pubkey(relay_pubkey);
    if (!db_name) {
        DEBUG_ERROR("Failed to generate database name");
        return -1;
    }
    
    strncpy(g_database_path, db_name, sizeof(g_database_path) - 1);
    g_database_path[sizeof(g_database_path) - 1] = '\0';
    
    printf("  Database: %s\n", db_name);
    
    free(db_name);
    return 0;
}

// ================================
// CONFIGURATION EVENT FUNCTIONS
// ================================

int store_config_event_in_database(const cJSON* event) {
    if (!event || !g_db) {
        return -1;
    }
    
    // Get event fields
    cJSON* id_obj = cJSON_GetObjectItem(event, "id");
    cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
    cJSON* created_at_obj = cJSON_GetObjectItem(event, "created_at");
    cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
    cJSON* content_obj = cJSON_GetObjectItem(event, "content");
    cJSON* sig_obj = cJSON_GetObjectItem(event, "sig");
    cJSON* tags_obj = cJSON_GetObjectItem(event, "tags");
    
    if (!id_obj || !pubkey_obj || !created_at_obj || !kind_obj || !content_obj || !sig_obj || !tags_obj) {
        return -1;
    }
    
    // Convert tags to JSON string
    char* tags_str = cJSON_Print(tags_obj);
    if (!tags_str) {
        return -1;
    }
    
    // Insert or replace the configuration event
    const char* sql = "INSERT OR REPLACE INTO events (id, pubkey, created_at, kind, event_type, content, sig, tags) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
    sqlite3_stmt* stmt;
    
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to prepare configuration event insert");
        free(tags_str);
        return -1;
    }
    
    sqlite3_bind_text(stmt, 1, cJSON_GetStringValue(id_obj), -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, cJSON_GetStringValue(pubkey_obj), -1, SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 3, (sqlite3_int64)cJSON_GetNumberValue(created_at_obj));
    sqlite3_bind_int(stmt, 4, (int)cJSON_GetNumberValue(kind_obj));
    sqlite3_bind_text(stmt, 5, "regular", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 6, cJSON_GetStringValue(content_obj), -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 7, cJSON_GetStringValue(sig_obj), -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 8, tags_str, -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    free(tags_str);
    
    if (rc == SQLITE_DONE) {
        return 0;
    } else {
        DEBUG_ERROR("Failed to store configuration event");
        return -1;
    }
}

cJSON* load_config_event_from_database(const char* relay_pubkey) {
    if (!g_db || !relay_pubkey) {
        return NULL;
    }
    
    // Configuration is now managed through config table, not events
    return NULL;
}

// ================================
// CONFIGURATION ACCESS FUNCTIONS
// ================================

const char* get_config_value(const char* key) {
    if (!key) {
        return NULL;
    }
    return get_config_value_from_table(key);
}

int get_config_int(const char* key, int default_value) {
    if (!key) {
        return default_value;
    }
    const char* value = get_config_value_from_table(key);
    if (!value) {
        return default_value;
    }
    int result = atoi(value);
    free((void*)value);
    return result;
}

int get_config_bool(const char* key, int default_value) {
    if (!key) {
        return default_value;
    }
    const char* value = get_config_value_from_table(key);
    if (!value) {
        return default_value;
    }
    int result = (strcmp(value, "1") == 0 ||
                  strcasecmp(value, "true") == 0 ||
                  strcasecmp(value, "yes") == 0) ? 1 : 0;
    free((void*)value);
    return result;
}

// ================================
// NIP-42 KIND-SPECIFIC AUTHENTICATION
// ================================

// Parse comma-separated kind list into array of integers
// Returns number of kinds parsed, or -1 on error
int parse_auth_required_kinds(const char* kinds_str, int* kinds_array, int max_kinds) {
    if (!kinds_str || !kinds_array || max_kinds <= 0) {
        return -1;
    }
    
    // Empty string means no kinds require auth
    if (strlen(kinds_str) == 0) {
        return 0;
    }
    
    char* str_copy = strdup(kinds_str);
    if (!str_copy) {
        return -1;
    }
    
    int count = 0;
    char* token = strtok(str_copy, ",");
    
    while (token && count < max_kinds) {
        // Trim whitespace
        while (*token == ' ' || *token == '\t') token++;
        char* end = token + strlen(token) - 1;
        while (end > token && (*end == ' ' || *end == '\t')) end--;
        *(end + 1) = '\0';
        
        // Convert to integer
        char* endptr;
        long kind = strtol(token, &endptr, 10);
        
        if (endptr != token && *endptr == '\0' && kind >= 0 && kind <= 65535) {
            kinds_array[count] = (int)kind;
            count++;
        }
        
        token = strtok(NULL, ",");
    }
    
    free(str_copy);
    return count;
}

// Check if a specific event kind requires NIP-42 authentication
int is_nip42_auth_required_for_kind(int event_kind) {
    const char* kinds_str = get_config_value("nip42_auth_required_kinds");
    if (!kinds_str) {
        return 0; // No authentication required if setting is missing
    }
    
    // Parse the kinds list
    int required_kinds[100]; // Support up to 100 different kinds
    int count = parse_auth_required_kinds(kinds_str, required_kinds, 100);
    
    if (count < 0) {
        return 0; // Parse error, default to no auth required
    }
    
    // Check if event_kind is in the list
    for (int i = 0; i < count; i++) {
        if (required_kinds[i] == event_kind) {
            return 1; // Authentication required for this kind
        }
    }
    
    return 0; // Authentication not required for this kind
}

// Get NIP-42 global authentication requirement
int is_nip42_auth_globally_required(void) {
    return get_config_bool("nip42_auth_required", 0);
}

// ================================
// FIRST-TIME STARTUP FUNCTIONS
// ================================

int is_first_time_startup(void) {
    char** existing_files = find_existing_db_files();
    if (existing_files) {
        // Free the array
        for (int i = 0; existing_files[i]; i++) {
            free(existing_files[i]);
        }
        free(existing_files);
        return 0;  // Not first time
    }
    return 1;  // First time
}

// ================================
// COMPATIBILITY FUNCTIONS
// ================================

int init_configuration_system(const char* config_dir_override, const char* config_file_override) {
    // Suppress unused parameter warnings for compatibility function
    (void)config_dir_override;
    (void)config_file_override;

    // Configuration system now uses direct database queries instead of cache
    // No initialization needed for cache system

    return 0;
}

void cleanup_configuration_system(void) {
    if (g_current_config) {
        cJSON_Delete(g_current_config);
        g_current_config = NULL;
    }

    if (g_pending_config_event) {
        cJSON_Delete(g_pending_config_event);
        g_pending_config_event = NULL;
    }

    // Configuration system now uses direct database queries instead of cache
    // No cleanup needed for cache system
}

int set_database_config(const char* key, const char* value, const char* changed_by) {
    // Suppress unused parameter warnings for compatibility function
    (void)key;
    (void)value;
    (void)changed_by;
    
    // Temporary compatibility function - does nothing for now
    // In the new system, configuration is only updated via events
    DEBUG_WARN("set_database_config called - not supported in event-based config");
    return 0;
}

// ================================
// KEY GENERATION FUNCTIONS
// ================================

// Helper function to generate random private key
int generate_random_private_key_bytes(unsigned char* privkey_bytes) {
    if (!privkey_bytes) {
        return -1;
    }
    
    FILE* urandom = fopen("/dev/urandom", "rb");
    if (!urandom) {
        DEBUG_ERROR("Failed to open /dev/urandom for key generation");
        return -1;
    }
    
    if (fread(privkey_bytes, 1, 32, urandom) != 32) {
        DEBUG_ERROR("Failed to read random bytes for private key");
        fclose(urandom);
        return -1;
    }
    fclose(urandom);
    
    // Verify the private key is valid using nostr_core_lib
    if (nostr_ec_private_key_verify(privkey_bytes) != NOSTR_SUCCESS) {
        DEBUG_ERROR("Generated private key failed validation");
        return -1;
    }
    
    return 0;
}

// ================================
// SECURE RELAY PRIVATE KEY STORAGE
// ================================

int store_relay_private_key(const char* relay_privkey_hex) {
    if (!relay_privkey_hex) {
        DEBUG_ERROR("Invalid relay private key for storage");
        return -1;
    }
    
    // Validate private key format (must be 64 hex characters)
    if (strlen(relay_privkey_hex) != 64) {
        DEBUG_ERROR("Invalid relay private key length (must be 64 hex characters)");
        return -1;
    }
    
    // Validate hex format
    for (int i = 0; i < 64; i++) {
        char c = relay_privkey_hex[i];
        if (!((c >= '0' && c <= '9') ||
              (c >= 'a' && c <= 'f') ||
              (c >= 'A' && c <= 'F'))) {
            DEBUG_ERROR("Invalid relay private key format (must be hex characters only)");
            return -1;
        }
    }
    
    if (!g_db) {
        DEBUG_ERROR("Database not available for relay private key storage");
        return -1;
    }
    
    const char* sql = "INSERT OR REPLACE INTO relay_seckey (private_key_hex) VALUES (?)";
    sqlite3_stmt* stmt;
    
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to prepare relay private key storage query");
        return -1;
    }
    
    sqlite3_bind_text(stmt, 1, relay_privkey_hex, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    if (rc == SQLITE_DONE) {
        return 0;
    } else {
        DEBUG_ERROR("Failed to store relay private key in database");
        return -1;
    }
}

char* get_relay_private_key(void) {
    if (!g_db) {
        DEBUG_ERROR("Database not available for relay private key retrieval");
        return NULL;
    }
    
    const char* sql = "SELECT private_key_hex FROM relay_seckey";
    sqlite3_stmt* stmt;
    
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to prepare relay private key retrieval query");
        return NULL;
    }
    
    char* private_key = NULL;
    if (sqlite3_step(stmt) == SQLITE_ROW) {
        const char* key_from_db = (const char*)sqlite3_column_text(stmt, 0);
        if (key_from_db && strlen(key_from_db) == 64) {
            private_key = malloc(65);  // 64 chars + null terminator
            if (private_key) {
                strcpy(private_key, key_from_db);
            }
        }
    }
    
    sqlite3_finalize(stmt);
    
    if (!private_key) {
        DEBUG_ERROR("Relay private key not found in secure storage");
    }
    
    return private_key;
}

const char* get_temp_relay_private_key(void) {
    if (strlen(g_temp_relay_privkey) == 64) {
        return g_temp_relay_privkey;
    }
    return NULL;
}

// ================================
// DEFAULT CONFIG EVENT CREATION
// ================================

cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
                                   const char* relay_privkey_hex,
                                   const char* relay_pubkey_hex,
                                   const cli_options_t* cli_options) {
    if (!admin_privkey_bytes || !relay_privkey_hex || !relay_pubkey_hex) {
        DEBUG_ERROR("Invalid parameters for creating default config event");
        return NULL;
    }
    
    // Create tags array with default configuration values
    cJSON* tags = cJSON_CreateArray();
    if (!tags) {
        DEBUG_ERROR("Failed to create tags array");
        return NULL;
    }
    
    // Add d tag with relay pubkey
    cJSON* d_tag = cJSON_CreateArray();
    cJSON_AddItemToArray(d_tag, cJSON_CreateString("d"));
    cJSON_AddItemToArray(d_tag, cJSON_CreateString(relay_pubkey_hex));
    cJSON_AddItemToArray(tags, d_tag);
    
    // Add relay keys
    cJSON* relay_pubkey_tag = cJSON_CreateArray();
    cJSON_AddItemToArray(relay_pubkey_tag, cJSON_CreateString("relay_pubkey"));
    cJSON_AddItemToArray(relay_pubkey_tag, cJSON_CreateString(relay_pubkey_hex));
    cJSON_AddItemToArray(tags, relay_pubkey_tag);
    
    // Note: relay_privkey is now stored securely in relay_seckey table
    // It is no longer included in the public configuration event
    
    // Add all default configuration values with command line overrides
    for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) {
        cJSON* tag = cJSON_CreateArray();
        cJSON_AddItemToArray(tag, cJSON_CreateString(DEFAULT_CONFIG_VALUES[i].key));
        
        // Check for command line overrides
        const char* value = DEFAULT_CONFIG_VALUES[i].value;
        if (cli_options) {
            // Override relay_port if specified on command line
            if (cli_options->port_override > 0 && strcmp(DEFAULT_CONFIG_VALUES[i].key, "relay_port") == 0) {
                char port_str[16];
                snprintf(port_str, sizeof(port_str), "%d", cli_options->port_override);
                cJSON_AddItemToArray(tag, cJSON_CreateString(port_str));
                printf("  Port: %d (overriding default %s)\n", cli_options->port_override, DEFAULT_CONFIG_VALUES[i].value);
            } else {
                cJSON_AddItemToArray(tag, cJSON_CreateString(value));
            }
        } else {
            cJSON_AddItemToArray(tag, cJSON_CreateString(value));
        }
        
        cJSON_AddItemToArray(tags, tag);
    }
    
    // Create and sign event using nostr_core_lib
    cJSON* event = nostr_create_and_sign_event(
        33334,                                          // kind
        "C Nostr Relay Configuration",                  // content
        tags,                                           // tags
        admin_privkey_bytes,                           // private key bytes for signing
        time(NULL)                                      // created_at timestamp
    );
    
    cJSON_Delete(tags); // Clean up tags as they were duplicated in nostr_create_and_sign_event
    
    if (!event) {
        DEBUG_ERROR("Failed to create and sign configuration event");
        return NULL;
    }
    
    // Log success information
    cJSON* id_obj = cJSON_GetObjectItem(event, "id");
    cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
    
    if (id_obj && pubkey_obj) {
        // printf("  Event ID: %s\n", cJSON_GetStringValue(id_obj));
        // printf("  Admin Public Key: %s\n", cJSON_GetStringValue(pubkey_obj));
    }
    
    return event;
}

// ================================
// IMPLEMENTED FUNCTIONS
// ================================

int first_time_startup_sequence(const cli_options_t* cli_options, char* admin_pubkey_out, char* relay_pubkey_out, char* relay_privkey_out) {

    // 1. Generate or use provided admin keypair
    unsigned char admin_privkey_bytes[32];
    char admin_privkey[65], admin_pubkey[65];
    int generated_admin_key = 0;  // Track if we generated a new admin key

    if (cli_options && strlen(cli_options->admin_pubkey_override) == 64) {
        // Use provided admin public key directly - skip private key generation entirely
        strncpy(admin_pubkey, cli_options->admin_pubkey_override, sizeof(admin_pubkey) - 1);
        admin_pubkey[sizeof(admin_pubkey) - 1] = '\0';

        // Validate the public key format (must be 64 hex characters)
        for (int i = 0; i < 64; i++) {
            char c = admin_pubkey[i];
            if (!((c >= '0' && c <= '9') ||
                  (c >= 'a' && c <= 'f') ||
                  (c >= 'A' && c <= 'F'))) {
                DEBUG_ERROR("Invalid admin public key format - must contain only hex characters");
                return -1;
            }
        }

        // Skip private key generation - we only need the pubkey for admin verification
        // Set a dummy private key that will never be used (not displayed or stored)
        memset(admin_privkey_bytes, 0, 32);  // Zero out for security
        memset(admin_privkey, 0, sizeof(admin_privkey));  // Zero out the hex string
        generated_admin_key = 0;  // Did not generate a new key
    } else {
        // Generate random admin keypair using /dev/urandom + nostr_core_lib
        if (generate_random_private_key_bytes(admin_privkey_bytes) != 0) {
            DEBUG_ERROR("Failed to generate admin private key");
            return -1;
        }
        nostr_bytes_to_hex(admin_privkey_bytes, 32, admin_privkey);

        // Derive public key from private key
        unsigned char admin_pubkey_bytes[32];
        if (nostr_ec_public_key_from_private_key(admin_privkey_bytes, admin_pubkey_bytes) != NOSTR_SUCCESS) {
            DEBUG_ERROR("Failed to derive admin public key");
            return -1;
        }
        nostr_bytes_to_hex(admin_pubkey_bytes, 32, admin_pubkey);
        generated_admin_key = 1;  // Generated a new key
    }

    // 2. Generate or use provided relay keypair
    unsigned char relay_privkey_bytes[32];
    char relay_privkey[65], relay_pubkey[65];

    if (cli_options && strlen(cli_options->relay_privkey_override) == 64) {
        // Use provided relay private key
        strncpy(relay_privkey, cli_options->relay_privkey_override, sizeof(relay_privkey) - 1);
        relay_privkey[sizeof(relay_privkey) - 1] = '\0';

        // Convert hex string to bytes
        if (nostr_hex_to_bytes(relay_privkey, relay_privkey_bytes, 32) != NOSTR_SUCCESS) {
            DEBUG_ERROR("Failed to convert relay private key hex to bytes");
            return -1;
        }

        // Validate the private key
        if (nostr_ec_private_key_verify(relay_privkey_bytes) != NOSTR_SUCCESS) {
            DEBUG_ERROR("Provided relay private key is invalid");
            return -1;
        }
    } else {
        // Generate random relay keypair using /dev/urandom + nostr_core_lib
        if (generate_random_private_key_bytes(relay_privkey_bytes) != 0) {
            DEBUG_ERROR("Failed to generate relay private key");
            return -1;
        }
        nostr_bytes_to_hex(relay_privkey_bytes, 32, relay_privkey);
    }

    unsigned char relay_pubkey_bytes[32];
    if (nostr_ec_public_key_from_private_key(relay_privkey_bytes, relay_pubkey_bytes) != NOSTR_SUCCESS) {
        DEBUG_ERROR("Failed to derive relay public key");
        return -1;
    }
    nostr_bytes_to_hex(relay_pubkey_bytes, 32, relay_pubkey);

    // 4. Create database with relay pubkey name
    if (create_database_with_relay_pubkey(relay_pubkey) != 0) {
        DEBUG_ERROR("Failed to create database with relay pubkey");
        return -1;
    }

    // 5. Store relay private key in temporary storage for later secure storage
    strncpy(g_temp_relay_privkey, relay_privkey, sizeof(g_temp_relay_privkey) - 1);
    g_temp_relay_privkey[sizeof(g_temp_relay_privkey) - 1] = '\0';

    // Note: Pubkeys will be stored in database by populate_all_config_values_atomic()
    // after database connection is established in main.c

    // Copy keys to output parameters
    if (admin_pubkey_out) {
        strncpy(admin_pubkey_out, admin_pubkey, 64);
        admin_pubkey_out[64] = '\0';
    }
    if (relay_pubkey_out) {
        strncpy(relay_pubkey_out, relay_pubkey, 64);
        relay_pubkey_out[64] = '\0';
    }
    if (relay_privkey_out) {
        strncpy(relay_privkey_out, relay_privkey, 64);
        relay_privkey_out[64] = '\0';
    }

    // 9. Print admin private key for user to save (only if we generated a new key)
    if (generated_admin_key) {
        printf("\n");
        printf("=================================================================\n");
        printf("IMPORTANT: SAVE THIS ADMIN PRIVATE KEY SECURELY!\n");
        printf("=================================================================\n");
        printf("Admin Private Key: %s\n", admin_privkey);
        printf("Admin Public Key:  %s\n", admin_pubkey);
        printf("Relay Public Key:  %s\n", relay_pubkey);
        printf("\nDatabase: %s\n", g_database_path);
        printf("\nThis admin private key is needed to update configuration!\n");
        printf("Store it safely - it will not be displayed again.\n");
        printf("=================================================================\n");
        printf("\n");
    } else {
        printf("\n");
        printf("=================================================================\n");
        printf("RELAY STARTUP COMPLETE\n");
        printf("=================================================================\n");
        printf("Using provided admin public key for authentication\n");
        printf("Admin Public Key:  %s\n", admin_pubkey);
        printf("Relay Public Key:  %s\n", relay_pubkey);
        printf("\nDatabase: %s\n", g_database_path);
        printf("=================================================================\n");
        printf("\n");
    }

    return 0;
}

int startup_existing_relay(const char* relay_pubkey, const cli_options_t* cli_options __attribute__((unused))) {
    if (!relay_pubkey) {
        DEBUG_ERROR("Invalid relay pubkey for existing relay startup");
        return -1;
    }

    printf("  Relay pubkey: %s\n", relay_pubkey);

    // Relay pubkey is now stored directly in database, no cache needed

    // Set database path
    char* db_name = get_database_name_from_relay_pubkey(relay_pubkey);
    if (!db_name) {
        DEBUG_ERROR("Failed to generate database name");
        return -1;
    }

    strncpy(g_database_path, db_name, sizeof(g_database_path) - 1);
    g_database_path[sizeof(g_database_path) - 1] = '\0';
    free(db_name);

    // NOTE: Database is already initialized in main.c before calling this function
    // Config table should already exist with complete configuration
    // CLI overrides will be applied after this function returns in main.c

    return 0;
}

// ================================
// CONFIGURATION FIELD VALIDATION
// ================================

// Validation helper functions
static int is_valid_port(const char* port_str) {
    if (!port_str) return 0;
    
    char* endptr;
    long port = strtol(port_str, &endptr, 10);
    
    // Must be valid number and in valid port range
    return (endptr != port_str && *endptr == '\0' && port >= 1 && port <= 65535);
}

static int is_valid_boolean(const char* bool_str) {
    if (!bool_str) return 0;
    
    return (strcasecmp(bool_str, "true") == 0 ||
            strcasecmp(bool_str, "false") == 0 ||
            strcasecmp(bool_str, "yes") == 0 ||
            strcasecmp(bool_str, "no") == 0 ||
            strcasecmp(bool_str, "1") == 0 ||
            strcasecmp(bool_str, "0") == 0);
}

static int is_valid_positive_integer(const char* int_str) {
    if (!int_str) return 0;
    
    char* endptr;
    long val = strtol(int_str, &endptr, 10);
    
    return (endptr != int_str && *endptr == '\0' && val >= 0);
}

static int is_valid_non_negative_integer(const char* int_str) {
    if (!int_str) return 0;
    
    char* endptr;
    long val = strtol(int_str, &endptr, 10);
    
    return (endptr != int_str && *endptr == '\0' && val >= 0);
}

static int is_valid_string_length(const char* str, size_t max_length) {
    if (!str) return 1;  // NULL strings are valid (use defaults)
    return strlen(str) <= max_length;
}

static int is_valid_pow_mode(const char* mode_str) {
    if (!mode_str) return 0;
    
    return (strcasecmp(mode_str, "basic") == 0 ||
            strcasecmp(mode_str, "strict") == 0 ||
            strcasecmp(mode_str, "disabled") == 0);
}

static int is_valid_hex_key(const char* key_str) {
    if (!key_str) return 0;
    
    // Must be exactly 64 hex characters
    if (strlen(key_str) != 64) return 0;
    
    // Must contain only hex characters
    for (int i = 0; i < 64; i++) {
        char c = key_str[i];
        if (!((c >= '0' && c <= '9') ||
              (c >= 'a' && c <= 'f') ||
              (c >= 'A' && c <= 'F'))) {
            return 0;
        }
    }
    return 1;
}

// Main validation function for configuration fields
static int validate_config_field(const char* key, const char* value, char* error_msg, size_t error_size) {
    if (!key || !value) {
        snprintf(error_msg, error_size, "key or value is NULL");
        return -1;
    }
    
    // Port validation
    if (strcmp(key, "relay_port") == 0) {
        if (!is_valid_port(value)) {
            snprintf(error_msg, error_size, "invalid port number '%s' (must be 1-65535)", value);
            return -1;
        }
        return 0;
    }
    
    // Connection limits
    if (strcmp(key, "max_connections") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid max_connections '%s' (must be positive integer)", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 1 || val > 10000) {
            snprintf(error_msg, error_size, "max_connections '%s' out of range (1-10000)", value);
            return -1;
        }
        return 0;
    }
    
    // Boolean fields
    if (strcmp(key, "auth_enabled") == 0 ||
        strcmp(key, "nip40_expiration_enabled") == 0 ||
        strcmp(key, "nip40_expiration_strict") == 0 ||
        strcmp(key, "nip40_expiration_filter") == 0) {
        if (!is_valid_boolean(value)) {
            snprintf(error_msg, error_size, "invalid boolean value '%s' for %s", value, key);
            return -1;
        }
        return 0;
    }
    
    // String length validation
    if (strcmp(key, "relay_description") == 0) {
        if (!is_valid_string_length(value, RELAY_DESCRIPTION_MAX_LENGTH)) {
            snprintf(error_msg, error_size, "relay_description too long (max %d characters)", RELAY_DESCRIPTION_MAX_LENGTH);
            return -1;
        }
        return 0;
    }
    
    if (strcmp(key, "relay_contact") == 0) {
        if (!is_valid_string_length(value, RELAY_CONTACT_MAX_LENGTH)) {
            snprintf(error_msg, error_size, "relay_contact too long (max %d characters)", RELAY_CONTACT_MAX_LENGTH);
            return -1;
        }
        return 0;
    }
    
    if (strcmp(key, "relay_software") == 0 ||
        strcmp(key, "relay_version") == 0) {
        if (!is_valid_string_length(value, 256)) {
            snprintf(error_msg, error_size, "%s too long (max 256 characters)", key);
            return -1;
        }
        return 0;
    }
    
    // PoW difficulty validation
    if (strcmp(key, "pow_min_difficulty") == 0) {
        if (!is_valid_non_negative_integer(value)) {
            snprintf(error_msg, error_size, "invalid pow_min_difficulty '%s' (must be non-negative integer)", value);
            return -1;
        }
        int val = atoi(value);
        if (val > 32) {  // 32 is practically impossible
            snprintf(error_msg, error_size, "pow_min_difficulty '%s' too high (max 32)", value);
            return -1;
        }
        return 0;
    }
    
    // PoW mode validation
    if (strcmp(key, "pow_mode") == 0) {
        if (!is_valid_pow_mode(value)) {
            snprintf(error_msg, error_size, "invalid pow_mode '%s' (must be basic, strict, or disabled)", value);
            return -1;
        }
        return 0;
    }
    
    // Time-based validation
    if (strcmp(key, "nip40_expiration_grace_period") == 0) {
        if (!is_valid_non_negative_integer(value)) {
            snprintf(error_msg, error_size, "invalid grace period '%s' (must be non-negative integer)", value);
            return -1;
        }
        int val = atoi(value);
        if (val > 86400) {  // Max 1 day
            snprintf(error_msg, error_size, "grace period '%s' too long (max 86400 seconds)", value);
            return -1;
        }
        return 0;
    }
    
    // Subscription limits
    if (strcmp(key, "max_subscriptions_per_client") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid max_subscriptions_per_client '%s'", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 1 || val > 1000) {
            snprintf(error_msg, error_size, "max_subscriptions_per_client '%s' out of range (1-1000)", value);
            return -1;
        }
        return 0;
    }
    
    if (strcmp(key, "max_total_subscriptions") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid max_total_subscriptions '%s'", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 1 || val > 100000) {
            snprintf(error_msg, error_size, "max_total_subscriptions '%s' out of range (1-100000)", value);
            return -1;
        }
        return 0;
    }
    
    if (strcmp(key, "max_filters_per_subscription") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid max_filters_per_subscription '%s'", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 1 || val > 100) {
            snprintf(error_msg, error_size, "max_filters_per_subscription '%s' out of range (1-100)", value);
            return -1;
        }
        return 0;
    }
    
    // Event limits
    if (strcmp(key, "max_event_tags") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid max_event_tags '%s'", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 1 || val > 1000) {
            snprintf(error_msg, error_size, "max_event_tags '%s' out of range (1-1000)", value);
            return -1;
        }
        return 0;
    }
    
    if (strcmp(key, "max_content_length") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid max_content_length '%s'", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 100 || val > 1048576) {  // 1MB max
            snprintf(error_msg, error_size, "max_content_length '%s' out of range (100-1048576)", value);
            return -1;
        }
        return 0;
    }
    
    if (strcmp(key, "max_message_length") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid max_message_length '%s'", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 1024 || val > 1048576) {  // 1KB to 1MB
            snprintf(error_msg, error_size, "max_message_length '%s' out of range (1024-1048576)", value);
            return -1;
        }
        return 0;
    }
    
    // Performance limits
    if (strcmp(key, "default_limit") == 0 || strcmp(key, "max_limit") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid %s '%s'", key, value);
            return -1;
        }
        int val = atoi(value);
        if (val < 1 || val > 50000) {
            snprintf(error_msg, error_size, "%s '%s' out of range (1-50000)", key, value);
            return -1;
        }
        return 0;
    }
    
    // Key validation for relay keys
    if (strcmp(key, "relay_pubkey") == 0 || strcmp(key, "relay_privkey") == 0) {
        if (!is_valid_hex_key(value)) {
            snprintf(error_msg, error_size, "invalid %s format (must be 64-character hex)", key);
            return -1;
        }
        return 0;
    }
    
    // Special validation for d tag (relay identifier)
    if (strcmp(key, "d") == 0) {
        if (!is_valid_hex_key(value)) {
            snprintf(error_msg, error_size, "invalid relay identifier 'd' format (must be 64-character hex)");
            return -1;
        }
        return 0;
    }
    
    // NIP-42 Authentication fields
    if (strcmp(key, "nip42_auth_required") == 0) {
        if (!is_valid_boolean(value)) {
            snprintf(error_msg, error_size, "invalid boolean value '%s' for nip42_auth_required", value);
            return -1;
        }
        return 0;
    }

    // NIP-59 Gift Wrap Timestamp Configuration
    if (strcmp(key, "nip59_timestamp_max_delay_sec") == 0) {
        if (!is_valid_non_negative_integer(value)) {
            snprintf(error_msg, error_size, "invalid nip59_timestamp_max_delay_sec '%s' (must be non-negative integer)", value);
            return -1;
        }
        long val = strtol(value, NULL, 10);
        if (val > 604800) {  // Max 7 days
            snprintf(error_msg, error_size, "nip59_timestamp_max_delay_sec '%s' too large (max 604800 seconds = 7 days)", value);
            return -1;
        }
        return 0;
    }
    
    if (strcmp(key, "nip42_auth_required_kinds") == 0) {
        // Validate comma-separated list of kind numbers
        if (!value || strlen(value) == 0) {
            return 0; // Empty list is valid
        }
        
        char* value_copy = strdup(value);
        if (!value_copy) {
            snprintf(error_msg, error_size, "memory allocation failed for kind validation");
            return -1;
        }
        
        char* token = strtok(value_copy, ",");
        while (token) {
            // Trim whitespace
            while (*token == ' ' || *token == '\t') token++;
            char* end = token + strlen(token) - 1;
            while (end > token && (*end == ' ' || *end == '\t')) end--;
            *(end + 1) = '\0';
            
            // Validate each kind number
            if (!is_valid_non_negative_integer(token)) {
                free(value_copy);
                snprintf(error_msg, error_size, "invalid kind number '%s' in nip42_auth_required_kinds", token);
                return -1;
            }
            
            int kind = atoi(token);
            if (kind < 0 || kind > 65535) {
                free(value_copy);
                snprintf(error_msg, error_size, "kind number '%s' out of range (0-65535)", token);
                return -1;
            }
            
            token = strtok(NULL, ",");
        }
        
        free(value_copy);
        return 0;
    }
    
    if (strcmp(key, "nip42_challenge_expiration") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid nip42_challenge_expiration '%s' (must be positive integer)", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 60 || val > 3600) {  // 1 minute to 1 hour
            snprintf(error_msg, error_size, "nip42_challenge_expiration '%s' out of range (60-3600 seconds)", value);
            return -1;
        }
        return 0;
    }
    
    if (strcmp(key, "nip42_challenge_window") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid nip42_challenge_window '%s' (must be positive integer)", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 300 || val > 7200) {  // 5 minutes to 2 hours
            snprintf(error_msg, error_size, "nip42_challenge_window '%s' out of range (300-7200 seconds)", value);
            return -1;
        }
        return 0;
    }
    
    if (strcmp(key, "nip42_max_auth_events") == 0) {
        if (!is_valid_positive_integer(value)) {
            snprintf(error_msg, error_size, "invalid nip42_max_auth_events '%s' (must be positive integer)", value);
            return -1;
        }
        int val = atoi(value);
        if (val < 10 || val > 10000) {  // 10 to 10,000 auth events
            snprintf(error_msg, error_size, "nip42_max_auth_events '%s' out of range (10-10000)", value);
            return -1;
        }
        return 0;
    }
    
    // Unknown field - log warning but allow
    DEBUG_WARN("Unknown configuration field");
    printf("  Field: %s = %s\n", key, value);
    return 0;
}

// Validate all fields in a configuration event
static int validate_configuration_event_fields(const cJSON* event, char* error_msg, size_t error_size) {
    if (!event) {
        snprintf(error_msg, error_size, "null configuration event");
        return -1;
    }
    
    
    cJSON* tags = cJSON_GetObjectItem(event, "tags");
    if (!tags || !cJSON_IsArray(tags)) {
        snprintf(error_msg, error_size, "missing or invalid tags array");
        return -1;
    }
    
    int validated_fields = 0;
    int validation_errors = 0;
    char field_error[512];
    
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags) {
        if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
            cJSON* tag_key = cJSON_GetArrayItem(tag, 0);
            cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
            
            if (tag_key && tag_value &&
                cJSON_IsString(tag_key) && cJSON_IsString(tag_value)) {
                
                const char* key = cJSON_GetStringValue(tag_key);
                const char* value = cJSON_GetStringValue(tag_value);
                
                if (validate_config_field(key, value, field_error, sizeof(field_error)) != 0) {
                    // Safely truncate the error message if needed
                    size_t prefix_len = strlen("field validation failed: ");
                    size_t available_space = error_size > prefix_len ? error_size - prefix_len - 1 : 0;
                    
                    if (available_space > 0) {
                        snprintf(error_msg, error_size, "field validation failed: %.*s",
                                (int)available_space, field_error);
                    } else {
                        strncpy(error_msg, "field validation failed", error_size - 1);
                        error_msg[error_size - 1] = '\0';
                    }
                    
                    DEBUG_ERROR("Configuration field validation failed");
                    printf("  Field: %s = %s\n", key, value);
                    printf("  Error: %s\n", field_error);
                    validation_errors++;
                    return -1;  // Stop on first error
                } else {
                    validated_fields++;
                }
            }
        }
    }
    
    if (validation_errors > 0) {
        char summary[256];
        snprintf(summary, sizeof(summary), "%d configuration fields failed validation", validation_errors);
        DEBUG_ERROR(summary);
        return -1;
    }

    return 0;
}

int process_configuration_event(const cJSON* event) {
    if (!event) {
        DEBUG_ERROR("Invalid configuration event");
        return -1;
    }

    // Validate event structure
    cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
    cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
    
    if (!kind_obj || cJSON_GetNumberValue(kind_obj) != 33334) {
        DEBUG_ERROR("Invalid event kind for configuration");
        return -1;
    }
    
    if (!pubkey_obj) {
        DEBUG_ERROR("Missing pubkey in configuration event");
        return -1;
    }
    
    // Verify it's from the admin
    const char* event_pubkey = cJSON_GetStringValue(pubkey_obj);
    const char* admin_pubkey = get_config_value("admin_pubkey");
    if (admin_pubkey && strlen(admin_pubkey) > 0) {
        if (strcmp(event_pubkey, admin_pubkey) != 0) {
            DEBUG_ERROR("Configuration event not from authorized admin");
            return -1;
        }
    }
    
    // Comprehensive event validation using nostr_core_lib

    // First validate the event structure (fields, format, etc.)
    if (nostr_validate_event_structure((cJSON*)event) != NOSTR_SUCCESS) {
        DEBUG_ERROR("Configuration event has invalid structure");
        return -1;
    }

    // Then validate the cryptographic signature
    if (nostr_verify_event_signature((cJSON*)event) != NOSTR_SUCCESS) {
        DEBUG_ERROR("Configuration event has invalid signature");
        return -1;
    }

    // NEW: Validate configuration field values
    char validation_error[512];
    if (validate_configuration_event_fields(event, validation_error, sizeof(validation_error)) != 0) {
        DEBUG_ERROR("Configuration field validation failed");
        printf("  Validation error: %s\n", validation_error);
        return -1;
    }
    
    // Store in database
    if (store_config_event_in_database(event) != 0) {
        DEBUG_ERROR("Failed to store configuration event");
        return -1;
    }
    
    // Apply configuration
    if (apply_configuration_from_event(event) != 0) {
        DEBUG_ERROR("Failed to apply configuration from event");
        return -1;
    }
    
    DEBUG_INFO("✓ Configuration event processed successfully with field validation");
    return 0;
}

// ================================
// RUNTIME CONFIGURATION HANDLERS
// ================================

// External functions and globals from main.c that need to be updated
extern void update_subscription_manager_config(void);
extern void init_pow_config(void);
extern void init_expiration_config(void);
extern void init_relay_info(void);

// Compare configuration values between old and new config
static const char* get_config_value_from_event(const cJSON* event, const char* key) {
    if (!event || !key) return NULL;
    
    cJSON* tags = cJSON_GetObjectItem(event, "tags");
    if (!tags || !cJSON_IsArray(tags)) return NULL;
    
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags) {
        if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
            cJSON* tag_key = cJSON_GetArrayItem(tag, 0);
            cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
            
            if (tag_key && tag_value &&
                cJSON_IsString(tag_key) && cJSON_IsString(tag_value)) {
                if (strcmp(cJSON_GetStringValue(tag_key), key) == 0) {
                    return cJSON_GetStringValue(tag_value);
                }
            }
        }
    }
    return NULL;
}

// Check if a configuration value has changed
static int config_value_changed(const cJSON* old_config, const cJSON* new_config, const char* key) {
    const char* old_value = get_config_value_from_event(old_config, key);
    const char* new_value = get_config_value_from_event(new_config, key);
    
    // Both NULL - no change
    if (!old_value && !new_value) return 0;
    
    // One is NULL, other isn't - changed
    if (!old_value || !new_value) return 1;
    
    // Compare string values
    return strcmp(old_value, new_value) != 0;
}

// Apply runtime configuration changes by calling appropriate handlers
int apply_runtime_config_handlers(const cJSON* old_config, const cJSON* new_config) {
    if (!new_config) return 0;
    
    int handlers_applied = 0;
    
    DEBUG_INFO("Checking for runtime configuration changes...");
    
    // Subscription Manager Configuration
    if (config_value_changed(old_config, new_config, "max_subscriptions_per_client") ||
        config_value_changed(old_config, new_config, "max_total_subscriptions")) {
        DEBUG_INFO("Subscription limits changed - updating subscription manager");
        update_subscription_manager_config();
        handlers_applied++;
    }
    
    // NIP-13 Proof of Work Configuration
    if (config_value_changed(old_config, new_config, "pow_min_difficulty") ||
        config_value_changed(old_config, new_config, "pow_mode")) {
        DEBUG_INFO("PoW configuration changed - reinitializing PoW system");
        init_pow_config();
        handlers_applied++;
    }
    
    // NIP-40 Expiration Configuration
    if (config_value_changed(old_config, new_config, "nip40_expiration_enabled") ||
        config_value_changed(old_config, new_config, "nip40_expiration_strict") ||
        config_value_changed(old_config, new_config, "nip40_expiration_filter") ||
        config_value_changed(old_config, new_config, "nip40_expiration_grace_period")) {
        DEBUG_INFO("Expiration configuration changed - reinitializing expiration system");
        init_expiration_config();
        handlers_applied++;
    }
    
    // NIP-11 Relay Information
    if (config_value_changed(old_config, new_config, "relay_description") ||
        config_value_changed(old_config, new_config, "relay_contact") ||
        config_value_changed(old_config, new_config, "relay_software") ||
        config_value_changed(old_config, new_config, "relay_version") ||
        config_value_changed(old_config, new_config, "max_message_length") ||
        config_value_changed(old_config, new_config, "max_event_tags") ||
        config_value_changed(old_config, new_config, "max_content_length")) {
        DEBUG_INFO("Relay information changed - reinitializing relay info");
        init_relay_info();
        handlers_applied++;
    }
    
    // Log configuration changes for audit
    if (handlers_applied > 0) {
        char audit_msg[512];
        snprintf(audit_msg, sizeof(audit_msg),
                "Configuration updated via admin event - %d system components reinitialized",
                handlers_applied);
        DEBUG_INFO(audit_msg);
    } else {
        DEBUG_INFO("No runtime configuration changes detected");
    }
    
    return handlers_applied;
}

int apply_configuration_from_event(const cJSON* event) {
    if (!event) {
        DEBUG_ERROR("Invalid event for configuration application");
        return -1;
    }
    
    DEBUG_INFO("Applying configuration from event...");
    
    // Store previous config for comparison
    cJSON* old_config = g_current_config;
    
    // Update cached configuration
    g_current_config = cJSON_Duplicate(event, 1);
    
    // Admin pubkey is now stored directly in database, no cache update needed
    
    // Apply runtime configuration changes
    int handlers_applied = apply_runtime_config_handlers(old_config, g_current_config);
    
    // Clean up old config
    if (old_config) {
        cJSON_Delete(old_config);
    }
    
    char success_msg[256];
    snprintf(success_msg, sizeof(success_msg),
             "Configuration applied from event (%d handlers executed)", handlers_applied);
    DEBUG_INFO(success_msg);
    return 0;
}

// ================================
// REAL-TIME EVENT HANDLER (called from main.c)
// ================================

// Handle configuration events received via WebSocket
int handle_configuration_event(cJSON* event, char* error_message, size_t error_size) {
    if (!event) {
        snprintf(error_message, error_size, "invalid: null configuration event");
        return -1;
    }

    DEBUG_INFO("Handling configuration event from WebSocket");

    // Use existing process_configuration_event function
    if (process_configuration_event(event) == 0) {
        // Success
        error_message[0] = '\0';  // Empty error message indicates success
        return 0;
    } else {
        // Failed to process
        snprintf(error_message, error_size, "error: failed to process configuration event");
        return -1;
    }
}

// ================================
// NEW ADMIN API IMPLEMENTATION
// ================================

// ================================
// CONFIG TABLE MANAGEMENT FUNCTIONS
// ================================

// Note: Config table is now created via embedded schema in sql_schema.h

// Get value from config table
const char* get_config_value_from_table(const char* key) {
    if (!g_db || !key) {
        return NULL;
    }

    const char* sql = "SELECT value FROM config WHERE key = ?";

    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return NULL;
    }

    sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);

    const char* result = NULL;

    int step_rc = sqlite3_step(stmt);

    if (step_rc == SQLITE_ROW) {
        const char* value = (char*)sqlite3_column_text(stmt, 0);
        if (value) {
            // Return a dynamically allocated string to prevent buffer reuse
            result = strdup(value);
        }
    }

    sqlite3_finalize(stmt);
    return result;
}

// Set value in config table
int set_config_value_in_table(const char* key, const char* value, const char* data_type,
                              const char* description, const char* category, int requires_restart) {
    if (!g_db || !key || !value || !data_type) {
        return -1;
    }
    
    const char* sql = "INSERT OR REPLACE INTO config (key, value, data_type, description, category, requires_restart) "
                     "VALUES (?, ?, ?, ?, ?, ?)";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return -1;
    }
    
    sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, value, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, data_type, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 4, description ? description : "", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 5, category ? category : "general", -1, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 6, requires_restart);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return (rc == SQLITE_DONE) ? 0 : -1;
}

// Update config in table (simpler version of set_config_value_in_table)
int update_config_in_table(const char* key, const char* value) {
    if (!g_db || !key || !value) {
        return -1;
    }
    
    // Additional validation: reject empty strings to prevent accidental deletion of config values
    if (strlen(value) == 0) {
        DEBUG_WARN("Attempted to update config with empty value - rejecting to prevent data loss");
        printf("  Rejected empty value for key: %s\n", key);
        return -1;
    }
    
    const char* sql = "UPDATE config SET value = ?, updated_at = strftime('%s', 'now') WHERE key = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return -1;
    }
    
    sqlite3_bind_text(stmt, 1, value, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, key, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return (rc == SQLITE_DONE) ? 0 : -1;
}

// DEPRECATED: This function is no longer used in the unified startup flow.
// The new populate_all_config_values_atomic() function handles all config creation atomically.
// This function is kept for backward compatibility but should not be called in new code.
int populate_default_config_values(void) {
    DEBUG_TRACE("Entering populate_default_config_values()");

    if (!g_db) {
        DEBUG_ERROR("Database not available for populating default config values");
        DEBUG_TRACE("Exiting populate_default_config_values() - no database");
        return -1;
    }

    // Log config table row count at start of populate_default_config_values
    sqlite3_stmt* count_stmt;
    const char* count_sql = "SELECT COUNT(*) FROM config";
    if (sqlite3_prepare_v2(g_db, count_sql, -1, &count_stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(count_stmt) == SQLITE_ROW) {
            // Row count check completed
        }
        sqlite3_finalize(count_stmt);
    }

    DEBUG_LOG("Populating missing default configuration values in table...");

    int keys_added = 0;
    int keys_skipped = 0;

    // Add all default configuration values to the table (only if they don't exist)
    for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) {
        const char* key = DEFAULT_CONFIG_VALUES[i].key;
        const char* value = DEFAULT_CONFIG_VALUES[i].value;

        // Check if key already exists in config table
        const char* check_sql = "SELECT COUNT(*) FROM config WHERE key = ?";
        sqlite3_stmt* check_stmt;
        
        int check_rc = sqlite3_prepare_v2(g_db, check_sql, -1, &check_stmt, NULL);
        if (check_rc != SQLITE_OK) {
            DEBUG_ERROR("Failed to prepare config existence check");
            continue;
        }
        
        sqlite3_bind_text(check_stmt, 1, key, -1, SQLITE_STATIC);
        
        int key_exists = 0;
        if (sqlite3_step(check_stmt) == SQLITE_ROW) {
            key_exists = sqlite3_column_int(check_stmt, 0) > 0;
        }
        sqlite3_finalize(check_stmt);
        
        // Skip if key already exists (preserve existing configuration)
        if (key_exists) {
            keys_skipped++;
            continue;
        }

        // Determine data type
        const char* data_type = "string";
        if (strcmp(key, "relay_port") == 0 ||
            strcmp(key, "max_connections") == 0 ||
            strcmp(key, "pow_min_difficulty") == 0 ||
            strcmp(key, "max_subscriptions_per_client") == 0 ||
            strcmp(key, "max_total_subscriptions") == 0 ||
            strcmp(key, "max_filters_per_subscription") == 0 ||
            strcmp(key, "max_event_tags") == 0 ||
            strcmp(key, "max_content_length") == 0 ||
            strcmp(key, "max_message_length") == 0 ||
            strcmp(key, "default_limit") == 0 ||
            strcmp(key, "max_limit") == 0 ||
            strcmp(key, "nip42_challenge_expiration") == 0 ||
            strcmp(key, "nip40_expiration_grace_period") == 0) {
            data_type = "integer";
        } else if (strcmp(key, "auth_enabled") == 0 ||
                  strcmp(key, "nip40_expiration_enabled") == 0 ||
                  strcmp(key, "nip40_expiration_strict") == 0 ||
                  strcmp(key, "nip40_expiration_filter") == 0 ||
                  strcmp(key, "nip42_auth_required") == 0) {
            data_type = "boolean";
        }

        // Set category
        const char* category = "general";
        if (strstr(key, "relay_")) {
            category = "relay";
        } else if (strstr(key, "nip40_")) {
            category = "expiration";
        } else if (strstr(key, "nip42_") || strstr(key, "auth_")) {
            category = "authentication";
        } else if (strstr(key, "pow_")) {
            category = "proof_of_work";
        } else if (strstr(key, "max_")) {
            category = "limits";
        }

        // Determine if requires restart (0 = dynamic, 1 = restart required)
        int requires_restart = 0;

        // Restart required configs
        if (strcmp(key, "relay_port") == 0 ||
            strcmp(key, "max_connections") == 0 ||
            strcmp(key, "auth_enabled") == 0 ||
            strcmp(key, "nip42_auth_required") == 0 ||
            strcmp(key, "nip42_auth_required_kinds") == 0 ||
            strcmp(key, "nip42_challenge_timeout") == 0 ||
            strcmp(key, "database_path") == 0) {
            requires_restart = 1;
        }

        // Only insert if key doesn't exist (INSERT will fail if key exists due to UNIQUE constraint)
        const char* insert_sql = "INSERT INTO config (key, value, data_type, description, category, requires_restart) "
                                "VALUES (?, ?, ?, ?, ?, ?)";
        
        sqlite3_stmt* insert_stmt;
        int insert_rc = sqlite3_prepare_v2(g_db, insert_sql, -1, &insert_stmt, NULL);
        if (insert_rc != SQLITE_OK) {
            char error_msg[256];
            snprintf(error_msg, sizeof(error_msg), "Failed to prepare insert for: %s", key);
            DEBUG_ERROR(error_msg);
            continue;
        }
        
        sqlite3_bind_text(insert_stmt, 1, key, -1, SQLITE_STATIC);
        sqlite3_bind_text(insert_stmt, 2, value, -1, SQLITE_STATIC);
        sqlite3_bind_text(insert_stmt, 3, data_type, -1, SQLITE_STATIC);
        sqlite3_bind_text(insert_stmt, 4, "", -1, SQLITE_STATIC);
        sqlite3_bind_text(insert_stmt, 5, category, -1, SQLITE_STATIC);
        sqlite3_bind_int(insert_stmt, 6, requires_restart);
        
        int step_rc = sqlite3_step(insert_stmt);
        sqlite3_finalize(insert_stmt);
        
        if (step_rc == SQLITE_DONE) {
            keys_added++;
        } else {
            // Silently skip if key already exists (UNIQUE constraint violation)
            if (step_rc != SQLITE_CONSTRAINT) {
                char error_msg[256];
                snprintf(error_msg, sizeof(error_msg), "Failed to insert default config: %s = %s (error: %s)",
                        key, value, sqlite3_errmsg(g_db));
                DEBUG_ERROR(error_msg);
            }
        }
    }

    if (keys_added > 0) {
        char success_msg[256];
        snprintf(success_msg, sizeof(success_msg),
                "Added %d missing default configuration values (%d existing keys preserved)",
                keys_added, keys_skipped);
        DEBUG_LOG(success_msg);
    } else if (keys_skipped > 0) {
        DEBUG_LOG("All default configuration keys already exist - no changes needed");
    }

    // Log config table row count at end of populate_default_config_values
    if (sqlite3_prepare_v2(g_db, count_sql, -1, &count_stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(count_stmt) == SQLITE_ROW) {
            // Row count check completed
        }
        sqlite3_finalize(count_stmt);
    }

    DEBUG_TRACE("Exiting populate_default_config_values() - success");
    return 0;
}

// DEPRECATED: This function is no longer used in the unified startup flow.
// The new populate_all_config_values_atomic() function handles pubkey storage atomically.
// This function is kept for backward compatibility but should not be called in new code.
int add_pubkeys_to_config_table(void) {
    if (!g_db) {
        DEBUG_ERROR("Database not available for pubkey storage");
        return -1;
    }

    // Log config table row count at start of add_pubkeys_to_config_table
    sqlite3_stmt* count_stmt;
    const char* count_sql = "SELECT COUNT(*) FROM config";
    if (sqlite3_prepare_v2(g_db, count_sql, -1, &count_stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(count_stmt) == SQLITE_ROW) {
            // Row count check completed
        }
        sqlite3_finalize(count_stmt);
    }

    DEBUG_INFO("Adding dynamically generated pubkeys to config table...");
    
    // Get pubkeys directly from database (no cache dependency)
    const char* admin_pubkey_cache = get_config_value_from_table("admin_pubkey");
    const char* relay_pubkey_cache = get_config_value_from_table("relay_pubkey");
    
    // For existing relays, admin_pubkey might already be in the database but not in cache yet
    // Try to load it from the database config table first
    DEBUG_LOG("Checking for existing admin_pubkey in config table");
    const char* admin_pubkey_from_db = get_config_value_from_table("admin_pubkey");
    DEBUG_TRACE("get_config_value_from_table('admin_pubkey') returned: %s (length: %zu)",
                admin_pubkey_from_db ? admin_pubkey_from_db : "NULL",
                admin_pubkey_from_db ? strlen(admin_pubkey_from_db) : 0);
    DEBUG_LOG("get_config_value_from_table returned: %s", admin_pubkey_from_db ? admin_pubkey_from_db : "NULL");
    if (admin_pubkey_from_db && strlen(admin_pubkey_from_db) == 64) {
        DEBUG_LOG("Found valid admin_pubkey in config table: %s", admin_pubkey_from_db);
        // Admin pubkey is now stored directly in database, no cache update needed
        DEBUG_INFO("✓ Loaded admin_pubkey from config table into cache");
        // Free the allocated string and return success - pubkey already in table
        free((char*)admin_pubkey_from_db);
        return 0;
    } else {
        DEBUG_LOG("No valid admin_pubkey found in config table (length: %zu)", admin_pubkey_from_db ? strlen(admin_pubkey_from_db) : 0);
    }
    if (admin_pubkey_from_db) {
        free((char*)admin_pubkey_from_db);
    }

    // If not in config table, try loading from old event-based config (migration scenario)
    const char* sql = "SELECT pubkey FROM events WHERE kind = 33334 ORDER BY created_at DESC LIMIT 1";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        if (sqlite3_step(stmt) == SQLITE_ROW) {
            const char* event_pubkey = (const char*)sqlite3_column_text(stmt, 0);
            if (event_pubkey && strlen(event_pubkey) == 64) {
                // Store in config table for future use
                if (set_config_value_in_table("admin_pubkey", event_pubkey, "string",
                                                "Administrator public key", "authentication", 0) == 0) {

                    // Admin pubkey is now stored directly in database, no cache update needed

                    sqlite3_finalize(stmt);
                    DEBUG_INFO("✓ Migrated admin_pubkey from old config event to config table");
                    return 0;
                }
            }
        }
        sqlite3_finalize(stmt);
    }

    // Use cache value for storage (either from first-time startup or just loaded from DB)
    const char* admin_pubkey = admin_pubkey_cache;
    const char* relay_pubkey = relay_pubkey_cache;

    if (!admin_pubkey || strlen(admin_pubkey) != 64) {
        DEBUG_ERROR("Admin pubkey not available or invalid for config table storage");
        return -1;
    }

    if (!relay_pubkey || strlen(relay_pubkey) != 64) {
        DEBUG_ERROR("Relay pubkey not available or invalid for config table storage");
        return -1;
    }

    // Store admin pubkey in config table
    if (set_config_value_in_table("admin_pubkey", admin_pubkey, "string",
                                    "Administrator public key", "authentication", 0) != 0) {
        DEBUG_ERROR("Failed to store admin_pubkey in config table");
        return -1;
    }

    // Store relay pubkey in config table
    if (set_config_value_in_table("relay_pubkey", relay_pubkey, "string",
                                    "Relay public key", "relay", 0) != 0) {
        DEBUG_ERROR("Failed to store relay_pubkey in config table");
        return -1;
    }

    DEBUG_INFO("✓ Dynamically generated pubkeys added to config table");
    printf("  Admin pubkey: %s\n", admin_pubkey ? admin_pubkey : "NULL");
    printf("  Relay pubkey: %s\n", relay_pubkey ? relay_pubkey : "NULL");

    // Log config table row count at end of add_pubkeys_to_config_table
    if (sqlite3_prepare_v2(g_db, count_sql, -1, &count_stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(count_stmt) == SQLITE_ROW) {
            // Row count check completed
        }
        sqlite3_finalize(count_stmt);
    }

    return 0;
}

// ================================
// ADMIN EVENT PROCESSING FUNCTIONS
// ================================

// Forward declaration for admin authorization function from main.c
extern int is_authorized_admin_event(cJSON* event);

// Process admin events (updated for Kind 23456)
int process_admin_event_in_config(cJSON* event, char* error_message, size_t error_size, struct lws* wsi) {
    cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
    if (!kind_obj || !cJSON_IsNumber(kind_obj)) {
        DEBUG_ERROR("Missing or invalid kind in admin event");
        snprintf(error_message, error_size, "invalid: missing or invalid kind");
        return -1;
    }

    int kind = (int)cJSON_GetNumberValue(kind_obj);

    // Route to appropriate handler based on kind
    switch (kind) {
        case 23456:  // New ephemeral auth rules management
            return process_admin_auth_event(event, error_message, error_size, wsi);
        default:
            DEBUG_ERROR("Unsupported admin event kind");
            printf("  Unsupported kind: %d\n", kind);
            snprintf(error_message, error_size, "invalid: unsupported admin event kind %d", kind);
            return -1;
    }
}

// Handle legacy Kind 33334 configuration management events
int process_admin_config_event(cJSON* event, char* error_message, size_t error_size) {

    // Parse tags to find query commands according to API specification
    cJSON* tags_obj = cJSON_GetObjectItem(event, "tags");
    if (tags_obj && cJSON_IsArray(tags_obj)) {
        cJSON* tag = NULL;
        cJSON_ArrayForEach(tag, tags_obj) {
            if (!cJSON_IsArray(tag) || cJSON_GetArraySize(tag) < 2) {
                continue;
            }
            
            cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
            cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
            
            if (!cJSON_IsString(tag_name) || !cJSON_IsString(tag_value)) {
                continue;
            }
            
            const char* tag_key = cJSON_GetStringValue(tag_name);
            const char* tag_val = cJSON_GetStringValue(tag_value);
            
            // Handle config_query commands per API spec
            if (strcmp(tag_key, "config_query") == 0) {
                printf("  Config Query: %s\n", tag_val);
                
                // Config queries are not implemented for legacy kind 33334
                snprintf(error_message, error_size, "config queries not supported for legacy kind 33334");
                return -1;
            }
        }
    }
    
    // Handle configuration updates (set action) - parse remaining tags for config updates
    if (!tags_obj || !cJSON_IsArray(tags_obj)) {
        snprintf(error_message, error_size, "invalid: configuration event must have tags");
        return -1;
    }
    
    // Begin transaction for atomic config updates
    int rc = sqlite3_exec(g_db, "BEGIN IMMEDIATE TRANSACTION", NULL, NULL, NULL);
    if (rc != SQLITE_OK) {
        snprintf(error_message, error_size, "failed to begin config transaction");
        return -1;
    }
    
    int updates_applied = 0;
    
    // Process each tag as a configuration parameter
    cJSON* config_tag = NULL;
    cJSON_ArrayForEach(config_tag, tags_obj) {
        if (!cJSON_IsArray(config_tag) || cJSON_GetArraySize(config_tag) < 2) {
            continue;
        }
        
        cJSON* tag_name = cJSON_GetArrayItem(config_tag, 0);
        cJSON* tag_value = cJSON_GetArrayItem(config_tag, 1);
        
        if (!cJSON_IsString(tag_name) || !cJSON_IsString(tag_value)) {
            continue;
        }
        
        const char* key = cJSON_GetStringValue(tag_name);
        const char* value = cJSON_GetStringValue(tag_value);
        
        // Skip query commands and system commands - only process config updates
        if (strcmp(key, "config_query") == 0) {
            continue;
        }
        
        
        // Update configuration in table
        if (update_config_in_table(key, value) == 0) {
            updates_applied++;
        }
    }
    
    if (updates_applied > 0) {
        sqlite3_exec(g_db, "COMMIT", NULL, NULL, NULL);

        char success_msg[256];
        snprintf(success_msg, sizeof(success_msg), "Applied %d configuration updates", updates_applied);
        DEBUG_INFO(success_msg);
    } else {
        sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
        snprintf(error_message, error_size, "no valid configuration parameters found");
        return -1;
    }
    
    return 0;
}

// Handle Kind 23456 auth rules management
int process_admin_auth_event(cJSON* event, char* error_message, size_t error_size, struct lws* wsi) {
    cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
    int kind = kind_obj ? (int)cJSON_GetNumberValue(kind_obj) : 0;

    // Extract and log additional event details for debugging

    // printf("  Content length: %zu\n", event_content ? strlen(event_content) : 0);
    // printf("  Has tags: %s\n", tags_obj ? "yes" : "no");
    // if (tags_obj && cJSON_IsArray(tags_obj)) {
    //     printf("  Tags count: %d\n", cJSON_GetArraySize(tags_obj));
    // }
    
    // Route all Kind 23456 events through the unified handler
    if (kind == 23456) {
        return handle_kind_23456_unified(event, error_message, error_size, wsi);
    }


    printf("  Unsupported kind: %d\n", kind);
    snprintf(error_message, error_size, "invalid: unsupported auth event kind %d", kind);
    return -1;
}

// ================================
// AUTH RULES MANAGEMENT FUNCTIONS
// ================================

// Add auth rule from configuration
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
                              const char* pattern_value) {
    if (!g_db || !rule_type || !pattern_type || !pattern_value) {
        return -1;
    }

    const char* sql = "INSERT INTO auth_rules (rule_type, pattern_type, pattern_value) "
                      "VALUES (?, ?, ?)";

    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return -1;
    }

    sqlite3_bind_text(stmt, 1, rule_type, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, pattern_type, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, pattern_value, -1, SQLITE_STATIC);

    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);

    return (rc == SQLITE_DONE) ? 0 : -1;
}

// Remove auth rule from configuration
int remove_auth_rule_from_config(const char* rule_type, const char* pattern_type,
                                const char* pattern_value) {
    if (!g_db || !rule_type || !pattern_type || !pattern_value) {
        return -1;
    }
    
    const char* sql = "DELETE FROM auth_rules WHERE rule_type = ? AND pattern_type = ? AND pattern_value = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return -1;
    }
    
    sqlite3_bind_text(stmt, 1, rule_type, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, pattern_type, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, pattern_value, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return (rc == SQLITE_DONE) ? 0 : -1;
}

// ================================
// UNIFIED TAG PARSING UTILITIES
// ================================

// Get the first tag name from an event
const char* get_first_tag_name(cJSON* event) {
    if (!event) return NULL;
    
    cJSON* tags_obj = cJSON_GetObjectItem(event, "tags");
    if (!tags_obj || !cJSON_IsArray(tags_obj)) return NULL;
    
    cJSON* first_tag = cJSON_GetArrayItem(tags_obj, 0);
    if (!first_tag || !cJSON_IsArray(first_tag) || cJSON_GetArraySize(first_tag) < 1) {
        return NULL;
    }
    
    cJSON* tag_name = cJSON_GetArrayItem(first_tag, 0);
    if (!tag_name || !cJSON_IsString(tag_name)) return NULL;
    
    return cJSON_GetStringValue(tag_name);
}

// Get tag value at specified index
const char* get_tag_value(cJSON* event, const char* tag_name, int value_index) {
    if (!event || !tag_name) return NULL;
    
    cJSON* tags_obj = cJSON_GetObjectItem(event, "tags");
    if (!tags_obj || !cJSON_IsArray(tags_obj)) return NULL;
    
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags_obj) {
        if (!cJSON_IsArray(tag) || cJSON_GetArraySize(tag) <= value_index) {
            continue;
        }
        
        cJSON* tag_key = cJSON_GetArrayItem(tag, 0);
        if (!tag_key || !cJSON_IsString(tag_key)) continue;
        
        if (strcmp(cJSON_GetStringValue(tag_key), tag_name) == 0) {
            cJSON* tag_value = cJSON_GetArrayItem(tag, value_index);
            if (tag_value && cJSON_IsString(tag_value)) {
                return cJSON_GetStringValue(tag_value);
            }
        }
    }
    return NULL;
}

// Parse auth query parameters from event tags
int parse_auth_query_parameters(cJSON* event, char** query_type, char** pattern_value) {
    if (!event || !query_type) return -1;
    
    *query_type = NULL;
    if (pattern_value) *pattern_value = NULL;
    
    const char* query_val = get_tag_value(event, "auth_query", 1);
    if (query_val) {
        *query_type = strdup(query_val);
        
        // For pattern queries, get the pattern value from the same tag
        if (strcmp(query_val, "pattern") == 0 && pattern_value) {
            const char* pattern_val = get_tag_value(event, "auth_query", 2);
            if (pattern_val) {
                *pattern_value = strdup(pattern_val);
            }
        }
        return 0;
    }
    return -1;
}

// ================================
// ADMIN RESPONSE EVENT SYSTEM
// ================================

// Create signed kind 23457 admin response event
cJSON* create_admin_response_event(const char* encrypted_content, const char* recipient_pubkey) {
    if (!encrypted_content || !recipient_pubkey) {
        DEBUG_ERROR("Invalid parameters for admin response event creation");
        return NULL;
    }
    
    // printf("  Recipient pubkey: %.16s...\n", recipient_pubkey);
    // printf("  Encrypted content length: %zu\n", strlen(encrypted_content));
    
    // Get relay private key for signing
    char* relay_privkey = get_relay_private_key();
    if (!relay_privkey) {
        DEBUG_ERROR("Relay private key not available for admin response signing");
        return NULL;
    }
    
    // Convert relay private key from hex to bytes
    unsigned char relay_privkey_bytes[32];
    if (nostr_hex_to_bytes(relay_privkey, relay_privkey_bytes, 32) != NOSTR_SUCCESS) {
        DEBUG_ERROR("Failed to convert relay private key from hex for admin response");
        free(relay_privkey);
        return NULL;
    }
    
    // Clean up private key string immediately
    free(relay_privkey);
    
    // Create tags array for kind 23457 event
    cJSON* tags = cJSON_CreateArray();
    if (!tags) {
        DEBUG_ERROR("Failed to create tags array for admin response event");
        memset(relay_privkey_bytes, 0, 32);
        return NULL;
    }
    
    // Add p tag with recipient pubkey (admin who sent the query)
    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);
    
    // Create and sign the kind 23457 event using nostr_core_lib
    cJSON* response_event = nostr_create_and_sign_event(
        23457,                                          // kind: admin response
        encrypted_content,                              // content: NIP-44 encrypted response
        tags,                                           // tags: p tag with recipient
        relay_privkey_bytes,                           // private key bytes for signing
        time(NULL)                                      // created_at timestamp
    );
    
    // Clean up private key bytes immediately after use
    memset(relay_privkey_bytes, 0, 32);
    cJSON_Delete(tags); // Clean up tags as they were duplicated in nostr_create_and_sign_event
    
    if (!response_event) {
        DEBUG_ERROR("Failed to create and sign kind 23457 admin response event");
        return NULL;
    }
    
    // Log success information
    cJSON* id_obj = cJSON_GetObjectItem(response_event, "id");
    cJSON* pubkey_obj = cJSON_GetObjectItem(response_event, "pubkey");
    
    if (id_obj && pubkey_obj) {
        // printf("  Event ID: %s\n", cJSON_GetStringValue(id_obj));
        // printf("  Relay pubkey: %.16s...\n", cJSON_GetStringValue(pubkey_obj));
    }
    
    return response_event;
}

// Encrypt admin response content using NIP-44
char* encrypt_admin_response_content(const cJSON* response_data, const char* recipient_pubkey) {
    if (!response_data || !recipient_pubkey) {
        DEBUG_ERROR("Invalid parameters for admin response encryption");
        return NULL;
    }
    

    // Convert response data to JSON string
    char* response_json = cJSON_Print(response_data);
    if (!response_json) {
        DEBUG_ERROR("Failed to serialize response data for encryption");
        return NULL;
    }

    // printf("  JSON length: %zu\n", strlen(response_json));
    // printf("  JSON preview: %.100s%s\n", response_json,
    //        strlen(response_json) > 100 ? "..." : "");
    
    // Get relay private key for encryption
    char* relay_privkey = get_relay_private_key();
    if (!relay_privkey) {
        DEBUG_ERROR("Relay private key not available for admin response encryption");
        free(response_json);
        return NULL;
    }
    
    // Convert relay private key from hex to bytes
    unsigned char relay_privkey_bytes[32];
    if (nostr_hex_to_bytes(relay_privkey, relay_privkey_bytes, 32) != NOSTR_SUCCESS) {
        DEBUG_ERROR("Failed to convert relay private key from hex for encryption");
        free(relay_privkey);
        free(response_json);
        return NULL;
    }
    
    // Clean up private key string immediately
    free(relay_privkey);
    
    // Convert recipient public key from hex to bytes
    unsigned char recipient_pubkey_bytes[32];
    if (nostr_hex_to_bytes(recipient_pubkey, recipient_pubkey_bytes, 32) != NOSTR_SUCCESS) {
        DEBUG_ERROR("Failed to convert recipient public key from hex for encryption");
        memset(relay_privkey_bytes, 0, 32);
        free(response_json);
        return NULL;
    }
    
    // Perform NIP-44 encryption (relay as sender, admin as recipient)
    // Buffer needs to accommodate: version(1) + nonce(32) + ciphertext(plaintext_size) + mac(32) + base64 overhead(~33%)
    // For 5KB plaintext: (1+32+5000+32)*1.34 ≈ 6800 bytes, use 16KB to be safe
    char encrypted_content[16384]; // Buffer for encrypted content (16KB)
    int encrypt_result = nostr_nip44_encrypt(
        relay_privkey_bytes,           // sender private key
        recipient_pubkey_bytes,        // recipient public key
        response_json,                 // plaintext to encrypt
        encrypted_content,             // output buffer
        sizeof(encrypted_content)      // output buffer size
    );
    
    // Clean up sensitive data immediately after use
    memset(relay_privkey_bytes, 0, 32);
    free(response_json);
    
    if (encrypt_result != NOSTR_SUCCESS) {
        DEBUG_ERROR("NIP-44 encryption failed for admin response");
        printf("  Encryption result code: %d\n", encrypt_result);
        return NULL;
    }

    // printf("  Encrypted content length: %zu\n", strlen(encrypted_content));
    // printf("  Encrypted preview: %.50s...\n", encrypted_content);
    
    // Return encrypted content as allocated string
    return strdup(encrypted_content);
}

// Send admin response event using relay's standard event distribution system
int send_admin_response_event(const cJSON* response_data, const char* recipient_pubkey, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;
    if (!response_data || !recipient_pubkey) {
        DEBUG_ERROR("Invalid parameters for admin response event transmission");
        return -1;
    }
    
    
    // Step 1: Encrypt response data using NIP-44
    char* encrypted_content = encrypt_admin_response_content(response_data, recipient_pubkey);
    if (!encrypted_content) {
        DEBUG_ERROR("Failed to encrypt admin response content");
        return -1;
    }
    
    // Step 2: Create signed kind 23457 event
    cJSON* response_event = create_admin_response_event(encrypted_content, recipient_pubkey);
    free(encrypted_content); // Clean up encrypted content after use
    
    if (!response_event) {
        DEBUG_ERROR("Failed to create admin response event");
        return -1;
    }
    
    cJSON* id_obj = cJSON_GetObjectItem(response_event, "id");
    if (id_obj) {
        // printf("  Event ID: %s\n", cJSON_GetStringValue(id_obj));
    }

    // Step 3: Store event in database for persistence
    extern int store_event(cJSON* event);
    if (store_event(response_event) != 0) {
        DEBUG_WARN("Failed to store admin response event in database (continuing with broadcast)");
    }
    
    // Step 4: Broadcast event to all matching subscriptions using relay's standard system
    extern int broadcast_event_to_subscriptions(cJSON* event);
    int broadcast_count = broadcast_event_to_subscriptions(response_event);
    
    if (broadcast_count >= 0) {

        // Clean up and return success - event creation succeeded regardless of broadcast count
        cJSON_Delete(response_event);
        return 0;
    } else {
        DEBUG_ERROR("Failed to broadcast admin response event to subscriptions");
        cJSON_Delete(response_event);
        return -1;
    }
}

// ================================
// WEBSOCKET RESPONSE SYSTEM (LEGACY)
// ================================


// Map query command types to proper response types for frontend routing
static const char* map_auth_query_type_to_response(const char* query_type) {
    if (!query_type) return "auth_rules_unknown";
    
    if (strcmp(query_type, "all") == 0) {
        return "auth_rules_all";
    } else if (strcmp(query_type, "whitelist") == 0) {
        return "auth_rules_whitelist";
    } else if (strcmp(query_type, "blacklist") == 0) {
        return "auth_rules_blacklist";
    } else if (strcmp(query_type, "pattern") == 0) {
        return "auth_rules_pattern";
    } else {
        return "auth_rules_unknown";
    }
}

// Map config query command types to proper response types for frontend routing
static const char* map_config_query_type_to_response(const char* query_type) {
    if (!query_type) return "config_unknown";
    
    if (strcmp(query_type, "all") == 0) {
        return "config_all";
    } else if (strcmp(query_type, "category") == 0) {
        return "config_category";
    } else if (strcmp(query_type, "key") == 0) {
        return "config_key";
    } else {
        return "config_unknown";
    }
}

// Build standardized query response
cJSON* build_query_response(const char* query_type, cJSON* results_array, int total_count) {
    if (!query_type || !results_array) return NULL;
    
    cJSON* response = cJSON_CreateObject();
    if (!response) return NULL;
    
    cJSON_AddStringToObject(response, "query_type", query_type);
    cJSON_AddNumberToObject(response, "total_results", total_count);
    cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));
    cJSON_AddItemToObject(response, "data", cJSON_Duplicate(results_array, 1));
    
    return response;
}

// ================================
// UNIFIED KIND 23456 HANDLER
// ================================

// Single unified handler for all Kind 23456 requests
int handle_kind_23456_unified(cJSON* event, char* error_message, size_t error_size, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;
    if (!event) {
        DEBUG_ERROR("invalid: null event");
        snprintf(error_message, error_size, "invalid: null event");
        return -1;
    }

    // Verify the event sender is the authorized admin
    cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
    if (!pubkey_obj || !cJSON_IsString(pubkey_obj)) {
        DEBUG_ERROR("invalid: missing sender pubkey in event");
        snprintf(error_message, error_size, "invalid: missing sender pubkey in event");
        return -1;
    }

    const char* sender_pubkey = cJSON_GetStringValue(pubkey_obj);
    const char* admin_pubkey = get_config_value("admin_pubkey");

    if (!admin_pubkey) {
        DEBUG_ERROR("error: admin pubkey not available for authorization check");
        snprintf(error_message, error_size, "error: admin pubkey not available for authorization check");
        return -1;
    }

    if (strcmp(sender_pubkey, admin_pubkey) != 0) {
        DEBUG_ERROR("invalid: unauthorized admin event - sender pubkey does not match admin pubkey");
        printf("  Sender pubkey: %.16s...\n", sender_pubkey);
        printf("  Admin pubkey: %.16s...\n", admin_pubkey);
        snprintf(error_message, error_size, "invalid: unauthorized admin event");
        return -1;
    }

    // Check if content is encrypted (NIP-44)
    cJSON* content_obj = cJSON_GetObjectItem(event, "content");
    if (!content_obj || !cJSON_IsString(content_obj)) {
        DEBUG_ERROR("invalid: missing or invalid content");
        snprintf(error_message, error_size, "invalid: missing or invalid content");
        return -1;
    }

    const char* content = cJSON_GetStringValue(content_obj);
    
    cJSON* decrypted_content = NULL;
    
    // Check if content looks like NIP-44 encrypted content (base64 string, not JSON)
    if (content && strlen(content) > 10 && content[0] != '[' && content[0] != '{') {
        // Get relay private key for decryption
        char* relay_privkey = get_relay_private_key();
        if (!relay_privkey) {
            DEBUG_ERROR("error: relay private key not available for decryption");
            snprintf(error_message, error_size, "error: relay private key not available for decryption");
            return -1;
        }
        
        // Get sender's pubkey from the event for NIP-44 decryption
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        if (!pubkey_obj || !cJSON_IsString(pubkey_obj)) {
            DEBUG_ERROR("invalid: missing sender pubkey in event");
            free(relay_privkey);
            snprintf(error_message, error_size, "invalid: missing sender pubkey in event");
            return -1;
        }

        const char* sender_pubkey = cJSON_GetStringValue(pubkey_obj);
        if (!sender_pubkey || strlen(sender_pubkey) != 64) {
            DEBUG_ERROR("invalid: invalid sender pubkey format");
            free(relay_privkey);
            snprintf(error_message, error_size, "invalid: invalid sender pubkey format");
            return -1;
        }
        
        // Convert relay private key from hex to bytes
        unsigned char relay_privkey_bytes[32];
        if (nostr_hex_to_bytes(relay_privkey, relay_privkey_bytes, 32) != NOSTR_SUCCESS) {
            DEBUG_ERROR("error: failed to convert relay private key");
            free(relay_privkey);
            snprintf(error_message, error_size, "error: failed to convert relay private key");
            return -1;
        }

        // Convert sender public key from hex to bytes
        unsigned char sender_pubkey_bytes[32];
        if (nostr_hex_to_bytes(sender_pubkey, sender_pubkey_bytes, 32) != NOSTR_SUCCESS) {
            DEBUG_ERROR("error: failed to convert sender public key");
            free(relay_privkey);
            snprintf(error_message, error_size, "error: failed to convert sender public key");
            return -1;
        }

        // Perform NIP-44 decryption (relay as recipient, admin as sender)
        char decrypted_text[16384]; // Buffer for decrypted content (16KB)
        int decrypt_result = nostr_nip44_decrypt(relay_privkey_bytes, sender_pubkey_bytes, content, decrypted_text, sizeof(decrypted_text));

        // Clean up private key immediately after use
        memset(relay_privkey_bytes, 0, 32);
        free(relay_privkey);

        if (decrypt_result != NOSTR_SUCCESS) {
            DEBUG_ERROR("error: NIP-44 decryption failed");
            snprintf(error_message, error_size, "error: NIP-44 decryption failed");
            return -1;
        }

        // Parse decrypted content as command array directly (NOT as NIP-17 inner event)
        // Kind 23456 events contain direct command arrays: ["command_name", arg1, arg2, ...]
        decrypted_content = cJSON_Parse(decrypted_text);

        if (!decrypted_content || !cJSON_IsArray(decrypted_content)) {
            DEBUG_ERROR("error: decrypted content is not valid command array");
            cJSON_Delete(decrypted_content);
            snprintf(error_message, error_size, "error: decrypted content is not valid command array");
            return -1;
        }

        // Replace event content with decrypted command array for processing
        cJSON_DeleteItemFromObject(event, "content");
        cJSON_AddStringToObject(event, "content", "decrypted");

        // Create synthetic tags from decrypted command array
        // Create new tags array with command tag first
        cJSON* new_tags = cJSON_CreateArray();

        // Add decrypted command as first tag
        if (cJSON_GetArraySize(decrypted_content) > 0) {
            cJSON* first_item = cJSON_GetArrayItem(decrypted_content, 0);
            if (cJSON_IsString(first_item)) {
                cJSON* command_tag = cJSON_CreateArray();
                cJSON_AddItemToArray(command_tag, cJSON_Duplicate(first_item, 1));

                // Add remaining items as tag values
                // Convert non-string items (objects, arrays, numbers) to JSON strings
                for (int i = 1; i < cJSON_GetArraySize(decrypted_content); i++) {
                    cJSON* item = cJSON_GetArrayItem(decrypted_content, i);
                    if (item) {
                        if (cJSON_IsString(item)) {
                            // Keep strings as-is
                            cJSON_AddItemToArray(command_tag, cJSON_Duplicate(item, 1));
                        } else if (cJSON_IsNumber(item)) {
                            // Convert numbers to strings
                            char num_str[32];
                            snprintf(num_str, sizeof(num_str), "%.0f", cJSON_GetNumberValue(item));
                            cJSON_AddItemToArray(command_tag, cJSON_CreateString(num_str));
                        } else if (cJSON_IsObject(item) || cJSON_IsArray(item)) {
                            // Convert objects/arrays to JSON strings
                            char* json_str = cJSON_PrintUnformatted(item);
                            if (json_str) {
                                cJSON_AddItemToArray(command_tag, cJSON_CreateString(json_str));
                                free(json_str);
                            }
                        }
                    }
                }

                cJSON_AddItemToArray(new_tags, command_tag);
            } else {
                DEBUG_ERROR("error: first item in decrypted array is not a string");
            }
        }

        // Add existing tags
        cJSON* existing_tags = cJSON_GetObjectItem(event, "tags");
        if (existing_tags && cJSON_IsArray(existing_tags)) {
            cJSON* tag = NULL;
            cJSON_ArrayForEach(tag, existing_tags) {
                cJSON_AddItemToArray(new_tags, cJSON_Duplicate(tag, 1));
            }
        }

        // Replace event tags with new tags
        cJSON_ReplaceItemInObject(event, "tags", new_tags);

        cJSON_Delete(decrypted_content);
    }
    
    // Parse first tag to determine action type (now from decrypted content if applicable)
    const char* action_type = get_first_tag_name(event);
    if (!action_type) {
        DEBUG_ERROR("invalid: missing or invalid first tag");
        snprintf(error_message, error_size, "invalid: missing or invalid first tag");
        return -1;
    }

    // Route to appropriate handler based on action type
    if (strcmp(action_type, "auth_query") == 0) {
        const char* query_type = get_tag_value(event, action_type, 1);
        if (!query_type) {
            DEBUG_ERROR("invalid: missing auth_query type");
            snprintf(error_message, error_size, "invalid: missing auth_query type");
            return -1;
        }
        return handle_auth_query_unified(event, query_type, error_message, error_size, wsi);
    }
    else if (strcmp(action_type, "config_query") == 0) {
        const char* query_type = get_tag_value(event, action_type, 1);
        if (!query_type) {
            DEBUG_ERROR("invalid: missing config_query type");
            snprintf(error_message, error_size, "invalid: missing config_query type");
            return -1;
        }
        return handle_config_query_unified(event, query_type, error_message, error_size, wsi);
    }
    else if (strcmp(action_type, "config_set") == 0) {
        const char* config_key = get_tag_value(event, action_type, 1);
        const char* config_value = get_tag_value(event, action_type, 2);
        if (!config_key || !config_value) {
            DEBUG_ERROR("invalid: missing config_set key or value");
            snprintf(error_message, error_size, "invalid: missing config_set key or value");
            return -1;
        }
        return handle_config_set_unified(event, config_key, config_value, error_message, error_size, wsi);
    }
    else if (strcmp(action_type, "config_update") == 0) {
        return handle_config_update_unified(event, error_message, error_size, wsi);
    }
    else if (strcmp(action_type, "system_command") == 0) {
        const char* command = get_tag_value(event, action_type, 1);
        if (!command) {
            DEBUG_ERROR("invalid: missing system_command type");
            snprintf(error_message, error_size, "invalid: missing system_command type");
            return -1;
        }
        return handle_system_command_unified(event, command, error_message, error_size, wsi);
    }
    else if (strcmp(action_type, "stats_query") == 0) {
        return handle_stats_query_unified(event, error_message, error_size, wsi);
    }
    else if (strcmp(action_type, "create_relay_event") == 0) {
        const char* kind_str = get_tag_value(event, action_type, 1);
        const char* event_data_json = get_tag_value(event, action_type, 2);
        if (!kind_str || !event_data_json) {
            DEBUG_ERROR("invalid: missing kind or event data");
            snprintf(error_message, error_size, "invalid: missing kind or event data");
            return -1;
        }
        return handle_create_relay_event_unified(event, kind_str, event_data_json, error_message, error_size, wsi);
    }
    else if (strcmp(action_type, "sql_query") == 0) {
        const char* query = get_tag_value(event, action_type, 1);
        if (!query) {
            DEBUG_ERROR("invalid: missing sql_query parameter");
            snprintf(error_message, error_size, "invalid: missing sql_query parameter");
            return -1;
        }
        return handle_sql_query_unified(event, query, error_message, error_size, wsi);
    }
    else if (strcmp(action_type, "whitelist") == 0 || strcmp(action_type, "blacklist") == 0) {
        // Handle auth rule modifications (existing logic from process_admin_auth_event)
        return handle_auth_rule_modification_unified(event, error_message, error_size, wsi);
    }
    else {
        char error_msg[256];
        snprintf(error_msg, sizeof(error_msg), "invalid: unknown Kind 23456 action type '%s'", action_type);
        DEBUG_ERROR(error_msg);
        snprintf(error_message, error_size, "invalid: unknown Kind 23456 action type '%s'", action_type);
        return -1;
    }
}

// Unified auth query handler
int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_message, size_t error_size, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;
    if (!g_db) {
        snprintf(error_message, error_size, "database not available");
        return -1;
    }
    
    const char* sql = NULL;
    int use_pattern_param = 0;
    char* pattern_value = NULL;
    
    // Build appropriate SQL query based on query type
    if (strcmp(query_type, "all") == 0) {
        sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE active = 1 ORDER BY rule_type, pattern_type";
    }
    else if (strcmp(query_type, "whitelist") == 0) {
        sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE rule_type LIKE '%whitelist%' AND active = 1 ORDER BY pattern_type";
    }
    else if (strcmp(query_type, "blacklist") == 0) {
        sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE rule_type LIKE '%blacklist%' AND active = 1 ORDER BY pattern_type";
    }
    else if (strcmp(query_type, "pattern") == 0) {
        // Get pattern value from tags
        pattern_value = (char*)get_tag_value(event, "auth_query", 2);
        if (!pattern_value) {
            snprintf(error_message, error_size, "invalid: pattern query requires pattern value");
            return -1;
        }
        sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE pattern_value = ? AND active = 1 ORDER BY rule_type, pattern_type";
        use_pattern_param = 1;
    }
    else {
        snprintf(error_message, error_size, "invalid: unknown auth query type '%s'", query_type);
        return -1;
    }
    
    // Execute query
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        snprintf(error_message, error_size, "failed to prepare auth query");
        return -1;
    }
    
    if (use_pattern_param && pattern_value) {
        sqlite3_bind_text(stmt, 1, pattern_value, -1, SQLITE_STATIC);
    }
    
    // Build results array
    cJSON* results_array = cJSON_CreateArray();
    if (!results_array) {
        sqlite3_finalize(stmt);
        snprintf(error_message, error_size, "failed to create results array");
        return -1;
    }
    
    int rule_count = 0;
    // printf("=== Auth Query Results (%s) ===\n", query_type);
    
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        const char* rule_type = (const char*)sqlite3_column_text(stmt, 0);
        const char* pattern_type = (const char*)sqlite3_column_text(stmt, 1);
        const char* pattern_value_result = (const char*)sqlite3_column_text(stmt, 2);
        
        // printf("  %s %s:%s -> %s\n",
        //        rule_type ? rule_type : "",
        //        pattern_type ? pattern_type : "",
        //        pattern_value_result ? pattern_value_result : "",
        //        action ? action : "allow");
        
        // Add rule to results array
        cJSON* rule_obj = cJSON_CreateObject();
        cJSON_AddStringToObject(rule_obj, "rule_type", rule_type ? rule_type : "");
        cJSON_AddStringToObject(rule_obj, "pattern_type", pattern_type ? pattern_type : "");
        cJSON_AddStringToObject(rule_obj, "pattern_value", pattern_value_result ? pattern_value_result : "");
        cJSON_AddStringToObject(rule_obj, "action", "allow"); // Simplified: rule_type determines behavior
        cJSON_AddItemToArray(results_array, rule_obj);
        
        rule_count++;
    }
    
    sqlite3_finalize(stmt);
    
    // Build and send response with mapped query type for frontend routing
    const char* mapped_query_type = map_auth_query_type_to_response(query_type);
    cJSON* response = build_query_response(mapped_query_type, results_array, rule_count);
    if (response) {
        // Get admin pubkey from event for response
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
        
        if (!admin_pubkey) {
            cJSON_Delete(response);
            cJSON_Delete(results_array);
            snprintf(error_message, error_size, "missing admin pubkey for response");
            return -1;
        }
        
        // Send response as signed kind 23457 event
        if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
            cJSON_Delete(response);
            cJSON_Delete(results_array);
            return 0;
        }
        cJSON_Delete(response);
    }
    
    cJSON_Delete(results_array);
    snprintf(error_message, error_size, "failed to send auth query response");
    return -1;
}

// Unified config query handler
int handle_config_query_unified(cJSON* event, const char* query_type, char* error_message, size_t error_size, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;
    if (!g_db) {
        snprintf(error_message, error_size, "database not available");
        return -1;
    }
    
    const char* sql = NULL;
    int use_pattern_param = 0;
    char* pattern_value = NULL;
    
    // Build appropriate SQL query based on query type
    if (strcmp(query_type, "all") == 0) {
        sql = "SELECT key, value, data_type, category, description FROM config ORDER BY category, key";
    }
    else if (strcmp(query_type, "category") == 0) {
        // Get category value from tags
        pattern_value = (char*)get_tag_value(event, "config_query", 2);
        if (!pattern_value) {
            snprintf(error_message, error_size, "invalid: category query requires category value");
            return -1;
        }
        sql = "SELECT key, value, data_type, category, description FROM config WHERE category = ? ORDER BY key";
        use_pattern_param = 1;
    }
    else if (strcmp(query_type, "key") == 0) {
        // Get key value from tags
        pattern_value = (char*)get_tag_value(event, "config_query", 2);
        if (!pattern_value) {
            snprintf(error_message, error_size, "invalid: key query requires key value");
            return -1;
        }
        sql = "SELECT key, value, data_type, category, description FROM config WHERE key = ? ORDER BY key";
        use_pattern_param = 1;
    }
    else {
        snprintf(error_message, error_size, "invalid: unknown config query type '%s'", query_type);
        return -1;
    }
    
    // Execute query
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        snprintf(error_message, error_size, "failed to prepare config query");
        return -1;
    }
    
    if (use_pattern_param && pattern_value) {
        sqlite3_bind_text(stmt, 1, pattern_value, -1, SQLITE_STATIC);
    }
    
    // Build results array
    cJSON* results_array = cJSON_CreateArray();
    if (!results_array) {
        sqlite3_finalize(stmt);
        snprintf(error_message, error_size, "failed to create results array");
        return -1;
    }
    
    int config_count = 0;
    
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        const char* key = (const char*)sqlite3_column_text(stmt, 0);
        const char* value = (const char*)sqlite3_column_text(stmt, 1);
        const char* data_type = (const char*)sqlite3_column_text(stmt, 2);
        const char* category = (const char*)sqlite3_column_text(stmt, 3);
        const char* description = (const char*)sqlite3_column_text(stmt, 4);
        
        
        // Add config item to results array
        cJSON* config_obj = cJSON_CreateObject();
        cJSON_AddStringToObject(config_obj, "key", key ? key : "");
        cJSON_AddStringToObject(config_obj, "value", value ? value : "");
        cJSON_AddStringToObject(config_obj, "data_type", data_type ? data_type : "string");
        cJSON_AddStringToObject(config_obj, "category", category ? category : "general");
        cJSON_AddStringToObject(config_obj, "description", description ? description : "");
        cJSON_AddItemToArray(results_array, config_obj);
        
        config_count++;
    }
    
    sqlite3_finalize(stmt);
    
    // Build and send response with mapped query type for frontend routing
    const char* mapped_query_type = map_config_query_type_to_response(query_type);
    cJSON* response = build_query_response(mapped_query_type, results_array, config_count);
    if (response) {
        // Get admin pubkey from event for response
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
        
        if (!admin_pubkey) {
            cJSON_Delete(response);
            cJSON_Delete(results_array);
            snprintf(error_message, error_size, "missing admin pubkey for response");
            return -1;
        }
        
        // Send response as signed kind 23457 event
        if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
            cJSON_Delete(response);
            cJSON_Delete(results_array);
            return 0;
        }
        cJSON_Delete(response);
    }
    
    cJSON_Delete(results_array);
    snprintf(error_message, error_size, "failed to send config query response");
    return -1;
}

// Unified config set handler
int handle_config_set_unified(cJSON* event, const char* config_key, const char* config_value, char* error_message, size_t error_size, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;
    if (!g_db) {
        snprintf(error_message, error_size, "database not available");
        return -1;
    }
    
    // Validate the configuration field before updating
    char validation_error[512];
    if (validate_config_field(config_key, config_value, validation_error, sizeof(validation_error)) != 0) {
        DEBUG_ERROR("Config field validation failed");
        printf("  Validation error: %s\n", validation_error);
        snprintf(error_message, error_size, "validation failed: %s", validation_error);
        return -1;
    }
    
    // Check if the config key exists in the table
    const char* check_sql = "SELECT COUNT(*) FROM config WHERE key = ?";
    sqlite3_stmt* check_stmt;
    
    int check_rc = sqlite3_prepare_v2(g_db, check_sql, -1, &check_stmt, NULL);
    if (check_rc != SQLITE_OK) {
        snprintf(error_message, error_size, "failed to prepare config existence check");
        return -1;
    }
    
    sqlite3_bind_text(check_stmt, 1, config_key, -1, SQLITE_STATIC);
    
    int config_exists = 0;
    if (sqlite3_step(check_stmt) == SQLITE_ROW) {
        config_exists = sqlite3_column_int(check_stmt, 0) > 0;
    }
    sqlite3_finalize(check_stmt);
    
    if (!config_exists) {
        snprintf(error_message, error_size, "error: configuration key '%s' not found", config_key);
        return -1;
    }
    
    // Update the configuration value
    if (update_config_in_table(config_key, config_value) != 0) {
        snprintf(error_message, error_size, "failed to update configuration in database");
        return -1;
    }
    
    
    // Build response
    cJSON* response = cJSON_CreateObject();
    cJSON_AddStringToObject(response, "command", "config_set");
    cJSON_AddStringToObject(response, "key", config_key);
    cJSON_AddStringToObject(response, "value", config_value);
    cJSON_AddStringToObject(response, "status", "success");
    cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));
    
    
    // Get admin pubkey from event for response
    cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
    const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
    
    if (!admin_pubkey) {
        cJSON_Delete(response);
        snprintf(error_message, error_size, "missing admin pubkey for response");
        return -1;
    }
    
    // Send response as signed kind 23457 event
    if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
        cJSON_Delete(response);
        return 0;
    }
    
    cJSON_Delete(response);
    snprintf(error_message, error_size, "failed to send config set response");
    return -1;
}

// Unified system command handler
int handle_system_command_unified(cJSON* event, const char* command, char* error_message, size_t error_size, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;
    if (!g_db) {
        snprintf(error_message, error_size, "database not available");
        return -1;
    }
    
    if (strcmp(command, "clear_all_auth_rules") == 0) {
        // Count existing rules first
        const char* count_sql = "SELECT COUNT(*) FROM auth_rules";
        sqlite3_stmt* count_stmt;
        
        int rc = sqlite3_prepare_v2(g_db, count_sql, -1, &count_stmt, NULL);
        if (rc != SQLITE_OK) {
            snprintf(error_message, error_size, "failed to prepare count query");
            return -1;
        }
        
        int rule_count = 0;
        if (sqlite3_step(count_stmt) == SQLITE_ROW) {
            rule_count = sqlite3_column_int(count_stmt, 0);
        }
        sqlite3_finalize(count_stmt);
        
        // Delete all auth rules
        const char* delete_sql = "DELETE FROM auth_rules";
        rc = sqlite3_exec(g_db, delete_sql, NULL, NULL, NULL);
        
        if (rc != SQLITE_OK) {
            snprintf(error_message, error_size, "failed to execute clear auth rules command");
            return -1;
        }
        
        // Build response
        cJSON* response = cJSON_CreateObject();
        cJSON_AddStringToObject(response, "command", "clear_all_auth_rules");
        cJSON_AddNumberToObject(response, "rules_cleared", rule_count);
        cJSON_AddStringToObject(response, "status", "success");
        cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));
        
        
        // Get admin pubkey from event for response
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
        
        if (!admin_pubkey) {
            cJSON_Delete(response);
            snprintf(error_message, error_size, "missing admin pubkey for response");
            return -1;
        }
        
        // Send response as signed kind 23457 event
        if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
            cJSON_Delete(response);
            return 0;
        }
        
        cJSON_Delete(response);
        snprintf(error_message, error_size, "failed to send clear auth rules response");
        return -1;
    }
    else if (strcmp(command, "delete_auth_rule") == 0) {
        // Get rule parameters from tags
        const char* rule_type = get_tag_value(event, "system_command", 2);
        const char* pattern_type = get_tag_value(event, "system_command", 3);
        const char* pattern_value = get_tag_value(event, "system_command", 4);
        
        if (!rule_type || !pattern_type || !pattern_value) {
            snprintf(error_message, error_size, "invalid: delete_auth_rule requires rule_type, pattern_type, and pattern_value");
            return -1;
        }
        
        DEBUG_INFO("Processing delete auth rule command");
        printf("  Rule type: %s\n", rule_type);
        printf("  Pattern type: %s\n", pattern_type);
        printf("  Pattern value: %s\n", pattern_value);
        
        // Check if rule exists before deletion
        const char* check_sql = "SELECT COUNT(*) FROM auth_rules WHERE rule_type = ? AND pattern_type = ? AND pattern_value = ?";
        sqlite3_stmt* check_stmt;
        
        int check_rc = sqlite3_prepare_v2(g_db, check_sql, -1, &check_stmt, NULL);
        if (check_rc != SQLITE_OK) {
            snprintf(error_message, error_size, "failed to prepare rule existence check");
            return -1;
        }
        
        sqlite3_bind_text(check_stmt, 1, rule_type, -1, SQLITE_STATIC);
        sqlite3_bind_text(check_stmt, 2, pattern_type, -1, SQLITE_STATIC);
        sqlite3_bind_text(check_stmt, 3, pattern_value, -1, SQLITE_STATIC);
        
        int rule_exists = 0;
        if (sqlite3_step(check_stmt) == SQLITE_ROW) {
            rule_exists = sqlite3_column_int(check_stmt, 0) > 0;
        }
        sqlite3_finalize(check_stmt);
        
        if (!rule_exists) {
            snprintf(error_message, error_size, "error: auth rule not found");
            return -1;
        }
        
        // Delete the specific auth rule
        if (remove_auth_rule_from_config(rule_type, pattern_type, pattern_value) != 0) {
            snprintf(error_message, error_size, "failed to delete auth rule from database");
            return -1;
        }
        
        // Build response
        cJSON* response = cJSON_CreateObject();
        cJSON_AddStringToObject(response, "command", "delete_auth_rule");
        cJSON_AddStringToObject(response, "rule_type", rule_type);
        cJSON_AddStringToObject(response, "pattern_type", pattern_type);
        cJSON_AddStringToObject(response, "pattern_value", pattern_value);
        cJSON_AddStringToObject(response, "status", "success");
        cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));
        
        
        // Get admin pubkey from event for response
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
        
        if (!admin_pubkey) {
            cJSON_Delete(response);
            snprintf(error_message, error_size, "missing admin pubkey for response");
            return -1;
        }
        
        // Send response as signed kind 23457 event
        if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
            cJSON_Delete(response);
            return 0;
        }
        
        cJSON_Delete(response);
        snprintf(error_message, error_size, "failed to send delete auth rule response");
        return -1;
    }
    else if (strcmp(command, "system_status") == 0) {
        // Build system status response
        cJSON* response = cJSON_CreateObject();
        cJSON_AddStringToObject(response, "command", "system_status");
        cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));

        cJSON* status_data = cJSON_CreateObject();
        cJSON_AddStringToObject(status_data, "database", g_db ? "connected" : "not_available");
        cJSON_AddStringToObject(status_data, "cache_status", "not_used");

        if (strlen(g_database_path) > 0) {
            cJSON_AddStringToObject(status_data, "database_path", g_database_path);
        }

        // Count configuration items and auth rules
        if (g_db) {
            sqlite3_stmt* stmt;

            // Config count
            if (sqlite3_prepare_v2(g_db, "SELECT COUNT(*) FROM config", -1, &stmt, NULL) == SQLITE_OK) {
                if (sqlite3_step(stmt) == SQLITE_ROW) {
                    cJSON_AddNumberToObject(status_data, "config_items", sqlite3_column_int(stmt, 0));
                }
                sqlite3_finalize(stmt);
            }

            // Auth rules count
            if (sqlite3_prepare_v2(g_db, "SELECT COUNT(*) FROM auth_rules", -1, &stmt, NULL) == SQLITE_OK) {
                if (sqlite3_step(stmt) == SQLITE_ROW) {
                    cJSON_AddNumberToObject(status_data, "auth_rules", sqlite3_column_int(stmt, 0));
                }
                sqlite3_finalize(stmt);
            }
        }

        cJSON_AddItemToObject(response, "data", status_data);

        printf("=== System Status ===\n");
        printf("Database: %s\n", g_db ? "Connected" : "Not available");
        printf("Cache status: Not used (direct database queries)\n");

        // Get admin pubkey from event for response
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;

        if (!admin_pubkey) {
            cJSON_Delete(response);
            snprintf(error_message, error_size, "missing admin pubkey for response");
            return -1;
        }

        // Send response as signed kind 23457 event
        if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
            cJSON_Delete(response);
            return 0;
        }

        cJSON_Delete(response);
        snprintf(error_message, error_size, "failed to send system status response");
        return -1;
    }
    else if (strcmp(command, "restart") == 0) {
        // Build restart acknowledgment response
        cJSON* response = cJSON_CreateObject();
        cJSON_AddStringToObject(response, "command", "restart");
        cJSON_AddStringToObject(response, "status", "initiating_restart");
        cJSON_AddStringToObject(response, "message", "Relay restart initiated - shutting down gracefully");
        cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));

        printf("=== Relay Restart Initiated ===\n");
        printf("Admin requested system restart\n");
        printf("Sending acknowledgment and initiating shutdown...\n");

        // Get admin pubkey from event for response
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;

        if (!admin_pubkey) {
            cJSON_Delete(response);
            snprintf(error_message, error_size, "missing admin pubkey for response");
            return -1;
        }

        // Send acknowledgment response as signed kind 23457 event
        if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
            // Trigger graceful shutdown by setting the global shutdown flag
            g_shutdown_flag = 1;

            cJSON_Delete(response);
            return 0;
        }

        cJSON_Delete(response);
        snprintf(error_message, error_size, "failed to send restart acknowledgment");
        return -1;
    }
    else {
        snprintf(error_message, error_size, "invalid: unknown system command '%s'", command);
        return -1;
    }
}

// Handle auth rule modifications (extracted from process_admin_auth_event)
int handle_auth_rule_modification_unified(cJSON* event, char* error_message, size_t error_size, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;
    cJSON* tags_obj = cJSON_GetObjectItem(event, "tags");
    if (!tags_obj || !cJSON_IsArray(tags_obj)) {
        snprintf(error_message, error_size, "invalid: auth rule event must have tags");
        return -1;
    }
    
    // Begin transaction for atomic auth rule updates
    int rc = sqlite3_exec(g_db, "BEGIN IMMEDIATE TRANSACTION", NULL, NULL, NULL);
    if (rc != SQLITE_OK) {
        snprintf(error_message, error_size, "failed to begin auth rule transaction");
        return -1;
    }
    
    int rules_processed = 0;
    cJSON* processed_rules = cJSON_CreateArray();
    if (!processed_rules) {
        sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
        snprintf(error_message, error_size, "failed to create response array");
        return -1;
    }
    
    // For Kind 23456 events, only process synthetic tags created from decrypted content
    // Skip original unencrypted tags (except p tag validation which is done elsewhere)
    cJSON* auth_tag = NULL;
    cJSON_ArrayForEach(auth_tag, tags_obj) {
        if (!cJSON_IsArray(auth_tag) || cJSON_GetArraySize(auth_tag) < 3) {
            continue;
        }
        
        cJSON* rule_type_obj = cJSON_GetArrayItem(auth_tag, 0);
        cJSON* pattern_type_obj = cJSON_GetArrayItem(auth_tag, 1);
        cJSON* pattern_value_obj = cJSON_GetArrayItem(auth_tag, 2);
        
        if (!cJSON_IsString(rule_type_obj) ||
            !cJSON_IsString(pattern_type_obj) ||
            !cJSON_IsString(pattern_value_obj)) {
            continue;
        }
        
        const char* rule_type = cJSON_GetStringValue(rule_type_obj);
        const char* pattern_type = cJSON_GetStringValue(pattern_type_obj);
        const char* pattern_value = cJSON_GetStringValue(pattern_value_obj);
        
        // Skip p tags - they are for routing, not auth rules
        if (strcmp(rule_type, "p") == 0) {
            continue;
        }
        
        // Process auth rule: ["blacklist"|"whitelist", "pubkey"|"hash", "value"]
        if (strcmp(rule_type, "blacklist") == 0 || strcmp(rule_type, "whitelist") == 0) {
            if (add_auth_rule_from_config(rule_type, pattern_type, pattern_value) == 0) {
                rules_processed++;
                
                // Add processed rule to response array
                cJSON* rule_obj = cJSON_CreateObject();
                cJSON_AddStringToObject(rule_obj, "rule_type", rule_type);
                cJSON_AddStringToObject(rule_obj, "pattern_type", pattern_type);
                cJSON_AddStringToObject(rule_obj, "pattern_value", pattern_value);
                cJSON_AddStringToObject(rule_obj, "action", "allow"); // Simplified: rule_type determines behavior
                cJSON_AddStringToObject(rule_obj, "status", "added");
                cJSON_AddItemToArray(processed_rules, rule_obj);
            }
        }
    }
    
    if (rules_processed > 0) {
        sqlite3_exec(g_db, "COMMIT", NULL, NULL, NULL);
        
        char success_msg[256];
        snprintf(success_msg, sizeof(success_msg), "Processed %d auth rule updates", rules_processed);
        DEBUG_INFO(success_msg);
        
        // Build and send response
        cJSON* response = cJSON_CreateObject();
        cJSON_AddStringToObject(response, "command", "auth_rule_modification");
        cJSON_AddNumberToObject(response, "rules_processed", rules_processed);
        cJSON_AddStringToObject(response, "status", "success");
        cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));
        cJSON_AddItemToObject(response, "processed_rules", processed_rules);
        
        printf("Processed %d auth rule modifications\n", rules_processed);
        
        // Get admin pubkey from event for response
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
        
        if (!admin_pubkey) {
            cJSON_Delete(response);
            snprintf(error_message, error_size, "missing admin pubkey for response");
            return -1;
        }
        
        // Send response as signed kind 23457 event
        if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
            cJSON_Delete(response);
            return 0;
        }
        
        cJSON_Delete(response);
        snprintf(error_message, error_size, "failed to send auth rule modification response");
        return -1;
    } else {
        sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
        cJSON_Delete(processed_rules);
        snprintf(error_message, error_size, "no valid auth rules found");
        return -1;
    }
}
// Unified stats query handler
int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_size, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;
    if (!g_db) {
        snprintf(error_message, error_size, "database not available");
        return -1;
    }

    // Build response with database statistics
    cJSON* response = cJSON_CreateObject();
    cJSON_AddStringToObject(response, "query_type", "stats_query");
    cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));

    // Get database file size
    struct stat db_stat;
    long long db_size = 0;
    if (stat(g_database_path, &db_stat) == 0) {
        db_size = db_stat.st_size;
    }
    cJSON_AddNumberToObject(response, "database_size_bytes", db_size);

    // Query total events count
    sqlite3_stmt* stmt;
    if (sqlite3_prepare_v2(g_db, "SELECT COUNT(*) FROM events", -1, &stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(stmt) == SQLITE_ROW) {
            cJSON_AddNumberToObject(response, "total_events", sqlite3_column_int64(stmt, 0));
        }
        sqlite3_finalize(stmt);
    }

    // Query event kinds distribution
    cJSON* event_kinds = cJSON_CreateArray();
    if (sqlite3_prepare_v2(g_db, "SELECT kind, count, percentage FROM event_kinds_view ORDER BY count DESC", -1, &stmt, NULL) == SQLITE_OK) {
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            cJSON* kind_obj = cJSON_CreateObject();
            cJSON_AddNumberToObject(kind_obj, "kind", sqlite3_column_int(stmt, 0));
            cJSON_AddNumberToObject(kind_obj, "count", sqlite3_column_int64(stmt, 1));
            cJSON_AddNumberToObject(kind_obj, "percentage", sqlite3_column_double(stmt, 2));
            cJSON_AddItemToArray(event_kinds, kind_obj);
        }
        sqlite3_finalize(stmt);
    }
    cJSON_AddItemToObject(response, "event_kinds", event_kinds);

    // Query time-based statistics
    cJSON* time_stats = cJSON_CreateObject();
    if (sqlite3_prepare_v2(g_db, "SELECT period, total_events FROM time_stats_view", -1, &stmt, NULL) == SQLITE_OK) {
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            const char* period = (const char*)sqlite3_column_text(stmt, 0);
            sqlite3_int64 count = sqlite3_column_int64(stmt, 1);

            if (strcmp(period, "total") == 0) {
                cJSON_AddNumberToObject(time_stats, "total", count);
            } else if (strcmp(period, "24h") == 0) {
                cJSON_AddNumberToObject(time_stats, "last_24h", count);
            } else if (strcmp(period, "7d") == 0) {
                cJSON_AddNumberToObject(time_stats, "last_7d", count);
            } else if (strcmp(period, "30d") == 0) {
                cJSON_AddNumberToObject(time_stats, "last_30d", count);
            }
        }
        sqlite3_finalize(stmt);
    }
    cJSON_AddItemToObject(response, "time_stats", time_stats);

    // Query top pubkeys
    cJSON* top_pubkeys = cJSON_CreateArray();
    if (sqlite3_prepare_v2(g_db, "SELECT pubkey, event_count, percentage FROM top_pubkeys_view ORDER BY event_count DESC LIMIT 10", -1, &stmt, NULL) == SQLITE_OK) {
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            cJSON* pubkey_obj = cJSON_CreateObject();
            const char* pubkey = (const char*)sqlite3_column_text(stmt, 0);
            cJSON_AddStringToObject(pubkey_obj, "pubkey", pubkey ? pubkey : "");
            cJSON_AddNumberToObject(pubkey_obj, "event_count", sqlite3_column_int64(stmt, 1));
            cJSON_AddNumberToObject(pubkey_obj, "percentage", sqlite3_column_double(stmt, 2));
            cJSON_AddItemToArray(top_pubkeys, pubkey_obj);
        }
        sqlite3_finalize(stmt);
    }
    cJSON_AddItemToObject(response, "top_pubkeys", top_pubkeys);

    // Get database creation timestamp (oldest event)
    if (sqlite3_prepare_v2(g_db, "SELECT MIN(created_at) FROM events", -1, &stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(stmt) == SQLITE_ROW) {
            sqlite3_int64 oldest_timestamp = sqlite3_column_int64(stmt, 0);
            if (oldest_timestamp > 0) {
                cJSON_AddNumberToObject(response, "database_created_at", (double)oldest_timestamp);
            }
        }
        sqlite3_finalize(stmt);
    }

    // Get latest event timestamp
    if (sqlite3_prepare_v2(g_db, "SELECT MAX(created_at) FROM events", -1, &stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(stmt) == SQLITE_ROW) {
            sqlite3_int64 latest_timestamp = sqlite3_column_int64(stmt, 0);
            if (latest_timestamp > 0) {
                cJSON_AddNumberToObject(response, "latest_event_at", (double)latest_timestamp);
            }
        }
        sqlite3_finalize(stmt);
    }


    // Get admin pubkey from event for response
    cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
    const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;

    if (!admin_pubkey) {
        cJSON_Delete(response);
        snprintf(error_message, error_size, "missing admin pubkey for response");
        return -1;
    }

    // Send response as signed kind 23457 event
    if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
        cJSON_Delete(response);
        return 0;
    }

    cJSON_Delete(response);
    snprintf(error_message, error_size, "failed to send stats query response");
    return -1;
}

// Unified create relay event handler
int handle_create_relay_event_unified(cJSON* event, const char* kind_str, const char* event_data_json, char* error_message, size_t error_size, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;

    if (!event || !kind_str || !event_data_json) {
        snprintf(error_message, error_size, "invalid: missing parameters for create_relay_event");
        return -1;
    }

    // Parse kind string to integer
    char* endptr;
    int kind = (int)strtol(kind_str, &endptr, 10);
    if (endptr == kind_str || *endptr != '\0') {
        snprintf(error_message, error_size, "invalid: kind must be a valid integer");
        return -1;
    }

    // Parse event data JSON
    cJSON* event_data = cJSON_Parse(event_data_json);
    if (!event_data) {
        snprintf(error_message, error_size, "invalid: event_data must be valid JSON");
        return -1;
    }

    // Call the existing implementation from api.c
    extern int handle_create_relay_event_command(cJSON* event, int kind, cJSON* event_data, char* error_message, size_t error_size, struct lws* wsi);
    int result = handle_create_relay_event_command(event, kind, event_data, error_message, error_size, wsi);

    // Clean up
    cJSON_Delete(event_data);

    return result;
}

// Unified config update handler - handles multiple config objects in single atomic command
int handle_config_update_unified(cJSON* event, char* error_message, size_t error_size, struct lws* wsi) {
    // Suppress unused parameter warning
    (void)wsi;
    if (!g_db) {
        snprintf(error_message, error_size, "database not available");
        return -1;
    }
    
    DEBUG_INFO("Processing unified config update command");
    
    // Extract config objects array from synthetic tags created by NIP-44 decryption
    // The decryption process creates synthetic tags like: ["config_update", [config_objects]]
    cJSON* tags_obj = cJSON_GetObjectItem(event, "tags");
    if (!tags_obj || !cJSON_IsArray(tags_obj)) {
        snprintf(error_message, error_size, "invalid: config update event must have tags");
        return -1;
    }
    
    // Find the config_update tag with config objects array
    cJSON* config_objects_array = NULL;
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags_obj) {
        if (!cJSON_IsArray(tag) || cJSON_GetArraySize(tag) < 2) {
            continue;
        }
        
        cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
        if (!tag_name || !cJSON_IsString(tag_name)) {
            continue;
        }
        
        if (strcmp(cJSON_GetStringValue(tag_name), "config_update") == 0) {
            // Found config_update tag, get the config objects array
            cJSON* config_array_item = cJSON_GetArrayItem(tag, 1);
            if (config_array_item) {
                // The config objects should be in a JSON string format in the tag
                if (cJSON_IsString(config_array_item)) {
                    // Parse the JSON string to get the actual array
                    const char* config_json = cJSON_GetStringValue(config_array_item);
                    config_objects_array = cJSON_Parse(config_json);
                } else if (cJSON_IsArray(config_array_item)) {
                    // Direct array reference
                    config_objects_array = cJSON_Duplicate(config_array_item, 1);
                }
            }
            break;
        }
    }
    
    if (!config_objects_array || !cJSON_IsArray(config_objects_array)) {
        snprintf(error_message, error_size, "invalid: config_update command requires config objects array");
        return -1;
    }
    
    int config_count = cJSON_GetArraySize(config_objects_array);
    DEBUG_INFO("Config update command contains config objects");
    printf("  Config objects count: %d\n", config_count);
    
    if (config_count == 0) {
        cJSON_Delete(config_objects_array);
        snprintf(error_message, error_size, "invalid: config_update command requires at least one config object");
        return -1;
    }
    
    // Begin transaction for atomic config updates
    int rc = sqlite3_exec(g_db, "BEGIN IMMEDIATE TRANSACTION", NULL, NULL, NULL);
    if (rc != SQLITE_OK) {
        cJSON_Delete(config_objects_array);
        snprintf(error_message, error_size, "failed to begin config update transaction");
        return -1;
    }
    
    int updates_applied = 0;
    int validation_errors = 0;
    char first_validation_error[512] = {0};  // Track first specific validation error
    char first_error_field[128] = {0};       // Track which field failed first
    cJSON* processed_configs = cJSON_CreateArray();
    if (!processed_configs) {
        sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
        cJSON_Delete(config_objects_array);
        snprintf(error_message, error_size, "failed to create response array");
        return -1;
    }
    
    // Process each config object in the array
    cJSON* config_obj = NULL;
    cJSON_ArrayForEach(config_obj, config_objects_array) {
        if (!cJSON_IsObject(config_obj)) {
            DEBUG_WARN("Skipping non-object item in config objects array");
            continue;
        }
        
        // Extract required fields from config object
        cJSON* key_obj = cJSON_GetObjectItem(config_obj, "key");
        cJSON* value_obj = cJSON_GetObjectItem(config_obj, "value");
        cJSON* data_type_obj = cJSON_GetObjectItem(config_obj, "data_type");
        cJSON* category_obj = cJSON_GetObjectItem(config_obj, "category");
        
        if (!key_obj || !cJSON_IsString(key_obj) ||
            !value_obj || !cJSON_IsString(value_obj)) {
            DEBUG_ERROR("Config object missing required key or value fields");
            validation_errors++;
            continue;
        }
        
        const char* key = cJSON_GetStringValue(key_obj);
        const char* value = cJSON_GetStringValue(value_obj);
        const char* data_type = data_type_obj && cJSON_IsString(data_type_obj) ? 
                               cJSON_GetStringValue(data_type_obj) : "string";
        const char* category = category_obj && cJSON_IsString(category_obj) ? 
                              cJSON_GetStringValue(category_obj) : "general";
        
        
        // Validate the configuration field before updating
        char validation_error[512];
        if (validate_config_field(key, value, validation_error, sizeof(validation_error)) != 0) {
            DEBUG_ERROR("Config field validation failed");
            printf("  Validation error: %s\n", validation_error);
            validation_errors++;
            
            // Capture first validation error for enhanced error message
            if (validation_errors == 1) {
                strncpy(first_validation_error, validation_error, sizeof(first_validation_error) - 1);
                first_validation_error[sizeof(first_validation_error) - 1] = '\0';
                strncpy(first_error_field, key, sizeof(first_error_field) - 1);
                first_error_field[sizeof(first_error_field) - 1] = '\0';
            }
            
            // Add failed config to response array
            cJSON* failed_config = cJSON_CreateObject();
            cJSON_AddStringToObject(failed_config, "key", key);
            cJSON_AddStringToObject(failed_config, "value", value);
            cJSON_AddStringToObject(failed_config, "data_type", data_type);
            cJSON_AddStringToObject(failed_config, "category", category);
            cJSON_AddStringToObject(failed_config, "status", "validation_failed");
            cJSON_AddStringToObject(failed_config, "error", validation_error);
            cJSON_AddItemToArray(processed_configs, failed_config);
            continue;
        }
        
        // Check if the config key exists in the table
        const char* check_sql = "SELECT COUNT(*) FROM config WHERE key = ?";
        sqlite3_stmt* check_stmt;
        
        int check_rc = sqlite3_prepare_v2(g_db, check_sql, -1, &check_stmt, NULL);
        if (check_rc != SQLITE_OK) {
            DEBUG_ERROR("Failed to prepare config existence check");
            validation_errors++;
            continue;
        }
        
        sqlite3_bind_text(check_stmt, 1, key, -1, SQLITE_STATIC);
        
        int config_exists = 0;
        if (sqlite3_step(check_stmt) == SQLITE_ROW) {
            config_exists = sqlite3_column_int(check_stmt, 0) > 0;
        }
        sqlite3_finalize(check_stmt);
        
        if (!config_exists) {
            DEBUG_ERROR("Configuration key not found");
            printf("  Key not found: %s\n", key);
            validation_errors++;
            
            // Add failed config to response array
            cJSON* failed_config = cJSON_CreateObject();
            cJSON_AddStringToObject(failed_config, "key", key);
            cJSON_AddStringToObject(failed_config, "value", value);
            cJSON_AddStringToObject(failed_config, "data_type", data_type);
            cJSON_AddStringToObject(failed_config, "category", category);
            cJSON_AddStringToObject(failed_config, "status", "key_not_found");
            cJSON_AddStringToObject(failed_config, "error", "configuration key not found in database");
            cJSON_AddItemToArray(processed_configs, failed_config);
            continue;
        }
        
        // Check if this config requires restart
        const char* requires_restart_sql = "SELECT requires_restart FROM config WHERE key = ?";
        sqlite3_stmt* restart_stmt;
        int requires_restart = 0;

        if (sqlite3_prepare_v2(g_db, requires_restart_sql, -1, &restart_stmt, NULL) == SQLITE_OK) {
            sqlite3_bind_text(restart_stmt, 1, key, -1, SQLITE_STATIC);
            if (sqlite3_step(restart_stmt) == SQLITE_ROW) {
                requires_restart = sqlite3_column_int(restart_stmt, 0);
            }
            sqlite3_finalize(restart_stmt);
        }

        // Update the configuration value in the table
        if (update_config_in_table(key, value) == 0) {
            updates_applied++;

            // For dynamic configs (requires_restart = 0), apply selective re-initialization
            if (requires_restart == 0) {
                DEBUG_INFO("Applying selective re-initialization for dynamic config changes");
                if (strcmp(key, "max_subscriptions_per_client") == 0 ||
                    strcmp(key, "max_total_subscriptions") == 0) {
                    DEBUG_INFO("Subscription limits changed - updating subscription manager");
                    update_subscription_manager_config();
                    // Also refresh NIP-11 relay info since max_subscriptions_per_client affects limitation field
                    DEBUG_INFO("Subscription limits changed - reinitializing relay info for NIP-11");
                    init_relay_info();
                } else if (strcmp(key, "pow_min_difficulty") == 0 ||
                            strcmp(key, "pow_mode") == 0) {
                    DEBUG_INFO("PoW configuration changed - reinitializing PoW system");
                    init_pow_config();
                } else if (strcmp(key, "nip40_expiration_enabled") == 0 ||
                            strcmp(key, "nip40_expiration_strict") == 0 ||
                            strcmp(key, "nip40_expiration_filter") == 0 ||
                            strcmp(key, "nip40_expiration_grace_period") == 0) {
                    DEBUG_INFO("Expiration configuration changed - reinitializing expiration system");
                    init_expiration_config();
                } else if (strcmp(key, "relay_description") == 0 ||
                            strcmp(key, "relay_contact") == 0 ||
                            strcmp(key, "relay_software") == 0 ||
                            strcmp(key, "relay_version") == 0 ||
                            strcmp(key, "max_message_length") == 0 ||
                            strcmp(key, "max_event_tags") == 0 ||
                            strcmp(key, "max_content_length") == 0) {
                    DEBUG_INFO("Relay information changed - reinitializing relay info");
                    init_relay_info();
                }
            }

            // Add successful config to response array
            cJSON* success_config = cJSON_CreateObject();
            cJSON_AddStringToObject(success_config, "key", key);
            cJSON_AddStringToObject(success_config, "value", value);
            cJSON_AddStringToObject(success_config, "data_type", data_type);
            cJSON_AddStringToObject(success_config, "category", category);
            cJSON_AddStringToObject(success_config, "status", "updated");
            cJSON_AddBoolToObject(success_config, "requires_restart", requires_restart);
            cJSON_AddItemToArray(processed_configs, success_config);

            DEBUG_INFO("✓ Config field updated successfully");
            printf("  Updated: %s = %s (restart: %s)\n", key, value, requires_restart ? "yes" : "no");
        } else {
            DEBUG_ERROR("Failed to update config field in database");
            printf("  Failed to update: %s = %s\n", key, value);
            validation_errors++;

            // Add failed config to response array
            cJSON* failed_config = cJSON_CreateObject();
            cJSON_AddStringToObject(failed_config, "key", key);
            cJSON_AddStringToObject(failed_config, "value", value);
            cJSON_AddStringToObject(failed_config, "data_type", data_type);
            cJSON_AddStringToObject(failed_config, "category", category);
            cJSON_AddStringToObject(failed_config, "status", "database_error");
            cJSON_AddStringToObject(failed_config, "error", "failed to update configuration in database");
            cJSON_AddItemToArray(processed_configs, failed_config);
        }
    }
    
    // Clean up config objects array
    cJSON_Delete(config_objects_array);
    
    // Determine transaction outcome
    if (updates_applied > 0 && validation_errors == 0) {
        // All updates successful
        sqlite3_exec(g_db, "COMMIT", NULL, NULL, NULL);

        char success_msg[256];
        snprintf(success_msg, sizeof(success_msg), "Applied %d configuration updates successfully", updates_applied);
        DEBUG_INFO(success_msg);
    } else if (updates_applied > 0 && validation_errors > 0) {
        // Partial success - rollback for atomic behavior
        sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
        
        char error_msg[256];
        snprintf(error_msg, sizeof(error_msg), "Config update failed: %d validation errors (atomic rollback)", validation_errors);
        DEBUG_ERROR(error_msg);
        
        // Build error response with validation details
        cJSON* error_response = cJSON_CreateObject();
        cJSON_AddStringToObject(error_response, "query_type", "config_update");
        cJSON_AddStringToObject(error_response, "status", "error");
        // Create enhanced error message with specific validation details
        char enhanced_error_message[1024];
        if (strlen(first_validation_error) > 0 && strlen(first_error_field) > 0) {
            snprintf(enhanced_error_message, sizeof(enhanced_error_message),
                    "field validation failed: %s - %s",
                    first_error_field, first_validation_error);
        } else {
            snprintf(enhanced_error_message, sizeof(enhanced_error_message),
                    "field validation failed: atomic rollback performed");
        }
        cJSON_AddStringToObject(error_response, "error", enhanced_error_message);
        cJSON_AddNumberToObject(error_response, "validation_errors", validation_errors);
        cJSON_AddNumberToObject(error_response, "timestamp", (double)time(NULL));
        cJSON_AddItemToObject(error_response, "data", processed_configs);
        
        // Get admin pubkey from event for error response
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
        
        if (admin_pubkey) {
            // Send error response as signed kind 23457 event
            if (send_admin_response_event(error_response, admin_pubkey, wsi) == 0) {
                DEBUG_INFO("Config update validation error response sent successfully");
                cJSON_Delete(error_response);
                return 0; // Return success after sending error response
            }
        }
        
        cJSON_Delete(error_response);
        snprintf(error_message, error_size, "validation failed: %d errors, atomic rollback performed", validation_errors);
        return -1;
    } else {
        // No updates applied
        sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
        
        // Build error response for no valid updates
        cJSON* error_response = cJSON_CreateObject();
        cJSON_AddStringToObject(error_response, "query_type", "config_update");
        cJSON_AddStringToObject(error_response, "status", "error");
        cJSON_AddStringToObject(error_response, "error", "no valid configuration updates found");
        cJSON_AddNumberToObject(error_response, "timestamp", (double)time(NULL));
        cJSON_AddItemToObject(error_response, "data", processed_configs);
        
        // Get admin pubkey from event for error response
        cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
        const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
        
        if (admin_pubkey) {
            // Send error response as signed kind 23457 event
            if (send_admin_response_event(error_response, admin_pubkey, wsi) == 0) {
                DEBUG_INFO("Config update 'no valid updates' error response sent successfully");
                cJSON_Delete(error_response);
                return 0; // Return success after sending error response
            }
        }
        
        cJSON_Delete(error_response);
        snprintf(error_message, error_size, "no valid configuration updates found");
        return -1;
    }
    
    // Build response with query_type for frontend routing
    cJSON* response = cJSON_CreateObject();
    cJSON_AddStringToObject(response, "query_type", "config_update");
    cJSON_AddStringToObject(response, "command", "config_update");
    cJSON_AddNumberToObject(response, "configs_processed", updates_applied);
    cJSON_AddNumberToObject(response, "total_configs", config_count);
    cJSON_AddStringToObject(response, "status", "success");
    cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));
    cJSON_AddItemToObject(response, "processed_configs", processed_configs);
    
    
    // Get admin pubkey from event for response
    cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
    const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
    
    if (!admin_pubkey) {
        cJSON_Delete(response);
        snprintf(error_message, error_size, "missing admin pubkey for response");
        return -1;
    }
    
    // Send response as signed kind 23457 event
    if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
        printf("  Response query_type: config_update\n");
        cJSON_Delete(response);
        return 0;
    }
    
    cJSON_Delete(response);
    snprintf(error_message, error_size, "failed to send config update response");
    return -1;
}




// ================================
// UNIFIED STARTUP FUNCTIONS
// ================================

// Apply CLI overrides to existing config table in a single atomic operation
int apply_cli_overrides_atomic(const cli_options_t* cli_options) {
    if (!g_db) {
        DEBUG_ERROR("Database not available for CLI overrides");
        return -1;
    }

    if (!cli_options) {
        DEBUG_ERROR("CLI options not provided");
        return -1;
    }

    // Check if there are any CLI overrides to apply
    int has_overrides = 0;
    if (cli_options->port_override > 0) has_overrides = 1;
    if (cli_options->admin_pubkey_override[0] != '\0') has_overrides = 1;
    if (cli_options->relay_privkey_override[0] != '\0') has_overrides = 1;

    if (!has_overrides) {
        DEBUG_INFO("No CLI overrides to apply");
        return 0;
    }

    DEBUG_INFO("Applying CLI overrides atomically");

    // Begin transaction
    char* err_msg = NULL;
    int rc = sqlite3_exec(g_db, "BEGIN IMMEDIATE TRANSACTION", NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to begin CLI overrides transaction: %s", err_msg);
        sqlite3_free(err_msg);
        return -1;
    }

    // Apply port override
    if (cli_options->port_override > 0) {
        char port_str[16];
        snprintf(port_str, sizeof(port_str), "%d", cli_options->port_override);

        if (update_config_in_table("relay_port", port_str) != 0) {
            DEBUG_ERROR("Failed to update relay_port override");
            sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
            return -1;
        }
        DEBUG_INFO("Applied CLI override: relay_port = %s", port_str);
    }

    // Apply admin_pubkey override
    if (cli_options->admin_pubkey_override[0] != '\0') {
        if (update_config_in_table("admin_pubkey", cli_options->admin_pubkey_override) != 0) {
            DEBUG_ERROR("Failed to update admin_pubkey override");
            sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
            return -1;
        }
        DEBUG_INFO("Applied CLI override: admin_pubkey");
    }

    // Apply relay_privkey override
    if (cli_options->relay_privkey_override[0] != '\0') {
        if (update_config_in_table("relay_privkey", cli_options->relay_privkey_override) != 0) {
            DEBUG_ERROR("Failed to update relay_privkey override");
            sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
            return -1;
        }
        DEBUG_INFO("Applied CLI override: relay_privkey");
    }

    // Commit transaction
    rc = sqlite3_exec(g_db, "COMMIT", NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to commit CLI overrides transaction: %s", err_msg);
        sqlite3_free(err_msg);
        sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
        return -1;
    }

    // Cache no longer exists - direct database queries are used

    DEBUG_INFO("Successfully applied CLI overrides atomically");
    return 0;
}

// Populate all config values atomically in a single transaction
int populate_all_config_values_atomic(const char* admin_pubkey, const char* relay_pubkey) {
    if (!g_db) {
        DEBUG_ERROR("Database not initialized");
        return -1;
    }

    if (!admin_pubkey || !relay_pubkey) {
        DEBUG_ERROR("Admin pubkey or relay pubkey not available");
        return -1;
    }

    // Begin transaction
    char* err_msg = NULL;
    int rc = sqlite3_exec(g_db, "BEGIN TRANSACTION;", NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to begin transaction: %s", err_msg);
        sqlite3_free(err_msg);
        return -1;
    }

    // Prepare INSERT OR REPLACE statement with all required fields
    sqlite3_stmt* stmt = NULL;
    const char* sql = "INSERT OR REPLACE INTO config (key, value, data_type, description, category, requires_restart) VALUES (?, ?, ?, ?, ?, ?)";
    rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to prepare statement: %s", sqlite3_errmsg(g_db));
        sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
        return -1;
    }

    // Insert all default config values
    for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) {
        const char* key = DEFAULT_CONFIG_VALUES[i].key;
        const char* value = DEFAULT_CONFIG_VALUES[i].value;

        // Determine data type
        const char* data_type = "string";
        if (strcmp(key, "relay_port") == 0 ||
            strcmp(key, "max_connections") == 0 ||
            strcmp(key, "pow_min_difficulty") == 0 ||
            strcmp(key, "max_subscriptions_per_client") == 0 ||
            strcmp(key, "max_total_subscriptions") == 0 ||
            strcmp(key, "max_filters_per_subscription") == 0 ||
            strcmp(key, "max_event_tags") == 0 ||
            strcmp(key, "max_content_length") == 0 ||
            strcmp(key, "max_message_length") == 0 ||
            strcmp(key, "default_limit") == 0 ||
            strcmp(key, "max_limit") == 0 ||
            strcmp(key, "nip42_challenge_expiration") == 0 ||
            strcmp(key, "nip40_expiration_grace_period") == 0) {
            data_type = "integer";
        } else if (strcmp(key, "auth_enabled") == 0 ||
                   strcmp(key, "nip40_expiration_enabled") == 0 ||
                   strcmp(key, "nip40_expiration_strict") == 0 ||
                   strcmp(key, "nip40_expiration_filter") == 0 ||
                   strcmp(key, "nip42_auth_required_events") == 0 ||
                   strcmp(key, "nip42_auth_required_subscriptions") == 0 ||
                   strcmp(key, "nip70_protected_events_enabled") == 0) {
            data_type = "boolean";
        }

        // Set category
        const char* category = "general";
        if (strstr(key, "relay_")) {
            category = "relay";
        } else if (strstr(key, "nip40_")) {
            category = "expiration";
        } else if (strstr(key, "nip42_") || strstr(key, "auth_")) {
            category = "authentication";
        } else if (strstr(key, "pow_")) {
            category = "proof_of_work";
        } else if (strstr(key, "max_")) {
            category = "limits";
        } else if (strstr(key, "nip70_")) {
            category = "protected_events";
        }

        // Determine if requires restart (0 = dynamic, 1 = restart required)
        int requires_restart = 0;

        // Restart required configs
        if (strcmp(key, "relay_port") == 0 ||
            strcmp(key, "max_connections") == 0 ||
            strcmp(key, "auth_enabled") == 0 ||
            strcmp(key, "nip42_auth_required_events") == 0 ||
            strcmp(key, "nip42_auth_required_subscriptions") == 0 ||
            strcmp(key, "nip42_auth_required_kinds") == 0 ||
            strcmp(key, "nip42_challenge_expiration") == 0 ||
            strcmp(key, "database_path") == 0) {
            requires_restart = 1;
        }

        sqlite3_reset(stmt);
        sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 2, value, -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 3, data_type, -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 4, "", -1, SQLITE_STATIC);  // description (empty for defaults)
        sqlite3_bind_text(stmt, 5, category, -1, SQLITE_STATIC);
        sqlite3_bind_int(stmt, 6, requires_restart);

        rc = sqlite3_step(stmt);
        if (rc != SQLITE_DONE) {
            DEBUG_ERROR("Failed to insert config key '%s': %s",
                       key, sqlite3_errmsg(g_db));
            sqlite3_finalize(stmt);
            sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
            return -1;
        }
    }

    // Insert admin_pubkey
    sqlite3_reset(stmt);
    sqlite3_bind_text(stmt, 1, "admin_pubkey", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, admin_pubkey, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, "string", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 4, "Administrator public key", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 5, "authentication", -1, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 6, 0);  // does not require restart
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        DEBUG_ERROR("Failed to insert admin_pubkey: %s", sqlite3_errmsg(g_db));
        sqlite3_finalize(stmt);
        sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
        return -1;
    }

    // Insert relay_pubkey
    sqlite3_reset(stmt);
    sqlite3_bind_text(stmt, 1, "relay_pubkey", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, relay_pubkey, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, "string", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 4, "Relay public key", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 5, "relay", -1, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 6, 0);  // does not require restart
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        DEBUG_ERROR("Failed to insert relay_pubkey: %s", sqlite3_errmsg(g_db));
        sqlite3_finalize(stmt);
        sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
        return -1;
    }

    // Insert monitoring system config entry (ephemeral kind 24567)
    // Note: Monitoring is automatically activated when clients subscribe to kind 24567
    sqlite3_reset(stmt);
    sqlite3_bind_text(stmt, 1, "kind_24567_reporting_throttle_sec", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, "5", -1, SQLITE_STATIC);  // integer, default 5 seconds
    sqlite3_bind_text(stmt, 3, "integer", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 4, "Minimum seconds between monitoring event reports (ephemeral kind 24567)", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 5, "monitoring", -1, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 6, 0);  // does not require restart
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        DEBUG_ERROR("Failed to insert kind_24567_reporting_throttle_sec: %s", sqlite3_errmsg(g_db));
        sqlite3_finalize(stmt);
        sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
        return -1;
    }

    sqlite3_finalize(stmt);

    // Commit transaction
    rc = sqlite3_exec(g_db, "COMMIT;", NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to commit transaction: %s", err_msg);
        sqlite3_free(err_msg);
        sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
        return -1;
    }

    DEBUG_INFO("Successfully populated all config values atomically");
    return 0;
}

// ================================
// CONFIGURATION CACHE MANAGEMENT
// ================================


// ================================
// HYBRID CONFIG ACCESS FUNCTIONS
// ================================

// Hybrid config getter (tries table first, falls back to event)
const char* get_config_value_hybrid(const char* key) {
    // Try table-based config first if available
    if (is_config_table_ready()) {
        const char* table_value = get_config_value_from_table(key);
        if (table_value) {
            return table_value;  // Already dynamically allocated
        }
    }

    // Fall back to event-based config, but ensure it's dynamically allocated
    const char* fallback_value = get_config_value(key);
    if (fallback_value) {
        return strdup(fallback_value);  // Make a copy since fallback might be static
    }

    return NULL;
}

// Check if config table is ready
int is_config_table_ready(void) {
    if (!g_db) return 0;
    
    const char* sql = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='config'";
    sqlite3_stmt* stmt;
    
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return 0;
    }
    
    int table_exists = 0;
    if (sqlite3_step(stmt) == SQLITE_ROW) {
        table_exists = sqlite3_column_int(stmt, 0) > 0;
    }
    sqlite3_finalize(stmt);
    
    if (!table_exists) {
        return 0;
    }
    
    // Check if table has configuration data
    const char* count_sql = "SELECT COUNT(*) FROM config";
    rc = sqlite3_prepare_v2(g_db, count_sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return 0;
    }
    
    int config_count = 0;
    if (sqlite3_step(stmt) == SQLITE_ROW) {
        config_count = sqlite3_column_int(stmt, 0);
    }
    sqlite3_finalize(stmt);
    
    return config_count > 0;
}

// Initialize configuration system with migration support
int initialize_config_system_with_migration(void) {
    DEBUG_INFO("Initializing configuration system with migration support...");
    
    // Configuration system now uses direct database queries instead of cache
    // No cache initialization needed
    memset(&g_migration_status, 0, sizeof(g_migration_status));
    
    // For new installations, config table should already exist from embedded schema
    DEBUG_INFO("✓ Configuration system initialized with table support");
    
    return 0;
}

// ================================
// RETRY INITIAL CONFIG EVENT STORAGE
// ================================

int retry_store_initial_config_event(void) {
    if (!g_pending_config_event) {
        // No pending event to store
        return 0;
    }
    
    DEBUG_INFO("Retrying storage of initial configuration event...");
    
    // Try to process the cached configuration event through admin API
    if (process_startup_config_event_with_fallback(g_pending_config_event) == 0) {
        DEBUG_INFO("✓ Initial configuration processed successfully through admin API on retry");
        
        // Clean up the pending event
        cJSON_Delete(g_pending_config_event);
        g_pending_config_event = NULL;
        return 0;
    } else {
        DEBUG_ERROR("Failed to process initial configuration through admin API on retry");
        return -1;
    }
}

// ================================
// CONFIG MIGRATION FUNCTIONS
// ================================

// Populate config table from a configuration event
int populate_config_table_from_event(const cJSON* event) {
    if (!event || !g_db) {
        return -1;
    }
    
    DEBUG_INFO("Populating config table from configuration event...");
    
    cJSON* tags = cJSON_GetObjectItem(event, "tags");
    if (!tags || !cJSON_IsArray(tags)) {
        DEBUG_ERROR("Configuration event missing tags array");
        return -1;
    }
    
    int configs_populated = 0;
    
    // Process each tag as a configuration parameter
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags) {
        if (!cJSON_IsArray(tag) || cJSON_GetArraySize(tag) < 2) {
            continue;
        }
        
        cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
        cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
        
        if (!cJSON_IsString(tag_name) || !cJSON_IsString(tag_value)) {
            continue;
        }
        
        const char* key = cJSON_GetStringValue(tag_name);
        const char* value = cJSON_GetStringValue(tag_value);
        
        // Skip relay identifier tag
        if (strcmp(key, "d") == 0) {
            continue;
        }
        
        // Determine data type for the config value
        const char* data_type = "string";
        if (strcmp(key, "relay_port") == 0 ||
            strcmp(key, "max_connections") == 0 ||
            strcmp(key, "pow_min_difficulty") == 0 ||
            strcmp(key, "max_subscriptions_per_client") == 0 ||
            strcmp(key, "max_total_subscriptions") == 0 ||
            strcmp(key, "max_filters_per_subscription") == 0 ||
            strcmp(key, "max_event_tags") == 0 ||
            strcmp(key, "max_content_length") == 0 ||
            strcmp(key, "max_message_length") == 0 ||
            strcmp(key, "default_limit") == 0 ||
            strcmp(key, "max_limit") == 0 ||
            strcmp(key, "nip42_challenge_expiration") == 0 ||
            strcmp(key, "nip40_expiration_grace_period") == 0) {
            data_type = "integer";
        } else if (strcmp(key, "auth_enabled") == 0 ||
                  strcmp(key, "nip40_expiration_enabled") == 0 ||
                  strcmp(key, "nip40_expiration_strict") == 0 ||
                  strcmp(key, "nip40_expiration_filter") == 0 ||
                  strcmp(key, "nip42_auth_required") == 0) {
            data_type = "boolean";
        }
        
        // Set category
        const char* category = "general";
        if (strstr(key, "relay_")) {
            category = "relay";
        } else if (strstr(key, "nip40_")) {
            category = "expiration";
        } else if (strstr(key, "nip42_") || strstr(key, "auth_")) {
            category = "authentication";
        } else if (strstr(key, "pow_")) {
            category = "proof_of_work";
        } else if (strstr(key, "max_")) {
            category = "limits";
        }
        
        // Determine if requires restart (0 = dynamic, 1 = restart required)
        int requires_restart = 0;

        // Restart required configs
        if (strcmp(key, "relay_port") == 0 ||
            strcmp(key, "max_connections") == 0 ||
            strcmp(key, "auth_enabled") == 0 ||
            strcmp(key, "nip42_auth_required") == 0 ||
            strcmp(key, "nip42_auth_required_kinds") == 0 ||
            strcmp(key, "nip42_challenge_timeout") == 0 ||
            strcmp(key, "database_path") == 0) {
            requires_restart = 1;
        }
        
        // Insert into config table
        if (set_config_value_in_table(key, value, data_type, NULL, category, requires_restart) == 0) {
            configs_populated++;
        } else {
            char error_msg[256];
            snprintf(error_msg, sizeof(error_msg), "Failed to populate config: %s = %s", key, value);
            DEBUG_ERROR(error_msg);
        }
    }
    
    if (configs_populated > 0) {
        char success_msg[256];
        snprintf(success_msg, sizeof(success_msg), "Populated %d configuration values from event", configs_populated);
        DEBUG_INFO(success_msg);
        return 0;
    } else {
        DEBUG_ERROR("No configuration values were populated from event");
        return -1;
    }
}

// Migrate configuration from existing events to config table
int migrate_config_from_events_to_table(void) {
    if (!g_db) {
        DEBUG_ERROR("Database not available for configuration migration");
        return -1;
    }
    
    DEBUG_INFO("Migrating configuration from events to config table...");
    
    // Load the most recent configuration event from database
    const char* relay_pubkey = get_config_value("relay_pubkey");
    cJSON* config_event = load_config_event_from_database(relay_pubkey);
    if (!config_event) {
        DEBUG_INFO("No existing configuration event found - migration not needed");
        return 0;
    }
    
    // Populate config table from the event
    int result = populate_config_table_from_event(config_event);
    
    // Clean up
    cJSON_Delete(config_event);
    
    if (result == 0) {
        DEBUG_INFO("✓ Configuration migration from events to table completed successfully");
    } else {
        DEBUG_ERROR("Configuration migration from events to table failed");
    }
    
    return result;
}

// ================================
// STARTUP CONFIGURATION PROCESSING
// ================================

// Process startup configuration event - bypasses auth and updates config table
int process_startup_config_event(const cJSON* event) {
    if (!event || !g_db) {
        DEBUG_ERROR("Invalid parameters for startup config processing");
        return -1;
    }
    
    
    // Validate event structure first
    cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
    if (!kind_obj || cJSON_GetNumberValue(kind_obj) != 33334) {
        DEBUG_ERROR("Invalid event kind for startup configuration");
        return -1;
    }
    
    cJSON* tags_obj = cJSON_GetObjectItem(event, "tags");
    if (!tags_obj || !cJSON_IsArray(tags_obj)) {
        DEBUG_ERROR("Startup configuration event missing tags");
        return -1;
    }
    
    // Begin transaction for atomic config updates
    int rc = sqlite3_exec(g_db, "BEGIN IMMEDIATE TRANSACTION", NULL, NULL, NULL);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to begin startup config transaction");
        return -1;
    }
    
    int updates_applied = 0;
    
    // Process each tag as a configuration parameter (same logic as process_admin_config_event)
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags_obj) {
        if (!cJSON_IsArray(tag) || cJSON_GetArraySize(tag) < 2) {
            continue;
        }
        
        cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
        cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
        
        if (!cJSON_IsString(tag_name) || !cJSON_IsString(tag_value)) {
            continue;
        }
        
        const char* key = cJSON_GetStringValue(tag_name);
        const char* value = cJSON_GetStringValue(tag_value);
        
        // Skip relay identifier tag and relay_pubkey (already in table)
        if (strcmp(key, "d") == 0 || strcmp(key, "relay_pubkey") == 0) {
            continue;
        }
        
        // Update configuration in table
        if (update_config_in_table(key, value) == 0) {
            updates_applied++;
        }
    }
    
    if (updates_applied > 0) {
        sqlite3_exec(g_db, "COMMIT", NULL, NULL, NULL);

        char success_msg[256];
        snprintf(success_msg, sizeof(success_msg),
                "Processed startup configuration: %d values updated in config table", updates_applied);
        DEBUG_INFO(success_msg);
        return 0;
    } else {
        sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
        DEBUG_ERROR("No valid configuration parameters found in startup event");
        return -1;
    }
}

// Process startup configuration event with fallback - for retry scenarios
int process_startup_config_event_with_fallback(const cJSON* event) {
    if (!event) {
        DEBUG_ERROR("Invalid event for startup config processing with fallback");
        return -1;
    }
    
    // Try to process through admin API first
    if (process_startup_config_event(event) == 0) {
        DEBUG_INFO("✓ Startup configuration processed successfully through admin API");
        return 0;
    }
    
    // If that fails, populate defaults and try again
    DEBUG_WARN("Startup config processing failed - ensuring defaults are populated");
    // COMMENTED OUT: Don't modify existing database config on restart
    // if (populate_default_config_values() != 0) {
    //     DEBUG_ERROR("Failed to populate default config values");
    //     return -1;
    // }
    
    // Retry processing
    if (process_startup_config_event(event) == 0) {
        DEBUG_INFO("✓ Startup configuration processed successfully after populating defaults");
        return 0;
    }
    
    DEBUG_ERROR("Startup configuration processing failed even after populating defaults");
    return -1;
}

// ================================
// DYNAMIC EVENT GENERATION FROM CONFIG TABLE
// ================================

// Generate synthetic configuration event from current config table data
cJSON* generate_config_event_from_table(void) {
    if (!g_db) {
        DEBUG_ERROR("Database not available for config event generation");
        return NULL;
    }
    
    DEBUG_INFO("Generating synthetic configuration event from config table...");
    
    // Get relay pubkey for event generation
    const char* relay_pubkey = get_config_value("relay_pubkey");
    if (!relay_pubkey || strlen(relay_pubkey) != 64) {
        // Try to get from unified cache
        relay_pubkey = get_config_value("relay_pubkey");
        if (!relay_pubkey || strlen(relay_pubkey) != 64) {
            DEBUG_ERROR("Relay pubkey not available for config event generation");
            return NULL;
        }
    }
    
    // Create the event structure
    cJSON* event = cJSON_CreateObject();
    if (!event) {
        DEBUG_ERROR("Failed to create config event object");
        return NULL;
    }
    
    // Set basic event fields - we'll generate a synthetic event
    cJSON_AddStringToObject(event, "id", "synthetic_config_event_id");
    cJSON_AddStringToObject(event, "pubkey", relay_pubkey); // Use relay pubkey as event author
    cJSON_AddNumberToObject(event, "created_at", (double)time(NULL));
    cJSON_AddNumberToObject(event, "kind", 33334);
    cJSON_AddStringToObject(event, "content", "C Nostr Relay Configuration");
    cJSON_AddStringToObject(event, "sig", "synthetic_signature");
    
    // Create tags array from config table
    cJSON* tags = cJSON_CreateArray();
    if (!tags) {
        DEBUG_ERROR("Failed to create tags array for config event");
        cJSON_Delete(event);
        return NULL;
    }
    
    // Add d tag with relay pubkey (addressable event identifier)
    cJSON* d_tag = cJSON_CreateArray();
    cJSON_AddItemToArray(d_tag, cJSON_CreateString("d"));
    cJSON_AddItemToArray(d_tag, cJSON_CreateString(relay_pubkey));
    cJSON_AddItemToArray(tags, d_tag);
    
    // Query all configuration values from the config table
    const char* sql = "SELECT key, value FROM config ORDER BY key";
    sqlite3_stmt* stmt;
    
    int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        DEBUG_ERROR("Failed to prepare config query for event generation");
        cJSON_Delete(tags);
        cJSON_Delete(event);
        return NULL;
    }
    
    int config_items_added = 0;
    
    // Add each config item as a tag
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        const char* key = (const char*)sqlite3_column_text(stmt, 0);
        const char* value = (const char*)sqlite3_column_text(stmt, 1);
        
        if (key && value) {
            cJSON* config_tag = cJSON_CreateArray();
            cJSON_AddItemToArray(config_tag, cJSON_CreateString(key));
            cJSON_AddItemToArray(config_tag, cJSON_CreateString(value));
            cJSON_AddItemToArray(tags, config_tag);
            config_items_added++;
        }
    }
    
    sqlite3_finalize(stmt);
    
    if (config_items_added == 0) {
        DEBUG_WARN("No configuration items found in config table for event generation");
        cJSON_Delete(tags);
        cJSON_Delete(event);
        return NULL;
    }
    
    // Add tags to event
    cJSON_AddItemToObject(event, "tags", tags);
    
    char success_msg[256];
    snprintf(success_msg, sizeof(success_msg),
             "Generated synthetic configuration event with %d configuration items", config_items_added);
    DEBUG_INFO(success_msg);
    
    return event;
}

// Check if a REQ filter requests configuration events
int req_filter_requests_config_events(const cJSON* filter) {
    if (!filter || !cJSON_IsObject(filter)) {
        return 0;
    }
    
    cJSON* kinds = cJSON_GetObjectItem(filter, "kinds");
    if (!kinds || !cJSON_IsArray(kinds)) {
        return 0;
    }
    
    // Check if kinds array contains configuration event kinds
    cJSON* kind_item = NULL;
    cJSON_ArrayForEach(kind_item, kinds) {
        int kind_val = (int)cJSON_GetNumberValue(kind_item);
        if (cJSON_IsNumber(kind_item) && kind_val == 33334) {
            return 1;
        }
    }
    
    return 0;
}

// Generate synthetic config event data for subscription (callback approach)
cJSON* generate_synthetic_config_event_for_subscription(const char* sub_id, const cJSON* filters) {
    if (!sub_id || !filters) {
        return NULL;
    }
    
    // Check if any filter requests configuration events
    int requests_config = 0;
    
    if (cJSON_IsArray(filters)) {
        cJSON* filter = NULL;
        cJSON_ArrayForEach(filter, filters) {
            if (req_filter_requests_config_events(filter)) {
                requests_config = 1;
                break;
            }
        }
    } else if (cJSON_IsObject(filters)) {
        requests_config = req_filter_requests_config_events(filters);
    }
    
    if (!requests_config) {
        // No config events requested
        return NULL;
    }
    
    DEBUG_INFO("Generating synthetic configuration event for subscription");
    
    // Generate synthetic config event from table
    cJSON* config_event = generate_config_event_from_table();
    if (!config_event) {
        DEBUG_ERROR("Failed to generate synthetic config event");
        return NULL;
    }
    
    // Create EVENT message for the subscription
    cJSON* event_msg = cJSON_CreateArray();
    cJSON_AddItemToArray(event_msg, cJSON_CreateString("EVENT"));
    cJSON_AddItemToArray(event_msg, cJSON_CreateString(sub_id));
    cJSON_AddItemToArray(event_msg, config_event);
    
    DEBUG_INFO("✓ Generated synthetic configuration event message");
    return event_msg;
}

/**
 * Generate a synthetic configuration event from config table data
 * This allows WebSocket clients to fetch configuration via REQ messages
 * Returns JSON string that must be freed by caller
 */
char* generate_config_event_json(void) {
    // Use the existing cJSON function and convert to string
    cJSON* event = generate_config_event_from_table();
    if (!event) {
        return NULL;
    }
    
    // Convert to JSON string
    char* json_string = cJSON_Print(event);
    cJSON_Delete(event);
    
    return json_string;
}