// NIP-11 Relay Information Document module
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <libwebsockets.h>
#include "../nostr_core_lib/cjson/cJSON.h"
#include "config.h"

// Forward declarations for logging functions
void log_info(const char* message);
void log_success(const char* message);
void log_error(const char* message);
void log_warning(const char* message);

// Forward declarations for configuration functions
const char* get_config_value(const char* key);
int get_config_int(const char* key, int default_value);
int get_config_bool(const char* key, int default_value);

// Forward declarations for global cache access
extern unified_config_cache_t g_unified_cache;

// Forward declarations for constants (defined in config.h and other headers)
#define HTTP_STATUS_OK 200
#define HTTP_STATUS_NOT_ACCEPTABLE 406
#define HTTP_STATUS_INTERNAL_SERVER_ERROR 500


/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             NIP-11 RELAY INFORMATION DOCUMENT
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

// Helper function to parse comma-separated string into cJSON array
cJSON* parse_comma_separated_array(const char* csv_string) {
    if (!csv_string || strlen(csv_string) == 0) {
        return cJSON_CreateArray();
    }

    cJSON* array = cJSON_CreateArray();
    if (!array) {
        return NULL;
    }

    char* csv_copy = strdup(csv_string);
    if (!csv_copy) {
        cJSON_Delete(array);
        return NULL;
    }

    char* token = strtok(csv_copy, ",");
    while (token) {
        // Trim whitespace
        while (*token == ' ') token++;
        char* end = token + strlen(token) - 1;
        while (end > token && *end == ' ') *end-- = '\0';

        if (strlen(token) > 0) {
            // Try to parse as number first (for supported_nips)
            char* endptr;
            long num = strtol(token, &endptr, 10);
            if (*endptr == '\0') {
                // It's a number
                cJSON_AddItemToArray(array, cJSON_CreateNumber(num));
            } else {
                // It's a string
                cJSON_AddItemToArray(array, cJSON_CreateString(token));
            }
        }
        token = strtok(NULL, ",");
    }

    free(csv_copy);
    return array;
}

// Initialize relay information using configuration system
void init_relay_info() {
    // Get all config values first (without holding mutex to avoid deadlock)
    // Note: These may be dynamically allocated strings that need to be freed
    const char* relay_name = get_config_value("relay_name");
    const char* relay_description = get_config_value("relay_description");
    const char* relay_software = get_config_value("relay_software");
    const char* relay_version = get_config_value("relay_version");
    const char* relay_contact = get_config_value("relay_contact");
    const char* relay_pubkey = get_config_value("relay_pubkey");
    const char* supported_nips_csv = get_config_value("supported_nips");
    const char* language_tags_csv = get_config_value("language_tags");
    const char* relay_countries_csv = get_config_value("relay_countries");
    const char* posting_policy = get_config_value("posting_policy");
    const char* payments_url = get_config_value("payments_url");

    // Get config values for limitations
    int max_message_length = get_config_int("max_message_length", 16384);
    int max_subscriptions_per_client = get_config_int("max_subscriptions_per_client", 20);
    int max_limit = get_config_int("max_limit", 5000);
    int max_event_tags = get_config_int("max_event_tags", 100);
    int max_content_length = get_config_int("max_content_length", 8196);
    int default_limit = get_config_int("default_limit", 500);
    int admin_enabled = get_config_bool("admin_enabled", 0);

    pthread_mutex_lock(&g_unified_cache.cache_lock);

    // Update relay information fields
    if (relay_name) {
        strncpy(g_unified_cache.relay_info.name, relay_name, sizeof(g_unified_cache.relay_info.name) - 1);
        free((char*)relay_name); // Free dynamically allocated string
    } else {
        strncpy(g_unified_cache.relay_info.name, "C Nostr Relay", sizeof(g_unified_cache.relay_info.name) - 1);
    }

    if (relay_description) {
        strncpy(g_unified_cache.relay_info.description, relay_description, sizeof(g_unified_cache.relay_info.description) - 1);
        free((char*)relay_description); // Free dynamically allocated string
    } else {
        strncpy(g_unified_cache.relay_info.description, "A high-performance Nostr relay implemented in C with SQLite storage", sizeof(g_unified_cache.relay_info.description) - 1);
    }

    if (relay_software) {
        strncpy(g_unified_cache.relay_info.software, relay_software, sizeof(g_unified_cache.relay_info.software) - 1);
        free((char*)relay_software); // Free dynamically allocated string
    } else {
        strncpy(g_unified_cache.relay_info.software, "https://git.laantungir.net/laantungir/c-relay.git", sizeof(g_unified_cache.relay_info.software) - 1);
    }

    if (relay_version) {
        strncpy(g_unified_cache.relay_info.version, relay_version, sizeof(g_unified_cache.relay_info.version) - 1);
        free((char*)relay_version); // Free dynamically allocated string
    } else {
        strncpy(g_unified_cache.relay_info.version, "0.2.0", sizeof(g_unified_cache.relay_info.version) - 1);
    }

    if (relay_contact) {
        strncpy(g_unified_cache.relay_info.contact, relay_contact, sizeof(g_unified_cache.relay_info.contact) - 1);
        free((char*)relay_contact); // Free dynamically allocated string
    }

    if (relay_pubkey) {
        strncpy(g_unified_cache.relay_info.pubkey, relay_pubkey, sizeof(g_unified_cache.relay_info.pubkey) - 1);
        free((char*)relay_pubkey); // Free dynamically allocated string
    }

    if (posting_policy) {
        strncpy(g_unified_cache.relay_info.posting_policy, posting_policy, sizeof(g_unified_cache.relay_info.posting_policy) - 1);
        free((char*)posting_policy); // Free dynamically allocated string
    }

    if (payments_url) {
        strncpy(g_unified_cache.relay_info.payments_url, payments_url, sizeof(g_unified_cache.relay_info.payments_url) - 1);
        free((char*)payments_url); // Free dynamically allocated string
    }
    
    // Initialize supported NIPs array from config
    if (supported_nips_csv) {
        g_unified_cache.relay_info.supported_nips = parse_comma_separated_array(supported_nips_csv);
        free((char*)supported_nips_csv); // Free dynamically allocated string
    } else {
        // Fallback to default supported NIPs
        g_unified_cache.relay_info.supported_nips = cJSON_CreateArray();
        if (g_unified_cache.relay_info.supported_nips) {
            cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(1));   // NIP-01: Basic protocol
            cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(9));   // NIP-09: Event deletion
            cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(11));  // NIP-11: Relay information
            cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(13));  // NIP-13: Proof of Work
            cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(15));  // NIP-15: EOSE
            cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(20));  // NIP-20: Command results
            cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(40));  // NIP-40: Expiration Timestamp
            cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(42));  // NIP-42: Authentication
        }
    }
    
    // Initialize server limitations using configuration
    g_unified_cache.relay_info.limitation = cJSON_CreateObject();
    if (g_unified_cache.relay_info.limitation) {
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_message_length", max_message_length);
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subscriptions", max_subscriptions_per_client);
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_limit", max_limit);
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subid_length", SUBSCRIPTION_ID_MAX_LENGTH);
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_event_tags", max_event_tags);
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_content_length", max_content_length);
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "min_pow_difficulty", g_unified_cache.pow_config.min_pow_difficulty);
        cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "auth_required", admin_enabled ? cJSON_True : cJSON_False);
        cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "payment_required", cJSON_False);
        cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "restricted_writes", cJSON_False);
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_lower_limit", 0);
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_upper_limit", 2147483647);
        cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "default_limit", default_limit);
    }
    
    // Initialize empty retention policies (can be configured later)
    g_unified_cache.relay_info.retention = cJSON_CreateArray();

    // Initialize language tags from config
    if (language_tags_csv) {
        g_unified_cache.relay_info.language_tags = parse_comma_separated_array(language_tags_csv);
        free((char*)language_tags_csv); // Free dynamically allocated string
    } else {
        // Fallback to global
        g_unified_cache.relay_info.language_tags = cJSON_CreateArray();
        if (g_unified_cache.relay_info.language_tags) {
            cJSON_AddItemToArray(g_unified_cache.relay_info.language_tags, cJSON_CreateString("*"));
        }
    }

    // Initialize relay countries from config
    if (relay_countries_csv) {
        g_unified_cache.relay_info.relay_countries = parse_comma_separated_array(relay_countries_csv);
        free((char*)relay_countries_csv); // Free dynamically allocated string
    } else {
        // Fallback to global
        g_unified_cache.relay_info.relay_countries = cJSON_CreateArray();
        if (g_unified_cache.relay_info.relay_countries) {
            cJSON_AddItemToArray(g_unified_cache.relay_info.relay_countries, cJSON_CreateString("*"));
        }
    }

    // Initialize content tags as empty array
    g_unified_cache.relay_info.tags = cJSON_CreateArray();

    // Initialize fees as empty object (no payment required by default)
    g_unified_cache.relay_info.fees = cJSON_CreateObject();

    pthread_mutex_unlock(&g_unified_cache.cache_lock);

    log_success("Relay information initialized with default values");
}

// Clean up relay information JSON objects
void cleanup_relay_info() {
    pthread_mutex_lock(&g_unified_cache.cache_lock);
    if (g_unified_cache.relay_info.supported_nips) {
        cJSON_Delete(g_unified_cache.relay_info.supported_nips);
        g_unified_cache.relay_info.supported_nips = NULL;
    }
    if (g_unified_cache.relay_info.limitation) {
        cJSON_Delete(g_unified_cache.relay_info.limitation);
        g_unified_cache.relay_info.limitation = NULL;
    }
    if (g_unified_cache.relay_info.retention) {
        cJSON_Delete(g_unified_cache.relay_info.retention);
        g_unified_cache.relay_info.retention = NULL;
    }
    if (g_unified_cache.relay_info.language_tags) {
        cJSON_Delete(g_unified_cache.relay_info.language_tags);
        g_unified_cache.relay_info.language_tags = NULL;
    }
    if (g_unified_cache.relay_info.relay_countries) {
        cJSON_Delete(g_unified_cache.relay_info.relay_countries);
        g_unified_cache.relay_info.relay_countries = NULL;
    }
    if (g_unified_cache.relay_info.tags) {
        cJSON_Delete(g_unified_cache.relay_info.tags);
        g_unified_cache.relay_info.tags = NULL;
    }
    if (g_unified_cache.relay_info.fees) {
        cJSON_Delete(g_unified_cache.relay_info.fees);
        g_unified_cache.relay_info.fees = NULL;
    }
    pthread_mutex_unlock(&g_unified_cache.cache_lock);
}

// Generate NIP-11 compliant JSON document
cJSON* generate_relay_info_json() {
    cJSON* info = cJSON_CreateObject();
    if (!info) {
        log_error("Failed to create relay info JSON object");
        return NULL;
    }

    pthread_mutex_lock(&g_unified_cache.cache_lock);

    // Defensive reinit: if relay_info appears empty (cache refresh wiped it), rebuild it directly from table
    if (strlen(g_unified_cache.relay_info.name) == 0 &&
        strlen(g_unified_cache.relay_info.description) == 0 &&
        strlen(g_unified_cache.relay_info.software) == 0) {
        log_warning("NIP-11 relay_info appears empty, rebuilding directly from config table");

        // Rebuild relay_info directly from config table to avoid circular cache dependency
        // Get values directly from table (similar to init_relay_info but without cache calls)
        const char* relay_name = get_config_value_from_table("relay_name");
        if (relay_name) {
            strncpy(g_unified_cache.relay_info.name, relay_name, sizeof(g_unified_cache.relay_info.name) - 1);
            free((char*)relay_name);
        } else {
            strncpy(g_unified_cache.relay_info.name, "C Nostr Relay", sizeof(g_unified_cache.relay_info.name) - 1);
        }

        const char* relay_description = get_config_value_from_table("relay_description");
        if (relay_description) {
            strncpy(g_unified_cache.relay_info.description, relay_description, sizeof(g_unified_cache.relay_info.description) - 1);
            free((char*)relay_description);
        } else {
            strncpy(g_unified_cache.relay_info.description, "A high-performance Nostr relay implemented in C with SQLite storage", sizeof(g_unified_cache.relay_info.description) - 1);
        }

        const char* relay_software = get_config_value_from_table("relay_software");
        if (relay_software) {
            strncpy(g_unified_cache.relay_info.software, relay_software, sizeof(g_unified_cache.relay_info.software) - 1);
            free((char*)relay_software);
        } else {
            strncpy(g_unified_cache.relay_info.software, "https://git.laantungir.net/laantungir/c-relay.git", sizeof(g_unified_cache.relay_info.software) - 1);
        }

        const char* relay_version = get_config_value_from_table("relay_version");
        if (relay_version) {
            strncpy(g_unified_cache.relay_info.version, relay_version, sizeof(g_unified_cache.relay_info.version) - 1);
            free((char*)relay_version);
        } else {
            strncpy(g_unified_cache.relay_info.version, "0.2.0", sizeof(g_unified_cache.relay_info.version) - 1);
        }

        const char* relay_contact = get_config_value_from_table("relay_contact");
        if (relay_contact) {
            strncpy(g_unified_cache.relay_info.contact, relay_contact, sizeof(g_unified_cache.relay_info.contact) - 1);
            free((char*)relay_contact);
        }

        const char* relay_pubkey = get_config_value_from_table("relay_pubkey");
        if (relay_pubkey) {
            strncpy(g_unified_cache.relay_info.pubkey, relay_pubkey, sizeof(g_unified_cache.relay_info.pubkey) - 1);
            free((char*)relay_pubkey);
        }

        const char* posting_policy = get_config_value_from_table("posting_policy");
        if (posting_policy) {
            strncpy(g_unified_cache.relay_info.posting_policy, posting_policy, sizeof(g_unified_cache.relay_info.posting_policy) - 1);
            free((char*)posting_policy);
        }

        const char* payments_url = get_config_value_from_table("payments_url");
        if (payments_url) {
            strncpy(g_unified_cache.relay_info.payments_url, payments_url, sizeof(g_unified_cache.relay_info.payments_url) - 1);
            free((char*)payments_url);
        }

        // Rebuild supported_nips array
        const char* supported_nips_csv = get_config_value_from_table("supported_nips");
        if (supported_nips_csv) {
            g_unified_cache.relay_info.supported_nips = parse_comma_separated_array(supported_nips_csv);
            free((char*)supported_nips_csv);
        } else {
            g_unified_cache.relay_info.supported_nips = cJSON_CreateArray();
            if (g_unified_cache.relay_info.supported_nips) {
                cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(1));
                cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(9));
                cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(11));
                cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(13));
                cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(15));
                cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(20));
                cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(40));
                cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(42));
            }
        }

        // Rebuild limitation object
        int max_message_length = 16384;
        const char* max_msg_str = get_config_value_from_table("max_message_length");
        if (max_msg_str) {
            max_message_length = atoi(max_msg_str);
            free((char*)max_msg_str);
        }

        int max_subscriptions_per_client = 20;
        const char* max_subs_str = get_config_value_from_table("max_subscriptions_per_client");
        if (max_subs_str) {
            max_subscriptions_per_client = atoi(max_subs_str);
            free((char*)max_subs_str);
        }

        int max_limit = 5000;
        const char* max_limit_str = get_config_value_from_table("max_limit");
        if (max_limit_str) {
            max_limit = atoi(max_limit_str);
            free((char*)max_limit_str);
        }

        int max_event_tags = 100;
        const char* max_tags_str = get_config_value_from_table("max_event_tags");
        if (max_tags_str) {
            max_event_tags = atoi(max_tags_str);
            free((char*)max_tags_str);
        }

        int max_content_length = 8196;
        const char* max_content_str = get_config_value_from_table("max_content_length");
        if (max_content_str) {
            max_content_length = atoi(max_content_str);
            free((char*)max_content_str);
        }

        int default_limit = 500;
        const char* default_limit_str = get_config_value_from_table("default_limit");
        if (default_limit_str) {
            default_limit = atoi(default_limit_str);
            free((char*)default_limit_str);
        }

        int admin_enabled = 0;
        const char* admin_enabled_str = get_config_value_from_table("admin_enabled");
        if (admin_enabled_str) {
            admin_enabled = (strcmp(admin_enabled_str, "true") == 0) ? 1 : 0;
            free((char*)admin_enabled_str);
        }

        g_unified_cache.relay_info.limitation = cJSON_CreateObject();
        if (g_unified_cache.relay_info.limitation) {
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_message_length", max_message_length);
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subscriptions", max_subscriptions_per_client);
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_limit", max_limit);
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subid_length", SUBSCRIPTION_ID_MAX_LENGTH);
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_event_tags", max_event_tags);
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_content_length", max_content_length);
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "min_pow_difficulty", g_unified_cache.pow_config.min_pow_difficulty);
            cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "auth_required", admin_enabled ? cJSON_True : cJSON_False);
            cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "payment_required", cJSON_False);
            cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "restricted_writes", cJSON_False);
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_lower_limit", 0);
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_upper_limit", 2147483647);
            cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "default_limit", default_limit);
        }

        // Rebuild other arrays (empty for now)
        g_unified_cache.relay_info.retention = cJSON_CreateArray();
        g_unified_cache.relay_info.language_tags = cJSON_CreateArray();
        if (g_unified_cache.relay_info.language_tags) {
            cJSON_AddItemToArray(g_unified_cache.relay_info.language_tags, cJSON_CreateString("*"));
        }
        g_unified_cache.relay_info.relay_countries = cJSON_CreateArray();
        if (g_unified_cache.relay_info.relay_countries) {
            cJSON_AddItemToArray(g_unified_cache.relay_info.relay_countries, cJSON_CreateString("*"));
        }
        g_unified_cache.relay_info.tags = cJSON_CreateArray();
        g_unified_cache.relay_info.fees = cJSON_CreateObject();

    }

    // Add basic relay information
    if (strlen(g_unified_cache.relay_info.name) > 0) {
        cJSON_AddStringToObject(info, "name", g_unified_cache.relay_info.name);
    }
    if (strlen(g_unified_cache.relay_info.description) > 0) {
        cJSON_AddStringToObject(info, "description", g_unified_cache.relay_info.description);
    }
    if (strlen(g_unified_cache.relay_info.banner) > 0) {
        cJSON_AddStringToObject(info, "banner", g_unified_cache.relay_info.banner);
    }
    if (strlen(g_unified_cache.relay_info.icon) > 0) {
        cJSON_AddStringToObject(info, "icon", g_unified_cache.relay_info.icon);
    }
    if (strlen(g_unified_cache.relay_info.pubkey) > 0) {
        cJSON_AddStringToObject(info, "pubkey", g_unified_cache.relay_info.pubkey);
    }
    if (strlen(g_unified_cache.relay_info.contact) > 0) {
        cJSON_AddStringToObject(info, "contact", g_unified_cache.relay_info.contact);
    }
    
    // Add supported NIPs
    if (g_unified_cache.relay_info.supported_nips) {
        cJSON_AddItemToObject(info, "supported_nips", cJSON_Duplicate(g_unified_cache.relay_info.supported_nips, 1));
    }
    
    // Add software information
    if (strlen(g_unified_cache.relay_info.software) > 0) {
        cJSON_AddStringToObject(info, "software", g_unified_cache.relay_info.software);
    }
    if (strlen(g_unified_cache.relay_info.version) > 0) {
        cJSON_AddStringToObject(info, "version", g_unified_cache.relay_info.version);
    }
    
    // Add policies
    if (strlen(g_unified_cache.relay_info.privacy_policy) > 0) {
        cJSON_AddStringToObject(info, "privacy_policy", g_unified_cache.relay_info.privacy_policy);
    }
    if (strlen(g_unified_cache.relay_info.terms_of_service) > 0) {
        cJSON_AddStringToObject(info, "terms_of_service", g_unified_cache.relay_info.terms_of_service);
    }
    if (strlen(g_unified_cache.relay_info.posting_policy) > 0) {
        cJSON_AddStringToObject(info, "posting_policy", g_unified_cache.relay_info.posting_policy);
    }
    
    // Add server limitations
    if (g_unified_cache.relay_info.limitation) {
        cJSON_AddItemToObject(info, "limitation", cJSON_Duplicate(g_unified_cache.relay_info.limitation, 1));
    }
    
    // Add retention policies if configured
    if (g_unified_cache.relay_info.retention && cJSON_GetArraySize(g_unified_cache.relay_info.retention) > 0) {
        cJSON_AddItemToObject(info, "retention", cJSON_Duplicate(g_unified_cache.relay_info.retention, 1));
    }
    
    // Add geographical and language information
    if (g_unified_cache.relay_info.relay_countries) {
        cJSON_AddItemToObject(info, "relay_countries", cJSON_Duplicate(g_unified_cache.relay_info.relay_countries, 1));
    }
    if (g_unified_cache.relay_info.language_tags) {
        cJSON_AddItemToObject(info, "language_tags", cJSON_Duplicate(g_unified_cache.relay_info.language_tags, 1));
    }
    if (g_unified_cache.relay_info.tags && cJSON_GetArraySize(g_unified_cache.relay_info.tags) > 0) {
        cJSON_AddItemToObject(info, "tags", cJSON_Duplicate(g_unified_cache.relay_info.tags, 1));
    }
    
    // Add payment information if configured
    if (strlen(g_unified_cache.relay_info.payments_url) > 0) {
        cJSON_AddStringToObject(info, "payments_url", g_unified_cache.relay_info.payments_url);
    }
    if (g_unified_cache.relay_info.fees && cJSON_GetObjectItem(g_unified_cache.relay_info.fees, "admission")) {
        cJSON_AddItemToObject(info, "fees", cJSON_Duplicate(g_unified_cache.relay_info.fees, 1));
    }
    
    pthread_mutex_unlock(&g_unified_cache.cache_lock);
    
    return info;
}

// NIP-11 HTTP session data structure for managing buffer lifetime
struct nip11_session_data {
    int type; // 0 for NIP-11
    char* json_buffer;
    size_t json_length;
    int headers_sent;
    int body_sent;
};

// Handle NIP-11 HTTP request with proper asynchronous buffer management
int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
    
    // Check if client accepts application/nostr+json
    int accepts_nostr_json = 0;
    if (accept_header) {
        if (strstr(accept_header, "application/nostr+json") != NULL) {
            accepts_nostr_json = 1;
        }
    }
    
    if (!accepts_nostr_json) {
        log_warning("HTTP request without proper Accept header for NIP-11");
        // Return 406 Not Acceptable
        unsigned char buf[LWS_PRE + 256];
        unsigned char *p = &buf[LWS_PRE];
        unsigned char *start = p;
        unsigned char *end = &buf[sizeof(buf) - 1];

        if (lws_add_http_header_status(wsi, HTTP_STATUS_NOT_ACCEPTABLE, &p, end)) {
            return -1;
        }
        if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char*)"text/plain", 10, &p, end)) {
            return -1;
        }
        if (lws_add_http_header_content_length(wsi, 0, &p, end)) {
            return -1;
        }
        if (lws_finalize_http_header(wsi, &p, end)) {
            return -1;
        }
        lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
        return -1; // Close connection
    }
    
    // Generate relay information JSON
    cJSON* info_json = generate_relay_info_json();
    if (!info_json) {
        log_error("Failed to generate relay info JSON");
        unsigned char buf[LWS_PRE + 256];
        unsigned char *p = &buf[LWS_PRE];
        unsigned char *start = p;
        unsigned char *end = &buf[sizeof(buf) - 1];

        if (lws_add_http_header_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, &p, end)) {
            return -1;
        }
        if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char*)"text/plain", 10, &p, end)) {
            return -1;
        }
        if (lws_add_http_header_content_length(wsi, 0, &p, end)) {
            return -1;
        }
        if (lws_finalize_http_header(wsi, &p, end)) {
            return -1;
        }
        lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
        return -1;
    }
    
    char* json_string = cJSON_Print(info_json);
    cJSON_Delete(info_json);

    if (!json_string) {
        log_error("Failed to serialize relay info JSON");
        unsigned char buf[LWS_PRE + 256];
        unsigned char *p = &buf[LWS_PRE];
        unsigned char *start = p;
        unsigned char *end = &buf[sizeof(buf) - 1];

        if (lws_add_http_header_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, &p, end)) {
            return -1;
        }
        if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char*)"text/plain", 10, &p, end)) {
            return -1;
        }
        if (lws_add_http_header_content_length(wsi, 0, &p, end)) {
            return -1;
        }
        if (lws_finalize_http_header(wsi, &p, end)) {
            return -1;
        }
        lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
        return -1;
    }

    size_t json_len = strlen(json_string);
    
    // Allocate session data to manage buffer lifetime across callbacks
    struct nip11_session_data* session_data = malloc(sizeof(struct nip11_session_data));
    if (!session_data) {
        log_error("Failed to allocate NIP-11 session data");
        free(json_string);
        return -1;
    }
    
    // Store JSON buffer in session data for asynchronous handling
    session_data->type = 0; // NIP-11
    session_data->json_buffer = json_string;
    session_data->json_length = json_len;
    session_data->headers_sent = 0;
    session_data->body_sent = 0;
    
    // Store session data in WSI user data for callback access
    lws_set_wsi_user(wsi, session_data);
    
    // Prepare HTTP response with CORS headers
    unsigned char buf[LWS_PRE + 1024];
    unsigned char *p = &buf[LWS_PRE];
    unsigned char *start = p;
    unsigned char *end = &buf[sizeof(buf) - 1];
    
    // Add status
    if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) {
        free(session_data->json_buffer);
        free(session_data);
        return -1;
    }
    
    // Add content type
    if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
                                     (unsigned char*)"application/nostr+json", 22, &p, end)) {
        free(session_data->json_buffer);
        free(session_data);
        return -1;
    }
    
    // Add content length
    if (lws_add_http_header_content_length(wsi, json_len, &p, end)) {
        free(session_data->json_buffer);
        free(session_data);
        return -1;
    }
    
    // Add CORS headers as required by NIP-11
    if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-origin:",
                                    (unsigned char*)"*", 1, &p, end)) {
        free(session_data->json_buffer);
        free(session_data);
        return -1;
    }
    if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-headers:",
                                    (unsigned char*)"content-type, accept", 20, &p, end)) {
        free(session_data->json_buffer);
        free(session_data);
        return -1;
    }
    if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-methods:",
                                    (unsigned char*)"GET, OPTIONS", 12, &p, end)) {
        free(session_data->json_buffer);
        free(session_data);
        return -1;
    }

    // Add Connection: close to ensure connection closes after response
    if (lws_add_http_header_by_name(wsi, (unsigned char*)"connection:", (unsigned char*)"close", 5, &p, end)) {
        free(session_data->json_buffer);
        free(session_data);
        return -1;
    }
    
    // Finalize headers
    if (lws_finalize_http_header(wsi, &p, end)) {
        free(session_data->json_buffer);
        free(session_data);
        return -1;
    }
    
    // Write headers
    if (lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS) < 0) {
        free(session_data->json_buffer);
        free(session_data);
        return -1;
    }
    
    session_data->headers_sent = 1;
    
    // Request callback for body transmission
    lws_callback_on_writable(wsi);

    return 0;
}

