#define _GNU_SOURCE
#include "nostr_websocket_tls.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <sys/select.h>

// mbedTLS headers
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/error.h"
#include "mbedtls/debug.h"
#include "psa/crypto.h"

// WebSocket magic string for handshake
#define WS_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
#define WS_KEY_LEN 24
#define MAX_HEADER_SIZE 4096
#define MAX_FRAME_SIZE 65536

// Debug logging (conditional compilation)
#if defined(ENABLE_FILE_LOGGING) && defined(ENABLE_WEBSOCKET_LOGGING)
static FILE* debug_log_file = NULL;
static void debug_log_init(void);
static void debug_log_message(const char* direction, const char* host, int port, const char* message);
static const char* get_timestamp(void);
#endif

// Transport layer abstraction
typedef struct {
    int (*connect)(void* ctx, const char* host, int port);
    int (*send)(void* ctx, const void* data, size_t len);
    int (*recv)(void* ctx, void* data, size_t len, int timeout_ms);
    int (*close)(void* ctx);
    void (*cleanup)(void* ctx);
} transport_ops_t;

// TCP transport context
typedef struct {
    int socket_fd;
} tcp_transport_t;

// TLS transport context (mbedTLS)
typedef struct {
    int socket_fd;
    mbedtls_ssl_context ssl;
    mbedtls_ssl_config conf;
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;
    int ssl_connected;
} tls_transport_t;

// WebSocket client structure
struct nostr_ws_client {
    nostr_ws_state_t state;
    char* host;
    int port;
    char* path;
    int use_tls;
    int timeout_ms;
    char receive_buffer[MAX_FRAME_SIZE];
    size_t receive_buffer_pos;
    
    // Transport layer
    transport_ops_t* transport;
    union {
        tcp_transport_t tcp;
        tls_transport_t tls;
    } transport_ctx;
};

// Transport layer implementations
static int tcp_connect(void* ctx, const char* host, int port);
static int tcp_send(void* ctx, const void* data, size_t len);
static int tcp_recv(void* ctx, void* data, size_t len, int timeout_ms);
static int tcp_close(void* ctx);
static void tcp_cleanup(void* ctx);

static int tls_connect(void* ctx, const char* host, int port);
static int tls_send(void* ctx, const void* data, size_t len);
static int tls_recv(void* ctx, void* data, size_t len, int timeout_ms);
static int tls_close(void* ctx);
static void tls_cleanup(void* ctx);

// Internal helper functions
static int ws_parse_url(const char* url, char** host, int* port, char** path, int* use_tls);
static int ws_create_handshake_key(char* key_out, size_t key_size);
static int ws_perform_handshake(nostr_ws_client_t* client, const char* key);
static int ws_send_frame(nostr_ws_client_t* client, ws_opcode_t opcode, const char* payload, size_t payload_len);
static int ws_receive_frame(nostr_ws_client_t* client, ws_opcode_t* opcode, char* payload, size_t* payload_len, int timeout_ms);
static void ws_mask_payload(char* payload, size_t len, uint32_t mask);
static uint32_t ws_generate_mask(void);

// Transport layer vtables
static transport_ops_t tcp_transport_ops = {
    .connect = tcp_connect,
    .send = tcp_send,
    .recv = tcp_recv,
    .close = tcp_close,
    .cleanup = tcp_cleanup
};

static transport_ops_t tls_transport_ops = {
    .connect = tls_connect,
    .send = tls_send,
    .recv = tls_recv,
    .close = tls_close,
    .cleanup = tls_cleanup
};

// ============================================================================
// Core WebSocket Functions
// ============================================================================

nostr_ws_client_t* nostr_ws_connect(const char* url) {
    if (!url) return NULL;
    
    nostr_ws_client_t* client = calloc(1, sizeof(nostr_ws_client_t));
    if (!client) return NULL;
    
    client->state = NOSTR_WS_CONNECTING;
    client->timeout_ms = 30000; // 30 second default timeout
    
    // Parse URL and determine if TLS is needed
    if (ws_parse_url(url, &client->host, &client->port, &client->path, &client->use_tls) != 0) {
        free(client);
        return NULL;
    }
    
    // URL parsed successfully
    
    // Set up transport layer
    if (client->use_tls) {
        client->transport = &tls_transport_ops;
        memset(&client->transport_ctx.tls, 0, sizeof(client->transport_ctx.tls));
        client->transport_ctx.tls.socket_fd = -1;
    } else {
        client->transport = &tcp_transport_ops;
        client->transport_ctx.tcp.socket_fd = -1;
    }
    
    // Connect to server
    if (client->transport->connect(&client->transport_ctx, client->host, client->port) != 0) {
        free(client->host);
        free(client->path);
        free(client);
        return NULL;
    }
    
    // Perform WebSocket handshake
    char handshake_key[WS_KEY_LEN + 1];
    if (ws_create_handshake_key(handshake_key, sizeof(handshake_key)) != 0) {
        client->transport->close(&client->transport_ctx);
        client->transport->cleanup(&client->transport_ctx);
        free(client->host);
        free(client->path);
        free(client);
        return NULL;
    }
    
    // Perform WebSocket handshake (silently)
    if (ws_perform_handshake(client, handshake_key) != 0) {
        client->transport->close(&client->transport_ctx);
        client->transport->cleanup(&client->transport_ctx);
        free(client->host);
        free(client->path);
        free(client);
        return NULL;
    }
    
    client->state = NOSTR_WS_CONNECTED;
    return client;
}

int nostr_ws_close(nostr_ws_client_t* client) {
    if (!client) return NOSTR_WS_ERROR_INVALID;
    
    if (client->state == NOSTR_WS_CONNECTED) {
        // Send close frame
        ws_send_frame(client, WS_OPCODE_CLOSE, NULL, 0);
        client->state = NOSTR_WS_CLOSING;
    }
    
    client->transport->close(&client->transport_ctx);
    client->transport->cleanup(&client->transport_ctx);
    
    free(client->host);
    free(client->path);
    client->state = NOSTR_WS_CLOSED;
    free(client);
    
    return NOSTR_WS_SUCCESS;
}

nostr_ws_state_t nostr_ws_get_state(nostr_ws_client_t* client) {
    if (!client) return NOSTR_WS_ERROR;
    return client->state;
}

int nostr_ws_send_text(nostr_ws_client_t* client, const char* message) {
    if (!client || !message || client->state != NOSTR_WS_CONNECTED) {
        return NOSTR_WS_ERROR_INVALID;
    }
    
    return ws_send_frame(client, WS_OPCODE_TEXT, message, strlen(message));
}

int nostr_ws_receive(nostr_ws_client_t* client, char* buffer, size_t buffer_size, int timeout_ms) {
    if (!client || !buffer || buffer_size == 0) {
        return NOSTR_WS_ERROR_INVALID;
    }
    
    // Allow receiving even when in CLOSING state to capture final messages
    if (client->state != NOSTR_WS_CONNECTED && client->state != NOSTR_WS_CLOSING) {
        return NOSTR_WS_ERROR_INVALID;
    }
    
    ws_opcode_t opcode;
    size_t payload_len = buffer_size - 1; // Leave space for null terminator
    
    int result = ws_receive_frame(client, &opcode, buffer, &payload_len, timeout_ms);
    if (result < 0) return result;
    
    // Handle different frame types
    switch (opcode) {
        case WS_OPCODE_TEXT:
            buffer[payload_len] = '\0'; // Null terminate text
            return (int)payload_len;
            
        case WS_OPCODE_PING:
            // Respond with pong
            ws_send_frame(client, WS_OPCODE_PONG, buffer, payload_len);
            // Continue receiving
            return nostr_ws_receive(client, buffer, buffer_size, timeout_ms);
            
        case WS_OPCODE_PONG:
            // Return pong frames with special prefix to distinguish from text
            // Format: "__PONG__" + payload
            const char* pong_prefix = "__PONG__";
            size_t prefix_len = strlen(pong_prefix);
            
            if (payload_len + prefix_len < buffer_size - 1) {
                memmove(buffer + prefix_len, buffer, payload_len);
                memcpy(buffer, pong_prefix, prefix_len);
                buffer[payload_len + prefix_len] = '\0';
                return (int)(payload_len + prefix_len);
            } else {
                // Not enough space for prefix, just return payload
                buffer[payload_len] = '\0';
                return (int)payload_len;
            }
            
        case WS_OPCODE_CLOSE:
            client->state = NOSTR_WS_CLOSING;
            buffer[payload_len] = '\0'; // Null terminate
            
            
            return (int)payload_len; // Return the close message content
            
        default:
            return NOSTR_WS_ERROR_PROTOCOL;
    }
}

int nostr_ws_ping(nostr_ws_client_t* client) {
    if (!client || client->state != NOSTR_WS_CONNECTED) {
        return NOSTR_WS_ERROR_INVALID;
    }
    
    return ws_send_frame(client, WS_OPCODE_PING, "ping", 4);
}

int nostr_ws_set_timeout(nostr_ws_client_t* client, int timeout_ms) {
    if (!client) return NOSTR_WS_ERROR_INVALID;
    client->timeout_ms = timeout_ms;
    return NOSTR_WS_SUCCESS;
}

// ============================================================================
// NOSTR-Specific Helper Functions (same as original)
// ============================================================================

int nostr_relay_send_req(nostr_ws_client_t* client, const char* subscription_id, cJSON* filters) {
    if (!client || !subscription_id || !filters) {
        return NOSTR_WS_ERROR_INVALID;
    }
    
    // Create REQ message: ["REQ", subscription_id, ...filters]
    cJSON* req_array = cJSON_CreateArray();
    cJSON_AddItemToArray(req_array, cJSON_CreateString("REQ"));
    cJSON_AddItemToArray(req_array, cJSON_CreateString(subscription_id));
    
    // Add filters - create a copy to avoid double-free
    if (cJSON_IsArray(filters)) {
        cJSON* filter;
        cJSON_ArrayForEach(filter, filters) {
            cJSON_AddItemToArray(req_array, cJSON_Duplicate(filter, 1));
        }
    } else {
        cJSON_AddItemToArray(req_array, cJSON_Duplicate(filters, 1));
    }
    
    char* req_string = cJSON_Print(req_array);
    if (!req_string) {
        cJSON_Delete(req_array);
        return NOSTR_WS_ERROR_MEMORY;
    }
    
    int result = nostr_ws_send_text(client, req_string);
    
    free(req_string);
    cJSON_Delete(req_array);
    
    return result;
}

int nostr_relay_send_event(nostr_ws_client_t* client, cJSON* event) {
    if (!client || !event) {
        return NOSTR_WS_ERROR_INVALID;
    }
    
    // Create EVENT message: ["EVENT", event] - duplicate event to avoid double-free
    cJSON* event_array = cJSON_CreateArray();
    cJSON_AddItemToArray(event_array, cJSON_CreateString("EVENT"));
    cJSON_AddItemToArray(event_array, cJSON_Duplicate(event, 1));
    
    char* event_string = cJSON_Print(event_array);
    if (!event_string) {
        cJSON_Delete(event_array);
        return NOSTR_WS_ERROR_MEMORY;
    }
    
    int result = nostr_ws_send_text(client, event_string);
    
    free(event_string);
    cJSON_Delete(event_array);
    
    return result;
}

int nostr_relay_send_close(nostr_ws_client_t* client, const char* subscription_id) {
    if (!client || !subscription_id) {
        return NOSTR_WS_ERROR_INVALID;
    }
    
    // Create CLOSE message: ["CLOSE", subscription_id]
    cJSON* close_array = cJSON_CreateArray();
    cJSON_AddItemToArray(close_array, cJSON_CreateString("CLOSE"));
    cJSON_AddItemToArray(close_array, cJSON_CreateString(subscription_id));
    
    char* close_string = cJSON_Print(close_array);
    if (!close_string) {
        cJSON_Delete(close_array);
        return NOSTR_WS_ERROR_MEMORY;
    }
    
    int result = nostr_ws_send_text(client, close_string);
    
    free(close_string);
    cJSON_Delete(close_array);
    
    return result;
}

int nostr_parse_relay_message(const char* message, char** message_type, cJSON** parsed_json) {
    if (!message || !message_type || !parsed_json) {
        return NOSTR_WS_ERROR_INVALID;
    }
    
    *message_type = NULL;
    *parsed_json = NULL;
    
    cJSON* json = cJSON_Parse(message);
    if (!json || !cJSON_IsArray(json)) {
        return NOSTR_WS_ERROR_PROTOCOL;
    }
    
    cJSON* type_item = cJSON_GetArrayItem(json, 0);
    if (!type_item || !cJSON_IsString(type_item)) {
        cJSON_Delete(json);
        return NOSTR_WS_ERROR_PROTOCOL;
    }
    
    *message_type = strdup(type_item->valuestring);
    *parsed_json = json;
    
    return NOSTR_WS_SUCCESS;
}

const char* nostr_ws_strerror(int error_code) {
    switch (error_code) {
        case NOSTR_WS_SUCCESS: return "Success";
        case NOSTR_WS_ERROR_INVALID: return "Invalid parameter";
        case NOSTR_WS_ERROR_NETWORK: return "Network error";
        case NOSTR_WS_ERROR_PROTOCOL: return "Protocol error";
        case NOSTR_WS_ERROR_MEMORY: return "Memory allocation error";
        case NOSTR_WS_ERROR_TLS: return "TLS error";
        default: return "Unknown error";
    }
}

// ============================================================================
// TCP Transport Implementation
// ============================================================================

static int tcp_connect(void* ctx, const char* host, int port) {
    tcp_transport_t* tcp = (tcp_transport_t*)ctx;
    
    // Create socket
    tcp->socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (tcp->socket_fd < 0) {
        return -1;
    }
    
    // Resolve hostname
    struct hostent* he = gethostbyname(host);
    if (!he) {
        close(tcp->socket_fd);
        tcp->socket_fd = -1;
        return -1;
    }
    
    // Set up address
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    memcpy(&addr.sin_addr, he->h_addr_list[0], he->h_length);
    
    // Connect
    if (connect(tcp->socket_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        close(tcp->socket_fd);
        tcp->socket_fd = -1;
        return -1;
    }
    
    return 0;
}

static int tcp_send(void* ctx, const void* data, size_t len) {
    tcp_transport_t* tcp = (tcp_transport_t*)ctx;
    return send(tcp->socket_fd, data, len, MSG_NOSIGNAL);
}

static int tcp_recv(void* ctx, void* data, size_t len, int timeout_ms) {
    tcp_transport_t* tcp = (tcp_transport_t*)ctx;
    
    if (timeout_ms > 0) {
        fd_set readfds;
        struct timeval tv;
        
        FD_ZERO(&readfds);
        FD_SET(tcp->socket_fd, &readfds);
        
        tv.tv_sec = timeout_ms / 1000;
        tv.tv_usec = (timeout_ms % 1000) * 1000;
        
        int result = select(tcp->socket_fd + 1, &readfds, NULL, NULL, &tv);
        if (result <= 0) return -1;
    }
    
    return recv(tcp->socket_fd, data, len, 0);
}

static int tcp_close(void* ctx) {
    tcp_transport_t* tcp = (tcp_transport_t*)ctx;
    if (tcp->socket_fd >= 0) {
        close(tcp->socket_fd);
        tcp->socket_fd = -1;
    }
    return 0;
}

static void tcp_cleanup(void* ctx) {
    tcp_close(ctx);
}

// ============================================================================
// TLS Transport Implementation (mbedTLS)
// ============================================================================

// Custom certificate verification callback (accept all certificates for NOSTR)
static int verify_cert(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags) {
    (void)data; (void)crt; (void)depth;
    *flags = 0; // Clear all verification flags (accept any certificate)
    return 0;
}

// Custom send function for mbedTLS
static int mbedtls_send(void *ctx, const unsigned char *buf, size_t len) {
    int sock_fd = *((int*)ctx);
    ssize_t sent = send(sock_fd, buf, len, MSG_NOSIGNAL);
    if (sent < 0) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            return MBEDTLS_ERR_SSL_WANT_WRITE;
        }
        return MBEDTLS_ERR_NET_SEND_FAILED;
    }
    return sent;
}

// Custom receive function for mbedTLS
static int mbedtls_recv(void *ctx, unsigned char *buf, size_t len) {
    int sock_fd = *((int*)ctx);
    ssize_t received = recv(sock_fd, buf, len, 0);
    if (received < 0) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            return MBEDTLS_ERR_SSL_WANT_READ;
        }
        return MBEDTLS_ERR_NET_RECV_FAILED;
    }
    if (received == 0) {
        return MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY;
    }
    return received;
}

static int tls_connect(void* ctx, const char* host, int port) {
    tls_transport_t* tls = (tls_transport_t*)ctx;
    int ret;
    
    // First establish TCP connection
    tcp_transport_t tcp_ctx = { .socket_fd = -1 };
    if (tcp_connect(&tcp_ctx, host, port) != 0) {
        return -1;
    }
    tls->socket_fd = tcp_ctx.socket_fd;
    
    // Initialize PSA crypto (required for this version of mbedTLS)
    psa_status_t psa_status = psa_crypto_init();
    if (psa_status != PSA_SUCCESS) {
        goto cleanup;
    }
    
    // Initialize mbedTLS structures
    mbedtls_ssl_init(&tls->ssl);
    mbedtls_ssl_config_init(&tls->conf);
    mbedtls_entropy_init(&tls->entropy);
    mbedtls_ctr_drbg_init(&tls->ctr_drbg);
    
    // Seed the random number generator
    const char *pers = "nostr_ws_client";
    if ((ret = mbedtls_ctr_drbg_seed(&tls->ctr_drbg, mbedtls_entropy_func, &tls->entropy,
                               (const unsigned char *)pers, strlen(pers))) != 0) {
        goto cleanup;
    }
    
    // Set up SSL configuration
    if ((ret = mbedtls_ssl_config_defaults(&tls->conf,
                    MBEDTLS_SSL_IS_CLIENT,
                    MBEDTLS_SSL_TRANSPORT_STREAM,
                    MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
        goto cleanup;
    }
    
    // RNG is configured automatically through mbedtls_ssl_config_defaults
    // The CTR_DRBG we initialized should be used automatically
    
    // Disable certificate verification for NOSTR (we only need encryption)
    mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
    mbedtls_ssl_conf_verify(&tls->conf, verify_cert, NULL);
    
    // Set up SSL context
    if ((ret = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
        goto cleanup;
    }
    
    // Set hostname for SNI
    if ((ret = mbedtls_ssl_set_hostname(&tls->ssl, host)) != 0) {
        goto cleanup;
    }
    
    // Set I/O functions
    mbedtls_ssl_set_bio(&tls->ssl, &tls->socket_fd, mbedtls_send, mbedtls_recv, NULL);
    
    // Perform TLS handshake
    while ((ret = mbedtls_ssl_handshake(&tls->ssl)) != 0) {
        if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
            goto cleanup;
        }
    }
    
    tls->ssl_connected = 1;
    return 0;
    
cleanup:
    mbedtls_ssl_free(&tls->ssl);
    mbedtls_ssl_config_free(&tls->conf);
    mbedtls_ctr_drbg_free(&tls->ctr_drbg);
    mbedtls_entropy_free(&tls->entropy);
    close(tls->socket_fd);
    tls->socket_fd = -1;
    return -1;
}

static int tls_send(void* ctx, const void* data, size_t len) {
    tls_transport_t* tls = (tls_transport_t*)ctx;
    if (!tls->ssl_connected) return -1;
    
    int ret = mbedtls_ssl_write(&tls->ssl, (const unsigned char*)data, len);
    if (ret < 0) {
        if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
            return -1; // Would block
        }
        return -1;
    }
    return ret;
}

static int tls_recv(void* ctx, void* data, size_t len, int timeout_ms) {
    tls_transport_t* tls = (tls_transport_t*)ctx;
    if (!tls->ssl_connected) {
        return -1;
    }
    
    // Check if mbedTLS has pending data first - this avoids unnecessary select() calls
    size_t pending = mbedtls_ssl_get_bytes_avail(&tls->ssl);
    if (pending == 0 && timeout_ms > 0) {
        // Only use select() if no data is pending in SSL buffers
        fd_set readfds;
        struct timeval tv;
        
        FD_ZERO(&readfds);
        FD_SET(tls->socket_fd, &readfds);
        
        tv.tv_sec = timeout_ms / 1000;
        tv.tv_usec = (timeout_ms % 1000) * 1000;
        
        int result = select(tls->socket_fd + 1, &readfds, NULL, NULL, &tv);
        if (result < 0) {
            return -1;
        } else if (result == 0) {
            return -1; // Timeout
        }
    }
    
    // Retry loop for SSL reads that want more data
    int attempts = 0;
    const int max_attempts = 10;
    
    while (attempts < max_attempts) {
        int ret = mbedtls_ssl_read(&tls->ssl, (unsigned char*)data, len);
        
        if (ret >= 0) {
            return ret; // Success or clean close
        }
        
        if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
            // Check if more data is available on socket
            fd_set readfds;
            struct timeval tv = {0, 100000}; // 100ms timeout
            
            FD_ZERO(&readfds);
            FD_SET(tls->socket_fd, &readfds);
            
            int select_result = select(tls->socket_fd + 1, &readfds, NULL, NULL, &tv);
            if (select_result <= 0) {
                return -1;
            }
            
            attempts++;
            continue; // Retry the SSL read
            
        } else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
            return -1;
            
        } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
            return 0; // Clean close
            
        } else if (ret == -0x7b00) { // MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET
            attempts++;
            continue; // This is normal, keep trying to read application data
            
        } else {
            return -1;
        }
    }
    return -1;
}

static int tls_close(void* ctx) {
    tls_transport_t* tls = (tls_transport_t*)ctx;
    
    if (tls->ssl_connected) {
        mbedtls_ssl_close_notify(&tls->ssl);
        tls->ssl_connected = 0;
    }
    
    if (tls->socket_fd >= 0) {
        close(tls->socket_fd);
        tls->socket_fd = -1;
    }
    
    return 0;
}

static void tls_cleanup(void* ctx) {
    tls_transport_t* tls = (tls_transport_t*)ctx;
    
    tls_close(ctx);
    
    mbedtls_ssl_free(&tls->ssl);
    mbedtls_ssl_config_free(&tls->conf);
    mbedtls_ctr_drbg_free(&tls->ctr_drbg);
    mbedtls_entropy_free(&tls->entropy);
}

// ============================================================================
// Internal Helper Functions
// ============================================================================

static int ws_parse_url(const char* url, char** host, int* port, char** path, int* use_tls) {
    if (!url || !host || !port || !path || !use_tls) return -1;
    
    *host = NULL;
    *path = NULL;
    *use_tls = 0;
    
    // Check protocol
    if (strncmp(url, "ws://", 5) == 0) {
        *use_tls = 0;
        url += 5;
        *port = 80;
    } else if (strncmp(url, "wss://", 6) == 0) {
        *use_tls = 1;
        url += 6;
        *port = 443;
    } else {
        return -1;
    }
    
    // Find path separator
    const char* path_start = strchr(url, '/');
    if (path_start) {
        *path = strdup(path_start);
    } else {
        *path = strdup("/");
    }
    
    // Extract host and port
    const char* port_start = strchr(url, ':');
    if (port_start && (!path_start || port_start < path_start)) {
        // Port specified
        size_t host_len = port_start - url;
        *host = strndup(url, host_len);
        *port = atoi(port_start + 1);
    } else {
        // No port specified
        size_t host_len = path_start ? (size_t)(path_start - url) : strlen(url);
        *host = strndup(url, host_len);
    }
    
    return 0;
}

static int ws_create_handshake_key(char* key_out, size_t key_size) {
    if (key_size < WS_KEY_LEN + 1) return -1;
    
    // Generate random 16 bytes
    unsigned char random_bytes[16];
    for (int i = 0; i < 16; i++) {
        random_bytes[i] = rand() & 0xFF;
    }
    
    // Base64 encode
    static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
    for (int i = 0; i < 16; i += 3) {
        int b = (random_bytes[i] << 16) | 
                (i + 1 < 16 ? random_bytes[i + 1] << 8 : 0) |
                (i + 2 < 16 ? random_bytes[i + 2] : 0);
        
        key_out[(i / 3) * 4 + 0] = base64_chars[(b >> 18) & 0x3F];
        key_out[(i / 3) * 4 + 1] = base64_chars[(b >> 12) & 0x3F];
        key_out[(i / 3) * 4 + 2] = base64_chars[(b >> 6) & 0x3F];
        key_out[(i / 3) * 4 + 3] = base64_chars[b & 0x3F];
    }
    
    key_out[WS_KEY_LEN] = '\0';
    return 0;
}

static int ws_perform_handshake(nostr_ws_client_t* client, const char* key) {
    // Build HTTP upgrade request
    char request[MAX_HEADER_SIZE];
    int len = snprintf(request, sizeof(request),
        "GET %s HTTP/1.1\r\n"
        "Host: %s:%d\r\n"
        "Upgrade: websocket\r\n"
        "Connection: Upgrade\r\n"
        "Sec-WebSocket-Key: %s\r\n"
        "Sec-WebSocket-Version: 13\r\n"
        "\r\n",
        client->path, client->host, client->port, key);
    
    if ((size_t)len >= sizeof(request)) {
        return -1;
    }
    
    // Send handshake request
    int sent = client->transport->send(&client->transport_ctx, request, len);
    if (sent != len) {
        return -1;
    }
    
    // Small delay to allow server to process
    usleep(100000); // 100ms
    
    // Read response
    char response[MAX_HEADER_SIZE];
    int total_received = 0;
    
    while ((size_t)total_received < sizeof(response) - 1) {
        int received = client->transport->recv(&client->transport_ctx, 
                                             response + total_received, 
                                             sizeof(response) - total_received - 1, 
                                             client->timeout_ms);
        if (received <= 0) {
            return -1;
        }
        
        total_received += received;
        response[total_received] = '\0';
        
        // Check if we have complete headers
        if (strstr(response, "\r\n\r\n")) break;
    }
    
    // Check if response starts with the correct HTTP status line
    if (strncmp(response, "HTTP/1.1 101 Switching Protocols\r\n", 34) != 0) {
        return -1;
    }
    
    if (!strstr(response, "Upgrade: websocket") && !strstr(response, "upgrade: websocket")) {
        return -1;
    }
    
    return 0;
}

static int ws_send_frame(nostr_ws_client_t* client, ws_opcode_t opcode, const char* payload, size_t payload_len) {
    if (!client) return -1;
    
    char frame[MAX_FRAME_SIZE];
    size_t frame_len = 0;
    
    
    // First byte: FIN=1, opcode
    frame[0] = 0x80 | (opcode & 0x0F);
    frame_len = 1;
    
    // Payload length and masking
    uint32_t mask = ws_generate_mask();
    
    if (payload_len < 126) {
        frame[1] = 0x80 | (payload_len & 0x7F); // MASK=1, length
        frame_len = 2;
    } else if (payload_len < 65536) {
        frame[1] = 0x80 | 126; // MASK=1, length=126
        frame[2] = (payload_len >> 8) & 0xFF;
        frame[3] = payload_len & 0xFF;
        frame_len = 4;
    } else {
        // Should not happen with our MAX_FRAME_SIZE
        return -1;
    }
    
    // Add mask
    frame[frame_len++] = (mask >> 24) & 0xFF;
    frame[frame_len++] = (mask >> 16) & 0xFF;
    frame[frame_len++] = (mask >> 8) & 0xFF;
    frame[frame_len++] = mask & 0xFF;
    
    // Add payload (masked)
    if (payload && payload_len > 0) {
        memcpy(frame + frame_len, payload, payload_len);
        ws_mask_payload(frame + frame_len, payload_len, mask);
        frame_len += payload_len;
    }
    
    // Log outgoing message to debug.log
#if defined(ENABLE_FILE_LOGGING) && defined(ENABLE_WEBSOCKET_LOGGING)
    if (opcode == WS_OPCODE_TEXT && payload && payload_len > 0) {
        debug_log_message("SEND", client->host, client->port, payload);
    }
#endif
    
    // Send frame
    int result = client->transport->send(&client->transport_ctx, frame, frame_len);
    
    return result == (int)frame_len ? 0 : -1;
}

static int ws_receive_frame(nostr_ws_client_t* client, ws_opcode_t* opcode, char* payload, size_t* payload_len, int timeout_ms) {
    if (!client || !opcode || !payload || !payload_len) return -1;
    
    char header[14]; // Max header size
    size_t header_len = 2; // Minimum header size
    
    // Read basic header
    int header_result = client->transport->recv(&client->transport_ctx, header, 2, timeout_ms);
    
    if (header_result != 2) {
        return -1;
    }
    
    // Parse header
    *opcode = (ws_opcode_t)(header[0] & 0x0F);
    uint8_t masked = (header[1] & 0x80) != 0;
    uint64_t len = header[1] & 0x7F;
    
    // Extended length
    if (len == 126) {  
        if (client->transport->recv(&client->transport_ctx, header + 2, 2, timeout_ms) != 2) {
            return -1;
        }
        len = ((uint16_t)header[2] << 8) | (uint8_t)header[3];
        header_len = 4;
    } else if (len == 127) {
        if (client->transport->recv(&client->transport_ctx, header + 2, 8, timeout_ms) != 8) {
            return -1;
        }
        // For simplicity, we don't support 64-bit lengths
        len = ((uint32_t)header[6] << 24) | ((uint32_t)header[7] << 16) |
              ((uint32_t)header[8] << 8) | header[9];
        header_len = 10;
    }
    
    // Check payload length
    if (len > *payload_len) {
        return -1;
    }
    
    // Read mask (if present)
    uint32_t mask = 0;
    if (masked) {
        if (client->transport->recv(&client->transport_ctx, header + header_len, 4, timeout_ms) != 4) {
            return -1;
        }
        mask = ((uint32_t)header[header_len] << 24) |
               ((uint32_t)header[header_len + 1] << 16) |
               ((uint32_t)header[header_len + 2] << 8) |
               header[header_len + 3];
        header_len += 4;
    }
    
    // Read payload
    if (len > 0) {
        size_t received = 0;
        while (received < len) {
            int chunk = client->transport->recv(&client->transport_ctx, 
                                              payload + received, 
                                              len - received, 
                                              timeout_ms);
            if (chunk <= 0) return -1;
            received += chunk;
        }
        
        // Unmask payload if needed
        if (masked) {
            ws_mask_payload(payload, len, mask);
        }
        
        // Log incoming text messages to debug.log
#if defined(ENABLE_FILE_LOGGING) && defined(ENABLE_WEBSOCKET_LOGGING)
        if (*opcode == WS_OPCODE_TEXT && len > 0) {
            // Null terminate for logging
            char temp_payload[len + 1];
            memcpy(temp_payload, payload, len);
            temp_payload[len] = '\0';
            debug_log_message("RECV", client->host, client->port, temp_payload);
        }
#endif
    }
    
    *payload_len = len;
    return 0;
}

static void ws_mask_payload(char* payload, size_t len, uint32_t mask) {
    unsigned char mask_bytes[4] = {
        (mask >> 24) & 0xFF,
        (mask >> 16) & 0xFF,
        (mask >> 8) & 0xFF,
        mask & 0xFF
    };
    
    for (size_t i = 0; i < len; i++) {
        payload[i] ^= mask_bytes[i % 4];
    }
}

static uint32_t ws_generate_mask(void) {
    return ((uint32_t)rand() << 16) | ((uint32_t)rand() & 0xFFFF);
}

// ============================================================================
// Debug Logging Functions
// ============================================================================

#if defined(ENABLE_FILE_LOGGING) && defined(ENABLE_WEBSOCKET_LOGGING)
static void debug_log_init(void) {
    if (!debug_log_file) {
        debug_log_file = fopen("debug.log", "a");
        if (debug_log_file) {
            fprintf(debug_log_file, "\n=== NOSTR WebSocket Debug Log Started ===\n");
            fflush(debug_log_file);
        }
    }
}


static const char* get_timestamp(void) {
    static char timestamp[32];
    struct timespec ts;
    struct tm *timeinfo;
    
    clock_gettime(CLOCK_REALTIME, &ts);
    timeinfo = localtime(&ts.tv_sec);
    
    // Format: HH:MM:SS.mmm (with milliseconds)
    strftime(timestamp, sizeof(timestamp), "%H:%M:%S", timeinfo);
    snprintf(timestamp + 8, sizeof(timestamp) - 8, ".%03ld", ts.tv_nsec / 1000000);
    
    return timestamp;
}

static void debug_log_message(const char* direction, const char* host, int port, const char* message) {
    debug_log_init();
    
    if (debug_log_file) {
        fprintf(debug_log_file, "[%s] %s %s:%d: %s\n", 
                get_timestamp(), direction, host, port, message);
        fflush(debug_log_file);
    }
}
#endif
