// Define _GNU_SOURCE to ensure all POSIX features are available
#define _GNU_SOURCE

// API module for serving embedded web content and NIP-17 admin messaging
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <libwebsockets.h>
#include "api.h"
#include "embedded_web_content.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 database functions
int store_event(cJSON* event);

// Handle HTTP request for embedded files (assumes GET)
int handle_embedded_file_request(struct lws* wsi, const char* requested_uri) {
    const char* file_path;

    // Handle /api requests
    char temp_path[256];
    if (strcmp(requested_uri, "/api") == 0) {
        // /api -> serve index.html
        file_path = "/";
    } else if (strncmp(requested_uri, "/api/", 5) == 0) {
        // Extract file path from /api/ prefix and add leading slash for lookup
        snprintf(temp_path, sizeof(temp_path), "/%s", requested_uri + 5); // Add leading slash
        file_path = temp_path;
    } else {
        log_warning("Embedded file request without /api prefix");
        lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
        return -1;
    }

    // Get embedded file
    embedded_file_t* file = get_embedded_file(file_path);
    if (!file) {
        log_warning("Embedded file not found");
        lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
        return -1;
    }

    // Allocate session data
    struct embedded_file_session_data* session_data = malloc(sizeof(struct embedded_file_session_data));
    if (!session_data) {
        log_error("Failed to allocate embedded file session data");
        return -1;
    }

    session_data->type = 1; // Embedded file
    session_data->data = file->data;
    session_data->size = file->size;
    session_data->content_type = file->content_type;
    session_data->headers_sent = 0;
    session_data->body_sent = 0;

    // Store session data
    lws_set_wsi_user(wsi, session_data);

    // Prepare HTTP response headers
    unsigned char buf[LWS_PRE + 1024];
    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_OK, &p, end)) {
        free(session_data);
        return -1;
    }

    if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char*)file->content_type, strlen(file->content_type), &p, end)) {
        free(session_data);
        return -1;
    }

    if (lws_add_http_header_content_length(wsi, file->size, &p, end)) {
        free(session_data);
        return -1;
    }

    // Add CORS headers (same as NIP-11 for consistency)
    if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-origin:", (unsigned char*)"*", 1, &p, end)) {
        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);
        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);
        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);
        return -1;
    }

    if (lws_finalize_http_header(wsi, &p, end)) {
        free(session_data);
        return -1;
    }

    // Write headers
    if (lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS) < 0) {
        free(session_data);
        return -1;
    }

    session_data->headers_sent = 1;

    // Request callback for body transmission
    lws_callback_on_writable(wsi);

    return 0;
}

// Handle HTTP_WRITEABLE for embedded files
int handle_embedded_file_writeable(struct lws* wsi) {
    struct embedded_file_session_data* session_data = (struct embedded_file_session_data*)lws_wsi_user(wsi);
    if (!session_data || session_data->headers_sent == 0 || session_data->body_sent == 1) {
        return 0;
    }

    // Allocate buffer for data transmission
    unsigned char *buf = malloc(LWS_PRE + session_data->size);
    if (!buf) {
        log_error("Failed to allocate buffer for embedded file transmission");
        free(session_data);
        lws_set_wsi_user(wsi, NULL);
        return -1;
    }

    // Copy data to buffer
    memcpy(buf + LWS_PRE, session_data->data, session_data->size);

    // Write data
    int write_result = lws_write(wsi, buf + LWS_PRE, session_data->size, LWS_WRITE_HTTP);

    // Free the transmission buffer
    free(buf);

    if (write_result < 0) {
        log_error("Failed to write embedded file data");
        free(session_data);
        lws_set_wsi_user(wsi, NULL);
        return -1;
    }

    // Mark as sent and clean up
    session_data->body_sent = 1;
    free(session_data);
    lws_set_wsi_user(wsi, NULL);

    return 0;
}
