/*
 * Ginxsom Blossom Server - FastCGI Application
 * Handles HEAD requests and other dynamic operations
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <fcgi_stdio.h>
#include <sqlite3.h>
#include <sys/stat.h>
#include <time.h>
#include <stdint.h>
#include <curl/curl.h>
#include "ginxsom.h"
#include "admin_api.h"
#include "../nostr_core_lib/nostr_core/request_validator.h"

// Debug macros removed

#define MAX_SHA256_LEN 65
#define MAX_PATH_LEN 512
#define MAX_MIME_LEN 128

// Database path
#define DB_PATH "db/ginxsom.db"

// BUD-06 X-Reason header constants
#define XREASON_MISSING_SHA256 "Missing required X-SHA-256 header"
#define XREASON_INVALID_SHA256 "X-SHA-256 must be 64 hex characters"
#define XREASON_MISSING_LENGTH "Missing required X-Content-Length header"
#define XREASON_INVALID_LENGTH "X-Content-Length must be a positive integer"
#define XREASON_FILE_TOO_LARGE "File size exceeds maximum allowed (100MB)"
#define XREASON_ZERO_LENGTH "File size cannot be zero"
#define XREASON_BLOB_EXISTS "Blob with this hash already exists"
#define XREASON_UNSUPPORTED_TYPE "Content type not supported by server policy"
#define XREASON_AUTH_REQUIRED "Authorization required for upload"
#define XREASON_AUTH_INVALID "Invalid or expired authorization"

// Forward declarations for config system
int initialize_server_config(void);
int apply_config_from_event(cJSON* event);
int get_config_file_path(char* path, size_t path_size);
int load_server_config(const char* config_path);
int run_interactive_setup(const char* config_path);

// Configuration system implementation
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

// Server configuration structure
typedef struct {
    char admin_pubkey[256];
    char admin_enabled[8];
    int config_loaded;
} server_config_t;

// Global configuration instance
static server_config_t g_server_config = {0};

// Global server private key (stored in memory only for security)
static char server_private_key[128] = {0};

// Function to get XDG config directory
const char* get_config_dir(char* buffer, size_t buffer_size) {
    const char* xdg_config = getenv("XDG_CONFIG_HOME");
    if (xdg_config) {
        snprintf(buffer, buffer_size, "%s/ginxsom", xdg_config);
        return buffer;
    }

    const char* home = getenv("HOME");
    if (home) {
        snprintf(buffer, buffer_size, "%s/.config/ginxsom", home);
        return buffer;
    }

    // Fallback
    return ".config/ginxsom";
}

// Load server configuration from database or create defaults
int initialize_server_config(void) {
    sqlite3* db = NULL;
    sqlite3_stmt* stmt = NULL;
    int rc;

    memset(&g_server_config, 0, sizeof(g_server_config));

    // Open database
    rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READONLY, NULL);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "CONFIG: Could not open database for config: %s\n", sqlite3_errmsg(db));
        // Config database doesn't exist - leave config uninitialized
        g_server_config.config_loaded = 0;
        return 0;
    }

    // Load admin_pubkey
    const char* sql = "SELECT value FROM server_config WHERE key = ?";
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        sqlite3_bind_text(stmt, 1, "admin_pubkey", -1, SQLITE_STATIC);
        rc = sqlite3_step(stmt);
        if (rc == SQLITE_ROW) {
            const char* value = (const char*)sqlite3_column_text(stmt, 0);
            if (value) {
                strncpy(g_server_config.admin_pubkey, value, sizeof(g_server_config.admin_pubkey) - 1);
            }
        }
        sqlite3_finalize(stmt);
    }

    // Load admin_enabled
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        sqlite3_bind_text(stmt, 1, "admin_enabled", -1, SQLITE_STATIC);
        rc = sqlite3_step(stmt);
        if (rc == SQLITE_ROW) {
            const char* value = (const char*)sqlite3_column_text(stmt, 0);
            if (value && strcmp(value, "true") == 0) {
                strcpy(g_server_config.admin_enabled, "true");
            } else {
                strcpy(g_server_config.admin_enabled, "false");
            }
        }
        sqlite3_finalize(stmt);
    }

    sqlite3_close(db);

    g_server_config.config_loaded = 1;
    fprintf(stderr, "CONFIG: Server configuration loaded\n");
    return 1;
}

// File-based configuration system
// Config file path resolution
int get_config_file_path(char* path, size_t path_size) {
    const char* home = getenv("HOME");
    const char* xdg_config = getenv("XDG_CONFIG_HOME");
    
    if (xdg_config) {
        snprintf(path, path_size, "%s/ginxsom/ginxsom_config_event.json", xdg_config);
    } else if (home) {
        snprintf(path, path_size, "%s/.config/ginxsom/ginxsom_config_event.json", home);
    } else {
        return 0;
    }
    return 1;
}

// Load and validate config event
int load_server_config(const char* config_path) {
    FILE* file = fopen(config_path, "r");
    if (!file) {
        return 0; // Config file doesn't exist
    }
    
    // Read entire file
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    fseek(file, 0, SEEK_SET);
    
    char* json_data = malloc(file_size + 1);
    if (!json_data) {
        fclose(file);
        return 0;
    }
    
    fread(json_data, 1, file_size, file);
    json_data[file_size] = '\0';
    fclose(file);
    
    // Parse and validate JSON event
    cJSON* event = cJSON_Parse(json_data);
    free(json_data);
    
    if (!event) {
        fprintf(stderr, "Invalid JSON in config file\n");
        return 0;
    }
    
    // Validate event structure and signature
    if (nostr_validate_event(event) != NOSTR_SUCCESS) {
        fprintf(stderr, "Invalid or corrupted config event\n");
        cJSON_Delete(event);
        return 0;
    }
    
    // Extract configuration and apply to server
    int result = apply_config_from_event(event);
    cJSON_Delete(event);
    
    return result;
}

// Extract config from validated event and apply to server
int apply_config_from_event(cJSON* event) {
    sqlite3* db;
    sqlite3_stmt* stmt;
    int rc;
    
    // Open database for config storage
    rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READWRITE, NULL);
    if (rc) {
        fprintf(stderr, "Failed to open database for config\n");
        return 0;
    }
    
    // Extract admin pubkey from event
    cJSON* pubkey_json = cJSON_GetObjectItem(event, "pubkey");
    if (!pubkey_json || !cJSON_IsString(pubkey_json)) {
        sqlite3_close(db);
        return 0;
    }
    const char* admin_pubkey = cJSON_GetStringValue(pubkey_json);
    
    // Store admin pubkey in database
    const char* insert_sql = "INSERT OR REPLACE INTO server_config (key, value, description) VALUES (?, ?, ?)";
    rc = sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        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, "Admin public key from config event", -1, SQLITE_STATIC);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);
    }
    
    // Extract server private key and store securely (in memory only)
    cJSON* tags = cJSON_GetObjectItem(event, "tags");
    if (tags && cJSON_IsArray(tags)) {
        cJSON* tag = NULL;
        cJSON_ArrayForEach(tag, tags) {
            if (!cJSON_IsArray(tag)) continue;
            
            cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
            cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
            
            if (!tag_name || !cJSON_IsString(tag_name) ||
                !tag_value || !cJSON_IsString(tag_value)) continue;
                
            const char* key = cJSON_GetStringValue(tag_name);
            const char* value = cJSON_GetStringValue(tag_value);
            
            if (strcmp(key, "server_privkey") == 0) {
                // Store server private key in global variable (memory only)
                strncpy(server_private_key, value, sizeof(server_private_key) - 1);
                server_private_key[sizeof(server_private_key) - 1] = '\0';
            } else {
                // Store other config values in database
                rc = sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL);
                if (rc == SQLITE_OK) {
                    sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
                    sqlite3_bind_text(stmt, 2, value, -1, SQLITE_STATIC);
                    sqlite3_bind_text(stmt, 3, "From config event", -1, SQLITE_STATIC);
                    sqlite3_step(stmt);
                    sqlite3_finalize(stmt);
                }
            }
        }
    }
    
    sqlite3_close(db);
    return 1;
}

// Interactive setup runner
int run_interactive_setup(const char* config_path) {
    printf("\n=== Ginxsom First-Time Setup Required ===\n");
    printf("No configuration found at: %s\n\n", config_path);
    printf("Options:\n");
    printf("1. Run interactive setup wizard\n");
    printf("2. Exit and create config manually\n");
    printf("Choice (1/2): ");
    
    char choice[10];
    if (!fgets(choice, sizeof(choice), stdin)) {
        return 1;
    }
    
    if (choice[0] == '1') {
        // Run setup script
        char script_path[512];
        snprintf(script_path, sizeof(script_path), "./scripts/setup.sh \"%s\"", config_path);
        return system(script_path);
    } else {
        printf("\nManual setup instructions:\n");
        printf("1. Run: ./scripts/generate_config.sh\n");
        printf("2. Place signed config at: %s\n", config_path);
        printf("3. Restart ginxsom\n");
        return 1;
    }
}

// Function declarations
void send_error_response(int status_code, const char* error_type, const char* message, const char* details);
void log_request(const char* method, const char* uri, const char* auth_status, int status_code);

// BUD-06 function declarations
void send_upload_error_response(int status_code, const char* error_type, const char* message, const char* x_reason);
void send_upload_success_response(const char* sha256, const char* content_type, long content_length);
int validate_sha256_format(const char* sha256);
int validate_content_length(const char* content_length_str, long* parsed_length);
int check_blob_exists(const char* sha256);
int validate_upload_headers(const char** sha256, const char** content_type, long* content_length, char* error_reason, size_t reason_size);
void handle_head_upload_request(void);

// BUD-08 NIP-94 function declarations
int nip94_is_enabled(void);
int nip94_get_origin(char* out, size_t out_size);
const char* mime_to_extension(const char* mime_type);
void nip94_build_blob_url(const char* origin, const char* sha256, const char* mime_type, char* out, size_t out_size);
int parse_png_dimensions(const unsigned char* data, size_t size, int* width, int* height);
int parse_jpeg_dimensions(const unsigned char* data, size_t size, int* width, int* height);
int parse_webp_dimensions(const unsigned char* data, size_t size, int* width, int* height);
int nip94_get_dimensions(const unsigned char* data, size_t size, const char* mime_type, int* width, int* height);
void nip94_emit_field(const char* url, const char* mime, const char* sha256, long size, int width, int height);

// BUD-09 Blob Report function declarations
int validate_report_event_structure(cJSON* event);
int extract_blob_hashes_from_report(cJSON* event, char blob_hashes[][65], int max_hashes);
int validate_report_types(cJSON* event);
int store_blob_report(const char* event_json, const char* reporter_pubkey);
void handle_report_request(void);

// Blob metadata structure
typedef struct {
    char sha256[MAX_SHA256_LEN];
    long size;
    char type[MAX_MIME_LEN];
    long uploaded_at;
    char filename[256];
    int found;
} blob_metadata_t;

// Insert blob metadata into database
int insert_blob_metadata(const char* sha256, long size, const char* type, 
                         long uploaded_at, const char* uploader_pubkey, 
                         const char* filename) {
    sqlite3* db;
    sqlite3_stmt* stmt;
    int rc;
    
    rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READWRITE, NULL);
    if (rc) {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        return 0;
    }
    
    const char* sql = "INSERT INTO blobs (sha256, size, type, uploaded_at, uploader_pubkey, filename) VALUES (?, ?, ?, ?, ?, ?)";
    
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 0;
    }
    
    // Bind parameters
    sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 2, size);
    sqlite3_bind_text(stmt, 3, type, -1, SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 4, uploaded_at);
    if (uploader_pubkey) {
        sqlite3_bind_text(stmt, 5, uploader_pubkey, -1, SQLITE_STATIC);
    } else {
        sqlite3_bind_null(stmt, 5);
    }
    if (filename) {
        sqlite3_bind_text(stmt, 6, filename, -1, SQLITE_STATIC);
    } else {
        sqlite3_bind_null(stmt, 6);
    }
    
    rc = sqlite3_step(stmt);
    
    int success = 0;
    if (rc == SQLITE_DONE) {
        success = 1;
    } else if (rc == SQLITE_CONSTRAINT) {
        // This is actually OK - blob already exists with same hash
        success = 1;
    } else {
        success = 0;
    }
    
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    return success;
}

// Get blob metadata from database
int get_blob_metadata(const char* sha256, blob_metadata_t* metadata) {
    sqlite3* db;
    sqlite3_stmt* stmt;
    int rc;
    


    
    rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READONLY, NULL);
    if (rc) {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        return 0;
    }
    
    const char* sql = "SELECT sha256, size, type, uploaded_at, filename FROM blobs WHERE sha256 = ?";
    
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 0;
    }
    
    sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    
    if (rc == SQLITE_ROW) {
        strncpy(metadata->sha256, (char*)sqlite3_column_text(stmt, 0), MAX_SHA256_LEN-1);
        metadata->size = sqlite3_column_int64(stmt, 1);
        strncpy(metadata->type, (char*)sqlite3_column_text(stmt, 2), MAX_MIME_LEN-1);
        metadata->uploaded_at = sqlite3_column_int64(stmt, 3);
        const char* filename = (char*)sqlite3_column_text(stmt, 4);
        if (filename) {
            strncpy(metadata->filename, filename, 255);
        } else {
            metadata->filename[0] = '\0';
        }
        metadata->found = 1;
    } else {
        metadata->found = 0;
    }
    
    sqlite3_finalize(stmt);
    sqlite3_close(db);

    return metadata->found;
}

// Check if physical file exists (with extension based on MIME type)
int file_exists_with_type(const char* sha256, const char* mime_type) {
    char filepath[MAX_PATH_LEN];
    const char* extension = mime_to_extension(mime_type);
    
    snprintf(filepath, sizeof(filepath), "blobs/%s%s", sha256, extension);
    

    
    struct stat st;
    int result = stat(filepath, &st);
    
    if (result == 0) {
        return 1;
    } else {
        return 0;
    }
}

// Handle HEAD request for blob
void handle_head_request(const char* sha256) {
    blob_metadata_t metadata = {0};
    

    
    // Validate SHA-256 format (64 hex characters)
    if (strlen(sha256) != 64) {
        printf("Status: 400 Bad Request\r\n");
        printf("Content-Type: text/plain\r\n\r\n");
        printf("Invalid SHA-256 hash format\n");
        return;
    }
    
    // Check if blob exists in database - this is the single source of truth
    if (!get_blob_metadata(sha256, &metadata)) {
        printf("Status: 404 Not Found\r\n");
        printf("Content-Type: text/plain\r\n\r\n");
        printf("Blob not found\n");
        return;
    }
    
    // Return successful HEAD response with metadata from database
    printf("Status: 200 OK\r\n");
    printf("Content-Type: %s\r\n", metadata.type);
    printf("Content-Length: %ld\r\n", metadata.size);
    printf("Cache-Control: public, max-age=31536000, immutable\r\n");
    printf("ETag: \"%s\"\r\n", metadata.sha256);
    
    // Add timing header for debugging
    printf("X-Ginxsom-Server: FastCGI\r\n");
    printf("X-Ginxsom-Timestamp: %ld\r\n", time(NULL));
    
    if (strlen(metadata.filename) > 0) {
        printf("X-Original-Filename: %s\r\n", metadata.filename);
    }
    
    printf("\r\n");
    // HEAD request - no body content
}

// Extract SHA-256 from request URI (Blossom compliant - ignores any extension)
const char* extract_sha256_from_uri(const char* uri) {
    static char sha256_buffer[MAX_SHA256_LEN];
    
    if (!uri || uri[0] != '/') {
        return NULL;
    }
    
    const char* start = uri + 1; // Skip leading '/'
    
    // Extract exactly 64 hex characters, ignoring anything after (extensions, etc.)
    int len = 0;
    for (int i = 0; i < 64 && start[i] != '\0'; i++) {
        char c = start[i];
        if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
            // If we hit a non-hex character before 64 chars, it's invalid
            if (len < 64) {
                return NULL;
            }
            break;
        }
        sha256_buffer[i] = c;
        len = i + 1;
    }
    
    // Must be exactly 64 hex characters
    if (len != 64) {
        return NULL;
    }
    
    sha256_buffer[64] = '\0';
    return sha256_buffer;
}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             BUD 02 - Upload & Authentication
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

// Parse Authorization header and extract JSON event
int parse_authorization_header(const char* auth_header, char* event_json, size_t json_size) {
    if (!auth_header || !event_json) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Check for "Nostr " prefix (case-insensitive)
    const char* prefix = "nostr ";
    size_t prefix_len = strlen(prefix);
    
    if (strncasecmp(auth_header, prefix, prefix_len) != 0) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Extract base64 encoded event after "Nostr "
    const char* base64_event = auth_header + prefix_len;

    
    // Decode base64 to JSON using nostr_core_lib base64 decode
    unsigned char decoded_buffer[4096];
    size_t decoded_len = base64_decode(base64_event, decoded_buffer);
    

    
    if (decoded_len == 0) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    if (decoded_len >= json_size) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Copy decoded JSON to output buffer
    memcpy(event_json, decoded_buffer, decoded_len);
    event_json[decoded_len] = '\0';
    

    return NOSTR_SUCCESS;
}

// Validate Blossom-specific event requirements (kind 24242)
int validate_blossom_event(cJSON* event, const char* expected_hash, const char* method) {
    if (!event) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    

    
    // Check event kind (must be 24242 for Blossom uploads)
    cJSON* kind_json = cJSON_GetObjectItem(event, "kind");
    if (!kind_json || !cJSON_IsNumber(kind_json)) {

        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    
    int kind = cJSON_GetNumberValue(kind_json);
    if (kind != 24242) {

        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    
    // Check that created_at exists (basic validation)
    cJSON* created_at_json = cJSON_GetObjectItem(event, "created_at");
    if (!created_at_json || !cJSON_IsNumber(created_at_json)) {

        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    
    // Look for expiration in tags
    cJSON* tags = cJSON_GetObjectItem(event, "tags");
    if (!tags || !cJSON_IsArray(tags)) {

        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    
    time_t expiration = 0;
    int found_method = 0;
    int found_hash = 0;
    
    // Parse tags for 't' (method), 'x' (hash), and 'expiration'
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags) {
        if (!cJSON_IsArray(tag)) continue;
        
        cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
        if (!tag_name || !cJSON_IsString(tag_name)) continue;
        
        const char* tag_name_str = cJSON_GetStringValue(tag_name);
        
        if (strcmp(tag_name_str, "t") == 0) {
            // Method tag
            cJSON* method_value = cJSON_GetArrayItem(tag, 1);
            if (method_value && cJSON_IsString(method_value)) {
                const char* event_method = cJSON_GetStringValue(method_value);
                if (strcmp(event_method, method) == 0) {
                    found_method = 1;

                }
            }
        } else if (strcmp(tag_name_str, "x") == 0) {
            // Hash tag
            cJSON* hash_value = cJSON_GetArrayItem(tag, 1);
            if (hash_value && cJSON_IsString(hash_value)) {
                const char* event_hash = cJSON_GetStringValue(hash_value);
                if (expected_hash && strcmp(event_hash, expected_hash) == 0) {
                    found_hash = 1;

                }
            }
        } else if (strcmp(tag_name_str, "expiration") == 0) {
            // Expiration tag
            cJSON* exp_value = cJSON_GetArrayItem(tag, 1);
            if (exp_value && cJSON_IsString(exp_value)) {
                expiration = (time_t)atol(cJSON_GetStringValue(exp_value));

            }
        }
    }
    
    // Check if method matches (required)
    if (!found_method) {

        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    
    // Check if hash matches (if provided)
    if (expected_hash && !found_hash) {

        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    
    // Check expiration
    time_t now = time(NULL);
    if (expiration > 0 && now > expiration) {

        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    

    return NOSTR_SUCCESS;
}

// Forward declarations for detailed validation functions
int detailed_structure_validation(cJSON* event);
int detailed_signature_validation(cJSON* event);
void analyze_event_fields(cJSON* event);
void hex_dump(const char* label, const unsigned char* data, size_t len);

// Detailed structure validation function removed (debug version)

// Debug functions removed (detailed_signature_validation, analyze_event_fields, hex_dump)

// Main authentication function - simplified version without debug logging
int authenticate_request(const char* auth_header, const char* method, const char* file_hash) {
    if (!auth_header) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Parse authorization header
    char event_json[4096];
    int parse_result = parse_authorization_header(auth_header, event_json, sizeof(event_json));
    if (parse_result != NOSTR_SUCCESS) {
        return parse_result;
    }
    
    // Parse JSON event
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    
    // Test structure validation first
    int structure_result = nostr_validate_event_structure(event);
    if (structure_result != NOSTR_SUCCESS) {
        cJSON_Delete(event);
        return structure_result;
    }
    
    // Test crypto validation
    int crypto_result = nostr_verify_event_signature(event);
    if (crypto_result != NOSTR_SUCCESS) {
        cJSON_Delete(event);
        return crypto_result;
    }
    
    // Test complete validation
    int validation_result = nostr_validate_event(event);
    if (validation_result != NOSTR_SUCCESS) {
        cJSON_Delete(event);
        return validation_result;
    }
    
    // Validate Blossom-specific requirements
    int blossom_result = validate_blossom_event(event, file_hash, method);
    if (blossom_result != NOSTR_SUCCESS) {
        cJSON_Delete(event);
        return blossom_result;
    }
    
    cJSON_Delete(event);
    return NOSTR_SUCCESS;
}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             AUTHENTICATION RULES SYSTEM (4.1.2)
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

// Old authentication system has been replaced with nostr_core_lib unified request validation
// All authentication rules and cache functionality now handled by nostr_validate_request()

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             BUD 04 - Mirroring
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

// HTTP download response structure
typedef struct {
    unsigned char* data;
    size_t size;
    char content_type[128];
    long http_code;
    size_t capacity;
} mirror_download_t;

// CURL write callback for collecting response data
static size_t write_callback(void* contents, size_t size, size_t nmemb, mirror_download_t* response) {
    size_t realsize = size * nmemb;
    
    if (!response) return 0;
    
    // Check if we need to expand buffer
    if (response->size + realsize >= response->capacity) {
        size_t new_capacity = response->capacity == 0 ? 8192 : response->capacity * 2;
        while (new_capacity < response->size + realsize + 1) {
            new_capacity *= 2;
        }
        
        unsigned char* new_data = realloc(response->data, new_capacity);
        if (!new_data) {
            return 0; // Out of memory
        }
        
        response->data = new_data;
        response->capacity = new_capacity;
    }
    
    memcpy(response->data + response->size, contents, realsize);
    response->size += realsize;
    response->data[response->size] = '\0'; // Null terminate for safety
    
    return realsize;
}

// CURL header callback for collecting Content-Type
static size_t header_callback(char* buffer, size_t size, size_t nitems, mirror_download_t* response) {
    size_t realsize = size * nitems;
    
    if (!response) return realsize;
    
    // Look for Content-Type header (case-insensitive)
    if (realsize > 14 && strncasecmp(buffer, "Content-Type:", 13) == 0) {
        // Skip "Content-Type:" and whitespace
        char* value = buffer + 13;
        while (*value == ' ' || *value == '\t') value++;
        
        // Find end of value (before \r\n)
        char* end = value;
        while (*end && *end != '\r' && *end != '\n') end++;
        
        // Copy content type, limiting to buffer size
        size_t copy_len = end - value;
        if (copy_len >= sizeof(response->content_type)) {
            copy_len = sizeof(response->content_type) - 1;
        }
        
        strncpy(response->content_type, value, copy_len);
        response->content_type[copy_len] = '\0';
    }
    
    return realsize;
}

// Validate URL for security (prevent SSRF attacks)
int validate_mirror_url(const char* url) {
    if (!url || strlen(url) == 0) {
        return 0; // Invalid URL
    }
    
    // Must start with https:// (security requirement)
    if (strncmp(url, "https://", 8) != 0) {
        return 0; // Only HTTPS allowed
    }
    
    // URL length check
    if (strlen(url) > 2048) {
        return 0; // URL too long
    }
    
    // Check for prohibited hosts/IPs (basic SSRF protection)
    const char* host_start = url + 8; // Skip "https://"
    
    // Block localhost and private IPs
    if (strncasecmp(host_start, "localhost", 9) == 0 ||
        strncasecmp(host_start, "127.", 4) == 0 ||
        strncasecmp(host_start, "192.168.", 8) == 0 ||
        strncasecmp(host_start, "10.", 3) == 0 ||
        strncmp(host_start, "172.16.", 7) == 0 ||
        strncmp(host_start, "172.17.", 7) == 0 ||
        strncmp(host_start, "172.18.", 7) == 0 ||
        strncmp(host_start, "172.19.", 7) == 0 ||
        strncmp(host_start, "172.2", 5) == 0 ||
        strncmp(host_start, "172.30.", 7) == 0 ||
        strncmp(host_start, "172.31.", 7) == 0) {
        return 0; // Private network blocked
    }
    
    return 1; // URL appears valid
}

// Detect/validate Content-Type
const char* determine_blob_content_type(const char* url, const char* header_content_type,
                                       const unsigned char* data, size_t size) {
    // Priority 1: Use Content-Type header if present and valid
    if (header_content_type && strlen(header_content_type) > 0) {
        // Extract main MIME type (before semicolon)
        static char clean_type[128];
        const char* semicolon = strchr(header_content_type, ';');
        size_t len = semicolon ? (size_t)(semicolon - header_content_type) : strlen(header_content_type);
        
        if (len < sizeof(clean_type)) {
            strncpy(clean_type, header_content_type, len);
            clean_type[len] = '\0';
            
            // Remove trailing whitespace
            while (len > 0 && (clean_type[len-1] == ' ' || clean_type[len-1] == '\t')) {
                clean_type[--len] = '\0';
            }
            
            return clean_type;
        }
    }
    
    // Priority 2: Detect from URL extension
    if (url) {
        const char* dot = strrchr(url, '.');
        if (dot && dot[1]) {
            const char* ext = dot + 1;
            
            // Remove query parameters
            const char* question = strchr(ext, '?');
            size_t ext_len = question ? (size_t)(question - ext) : strlen(ext);
            
            if (ext_len > 0) {
                if (strncasecmp(ext, "png", ext_len) == 0) return "image/png";
                if (strncasecmp(ext, "jpg", ext_len) == 0) return "image/jpeg";
                if (strncasecmp(ext, "jpeg", ext_len) == 0) return "image/jpeg";
                if (strncasecmp(ext, "gif", ext_len) == 0) return "image/gif";
                if (strncasecmp(ext, "webp", ext_len) == 0) return "image/webp";
                if (strncasecmp(ext, "pdf", ext_len) == 0) return "application/pdf";
                if (strncasecmp(ext, "mp4", ext_len) == 0) return "video/mp4";
                if (strncasecmp(ext, "mp3", ext_len) == 0) return "audio/mpeg";
                if (strncasecmp(ext, "txt", ext_len) == 0) return "text/plain";
            }
        }
    }
    
    // Priority 3: Basic content detection from data
    if (data && size >= 8) {
        // PNG signature
        if (memcmp(data, "\x89PNG\r\n\x1a\n", 8) == 0) {
            return "image/png";
        }
        // JPEG signature
        if (size >= 3 && memcmp(data, "\xff\xd8\xff", 3) == 0) {
            return "image/jpeg";
        }
        // GIF signature
        if (memcmp(data, "GIF87a", 6) == 0 || memcmp(data, "GIF89a", 6) == 0) {
            return "image/gif";
        }
        // PDF signature
        if (memcmp(data, "%PDF-", 5) == 0) {
            return "application/pdf";
        }
    }
    
    // Default fallback
    return "application/octet-stream";
}

// Download blob from remote URL
mirror_download_t* download_blob_from_url(const char* url, size_t max_size) {
    if (!url || !validate_mirror_url(url)) {
        return NULL;
    }
    
    CURL* curl = curl_easy_init();
    if (!curl) {
        return NULL;
    }
    
    mirror_download_t* download = calloc(1, sizeof(mirror_download_t));
    if (!download) {
        curl_easy_cleanup(curl);
        return NULL;
    }
    
    // Initialize download structure
    download->data = malloc(8192);
    if (!download->data) {
        free(download);
        curl_easy_cleanup(curl);
        return NULL;
    }
    download->capacity = 8192;
    download->size = 0;
    download->content_type[0] = '\0';
    
    // Configure CURL
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, download);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, download);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "Ginxsom-Blossom/1.0");
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
    
    // Set maximum file size
    curl_easy_setopt(curl, CURLOPT_MAXFILESIZE, (long)max_size);
    
    // Perform the request
    CURLcode res = curl_easy_perform(curl);
    
    // Get HTTP response code
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &download->http_code);
    
    curl_easy_cleanup(curl);
    
    // Check for errors
    if (res != CURLE_OK || download->http_code != 200) {
        free(download->data);
        free(download);
        return NULL;
    }
    
    return download;
}

// Free download result memory
void free_mirror_download(mirror_download_t* download) {
    if (download) {
        if (download->data) {
            free(download->data);
        }
        free(download);
    }
}

// Parse JSON request body to extract URL
int parse_mirror_request_body(const char* json_body, char* url_buffer, size_t url_buffer_size) {
    if (!json_body || !url_buffer || url_buffer_size == 0) {
        return 0;
    }
    
    cJSON* json = cJSON_Parse(json_body);
    if (!json) {
        return 0; // Invalid JSON
    }
    
    cJSON* url_item = cJSON_GetObjectItem(json, "url");
    if (!url_item || !cJSON_IsString(url_item)) {
        cJSON_Delete(json);
        return 0; // Missing or invalid URL field
    }
    
    const char* url = cJSON_GetStringValue(url_item);
    if (!url || strlen(url) >= url_buffer_size) {
        cJSON_Delete(json);
        return 0; // URL too long or null
    }
    
    strcpy(url_buffer, url);
    cJSON_Delete(json);
    
    return 1; // Success
}

// Handle PUT /mirror requests (BUD-04)
void handle_mirror_request(void) {
    // Log the incoming request
    log_request("PUT", "/mirror", "pending", 0);
    
    // Get HTTP headers
    const char* content_type = getenv("CONTENT_TYPE");
    const char* content_length_str = getenv("CONTENT_LENGTH");
    
    // Validate Content-Type
    if (!content_type || strstr(content_type, "application/json") == NULL) {
        send_error_response(400, "invalid_content_type",
                          "Content-Type must be application/json",
                          "The mirror endpoint requires JSON request body");
        log_request("PUT", "/mirror", "none", 400);
        return;
    }
    
    // Validate Content-Length
    if (!content_length_str) {
        send_error_response(400, "missing_header",
                          "Content-Length header required",
                          "The Content-Length header must be specified");
        log_request("PUT", "/mirror", "none", 400);
        return;
    }
    
    long content_length = atol(content_length_str);
    if (content_length <= 0 || content_length > 4096) { // 4KB max for JSON
        send_error_response(400, "invalid_content_length",
                          "Invalid content length",
                          "JSON request body must be between 1 byte and 4KB");
        log_request("PUT", "/mirror", "none", 400);
        return;
    }
    
    // Read JSON request body
    char* json_body = malloc(content_length + 1);
    if (!json_body) {
        send_error_response(500, "memory_error",
                          "Failed to allocate memory",
                          "Internal server error");
        log_request("PUT", "/mirror", "none", 500);
        return;
    }
    
    size_t bytes_read = fread(json_body, 1, content_length, stdin);
    if (bytes_read != (size_t)content_length) {
        free(json_body);
        send_error_response(400, "incomplete_body",
                          "Failed to read complete request body",
                          "The request body was incomplete");
        log_request("PUT", "/mirror", "none", 400);
        return;
    }
    json_body[content_length] = '\0';
    
    // Parse JSON to extract URL
    char url[2048];
    if (!parse_mirror_request_body(json_body, url, sizeof(url))) {
        free(json_body);
        send_error_response(400, "invalid_json",
                          "Invalid JSON or missing URL field",
                          "Request body must be valid JSON with 'url' field");
        log_request("PUT", "/mirror", "none", 400);
        return;
    }
    
    free(json_body);
    
    // Validate URL
    if (!validate_mirror_url(url)) {
        send_error_response(400, "invalid_url",
                          "Invalid or prohibited URL",
                          "URL must be HTTPS and not point to private networks");
        log_request("PUT", "/mirror", "none", 400);
        return;
    }
    
    // Check for authorization
    const char* auth_header = getenv("HTTP_AUTHORIZATION");
    const char* expected_hash = NULL;
    const char* uploader_pubkey = NULL;
    static char pubkey_buffer[256];
    static char hash_buffer[65];
    
    if (auth_header) {
        // Parse and validate authorization
        int auth_result = authenticate_request(auth_header, "upload", NULL);
        if (auth_result != NOSTR_SUCCESS) {
            send_error_response(401, "authentication_failed",
                              "Invalid authentication",
                              "The provided authorization is invalid");
            log_request("PUT", "/mirror", "auth_failed", 401);
            return;
        }
        
        // Extract hash and pubkey from authorization
        char event_json[4096];
        int parse_result = parse_authorization_header(auth_header, event_json, sizeof(event_json));
        if (parse_result == NOSTR_SUCCESS) {
            cJSON* event = cJSON_Parse(event_json);
            if (event) {
                // Extract pubkey
                cJSON* pubkey_json = cJSON_GetObjectItem(event, "pubkey");
                if (pubkey_json && cJSON_IsString(pubkey_json)) {
                    const char* temp_pubkey = cJSON_GetStringValue(pubkey_json);
                    if (temp_pubkey) {
                        strncpy(pubkey_buffer, temp_pubkey, sizeof(pubkey_buffer)-1);
                        pubkey_buffer[sizeof(pubkey_buffer)-1] = '\0';
                        uploader_pubkey = pubkey_buffer;
                    }
                }
                
                // Extract expected hash from 'x' tag
                cJSON* tags = cJSON_GetObjectItem(event, "tags");
                if (tags && cJSON_IsArray(tags)) {
                    cJSON* tag = NULL;
                    cJSON_ArrayForEach(tag, tags) {
                        if (!cJSON_IsArray(tag)) continue;
                        
                        cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
                        if (!tag_name || !cJSON_IsString(tag_name)) continue;
                        
                        if (strcmp(cJSON_GetStringValue(tag_name), "x") == 0) {
                            cJSON* hash_value = cJSON_GetArrayItem(tag, 1);
                            if (hash_value && cJSON_IsString(hash_value)) {
                                const char* temp_hash = cJSON_GetStringValue(hash_value);
                                if (temp_hash && strlen(temp_hash) == 64) {
                                    strncpy(hash_buffer, temp_hash, sizeof(hash_buffer)-1);
                                    hash_buffer[sizeof(hash_buffer)-1] = '\0';
                                    expected_hash = hash_buffer;
                                    break;
                                }
                            }
                        }
                    }
                }
                
                cJSON_Delete(event);
            }
        }
        
        if (!expected_hash) {
            send_error_response(400, "missing_hash",
                              "Authorization missing expected hash",
                              "Upload authorization must contain 'x' tag with expected hash");
            log_request("PUT", "/mirror", "auth_invalid", 400);
            return;
        }
    }
    
    // Download the blob
    mirror_download_t* download = download_blob_from_url(url, 100 * 1024 * 1024); // 100MB limit
    if (!download) {
        send_error_response(400, "download_failed",
                          "Failed to download blob from URL",
                          "Could not fetch the specified URL or file too large");
        log_request("PUT", "/mirror", uploader_pubkey ? "authenticated" : "anonymous", 400);
        return;
    }
    
    // Calculate hash of downloaded content
    unsigned char hash[32];
    if (nostr_sha256(download->data, download->size, hash) != NOSTR_SUCCESS) {
        free_mirror_download(download);
        send_error_response(500, "hash_error",
                          "Failed to calculate hash",
                          "Internal server error during hash calculation");
        log_request("PUT", "/mirror", uploader_pubkey ? "authenticated" : "anonymous", 500);
        return;
    }
    
    // Convert hash to hex string
    char sha256_hex[65];
    nostr_bytes_to_hex(hash, 32, sha256_hex);
    
    // If authorization provided, verify hash matches
    if (expected_hash && strcmp(sha256_hex, expected_hash) != 0) {
        free_mirror_download(download);
        send_error_response(400, "hash_mismatch",
                          "Downloaded content hash does not match authorization",
                          "The file hash does not match the expected hash in the authorization event");
        log_request("PUT", "/mirror", "auth_mismatch", 400);
        return;
    }
    
    // Determine content type
    const char* content_type_final = determine_blob_content_type(url, download->content_type,
                                                               download->data, download->size);
    
    // Determine file extension from Content-Type using centralized mapping
    const char* extension = mime_to_extension(content_type_final);
    
    // Save file to blobs directory
    char filepath[MAX_PATH_LEN];
    snprintf(filepath, sizeof(filepath), "blobs/%s%s", sha256_hex, extension);
    
    FILE* outfile = fopen(filepath, "wb");
    if (!outfile) {
        free_mirror_download(download);
        send_error_response(500, "file_error",
                          "Failed to create file",
                          "Internal server error during file creation");
        log_request("PUT", "/mirror", uploader_pubkey ? "authenticated" : "anonymous", 500);
        return;
    }
    
    size_t bytes_written = fwrite(download->data, 1, download->size, outfile);
    fclose(outfile);
    
    if (bytes_written != download->size) {
        unlink(filepath); // Clean up partial file
        free_mirror_download(download);
        send_error_response(500, "write_error",
                          "Failed to write complete file",
                          "Internal server error during file write");
        log_request("PUT", "/mirror", uploader_pubkey ? "authenticated" : "anonymous", 500);
        return;
    }
    
    // Set file permissions
    chmod(filepath, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    
    // Store metadata in database
    time_t uploaded_time = time(NULL);
    if (!insert_blob_metadata(sha256_hex, download->size, content_type_final,
                             uploaded_time, uploader_pubkey, NULL)) {
        unlink(filepath); // Clean up file
        free_mirror_download(download);
        send_error_response(500, "database_error",
                          "Failed to store blob metadata",
                          "Internal server error during database operation");
        log_request("PUT", "/mirror", uploader_pubkey ? "authenticated" : "anonymous", 500);
        return;
    }
    
    // Get origin from config
    char origin[256];
    nip94_get_origin(origin, sizeof(origin));
    
    // Build canonical blob URL
    char blob_url[512];
    nip94_build_blob_url(origin, sha256_hex, content_type_final, blob_url, sizeof(blob_url));
    
    // Get dimensions for NIP-94 metadata
    int width = 0, height = 0;
    nip94_get_dimensions(download->data, download->size, content_type_final, &width, &height);
    
    // Return success response with blob descriptor
    printf("Status: 200 OK\r\n");
    printf("Content-Type: application/json\r\n\r\n");
    printf("{\n");
    printf("  \"sha256\": \"%s\",\n", sha256_hex);
    printf("  \"size\": %zu,\n", download->size);
    printf("  \"type\": \"%s\",\n", content_type_final);
    printf("  \"uploaded\": %ld,\n", uploaded_time);
    printf("  \"url\": \"%s\"", blob_url);
    
    // Add NIP-94 metadata if enabled
    if (nip94_is_enabled()) {
        printf(",\n");
        nip94_emit_field(blob_url, content_type_final, sha256_hex, download->size, width, height);
    }
    
    printf("\n}\n");
    
    free_mirror_download(download);
    log_request("PUT", "/mirror", uploader_pubkey ? "authenticated" : "anonymous", 200);
}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             BUD 06 - Upload Requirements (Pre-flight Validation)
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

// Enhanced error response with X-Reason header for BUD-06
void send_upload_error_response(int status_code, const char* error_type,
                               const char* message, const char* x_reason) {
    const char* status_text;
    switch (status_code) {
        case 400: status_text = "Bad Request"; break;
        case 401: status_text = "Unauthorized"; break;
        case 409: status_text = "Conflict"; break;
        case 411: status_text = "Length Required"; break;
        case 413: status_text = "Content Too Large"; break;
        case 415: status_text = "Unsupported Media Type"; break;
        case 500: status_text = "Internal Server Error"; break;
        default: status_text = "Error"; break;
    }
    
    printf("Status: %d %s\r\n", status_code, status_text);
    printf("Content-Type: application/json\r\n");
    if (x_reason) {
        printf("X-Reason: %s\r\n", x_reason);
    }
    printf("\r\n");
    printf("{\n");
    printf("  \"error\": \"%s\",\n", error_type);
    printf("  \"message\": \"%s\"", message);
    if (x_reason) {
        printf(",\n  \"x_reason\": \"%s\"", x_reason);
    }
    printf("\n}\n");
}

// Success response for validated upload requirements
void send_upload_success_response(const char* sha256, const char* content_type, long content_length) {
    printf("Status: 200 OK\r\n");
    printf("Content-Type: application/json\r\n");
    printf("X-Upload-Status: Ready\r\n");
    printf("\r\n");
    printf("{\n");
    printf("  \"message\": \"Upload requirements validated\",\n");
    printf("  \"sha256\": \"%s\",\n", sha256);
    printf("  \"content_type\": \"%s\",\n", content_type);
    printf("  \"content_length\": %ld\n", content_length);
    printf("}\n");
}

// Validate SHA-256 format (64 hex characters)
int validate_sha256_format(const char* sha256) {
    if (!sha256 || strlen(sha256) != 64) {
        return 0; // Invalid format
    }
    
    // Check that all characters are hex
    for (int i = 0; i < 64; i++) {
        char c = sha256[i];
        if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
            return 0; // Invalid hex character
        }
    }
    
    return 1; // Valid format
}

// Parse and validate X-Content-Length header
int validate_content_length(const char* content_length_str, long* parsed_length) {
    if (!content_length_str || !parsed_length) {
        return 0; // Invalid input
    }
    
    char* endptr;
    long length = strtol(content_length_str, &endptr, 10);
    
    // Always set parsed_length so caller can check the actual value
    *parsed_length = length;
    
    // Check if conversion was successful and no trailing characters
    if (*endptr != '\0') {
        return 0; // Invalid number format
    }
    
    // Check for valid size range
    if (length <= 0) {
        return 0; // Zero or negative size not allowed
    }
    
    if (length > 100 * 1024 * 1024) { // 100MB limit
        return -1; // File too large
    }
    
    return 1; // Valid length
}

// Check if blob already exists in database
int check_blob_exists(const char* sha256) {
    if (!sha256) {
        return 0;
    }
    
    blob_metadata_t metadata = {0};
    return get_blob_metadata(sha256, &metadata);
}

// Validate upload headers and extract values
int validate_upload_headers(const char** sha256, const char** content_type,
                           long* content_length, char* error_reason, size_t reason_size) {
    // Get X-SHA-256 header
    const char* sha256_header = getenv("HTTP_X_SHA_256");
    if (!sha256_header) {
        strncpy(error_reason, XREASON_MISSING_SHA256, reason_size - 1);
        error_reason[reason_size - 1] = '\0';
        return 400; // Bad Request
    }
    
    // Validate SHA-256 format
    if (!validate_sha256_format(sha256_header)) {
        strncpy(error_reason, XREASON_INVALID_SHA256, reason_size - 1);
        error_reason[reason_size - 1] = '\0';
        return 400; // Bad Request
    }
    
    // Get X-Content-Length header
    const char* length_header = getenv("HTTP_X_CONTENT_LENGTH");
    if (!length_header) {
        strncpy(error_reason, XREASON_MISSING_LENGTH, reason_size - 1);
        error_reason[reason_size - 1] = '\0';
        return 411; // Length Required
    }
    
    // Validate content length
    long parsed_length;
    int length_result = validate_content_length(length_header, &parsed_length);
    if (length_result == 0) {
        if (parsed_length == 0) {
            strncpy(error_reason, XREASON_ZERO_LENGTH, reason_size - 1);
        } else {
            strncpy(error_reason, XREASON_INVALID_LENGTH, reason_size - 1);
        }
        error_reason[reason_size - 1] = '\0';
        return 400; // Bad Request
    } else if (length_result == -1) {
        strncpy(error_reason, XREASON_FILE_TOO_LARGE, reason_size - 1);
        error_reason[reason_size - 1] = '\0';
        return 413; // Content Too Large
    }
    
    // Get X-Content-Type header (optional)
    const char* type_header = getenv("HTTP_X_CONTENT_TYPE");
    
    // Set output values
    *sha256 = sha256_header;
    *content_type = type_header ? type_header : "application/octet-stream";
    *content_length = parsed_length;
    
    return 200; // Success
}

// Main BUD-06 handler function
void handle_head_upload_request(void) {
    // Log the incoming request
    log_request("HEAD", "/upload", "pending", 0);
    
    // Validate upload headers
    const char* sha256 = NULL;
    const char* content_type = NULL;
    long content_length = 0;
    char error_reason[256];
    
    int validation_result = validate_upload_headers(&sha256, &content_type,
                                                   &content_length, error_reason, sizeof(error_reason));
    
    if (validation_result != 200) {
        // Header validation failed
        const char* error_type;
        switch (validation_result) {
            case 400: error_type = "invalid_headers"; break;
            case 411: error_type = "length_required"; break;
            case 413: error_type = "payload_too_large"; break;
            default: error_type = "validation_error"; break;
        }
        
        send_upload_error_response(validation_result, error_type, error_reason, error_reason);
        log_request("HEAD", "/upload", "none", validation_result);
        return;
    }
    
    // Check if blob already exists (duplicate detection)
    if (check_blob_exists(sha256)) {
        send_upload_error_response(409, "blob_exists", "Blob with this hash already exists", XREASON_BLOB_EXISTS);
        log_request("HEAD", "/upload", "none", 409);
        return;
    }
    
    // Check for optional authorization
    const char* auth_header = getenv("HTTP_AUTHORIZATION");
    const char* auth_status = "none";
    
    if (auth_header) {
        // Validate authorization if provided
        nostr_request_t request = {
            .operation = "upload",
            .auth_header = auth_header,
            .event = NULL,
            .resource_hash = sha256,
            .mime_type = content_type,
            .file_size = content_length,
            .client_ip = getenv("REMOTE_ADDR"),
            .app_context = NULL
        };
        
        nostr_request_result_t result;
        int auth_result = nostr_validate_request(&request, &result);
        
        if (auth_result != NOSTR_SUCCESS || !result.valid) {
            const char* error_type = "authentication_failed";
            const char* message = "Invalid or expired authentication";
            const char* details = result.reason[0] ? result.reason : "Authentication validation failed";
            
            // Provide more specific error messages based on the reason
            if (strstr(result.reason, "whitelist")) {
                error_type = "pubkey_not_whitelisted";
                message = "Public key not authorized";
                details = result.reason;
            } else if (strstr(result.reason, "blacklist")) {
                error_type = "access_denied";
                message = "Access denied by policy";
                details = result.reason;
            } else if (strstr(result.reason, "size")) {
                error_type = "file_too_large";
                message = "File size exceeds policy limits";
                details = result.reason;
            }
            
            send_upload_error_response(401, error_type, message, details);
            log_request("HEAD", "/upload", "auth_failed", 401);
            return;
        }
        auth_status = "authenticated";
    }
    
    // All validations passed - return success
    send_upload_success_response(sha256, content_type, content_length);
    log_request("HEAD", "/upload", auth_status, 200);
}

// Enhanced error response helper functions
void send_error_response(int status_code, const char* error_type, const char* message, const char* details) {
    const char* status_text;
    switch (status_code) {
        case 400: status_text = "Bad Request"; break;
        case 401: status_text = "Unauthorized"; break;
        case 409: status_text = "Conflict"; break;
        case 413: status_text = "Payload Too Large"; break;
        case 500: status_text = "Internal Server Error"; break;
        default: status_text = "Error"; break;
    }
    
    printf("Status: %d %s\r\n", status_code, status_text);
    printf("Content-Type: application/json\r\n\r\n");
    printf("{\n");
    printf("  \"error\": \"%s\",\n", error_type);
    printf("  \"message\": \"%s\"", message);
    if (details) {
        printf(",\n  \"details\": \"%s\"", details);
    }
    printf("\n}\n");
}

void log_request(const char* method, const char* uri, const char* auth_status, int status_code) {
    time_t now = time(NULL);
    struct tm* tm_info = localtime(&now);
    char timestamp[64];
    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
    
    // For now, log to stdout - later can be configured to log files
    fprintf(stderr, "LOG: [%s] %s %s - Auth: %s - Status: %d\r\n", 
           timestamp, method ? method : "NULL", uri ? uri : "NULL", 
           auth_status ? auth_status : "none", status_code);
}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             BUD 08 - Nip94 File Metadata Tags
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

// Check if NIP-94 metadata emission is enabled
int nip94_is_enabled(void) {
    sqlite3* db;
    sqlite3_stmt* stmt;
    int rc, enabled = 1; // Default enabled
    
    rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READONLY, NULL);
    if (rc) {
        return 1; // Default enabled on DB error
    }
    
    const char* sql = "SELECT value FROM server_config WHERE key = 'nip94_enabled'";
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        rc = sqlite3_step(stmt);
        if (rc == SQLITE_ROW) {
            const char* value = (const char*)sqlite3_column_text(stmt, 0);
            enabled = (value && strcmp(value, "true") == 0) ? 1 : 0;
        }
        sqlite3_finalize(stmt);
    }
    sqlite3_close(db);
    
    return enabled;
}

// Get CDN origin for blob URLs
int nip94_get_origin(char* out, size_t out_size) {
    if (!out || out_size == 0) {
        return 0;
    }
    
    sqlite3* db;
    sqlite3_stmt* stmt;
    int rc;
    
    rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READONLY, NULL);
    if (rc) {
        // Default on DB error
        strncpy(out, "http://localhost:9001", out_size - 1);
        out[out_size - 1] = '\0';
        return 1;
    }
    
    const char* sql = "SELECT value FROM server_config WHERE key = 'cdn_origin'";
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
        rc = sqlite3_step(stmt);
        if (rc == SQLITE_ROW) {
            const char* value = (const char*)sqlite3_column_text(stmt, 0);
            if (value) {
                strncpy(out, value, out_size - 1);
                out[out_size - 1] = '\0';
                sqlite3_finalize(stmt);
                sqlite3_close(db);
                return 1;
            }
        }
        sqlite3_finalize(stmt);
    }
    
    sqlite3_close(db);
    
    // Default fallback
    strncpy(out, "http://localhost:9001", out_size - 1);
    out[out_size - 1] = '\0';
    return 1;
}

// Centralized MIME type to file extension mapping
const char* mime_to_extension(const char* mime_type) {
    if (!mime_type) {
        return ".bin";
    }
    
    if (strstr(mime_type, "image/jpeg")) {
        return ".jpg";
    } else if (strstr(mime_type, "image/webp")) {
        return ".webp";
    } else if (strstr(mime_type, "image/png")) {
        return ".png";
    } else if (strstr(mime_type, "image/gif")) {
        return ".gif";
    } else if (strstr(mime_type, "video/mp4")) {
        return ".mp4";
    } else if (strstr(mime_type, "video/webm")) {
        return ".webm";
    } else if (strstr(mime_type, "audio/mpeg")) {
        return ".mp3";
    } else if (strstr(mime_type, "audio/ogg")) {
        return ".ogg";
    } else if (strstr(mime_type, "text/plain")) {
        return ".txt";
    } else if (strstr(mime_type, "application/pdf")) {
        return ".pdf";
    } else {
        return ".bin";
    }
}

// Build canonical blob URL from origin + sha256 + extension
void nip94_build_blob_url(const char* origin, const char* sha256, const char* mime_type, char* out, size_t out_size) {
    if (!origin || !sha256 || !out || out_size == 0) {
        return;
    }
    
    const char* extension = mime_to_extension(mime_type);
    snprintf(out, out_size, "%s/%s%s", origin, sha256, extension);
}

// Parse PNG dimensions from IHDR chunk
int parse_png_dimensions(const unsigned char* data, size_t size, int* width, int* height) {
    if (!data || size < 24 || !width || !height) {
        return 0;
    }
    
    // Verify PNG signature
    if (memcmp(data, "\x89PNG\r\n\x1a\n", 8) != 0) {
        return 0;
    }
    
    // IHDR chunk should start at offset 8
    // Skip chunk length (4 bytes) and chunk type "IHDR" (4 bytes)
    // Width is at offset 16 (4 bytes, big-endian)
    // Height is at offset 20 (4 bytes, big-endian)
    if (size >= 24) {
        *width = (data[16] << 24) | (data[17] << 16) | (data[18] << 8) | data[19];
        *height = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23];
        return 1;
    }
    
    return 0;
}

// Parse JPEG dimensions from SOF0 or SOF2 markers
int parse_jpeg_dimensions(const unsigned char* data, size_t size, int* width, int* height) {
    if (!data || size < 10 || !width || !height) {
        return 0;
    }
    
    // Verify JPEG signature
    if (size < 3 || memcmp(data, "\xff\xd8\xff", 3) != 0) {
        return 0;
    }
    
    size_t pos = 2;
    while (pos < size - 1) {
        // Look for marker
        if (data[pos] != 0xff) {
            pos++;
            continue;
        }
        
        unsigned char marker = data[pos + 1];
        pos += 2;
        
        // SOF0 (0xc0) or SOF2 (0xc2)
        if (marker == 0xc0 || marker == 0xc2) {
            // Skip length (2 bytes) and precision (1 byte)
            if (pos + 5 < size) {
                pos += 3;
                // Height (2 bytes, big-endian)
                *height = (data[pos] << 8) | data[pos + 1];
                pos += 2;
                // Width (2 bytes, big-endian)
                *width = (data[pos] << 8) | data[pos + 1];
                return 1;
            }
            return 0;
        } else if ((marker >= 0xe0 && marker <= 0xef) ||
                   (marker >= 0xc4 && marker <= 0xdf && marker != 0xc8)) {
            // Skip over other segments
            if (pos + 1 < size) {
                size_t seg_len = (data[pos] << 8) | data[pos + 1];
                pos += seg_len;
            } else {
                break;
            }
        } else {
            pos++;
        }
    }
    
    return 0;
}

// Parse WebP dimensions from VP8/VP8L/VP8X chunks
int parse_webp_dimensions(const unsigned char* data, size_t size, int* width, int* height) {
    if (!data || size < 20 || !width || !height) {
        return 0;
    }
    
    // Verify RIFF/WEBP header
    if (memcmp(data, "RIFF", 4) != 0 || memcmp(data + 8, "WEBP", 4) != 0) {
        return 0;
    }
    
    // Check chunk type at offset 12
    if (memcmp(data + 12, "VP8 ", 4) == 0) {
        // VP8 lossy format
        if (size >= 30) {
            // Skip to frame header (offset 26)
            *width = ((data[28] | (data[29] << 8)) & 0x3fff);
            *height = ((data[26] | (data[27] << 8)) & 0x3fff);
            return 1;
        }
    } else if (memcmp(data + 12, "VP8L", 4) == 0) {
        // VP8L lossless format
        if (size >= 25) {
            // Width and height are in bits 0-13 and 14-27 of a 32-bit value at offset 21
            uint32_t dim_data = data[21] | (data[22] << 8) | (data[23] << 16) | (data[24] << 24);
            *width = (dim_data & 0x3fff) + 1;
            *height = ((dim_data >> 14) & 0x3fff) + 1;
            return 1;
        }
    } else if (memcmp(data + 12, "VP8X", 4) == 0) {
        // VP8X extended format
        if (size >= 30) {
            // Width (3 bytes, little-endian) at offset 24
            // Height (3 bytes, little-endian) at offset 27
            *width = (data[24] | (data[25] << 8) | (data[26] << 16)) + 1;
            *height = (data[27] | (data[28] << 8) | (data[29] << 16)) + 1;
            return 1;
        }
    }
    
    return 0;
}

// Get file dimensions based on MIME type
int nip94_get_dimensions(const unsigned char* data, size_t size, const char* mime_type, int* width, int* height) {
    if (!data || !mime_type || !width || !height) {
        return 0;
    }
    
    if (strstr(mime_type, "image/png")) {
        return parse_png_dimensions(data, size, width, height);
    } else if (strstr(mime_type, "image/jpeg")) {
        return parse_jpeg_dimensions(data, size, width, height);
    } else if (strstr(mime_type, "image/webp")) {
        return parse_webp_dimensions(data, size, width, height);
    }
    
    return 0;
}

// Emit NIP-94 metadata field to stdout
void nip94_emit_field(const char* url, const char* mime, const char* sha256, long size, int width, int height) {
    if (!url || !mime || !sha256) {
        return;
    }
    
    printf("  \"nip94\": [\n");
    printf("    [\"url\", \"%s\"],\n", url);
    printf("    [\"m\", \"%s\"],\n", mime);
    printf("    [\"x\", \"%s\"],\n", sha256);
    printf("    [\"size\", \"%ld\"]", size);
    
    // Add dim tag if dimensions are available
    if (width > 0 && height > 0) {
        printf(",\n    [\"dim\", \"%dx%d\"]", width, height);
    }
    
    printf("\n  ]");
}

// Handle GET /list/<pubkey> requests
void handle_list_request(const char* pubkey) {

    
    // Log the incoming request
    log_request("GET", "/list", "pending", 0);
    
    // Validate pubkey format (64 hex characters)
    if (!pubkey || strlen(pubkey) != 64) {
        send_error_response(400, "invalid_pubkey", "Invalid pubkey format", "Pubkey must be 64 hex characters");
        log_request("GET", "/list", "none", 400);
        return;
    }
    
    // Validate hex characters
    for (int i = 0; i < 64; i++) {
        char c = pubkey[i];
        if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
            send_error_response(400, "invalid_pubkey", "Invalid pubkey format", "Pubkey must contain only hex characters");
            log_request("GET", "/list", "none", 400);
            return;
        }
    }
    
    // Get query parameters for since/until filtering
    const char* query_string = getenv("QUERY_STRING");
    long since_timestamp = 0;
    long until_timestamp = 0;
    
    if (query_string) {

        
        // Parse since parameter
        const char* since_param = strstr(query_string, "since=");
        if (since_param) {
            since_timestamp = atol(since_param + 6);

        }
        
        // Parse until parameter  
        const char* until_param = strstr(query_string, "until=");
        if (until_param) {
            until_timestamp = atol(until_param + 6);

        }
    }
    
    // Check for optional authorization
    const char* auth_header = getenv("HTTP_AUTHORIZATION");
    const char* auth_status = "none";
    
    if (auth_header) {

        nostr_request_t request = {
            .operation = "list",
            .auth_header = auth_header,
            .event = NULL,
            .resource_hash = NULL,
            .mime_type = NULL,
            .file_size = 0,
            .client_ip = getenv("REMOTE_ADDR"),
            .app_context = NULL
        };
        
        nostr_request_result_t result;
        int auth_result = nostr_validate_request(&request, &result);
        
        if (auth_result != NOSTR_SUCCESS || !result.valid) {
            const char* error_type = "authentication_failed";
            const char* message = "Invalid or expired authentication";
            const char* details = result.reason[0] ? result.reason : "The provided Nostr event is invalid, expired, or does not authorize this operation";
            
            // Provide more specific error messages based on the reason
            if (strstr(result.reason, "whitelist")) {
                error_type = "pubkey_not_whitelisted";
                message = "Public key not authorized";
            } else if (strstr(result.reason, "blacklist")) {
                error_type = "access_denied";
                message = "Access denied by policy";
            }
            
            send_error_response(401, error_type, message, details);
            log_request("GET", "/list", "failed", 401);
            return;
        }
        auth_status = "authenticated";
    }
    
    // Query database for blobs uploaded by this pubkey
    sqlite3* db;
    sqlite3_stmt* stmt;
    int rc;
    
    rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READONLY, NULL);
    if (rc) {

        send_error_response(500, "database_error", "Failed to access database", "Internal server error");
        log_request("GET", "/list", auth_status, 500);
        return;
    }
    
    // Build SQL query with optional timestamp filtering
    char sql[1024];
    if (since_timestamp > 0 && until_timestamp > 0) {
        snprintf(sql, sizeof(sql), 
                "SELECT sha256, size, type, uploaded_at, filename FROM blobs WHERE uploader_pubkey = ? AND uploaded_at >= ? AND uploaded_at <= ? ORDER BY uploaded_at DESC");
    } else if (since_timestamp > 0) {
        snprintf(sql, sizeof(sql), 
                "SELECT sha256, size, type, uploaded_at, filename FROM blobs WHERE uploader_pubkey = ? AND uploaded_at >= ? ORDER BY uploaded_at DESC");
    } else if (until_timestamp > 0) {
        snprintf(sql, sizeof(sql), 
                "SELECT sha256, size, type, uploaded_at, filename FROM blobs WHERE uploader_pubkey = ? AND uploaded_at <= ? ORDER BY uploaded_at DESC");
    } else {
        snprintf(sql, sizeof(sql), 
                "SELECT sha256, size, type, uploaded_at, filename FROM blobs WHERE uploader_pubkey = ? ORDER BY uploaded_at DESC");
    }
    

    
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {

        sqlite3_close(db);
        send_error_response(500, "database_error", "Failed to prepare query", "Internal server error");
        log_request("GET", "/list", auth_status, 500);
        return;
    }
    
    // Bind parameters
    sqlite3_bind_text(stmt, 1, pubkey, -1, SQLITE_STATIC);
    int param_index = 2;
    
    if (since_timestamp > 0) {
        sqlite3_bind_int64(stmt, param_index++, since_timestamp);
    }
    if (until_timestamp > 0) {
        sqlite3_bind_int64(stmt, param_index++, until_timestamp);
    }
    
    // Start JSON response
    printf("Status: 200 OK\r\n");
    printf("Content-Type: application/json\r\n\r\n");
    printf("[\n");
    
    int first_item = 1;
    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        if (!first_item) {
            printf(",\n");
        }
        first_item = 0;
        
        const char* sha256 = (const char*)sqlite3_column_text(stmt, 0);
        long size = sqlite3_column_int64(stmt, 1);
        const char* type = (const char*)sqlite3_column_text(stmt, 2);
        long uploaded_at = sqlite3_column_int64(stmt, 3);
        const char* filename = (const char*)sqlite3_column_text(stmt, 4);
        
        // Get origin from config for consistent URL generation
        char origin[256];
        nip94_get_origin(origin, sizeof(origin));
        
        // Build canonical blob URL using centralized function
        char blob_url[512];
        nip94_build_blob_url(origin, sha256, type, blob_url, sizeof(blob_url));
        
        // Output blob descriptor JSON
        printf("  {\n");
        printf("    \"url\": \"%s\",\n", blob_url);
        printf("    \"sha256\": \"%s\",\n", sha256);
        printf("    \"size\": %ld,\n", size);
        printf("    \"type\": \"%s\",\n", type);
        printf("    \"uploaded\": %ld", uploaded_at);
        
        // Add optional filename if available
        if (filename && strlen(filename) > 0) {
            printf(",\n    \"filename\": \"%s\"", filename);
        }
        
        printf("\n  }");
    }
    
    printf("\n]\n");
    
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    

    log_request("GET", "/list", auth_status, 200);
}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             BUD 09 - Blob Report (NIP-56 Report Events)
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

// Validate NIP-56 report event structure
int validate_report_event_structure(cJSON* event) {
    if (!event) {
        return 0;
    }
    
    // Must be kind 1984
    cJSON* kind_json = cJSON_GetObjectItem(event, "kind");
    if (!kind_json || !cJSON_IsNumber(kind_json)) {
        return 0;
    }
    if (cJSON_GetNumberValue(kind_json) != 1984) {
        return 0;
    }
    
    // Must have tags array
    cJSON* tags = cJSON_GetObjectItem(event, "tags");
    if (!tags || !cJSON_IsArray(tags)) {
        return 0;
    }
    
    // Must have at least one 'x' tag
    int has_x_tag = 0;
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags) {
        if (!cJSON_IsArray(tag)) continue;
        cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
        if (tag_name && cJSON_IsString(tag_name)) {
            const char* tag_name_str = cJSON_GetStringValue(tag_name);
            if (tag_name_str && strcmp(tag_name_str, "x") == 0) {
                has_x_tag = 1;
                break;
            }
        }
    }
    
    return has_x_tag;
}

// Extract SHA-256 blob hashes from 'x' tags
int extract_blob_hashes_from_report(cJSON* event, char blob_hashes[][65], int max_hashes) {
    if (!event || !blob_hashes) {
        return 0;
    }
    
    cJSON* tags = cJSON_GetObjectItem(event, "tags");
    if (!tags || !cJSON_IsArray(tags)) {
        return 0;
    }
    
    int hash_count = 0;
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags) {
        if (hash_count >= max_hashes) break;
        if (!cJSON_IsArray(tag)) continue;
        
        cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
        if (!tag_name || !cJSON_IsString(tag_name)) continue;
        
        const char* tag_name_str = cJSON_GetStringValue(tag_name);
        if (tag_name_str && strcmp(tag_name_str, "x") == 0) {
            cJSON* hash_value = cJSON_GetArrayItem(tag, 1);
            if (hash_value && cJSON_IsString(hash_value)) {
                const char* hash = cJSON_GetStringValue(hash_value);
                if (hash && validate_sha256_format(hash)) {
                    strncpy(blob_hashes[hash_count], hash, 64);
                    blob_hashes[hash_count][64] = '\0';
                    hash_count++;
                }
            }
        }
    }
    
    return hash_count;
}

// Validate NIP-56 report types in x tags
int validate_report_types(cJSON* event) {
    if (!event) {
        return 0;
    }
    
    cJSON* tags = cJSON_GetObjectItem(event, "tags");
    if (!tags || !cJSON_IsArray(tags)) {
        return 0;
    }
    
    // Valid NIP-56 report types
    const char* valid_types[] = {
        "nudity", "malware", "profanity", "illegal",
        "spam", "impersonation", "other", NULL
    };
    
    cJSON* tag = NULL;
    cJSON_ArrayForEach(tag, tags) {
        if (!cJSON_IsArray(tag)) continue;
        
        cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
        if (!tag_name || !cJSON_IsString(tag_name)) continue;
        
        const char* tag_name_str = cJSON_GetStringValue(tag_name);
        if (tag_name_str && strcmp(tag_name_str, "x") == 0) {
            // Check if report type is provided and valid (optional)
            cJSON* report_type = cJSON_GetArrayItem(tag, 2);
            if (report_type && cJSON_IsString(report_type)) {
                const char* type_str = cJSON_GetStringValue(report_type);
                if (type_str) {
                    // Validate against known types (but allow unknown types per spec)
                    for (int i = 0; valid_types[i] != NULL; i++) {
                        if (strcmp(type_str, valid_types[i]) == 0) {
                            break;
                        }
                    }
                    // Note: Allow unknown types as per NIP-56 spec flexibility
                }
            }
        }
    }
    
    return 1; // Always return success - report types are informational
}

// Store blob report in database (optional server behavior)
int store_blob_report(const char* event_json, const char* reporter_pubkey) {
    // Optional implementation - servers MAY store reports
    sqlite3* db;
    sqlite3_stmt* stmt;
    int rc;
    
    rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READWRITE, NULL);
    if (rc) {
        return 0;
    }
    
    // Check if blob_reports table exists, create if not
    const char* create_table_sql =
        "CREATE TABLE IF NOT EXISTS blob_reports ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        "report_event TEXT NOT NULL, "
        "reporter_pubkey TEXT, "
        "reported_at INTEGER NOT NULL, "
        "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
        ")";
    
    rc = sqlite3_exec(db, create_table_sql, NULL, NULL, NULL);
    if (rc != SQLITE_OK) {
        sqlite3_close(db);
        return 0;
    }
    
    const char* sql = "INSERT INTO blob_reports (report_event, reporter_pubkey, reported_at) VALUES (?, ?, strftime('%s', 'now'))";
    
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        sqlite3_close(db);
        return 0;
    }
    
    sqlite3_bind_text(stmt, 1, event_json, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, reporter_pubkey, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    int success = (rc == SQLITE_DONE);
    
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    
    return success;
}

// Handle PUT /report requests (BUD-09)
void handle_report_request(void) {
    // Log the incoming request
    log_request("PUT", "/report", "pending", 0);
    
    // Validate HTTP method (only PUT allowed)
    const char* request_method = getenv("REQUEST_METHOD");
    if (!request_method || strcmp(request_method, "PUT") != 0) {
        send_error_response(405, "method_not_allowed", "Only PUT method allowed", "The /report endpoint only accepts PUT requests");
        log_request(request_method ? request_method : "NULL", "/report", "none", 405);
        return;
    }
    
    // Validate Content-Type
    const char* content_type = getenv("CONTENT_TYPE");
    if (!content_type || strstr(content_type, "application/json") == NULL) {
        send_error_response(415, "unsupported_media_type", "Content-Type must be application/json", "Report requests must be JSON");
        log_request("PUT", "/report", "none", 415);
        return;
    }
    
    // Validate Content-Length
    const char* content_length_str = getenv("CONTENT_LENGTH");
    if (!content_length_str) {
        send_error_response(400, "missing_content_length", "Content-Length header required", "Request body size must be specified");
        log_request("PUT", "/report", "none", 400);
        return;
    }
    
    long content_length = atol(content_length_str);
    if (content_length <= 0 || content_length > 10240) { // 10KB limit for report events
        send_error_response(400, "invalid_content_length", "Invalid content length", "Report events must be between 1 byte and 10KB");
        log_request("PUT", "/report", "none", 400);
        return;
    }
    
    // Read JSON request body
    char* json_body = malloc(content_length + 1);
    if (!json_body) {
        send_error_response(500, "memory_error", "Failed to allocate memory", "Internal server error");
        log_request("PUT", "/report", "none", 500);
        return;
    }
    
    size_t bytes_read = fread(json_body, 1, content_length, stdin);
    if (bytes_read != (size_t)content_length) {
        free(json_body);
        send_error_response(400, "incomplete_body", "Failed to read complete request body", "The request body was incomplete");
        log_request("PUT", "/report", "none", 400);
        return;
    }
    json_body[content_length] = '\0';
    
    // Parse JSON event
    cJSON* event = cJSON_Parse(json_body);
    if (!event) {
        free(json_body);
        send_error_response(400, "invalid_json", "Invalid JSON format", "Request body must be valid JSON");
        log_request("PUT", "/report", "none", 400);
        return;
    }
    
    // Validate event structure (NIP-56 kind 1984 with x tags)
    if (!validate_report_event_structure(event)) {
        cJSON_Delete(event);
        free(json_body);
        send_error_response(400, "invalid_report_event", "Invalid report event structure", "Report must be NIP-56 kind 1984 event with x tags");
        log_request("PUT", "/report", "none", 400);
        return;
    }
    
    // Validate nostr event signature and structure
    int structure_result = nostr_validate_event_structure(event);
    if (structure_result != NOSTR_SUCCESS) {
        cJSON_Delete(event);
        free(json_body);
        send_error_response(400, "invalid_event_structure", "Invalid nostr event structure", "Event does not conform to nostr event format");
        log_request("PUT", "/report", "structure_invalid", 400);
        return;
    }
    
    int crypto_result = nostr_verify_event_signature(event);
    if (crypto_result != NOSTR_SUCCESS) {
        cJSON_Delete(event);
        free(json_body);
        send_error_response(400, "invalid_signature", "Invalid event signature", "Event signature verification failed");
        log_request("PUT", "/report", "signature_invalid", 400);
        return;
    }
    
    // Extract blob hashes from x tags
    char blob_hashes[10][65]; // Support up to 10 blob hashes per report
    int hash_count = extract_blob_hashes_from_report(event, blob_hashes, 10);
    if (hash_count == 0) {
        cJSON_Delete(event);
        free(json_body);
        send_error_response(400, "no_blob_hashes", "No valid blob hashes found", "Report must contain at least one valid SHA-256 hash in x tags");
        log_request("PUT", "/report", "no_hashes", 400);
        return;
    }
    
    // Validate report types (optional validation)
    validate_report_types(event);
    
    // Extract reporter pubkey
    cJSON* pubkey_json = cJSON_GetObjectItem(event, "pubkey");
    const char* reporter_pubkey = NULL;
    if (pubkey_json && cJSON_IsString(pubkey_json)) {
        reporter_pubkey = cJSON_GetStringValue(pubkey_json);
    }
    
    // Optional: Store report in database (server behavior)
    if (reporter_pubkey) {
        store_blob_report(json_body, reporter_pubkey);
    }
    
    // Clean up
    cJSON_Delete(event);
    free(json_body);
    
    // Return success response
    printf("Status: 200 OK\r\n");
    printf("Content-Type: application/json\r\n\r\n");
    printf("{\n");
    printf("  \"message\": \"Report received\",\n");
    printf("  \"reported_blobs\": %d,\n", hash_count);
    printf("  \"reporter\": \"%s\"\n", reporter_pubkey ? reporter_pubkey : "anonymous");
    printf("}\n");
    
    log_request("PUT", "/report", reporter_pubkey ? "authenticated" : "anonymous", 200);
}

// Handle DELETE /<sha256> requests
void handle_delete_request(const char* sha256) {

    
    // Log the incoming request
    log_request("DELETE", "/delete", "pending", 0);
    
    // Validate SHA-256 format (64 hex characters)
    if (!sha256 || strlen(sha256) != 64) {
        send_error_response(400, "invalid_hash", "Invalid SHA-256 hash format", "Hash must be 64 hex characters");
        log_request("DELETE", "/delete", "none", 400);
        return;
    }
    
    // Validate hex characters
    for (int i = 0; i < 64; i++) {
        char c = sha256[i];
        if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
            send_error_response(400, "invalid_hash", "Invalid SHA-256 hash format", "Hash must contain only hex characters");
            log_request("DELETE", "/delete", "none", 400);
            return;
        }
    }
    
    // Require authorization for delete operations
    const char* auth_header = getenv("HTTP_AUTHORIZATION");
    if (!auth_header) {
        send_error_response(401, "authorization_required", "Authorization required for delete operations", 
                          "Delete operations require a valid Nostr authorization event");
        log_request("DELETE", "/delete", "missing_auth", 401);
        return;
    }
    
    // Authenticate the request with enhanced rules system
    nostr_request_t request = {
        .operation = "delete",
        .auth_header = auth_header,
        .event = NULL,
        .resource_hash = sha256,
        .mime_type = NULL,
        .file_size = 0,
        .client_ip = getenv("REMOTE_ADDR"),
        .app_context = NULL
    };
    
    nostr_request_result_t result;
    int auth_result = nostr_validate_request(&request, &result);
    
    if (auth_result != NOSTR_SUCCESS || !result.valid) {
        const char* error_type = "authentication_failed";
        const char* message = "Invalid or expired authentication";
        const char* details = result.reason[0] ? result.reason : "The provided Nostr event is invalid, expired, or does not authorize this operation";
        
        // Provide more specific error messages based on the reason
        if (strstr(result.reason, "whitelist")) {
            error_type = "pubkey_not_whitelisted";
            message = "Public key not authorized";
        } else if (strstr(result.reason, "blacklist")) {
            error_type = "access_denied";
            message = "Access denied by policy";
        }
        
        send_error_response(401, error_type, message, details);
        log_request("DELETE", "/delete", "failed", 401);
        return;
    }
    
    // Extract pubkey from authorization for ownership check
    char event_json[4096];
    int parse_result = parse_authorization_header(auth_header, event_json, sizeof(event_json));
    if (parse_result != NOSTR_SUCCESS) {
        send_error_response(401, "authentication_failed", "Failed to parse authorization", 
                          "The provided authorization could not be parsed");
        log_request("DELETE", "/delete", "parse_failed", 401);
        return;
    }
    
    cJSON* event = cJSON_Parse(event_json);
    if (!event) {
        send_error_response(401, "authentication_failed", "Invalid JSON in authorization", 
                          "The provided authorization contains invalid JSON");
        log_request("DELETE", "/delete", "invalid_json", 401);
        return;
    }
    
    cJSON* pubkey_json = cJSON_GetObjectItem(event, "pubkey");
    if (!pubkey_json || !cJSON_IsString(pubkey_json)) {
        cJSON_Delete(event);
        send_error_response(401, "authentication_failed", "Missing pubkey in authorization", 
                          "The provided authorization does not contain a valid pubkey");
        log_request("DELETE", "/delete", "missing_pubkey", 401);
        return;
    }
    
    // Copy auth_pubkey to local buffer before deleting the cJSON object
    char auth_pubkey_copy[256] = {0};
    const char* temp_auth_pubkey = cJSON_GetStringValue(pubkey_json);
    if (temp_auth_pubkey) {
        strncpy(auth_pubkey_copy, temp_auth_pubkey, sizeof(auth_pubkey_copy) - 1);
    }
    cJSON_Delete(event);
    const char* auth_pubkey = auth_pubkey_copy; // Use the copied version
    
    // Check if blob exists in database
    sqlite3* db;
    sqlite3_stmt* stmt;
    int rc;
    
    rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READWRITE, NULL);
    if (rc) {

        send_error_response(500, "database_error", "Failed to access database", "Internal server error");
        log_request("DELETE", "/delete", "authenticated", 500);
        return;
    }
    
    // Query blob metadata and check ownership
    const char* sql = "SELECT uploader_pubkey, type FROM blobs WHERE sha256 = ?";
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {

        sqlite3_close(db);
        send_error_response(500, "database_error", "Failed to prepare query", "Internal server error");
        log_request("DELETE", "/delete", "authenticated", 500);
        return;
    }
    
    sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_ROW) {
        sqlite3_finalize(stmt);
        sqlite3_close(db);
        send_error_response(404, "blob_not_found", "Blob not found", "The specified blob does not exist");
        log_request("DELETE", "/delete", "authenticated", 404);
        return;
    }
    
    // Get blob metadata
    const char* uploader_pubkey = (const char*)sqlite3_column_text(stmt, 0);
    const char* blob_type = (const char*)sqlite3_column_text(stmt, 1);

    
    // Create copies of the strings since they may be invalidated after finalize
    char uploader_pubkey_copy[256] = {0};
    char blob_type_copy[128] = {0};
    
    if (uploader_pubkey) {
        strncpy(uploader_pubkey_copy, uploader_pubkey, sizeof(uploader_pubkey_copy) - 1);
    }
    if (blob_type) {
        strncpy(blob_type_copy, blob_type, sizeof(blob_type_copy) - 1);
    }
    
    sqlite3_finalize(stmt);
    
          
    // Check ownership - only the uploader can delete
if (!uploader_pubkey_copy[0] || strcmp(uploader_pubkey_copy, auth_pubkey) != 0) {
        sqlite3_close(db);
        send_error_response(403, "access_denied", "Access denied", "You can only delete blobs that you uploaded");
        log_request("DELETE", "/delete", "ownership_denied", 403);
        return;
    } else {
    }
    

    
    // Delete from database first
    const char* delete_sql = "DELETE FROM blobs WHERE sha256 = ?";
    rc = sqlite3_prepare_v2(db, delete_sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {

        sqlite3_close(db);
        send_error_response(500, "database_error", "Failed to prepare delete", "Internal server error");
        log_request("DELETE", "/delete", "authenticated", 500);
        return;
    }
    
    sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    
    if (rc != SQLITE_DONE) {

        send_error_response(500, "database_error", "Failed to delete blob metadata", "Internal server error");
        log_request("DELETE", "/delete", "authenticated", 500);
        return;
    }
    

    
    // Determine file extension from MIME type and delete physical file
    const char* extension = "";
    if (strstr(blob_type_copy, "image/jpeg")) {
        extension = ".jpg";
    } else if (strstr(blob_type_copy, "image/webp")) {
        extension = ".webp";
    } else if (strstr(blob_type_copy, "image/png")) {
        extension = ".png";
    } else if (strstr(blob_type_copy, "image/gif")) {
        extension = ".gif";
    } else if (strstr(blob_type_copy, "video/mp4")) {
        extension = ".mp4";
    } else if (strstr(blob_type_copy, "video/webm")) {
        extension = ".webm";
    } else if (strstr(blob_type_copy, "audio/mpeg")) {
        extension = ".mp3";
    } else if (strstr(blob_type_copy, "audio/ogg")) {
        extension = ".ogg";
    } else if (strstr(blob_type_copy, "text/plain")) {
        extension = ".txt";
    } else {
        extension = ".bin";
    }
    
    char filepath[MAX_PATH_LEN];
    snprintf(filepath, sizeof(filepath), "blobs/%s%s", sha256, extension);
    
    // Delete the physical file
    if (unlink(filepath) != 0) {
        // Warning: failed to delete physical file
    } else {
        // Successfully deleted physical file
    }


    

    // Return success response
    printf("Status: 200 OK\r\n");
    printf("Content-Type: application/json\r\n\r\n");
    printf("{\n");
    printf("  \"message\": \"Blob deleted successfully\",\n");
    printf("  \"sha256\": \"%s\"\n", sha256);
    printf("}\n");

    log_request("DELETE", "/delete", "authenticated", 200);
}

// Handle PUT /upload requests
void handle_upload_request(void) {
    
    // Log the incoming request
    log_request("PUT", "/upload", "pending", 0);
    
    // Get HTTP headers
    const char* content_type = getenv("CONTENT_TYPE");
    const char* content_length_str = getenv("CONTENT_LENGTH");
    


    
    // Validate required headers
    if (!content_type) {
        send_error_response(400, "missing_header", "Content-Type header required", "The Content-Type header must be specified for file uploads");
        log_request("PUT", "/upload", "none", 400);
        return;
    }
    
    if (!content_length_str) {
        send_error_response(400, "missing_header", "Content-Length header required", "The Content-Length header must be specified for file uploads");
        log_request("PUT", "/upload", "none", 400);
        return;
    }
    
    long content_length = atol(content_length_str);
    if (content_length <= 0 || content_length > 100 * 1024 * 1024) { // 100MB limit
        send_error_response(413, "payload_too_large", "File size must be between 1 byte and 100MB", "Maximum allowed file size is 100MB");
        log_request("PUT", "/upload", "none", 413);
        return;
    }
    
    // Get Authorization header for authentication
    const char* auth_header = getenv("HTTP_AUTHORIZATION");

    
    // Store uploader pubkey for metadata (will be extracted during auth if provided)
    const char* uploader_pubkey = NULL;
    if (auth_header) {
        log_request("PUT", "/upload", "auth_provided", 0);
    } else {
        log_request("PUT", "/upload", "anonymous", 0);
    }
    
    // Read file data from stdin
    unsigned char* file_data = malloc(content_length);
    if (!file_data) {
        printf("Status: 500 Internal Server Error\r\n");
        printf("Content-Type: text/plain\r\n\r\n");
        printf("Memory allocation failed\n");
        return;
    }
    
    size_t bytes_read = fread(file_data, 1, content_length, stdin);
    if (bytes_read != (size_t)content_length) {

        free(file_data);
        printf("Status: 400 Bad Request\r\n");
        printf("Content-Type: text/plain\r\n\r\n");
        printf("Failed to read complete file data\n");
        return;
    }

    // Calculate SHA-256 hash using nostr_core function
    unsigned char hash[32];


    
    if (nostr_sha256(file_data, content_length, hash) != NOSTR_SUCCESS) {
        free(file_data);
        printf("Status: 500 Internal Server Error\r\n");
        printf("Content-Type: text/plain\r\n\r\n");
        printf("Hash calculation failed\n");
        return;
    }
    
    // Convert hash to hex string
    char sha256_hex[65];
    nostr_bytes_to_hex(hash, 32, sha256_hex);

    fflush(stderr);
    

    
    // Use new unified request validation system
    fprintf(stderr, "AUTH: About to perform authentication - auth_header present: %s\r\n", auth_header ? "YES" : "NO");
    
    // Create request structure for validation
    nostr_request_t request = {
        .operation = "upload",
        .auth_header = auth_header,
        .event = NULL,
        .resource_hash = sha256_hex,
        .mime_type = content_type,
        .file_size = content_length,
        .client_ip = getenv("REMOTE_ADDR"),
        .app_context = NULL
    };
    
    nostr_request_result_t result;
    int auth_result = nostr_validate_request(&request, &result);
    fprintf(stderr, "AUTH: nostr_validate_request returned: %d, valid: %d, reason: %s\r\n",
            auth_result, result.valid, result.reason);
    
    if (auth_result == NOSTR_SUCCESS && !result.valid) {
        free(file_data);
        
        // Use the detailed reason from the authentication system
        const char* error_type = "authentication_failed";
        const char* message = "Authentication failed";
        const char* details = result.reason[0] ? result.reason : "The request failed authentication";
        
        // Provide more specific error types based on the reason content
        if (strstr(result.reason, "whitelist")) {
            error_type = "pubkey_not_whitelisted";
            message = "Public key not authorized";
        } else if (strstr(result.reason, "blacklist")) {
            error_type = "access_denied";
            message = "Access denied by policy";
        } else if (strstr(result.reason, "expired")) {
            error_type = "event_expired";
            message = "Authentication event expired";
        } else if (strstr(result.reason, "signature")) {
            error_type = "invalid_signature";
            message = "Invalid cryptographic signature";
        } else if (strstr(result.reason, "size")) {
            error_type = "file_too_large";
            message = "File size exceeds policy limits";
        } else if (strstr(result.reason, "MIME") || strstr(result.reason, "mime")) {
            error_type = "unsupported_type";
            message = "File type not allowed by policy";
        } else if (strstr(result.reason, "hash")) {
            error_type = "hash_blocked";
            message = "File hash blocked by policy";
        } else if (strstr(result.reason, "format") || strstr(result.reason, "invalid")) {
            error_type = "invalid_format";
            message = "Invalid authorization format";
        }
        
        send_error_response(401, error_type, message, details);
        log_request("PUT", "/upload", "auth_failed", 401);
        return;
    }
    
    // Extract uploader pubkey from validation result if auth was provided
    if (auth_header && result.pubkey[0]) {
        static char pubkey_buffer[256];
        strncpy(pubkey_buffer, result.pubkey, sizeof(pubkey_buffer)-1);
        pubkey_buffer[sizeof(pubkey_buffer)-1] = '\0';
        uploader_pubkey = pubkey_buffer;
    }
    

    
    // Get dimensions from in-memory buffer before saving file
    int width = 0, height = 0;
    nip94_get_dimensions(file_data, content_length, content_type, &width, &height);
    
    // Determine file extension from Content-Type using centralized mapping
    const char* extension = mime_to_extension(content_type);
    
    // Save file to blobs directory with SHA-256 + extension
    char filepath[MAX_PATH_LEN];
    snprintf(filepath, sizeof(filepath), "blobs/%s%s", sha256_hex, extension);
    

    
    FILE* outfile = fopen(filepath, "wb");
    if (!outfile) {
        free(file_data);
        printf("Status: 500 Internal Server Error\r\n");
        printf("Content-Type: text/plain\r\n\r\n");
        printf("Failed to create file\n");
        return;
    }
    
    size_t bytes_written = fwrite(file_data, 1, content_length, outfile);
    fclose(outfile);
    
    // Set file permissions to 644 (owner read/write, group/others read) - standard for web files
    if (chmod(filepath, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) {
        fprintf(stderr, "WARNING: Failed to set file permissions for %s\r\n", filepath);
        // Continue anyway - this is not a fatal error
    } else {

    }
    free(file_data);
    
    if (bytes_written != (size_t)content_length) {
        // Clean up partial file
        unlink(filepath);
        printf("Status: 500 Internal Server Error\r\n");
        printf("Content-Type: text/plain\r\n\r\n");
        printf("Failed to write complete file\n");
        return;
    }
    

    
    // Extract filename from Content-Disposition header if present
    const char* filename = NULL;
    const char* content_disposition = getenv("HTTP_CONTENT_DISPOSITION");

    
    if (content_disposition) {

        // Look for filename= in Content-Disposition header
        const char* filename_start = strstr(content_disposition, "filename=");
        if (filename_start) {

            filename_start += 9; // Skip "filename="

            
            // Handle quoted filenames
            if (*filename_start == '"') {

                filename_start++; // Skip opening quote
                // Find closing quote
                const char* filename_end = strchr(filename_start, '"');
                if (filename_end) {
                    // Extract filename between quotes
                    static char filename_buffer[256];
                    size_t filename_len = filename_end - filename_start;

                    if (filename_len < sizeof(filename_buffer)) {
                        strncpy(filename_buffer, filename_start, filename_len);
                        filename_buffer[filename_len] = '\0';
                        filename = filename_buffer;

                    } else {

                    }
                } else {

                }
            } else {

                // Unquoted filename - extract until space or end
                const char* filename_end = filename_start;
                while (*filename_end && *filename_end != ' ' && *filename_end != ';') {
                    filename_end++;
                }
                static char filename_buffer[256];
                size_t filename_len = filename_end - filename_start;

                if (filename_len < sizeof(filename_buffer)) {
                    strncpy(filename_buffer, filename_start, filename_len);
                    filename_buffer[filename_len] = '\0';
                    filename = filename_buffer;

                } else {

                }
            }
        } else {

        }
    } else {

    }
    

    
    // Store blob metadata in database
    time_t uploaded_time = time(NULL);
    if (!insert_blob_metadata(sha256_hex, content_length, content_type, uploaded_time, uploader_pubkey, filename)) {
        // Database insertion failed - clean up the physical file to maintain consistency

        unlink(filepath);
        printf("Status: 500 Internal Server Error\r\n");
        printf("Content-Type: text/plain\r\n\r\n");
        printf("Failed to store blob metadata\n");
        return;
    }
    

    
    // Get origin from config
    char origin[256];
    nip94_get_origin(origin, sizeof(origin));
    
    // Build canonical blob URL
    char blob_url[512];
    nip94_build_blob_url(origin, sha256_hex, content_type, blob_url, sizeof(blob_url));
    
    // Return success response with blob descriptor
    printf("Status: 200 OK\r\n");
    printf("Content-Type: application/json\r\n\r\n");
    printf("{\n");
    printf("  \"sha256\": \"%s\",\n", sha256_hex);
    printf("  \"size\": %ld,\n", content_length);
    printf("  \"type\": \"%s\",\n", content_type);
    printf("  \"uploaded\": %ld,\n", uploaded_time);
    printf("  \"url\": \"%s\"", blob_url);
    
    // Add NIP-94 metadata if enabled
    if (nip94_is_enabled()) {
        printf(",\n");
        nip94_emit_field(blob_url, content_type, sha256_hex, content_length, width, height);
    }
    
    printf("\n}\n");
    

}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             MAIN
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////


int main(void) {
    fprintf(stderr, "STARTUP: FastCGI application starting up\r\n");
    fflush(stderr);

    // Initialize server configuration and identity
    // Try file-based config first, then fall back to database config
    char config_path[512];
    int config_loaded = 0;
    
    if (get_config_file_path(config_path, sizeof(config_path))) {
        fprintf(stderr, "STARTUP: Checking for config file at: %s\n", config_path);
        if (load_server_config(config_path)) {
            fprintf(stderr, "STARTUP: File-based configuration loaded successfully\n");
            config_loaded = 1;
        } else {
            fprintf(stderr, "STARTUP: No valid file-based config found, trying database config\n");
        }
    }
    
    // Fall back to database configuration if file config failed
    if (!config_loaded && !initialize_server_config()) {
        fprintf(stderr, "STARTUP: No configuration found - server starting in setup mode\n");
        fprintf(stderr, "STARTUP: Run interactive setup with: ginxsom --setup\n");
        // For interactive mode (when stdin is available), offer setup
        if (isatty(STDIN_FILENO) && get_config_file_path(config_path, sizeof(config_path))) {
            return run_interactive_setup(config_path);
        }
    } else if (!config_loaded) {
        fprintf(stderr, "STARTUP: Database configuration loaded successfully\n");
    }
    
    // CRITICAL: Initialize nostr crypto system for cryptographic operations
    fprintf(stderr, "STARTUP: Initializing nostr crypto system...\r\n");
    if (nostr_crypto_init() != 0) {
        fprintf(stderr, "FATAL ERROR: Failed to initialize nostr crypto system\r\n");
        return 1;
    }
    fprintf(stderr, "STARTUP: nostr crypto system initialized successfully\r\n");
    
    // Initialize request validator system
    fprintf(stderr, "STARTUP: Initializing request validator system...\r\n");
    if (nostr_request_validator_init(DB_PATH, "ginxsom") != NOSTR_SUCCESS) {
        fprintf(stderr, "FATAL ERROR: Failed to initialize request validator system\r\n");
        return 1;
    }
    fprintf(stderr, "STARTUP: Request validator system initialized successfully\r\n");
    fflush(stderr);
    while (FCGI_Accept() >= 0) {
        const char* request_method = getenv("REQUEST_METHOD");
        const char* request_uri = getenv("REQUEST_URI");
        
        
        if (!request_method || !request_uri) {
            printf("Status: 400 Bad Request\r\n");
            printf("Content-Type: text/plain\r\n\r\n");
            printf("Invalid request\n");
            continue;
        }
        
        // Route HEAD /upload pre-flight (BUD-06) before generic HEAD blob handler
        if (strcmp(request_method, "HEAD") == 0 && strcmp(request_uri, "/upload") == 0) {
            // Handle HEAD /upload requests (BUD-06 pre-flight validation)
            handle_head_upload_request();
        } else if (strcmp(request_method, "HEAD") == 0) {
            // Handle HEAD requests for blob metadata
            const char* sha256 = extract_sha256_from_uri(request_uri);
            if (sha256) {
                handle_head_request(sha256);
                log_request("HEAD", request_uri, "none", 200); // Assuming success - could be enhanced to track actual status
            } else {
                printf("Status: 400 Bad Request\r\n");
                printf("Content-Type: text/plain\r\n\r\n");
                printf("Invalid SHA-256 hash in URI\n");
                log_request("HEAD", request_uri, "none", 400);
            }
        } else if (strcmp(request_method, "PUT") == 0 && strcmp(request_uri, "/upload") == 0) {
            // Handle PUT /upload requests with authentication
            handle_upload_request();
        } else if (strcmp(request_method, "PUT") == 0 && strcmp(request_uri, "/mirror") == 0) {
            // Handle PUT /mirror requests (BUD-04)
            handle_mirror_request();
        } else if (strcmp(request_method, "PUT") == 0 && strcmp(request_uri, "/report") == 0) {
            // Handle PUT /report requests (BUD-09)
            handle_report_request();
        } else if (strncmp(request_uri, "/api/", 5) == 0) {
            // Handle admin API requests
            handle_admin_api_request(request_method, request_uri);
        } else if (strcmp(request_method, "GET") == 0 && strncmp(request_uri, "/list/", 6) == 0) {
            // Handle GET /list/<pubkey> requests
            const char* pubkey = request_uri + 6; // Skip "/list/"
            
            // Extract pubkey from URI (remove query string if present)
            static char pubkey_buffer[65];
            const char* query_start = strchr(pubkey, '?');
            size_t pubkey_len;
            
            if (query_start) {
                pubkey_len = query_start - pubkey;
            } else {
                pubkey_len = strlen(pubkey);
            }
            
            if (pubkey_len == 64) { // Valid pubkey length
                strncpy(pubkey_buffer, pubkey, 64);
                pubkey_buffer[64] = '\0';
                handle_list_request(pubkey_buffer);
            } else {
                send_error_response(400, "invalid_pubkey", "Invalid pubkey format", "Pubkey must be 64 hex characters");
                log_request("GET", request_uri, "none", 400);
            }
        } else if (strcmp(request_method, "DELETE") == 0) {
            // Handle DELETE /<sha256> requests
            const char* sha256 = extract_sha256_from_uri(request_uri);

            if (sha256) {
                handle_delete_request(sha256);
            } else {
                send_error_response(400, "invalid_hash", "Invalid SHA-256 hash in URI", "URI must contain a valid 64-character hex hash");
                log_request("DELETE", request_uri, "none", 400);
            }
        } else {
            // Other methods not implemented yet
            printf("Status: 501 Not Implemented\r\n");
            printf("Content-Type: text/plain\r\n\r\n");
            printf("Method %s not implemented\n", request_method);
            log_request(request_method, request_uri, "none", 501);
        }
    }
    
    return 0;
}
