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

#define _GNU_SOURCE
#include "ginxsom.h"
#include "relay_client.h"
#include "admin_commands.h"
#include "../nostr_core_lib/nostr_core/nostr_common.h"
#include "../nostr_core_lib/nostr_core/utils.h"
#include <getopt.h>
#include <curl/curl.h>
#include <sqlite3.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>
#include <sched.h>

// Centralized logging system (declaration in ginxsom.h)
void app_log(log_level_t level, const char *format, ...) {
  FILE *log_file = fopen("logs/app/app.log", "a");
  if (!log_file) {
    return; // Silently fail if we can't open log file
  }
  
  // Get timestamp
  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);
  
  // Get log level string
  const char *level_str;
  switch (level) {
    case LOG_DEBUG: level_str = "DEBUG"; break;
    case LOG_INFO:  level_str = "INFO"; break;
    case LOG_WARN:  level_str = "WARN"; break;
    case LOG_ERROR: level_str = "ERROR"; break;
    default:        level_str = "UNKNOWN"; break;
  }
  
  // Write log prefix with timestamp, PID, and level
  fprintf(log_file, "[%s] [PID:%d] [%s] ", timestamp, getpid(), level_str);
  
  // Write formatted message
  va_list args;
  va_start(args, format);
  vfprintf(log_file, format, args);
  va_end(args);
  
  // Ensure newline
  fprintf(log_file, "\n");
  
  fclose(log_file);
}

#define MAX_SHA256_LEN 65
#define MAX_PATH_LEN 4096
#define MAX_MIME_LEN 128

// Configuration variables - can be overridden via command line
char g_db_path[MAX_PATH_LEN] = "";  // Will be set based on pubkey
char g_storage_dir[MAX_PATH_LEN] = ".";

// Key management variables
char g_admin_pubkey[65] = "";  // Admin public key for authorization
char g_blossom_seckey[65] = "";  // Blossom server private key for decryption/signing
char g_blossom_pubkey[65] = "";  // Blossom server public key (derived from seckey)
int g_generate_keys = 0;       // Flag to generate keys on startup (deprecated)

// Use global configuration variables
#define DB_PATH g_db_path


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


// 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";
}


// Database initialization function
int initialize_database(const char *db_path) {
  sqlite3 *db;
  char *err_msg = NULL;
  int rc;

  // Check if database file exists
  struct stat st;
  int db_exists = (stat(db_path, &st) == 0);

  // Open database with CREATE flag
  rc = sqlite3_open_v2(db_path, &db, 
                       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);
    return -1;
  }

  // If database was just created, initialize schema
  if (!db_exists) {
    fprintf(stderr, "Database not found, initializing schema...\n");

    // Enable foreign key constraints
    rc = sqlite3_exec(db, "PRAGMA foreign_keys = ON;", NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to enable foreign keys: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create blobs table
    const char *create_blobs = 
      "CREATE TABLE IF NOT EXISTS blobs ("
      "  sha256 TEXT PRIMARY KEY NOT NULL,"
      "  size INTEGER NOT NULL,"
      "  type TEXT NOT NULL,"
      "  uploaded_at INTEGER NOT NULL,"
      "  uploader_pubkey TEXT,"
      "  filename TEXT,"
      "  CHECK (length(sha256) = 64),"
      "  CHECK (size >= 0),"
      "  CHECK (uploaded_at > 0)"
      ");";
    
    rc = sqlite3_exec(db, create_blobs, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create blobs table: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create config table
    const char *create_config =
      "CREATE TABLE IF NOT EXISTS config ("
      "  key TEXT PRIMARY KEY NOT NULL,"
      "  value TEXT NOT NULL,"
      "  description TEXT,"
      "  created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),"
      "  updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))"
      ");";
    
    rc = sqlite3_exec(db, create_config, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create config table: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create system table for runtime metrics (read-only, updated by server)
    const char *create_system =
      "CREATE TABLE IF NOT EXISTS system ("
      "  key TEXT PRIMARY KEY NOT NULL,"
      "  value TEXT NOT NULL,"
      "  updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))"
      ");";
    
    rc = sqlite3_exec(db, create_system, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create system table: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create auth_rules table (c-relay compatible schema)
    const char *create_auth_rules =
      "CREATE TABLE IF NOT EXISTS auth_rules ("
      "  id INTEGER PRIMARY KEY AUTOINCREMENT,"
      "  rule_type TEXT NOT NULL,"
      "  pattern_type TEXT NOT NULL,"
      "  pattern_value TEXT NOT NULL,"
      "  active INTEGER NOT NULL DEFAULT 1,"
      "  created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),"
      "  updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))"
      ");";
    
    rc = sqlite3_exec(db, create_auth_rules, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create auth_rules table: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create indexes
    const char *create_indexes =
      "CREATE INDEX IF NOT EXISTS idx_blobs_uploaded_at ON blobs(uploaded_at);"
      "CREATE INDEX IF NOT EXISTS idx_blobs_uploader_pubkey ON blobs(uploader_pubkey);"
      "CREATE INDEX IF NOT EXISTS idx_blobs_type ON blobs(type);"
      "CREATE INDEX IF NOT EXISTS idx_config_updated_at ON config(updated_at);"
      "CREATE INDEX IF NOT EXISTS idx_auth_rules_type ON auth_rules(rule_type);"
      "CREATE INDEX IF NOT EXISTS idx_auth_rules_pattern ON auth_rules(pattern_type, pattern_value);"
      "CREATE INDEX IF NOT EXISTS idx_auth_rules_active ON auth_rules(active);";
    
    rc = sqlite3_exec(db, create_indexes, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create indexes: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Insert default configuration
    const char *insert_config =
      "INSERT OR IGNORE INTO config (key, value, description) VALUES"
      "  ('max_file_size', '104857600', 'Maximum file size in bytes (100MB)'),"
      "  ('auth_rules_enabled', 'true', 'Whether authentication rules are enabled for uploads'),"
      "  ('server_name', 'ginxsom', 'Server name for responses'),"
      "  ('admin_pubkey', '', 'Admin public key for API access'),"
      "  ('admin_enabled', 'true', 'Whether admin API is enabled'),"
      "  ('nip42_require_auth', 'false', 'Enable NIP-42 challenge/response authentication'),"
      "  ('nip42_challenge_timeout', '600', 'NIP-42 challenge timeout in seconds'),"
      "  ('nip42_time_tolerance', '300', 'NIP-42 timestamp tolerance in seconds'),"
      "  ('enable_relay_connect', 'true', 'Enable connection to Nostr relays'),"
      "  ('kind_0_content', '{\"name\":\"Ginxsom Blossom Server\",\"about\":\"A Nostr-enabled Blossom media server\",\"picture\":\"\"}', 'JSON content for Kind 0 profile event'),"
      "  ('kind_10002_tags', '[\"wss://relay.laantungir.net\"]', 'JSON array of relay URLs for Kind 10002');";
    
    rc = sqlite3_exec(db, insert_config, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to insert default config: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create storage_stats view (legacy - kept for backward compatibility)
    const char *create_view =
      "CREATE VIEW IF NOT EXISTS storage_stats AS "
      "SELECT "
      "  COUNT(*) as total_blobs, "
      "  SUM(size) as total_bytes, "
      "  AVG(size) as avg_blob_size, "
      "  MIN(uploaded_at) as first_upload, "
      "  MAX(uploaded_at) as last_upload, "
      "  COUNT(DISTINCT uploader_pubkey) as unique_uploaders "
      "FROM blobs;";
    
    rc = sqlite3_exec(db, create_view, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create storage_stats view: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create blob_overview view for admin dashboard with system metrics
    const char *create_overview_view =
      "CREATE VIEW IF NOT EXISTS blob_overview AS "
      "SELECT "
      "  COUNT(*) as total_blobs, "
      "  COALESCE(SUM(size), 0) as total_bytes, "
      "  MIN(uploaded_at) as first_upload, "
      "  MAX(uploaded_at) as last_upload, "
      "  (SELECT value FROM system WHERE key = 'version') as version, "
      "  (SELECT value FROM system WHERE key = 'process_id') as process_id, "
      "  (SELECT value FROM system WHERE key = 'memory_mb') as memory_mb, "
      "  (SELECT value FROM system WHERE key = 'cpu_core') as cpu_core, "
      "  (SELECT value FROM system WHERE key = 'fs_blob_count') as fs_blob_count, "
      "  (SELECT value FROM system WHERE key = 'fs_blob_size_mb') as fs_blob_size_mb "
      "FROM blobs;";
    
    rc = sqlite3_exec(db, create_overview_view, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create blob_overview view: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create blob_type_distribution view for MIME type statistics
    const char *create_type_view =
      "CREATE VIEW IF NOT EXISTS blob_type_distribution AS "
      "SELECT "
      "  type as mime_type, "
      "  COUNT(*) as blob_count, "
      "  SUM(size) as total_bytes, "
      "  ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM blobs), 2) as percentage "
      "FROM blobs "
      "GROUP BY type "
      "ORDER BY blob_count DESC;";
    
    rc = sqlite3_exec(db, create_type_view, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create blob_type_distribution view: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create blob_time_stats view for time-based statistics
    const char *create_time_view =
      "CREATE VIEW IF NOT EXISTS blob_time_stats AS "
      "SELECT "
      "  COUNT(CASE WHEN uploaded_at >= strftime('%s', 'now', '-1 day') THEN 1 END) as blobs_24h, "
      "  COUNT(CASE WHEN uploaded_at >= strftime('%s', 'now', '-7 days') THEN 1 END) as blobs_7d, "
      "  COUNT(CASE WHEN uploaded_at >= strftime('%s', 'now', '-30 days') THEN 1 END) as blobs_30d "
      "FROM blobs;";
    
    rc = sqlite3_exec(db, create_time_view, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create blob_time_stats view: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    // Create top_uploaders view for pubkey statistics
    const char *create_uploaders_view =
      "CREATE VIEW IF NOT EXISTS top_uploaders AS "
      "SELECT "
      "  uploader_pubkey, "
      "  COUNT(*) as blob_count, "
      "  SUM(size) as total_bytes, "
      "  ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM blobs), 2) as percentage, "
      "  MIN(uploaded_at) as first_upload, "
      "  MAX(uploaded_at) as last_upload "
      "FROM blobs "
      "WHERE uploader_pubkey IS NOT NULL "
      "GROUP BY uploader_pubkey "
      "ORDER BY blob_count DESC "
      "LIMIT 20;";
    
    rc = sqlite3_exec(db, create_uploaders_view, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
      fprintf(stderr, "Failed to create top_uploaders view: %s\n", err_msg);
      sqlite3_free(err_msg);
      sqlite3_close(db);
      return -1;
    }

    fprintf(stderr, "Database schema initialized successfully\n");
  }

  sqlite3_close(db);
  return 0;
}

// Function declarations
void handle_options_request(void);
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);

// Key management functions
int generate_random_private_key_bytes(unsigned char *key_bytes, size_t len);
int generate_server_keypair(void);
int load_server_keys(void);
int store_blossom_private_key(const char *seckey);
int get_blossom_private_key(char *seckey_out, size_t max_len);
int derive_pubkey_from_privkey(const char *privkey_hex, char *pubkey_hex_out);
int validate_database_pubkey_match(const char *db_path, const char *expected_pubkey);
int set_db_path_from_pubkey(const char *pubkey);

// External validator function declarations
const char *nostr_request_validator_get_last_violation_type(void);
int nostr_generate_nip42_challenge(char *challenge_out, size_t challenge_size, const char *client_ip);

// NIP-42 function declarations
void handle_auth_challenge_request(void);

// Handler function declarations with validation support
void handle_delete_request_with_validation(const char *sha256, nostr_request_result_t *validation_result);

// Key management function implementations

// Derive public key from private key hex string
int derive_pubkey_from_privkey(const char *privkey_hex, char *pubkey_hex_out) {
  if (!privkey_hex || strlen(privkey_hex) != 64) {
    fprintf(stderr, "ERROR: Invalid private key format\n");
    return -1;
  }
  
  unsigned char seckey_bytes[32];
  if (nostr_hex_to_bytes(privkey_hex, seckey_bytes, 32) != NOSTR_SUCCESS) {
    fprintf(stderr, "ERROR: Failed to parse private key hex\n");
    return -1;
  }
  
  unsigned char pubkey_bytes[32];
  if (nostr_ec_public_key_from_private_key(seckey_bytes, pubkey_bytes) != NOSTR_SUCCESS) {
    fprintf(stderr, "ERROR: Failed to derive public key\n");
    return -1;
  }
  
  nostr_bytes_to_hex(pubkey_bytes, 32, pubkey_hex_out);
  return 0;
}

// Set database path based on pubkey (db/<pubkey>.db)
int set_db_path_from_pubkey(const char *pubkey) {
  if (!pubkey || strlen(pubkey) != 64) {
    fprintf(stderr, "ERROR: Invalid pubkey for database naming\n");
    return -1;
  }
  
  snprintf(g_db_path, sizeof(g_db_path), "db/%s.db", pubkey);
  fprintf(stderr, "DATABASE: Set database path to %s\n", g_db_path);
  return 0;
}

// Validate that database filename matches the pubkey stored inside
int validate_database_pubkey_match(const char *db_path, const char *expected_pubkey) {
  // Extract pubkey from database filename
  const char *filename = strrchr(db_path, '/');
  if (!filename) {
    filename = db_path;
  } else {
    filename++; // Skip the '/'
  }
  
  // Check if filename matches pattern: <pubkey>.db
  size_t filename_len = strlen(filename);
  if (filename_len != 67) { // 64 chars + ".db" = 67
    fprintf(stderr, "WARNING: Database filename doesn't match pubkey pattern: %s\n", filename);
    return 0; // Don't enforce for non-standard names (backward compatibility)
  }
  
  // Extract pubkey from filename (first 64 chars)
  char filename_pubkey[65];
  strncpy(filename_pubkey, filename, 64);
  filename_pubkey[64] = '\0';
  
  // Compare with expected pubkey
  if (strcasecmp(filename_pubkey, expected_pubkey) != 0) {
    fprintf(stderr, "ERROR: Database pubkey mismatch!\n");
    fprintf(stderr, "  Database filename: %s\n", filename_pubkey);
    fprintf(stderr, "  Expected pubkey:   %s\n", expected_pubkey);
    fprintf(stderr, "  → Database filename doesn't match the keys stored inside\n");
    return -1;
  }
  
  fprintf(stderr, "DATABASE: Pubkey validation passed: %s\n", filename_pubkey);
  return 0;
}

// Generate random private key bytes using /dev/urandom
int generate_random_private_key_bytes(unsigned char *key_bytes, size_t len) {
  FILE *fp = fopen("/dev/urandom", "rb");
  if (!fp) {
    fprintf(stderr, "ERROR: Cannot open /dev/urandom for key generation\n");
    return -1;
  }

  size_t bytes_read = fread(key_bytes, 1, len, fp);
  fclose(fp);

  if (bytes_read != len) {
    fprintf(stderr, "ERROR: Failed to read %zu bytes from /dev/urandom\n", len);
    return -1;
  }

  return 0;
}

// Generate server keypair and store in database
int generate_server_keypair(void) {
  fprintf(stderr, "DEBUG: generate_server_keypair() called\n");
  unsigned char seckey_bytes[32];
  char seckey_hex[65];
  char pubkey_hex[65];

  // Generate random private key
  fprintf(stderr, "DEBUG: Generating random private key...\n");
  if (generate_random_private_key_bytes(seckey_bytes, 32) != 0) {
    fprintf(stderr, "DEBUG: Failed to generate random bytes\n");
    return -1;
  }

  // Validate the private key
  if (nostr_ec_private_key_verify(seckey_bytes) != NOSTR_SUCCESS) {
    fprintf(stderr, "ERROR: Generated invalid private key\n");
    return -1;
  }

  // Convert to hex
  nostr_bytes_to_hex(seckey_bytes, 32, seckey_hex);

  // Derive public key
  unsigned char pubkey_bytes[32];
  if (nostr_ec_public_key_from_private_key(seckey_bytes, pubkey_bytes) != NOSTR_SUCCESS) {
    fprintf(stderr, "ERROR: Failed to derive public key\n");
    return -1;
  }

  // Convert public key to hex
  nostr_bytes_to_hex(pubkey_bytes, 32, pubkey_hex);
  
  // Store pubkey in global variable
  strncpy(g_blossom_pubkey, pubkey_hex, sizeof(g_blossom_pubkey) - 1);
  g_blossom_pubkey[64] = '\0';
  
  // Store seckey in global variable
  strncpy(g_blossom_seckey, seckey_hex, sizeof(g_blossom_seckey) - 1);
  g_blossom_seckey[64] = '\0';

  // Set database path based on pubkey (MUST be done before storing keys)
  if (set_db_path_from_pubkey(pubkey_hex) != 0) {
    fprintf(stderr, "ERROR: Failed to set database path\n");
    return -1;
  }
  
  // Initialize database with new path
  if (initialize_database(g_db_path) != 0) {
    fprintf(stderr, "ERROR: Failed to initialize database at %s\n", g_db_path);
    return -1;
  }

  // Store private key securely
  if (store_blossom_private_key(seckey_hex) != 0) {
    fprintf(stderr, "ERROR: Failed to store blossom private key\n");
    return -1;
  }

  // Store public key in config
  sqlite3 *db;
  sqlite3_stmt *stmt;
  int rc;

  rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE, NULL);
  if (rc) {
    fprintf(stderr, "ERROR: Can't open database for config: %s\n", sqlite3_errmsg(db));
    return -1;
  }

  const char *sql = "INSERT OR REPLACE INTO config (key, value, description) VALUES (?, ?, ?)";
  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "ERROR: SQL prepare failed: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);
    return -1;
  }

  sqlite3_bind_text(stmt, 1, "blossom_pubkey", -1, SQLITE_STATIC);
  sqlite3_bind_text(stmt, 2, pubkey_hex, -1, SQLITE_STATIC);
  sqlite3_bind_text(stmt, 3, "Blossom server's public key for Nostr communication", -1, SQLITE_STATIC);

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

  if (rc != SQLITE_DONE) {
    fprintf(stderr, "ERROR: Failed to store blossom public key in config\n");
    return -1;
  }

  // Display keys for admin setup
  fprintf(stderr, "========================================\n");
  fprintf(stderr, "SERVER KEYPAIR GENERATED SUCCESSFULLY\n");
  fprintf(stderr, "========================================\n");
  fprintf(stderr, "Blossom Public Key:  %s\n", pubkey_hex);
  fprintf(stderr, "Blossom Private Key: %s\n", seckey_hex);
  fprintf(stderr, "Database Path:       %s\n", g_db_path);
  fprintf(stderr, "========================================\n");
  fprintf(stderr, "IMPORTANT: Save the private key securely!\n");
  fprintf(stderr, "This key is used for decrypting admin messages.\n");
  fprintf(stderr, "========================================\n");

  return 0;
}

// Load server keys from database
int load_server_keys(void) {
  fprintf(stderr, "DEBUG: load_server_keys() called\n");
  sqlite3 *db;
  sqlite3_stmt *stmt;
  int rc;

  // Try to load blossom private key
  fprintf(stderr, "DEBUG: Trying to load blossom private key...\n");
  if (get_blossom_private_key(g_blossom_seckey, sizeof(g_blossom_seckey)) != 0) {
    fprintf(stderr, "DEBUG: No blossom private key found in database\n");
    return -1; // Keys must exist in database
  }
  
  fprintf(stderr, "STARTUP: Blossom private key loaded successfully\n");
  
  // Derive public key from private key
  if (derive_pubkey_from_privkey(g_blossom_seckey, g_blossom_pubkey) != 0) {
    fprintf(stderr, "ERROR: Failed to derive public key from private key\n");
    return -1;
  }
  
  fprintf(stderr, "STARTUP: Derived blossom pubkey: %s\n", g_blossom_pubkey);
  
  // Validate database filename matches pubkey
  if (validate_database_pubkey_match(g_db_path, g_blossom_pubkey) != 0) {
    fprintf(stderr, "ERROR: Database pubkey validation failed\n");
    return -1;
  }

  // Load admin pubkey from command line or config
  if (strlen(g_admin_pubkey) == 0) {
    // Try to load from database config
    rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READONLY, NULL);
    if (rc == SQLITE_OK) {
      const char *sql = "SELECT value FROM config WHERE key = 'admin_pubkey'";
      rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
      if (rc == SQLITE_OK) {
        rc = sqlite3_step(stmt);
        if (rc == SQLITE_ROW) {
          const char *pubkey = (const char *)sqlite3_column_text(stmt, 0);
          if (pubkey && strlen(pubkey) == 64) {
            strncpy(g_admin_pubkey, pubkey, sizeof(g_admin_pubkey) - 1);
            fprintf(stderr, "STARTUP: Admin pubkey loaded from config: %s\n", g_admin_pubkey);
          }
        }
        sqlite3_finalize(stmt);
      }
      sqlite3_close(db);
    }
  } else {
    // Store admin pubkey in config if provided via command line
    rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE, NULL);
    if (rc == SQLITE_OK) {
      const char *sql = "INSERT OR REPLACE INTO config (key, value, description) VALUES (?, ?, ?)";
      rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
      if (rc == SQLITE_OK) {
        sqlite3_bind_text(stmt, 1, "admin_pubkey", -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 2, g_admin_pubkey, -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 3, "Admin public key for management authentication", -1, SQLITE_STATIC);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);
      }
      sqlite3_close(db);
    }
  }

  return 0;
}

// Store blossom private key in dedicated table
int store_blossom_private_key(const char *seckey) {
  sqlite3 *db;
  sqlite3_stmt *stmt;
  int rc;

  // Validate key format
  if (!seckey || strlen(seckey) != 64) {
    fprintf(stderr, "ERROR: Invalid blossom private key format\n");
    return -1;
  }

  // Create blossom_seckey table if it doesn't exist
  rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
  if (rc) {
    fprintf(stderr, "ERROR: Can't open database: %s\n", sqlite3_errmsg(db));
    return -1;
  }

  // Create table
  const char *create_sql = "CREATE TABLE IF NOT EXISTS blossom_seckey (id INTEGER PRIMARY KEY CHECK (id = 1), seckey TEXT NOT NULL, created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), CHECK (length(seckey) = 64))";
  rc = sqlite3_exec(db, create_sql, NULL, NULL, NULL);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "ERROR: Failed to create blossom_seckey table: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);
    return -1;
  }

  // Store key
  const char *sql = "INSERT OR REPLACE INTO blossom_seckey (id, seckey) VALUES (1, ?)";
  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "ERROR: SQL prepare failed: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);
    return -1;
  }

  sqlite3_bind_text(stmt, 1, seckey, -1, SQLITE_STATIC);
  rc = sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  sqlite3_close(db);

  if (rc != SQLITE_DONE) {
    fprintf(stderr, "ERROR: Failed to store blossom private key\n");
    return -1;
  }

  return 0;
}

// Get blossom private key from database
int get_blossom_private_key(char *seckey_out, size_t max_len) {
  sqlite3 *db;
  sqlite3_stmt *stmt;
  int rc;

  rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READONLY, NULL);
  if (rc) {
    return -1;
  }

  const char *sql = "SELECT seckey FROM blossom_seckey WHERE id = 1 LIMIT 1";
  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
  if (rc != SQLITE_OK) {
    sqlite3_close(db);
    return -1;
  }

  rc = sqlite3_step(stmt);
  if (rc == SQLITE_ROW) {
    const char *seckey = (const char *)sqlite3_column_text(stmt, 0);
    if (seckey && strlen(seckey) == 64 && strlen(seckey) < max_len) {
      strcpy(seckey_out, seckey);
      sqlite3_finalize(stmt);
      sqlite3_close(db);
      return 0;
    }
  }

  sqlite3_finalize(stmt);
  sqlite3_close(db);
  return -1;
}

// Helper function to count filesystem blobs
static int count_filesystem_blobs(long *total_count, long *total_size_bytes) {
  DIR *dir = opendir(g_storage_dir);
  if (!dir) {
    return -1;
  }
  
  *total_count = 0;
  *total_size_bytes = 0;
  
  struct dirent *entry;
  while ((entry = readdir(dir)) != NULL) {
    // Skip . and ..
    if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
      continue;
    }
    
    // Build full path
    char filepath[MAX_PATH_LEN];
    snprintf(filepath, sizeof(filepath), "%s/%s", g_storage_dir, entry->d_name);
    
    // Get file stats
    struct stat st;
    if (stat(filepath, &st) == 0 && S_ISREG(st.st_mode)) {
      (*total_count)++;
      *total_size_bytes += st.st_size;
    }
  }
  
  closedir(dir);
  return 0;
}

// Helper function to get memory usage in MB from /proc/self/status
static long get_memory_usage_mb(void) {
  FILE *fp = fopen("/proc/self/status", "r");
  if (!fp) {
    return -1;
  }
  
  char line[256];
  long vmrss_kb = -1;
  
  while (fgets(line, sizeof(line), fp)) {
    if (strncmp(line, "VmRSS:", 6) == 0) {
      // Parse VmRSS value (in kB)
      char *p = line + 6;
      while (*p == ' ' || *p == '\t') p++;
      vmrss_kb = atol(p);
      break;
    }
  }
  
  fclose(fp);
  
  if (vmrss_kb > 0) {
    return vmrss_kb / 1024; // Convert kB to MB
  }
  return -1;
}

// Helper function to get CPU core
static int get_cpu_core(void) {
#ifdef __linux__
  return sched_getcpu();
#else
  return -1;
#endif
}

// Update system metrics in system table (key-value pairs)
static int update_system_metrics(void) {
  sqlite3 *db;
  sqlite3_stmt *stmt;
  int rc;
  
  rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE, NULL);
  if (rc != SQLITE_OK) {
    return -1;
  }
  
  // Get system metrics
  int pid = getpid();
  long memory_mb = get_memory_usage_mb();
  int cpu_core = get_cpu_core();
  long fs_blob_count = 0;
  long fs_blob_size = 0;
  count_filesystem_blobs(&fs_blob_count, &fs_blob_size);
  long fs_blob_size_mb = fs_blob_size / (1024 * 1024);
  
  // Prepare INSERT OR REPLACE statement for key-value updates
  const char *sql = "INSERT OR REPLACE INTO system (key, value, updated_at) VALUES (?, ?, strftime('%s', 'now'))";
  
  // Update version
  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
  if (rc == SQLITE_OK) {
    sqlite3_bind_text(stmt, 1, "version", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, VERSION, -1, SQLITE_STATIC);
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);
  }
  
  // Update process_id
  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
  if (rc == SQLITE_OK) {
    char pid_str[32];
    snprintf(pid_str, sizeof(pid_str), "%d", pid);
    sqlite3_bind_text(stmt, 1, "process_id", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, pid_str, -1, SQLITE_TRANSIENT);
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);
  }
  
  // Update memory_mb
  if (memory_mb > 0) {
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
      char mem_str[32];
      snprintf(mem_str, sizeof(mem_str), "%ld", memory_mb);
      sqlite3_bind_text(stmt, 1, "memory_mb", -1, SQLITE_STATIC);
      sqlite3_bind_text(stmt, 2, mem_str, -1, SQLITE_TRANSIENT);
      sqlite3_step(stmt);
      sqlite3_finalize(stmt);
    }
  }
  
  // Update cpu_core
  if (cpu_core >= 0) {
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc == SQLITE_OK) {
      char core_str[32];
      snprintf(core_str, sizeof(core_str), "%d", cpu_core);
      sqlite3_bind_text(stmt, 1, "cpu_core", -1, SQLITE_STATIC);
      sqlite3_bind_text(stmt, 2, core_str, -1, SQLITE_TRANSIENT);
      sqlite3_step(stmt);
      sqlite3_finalize(stmt);
    }
  }
  
  // Update fs_blob_count
  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
  if (rc == SQLITE_OK) {
    char count_str[32];
    snprintf(count_str, sizeof(count_str), "%ld", fs_blob_count);
    sqlite3_bind_text(stmt, 1, "fs_blob_count", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, count_str, -1, SQLITE_TRANSIENT);
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);
  }
  
  // Update fs_blob_size_mb
  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
  if (rc == SQLITE_OK) {
    char size_str[32];
    snprintf(size_str, sizeof(size_str), "%ld", fs_blob_size_mb);
    sqlite3_bind_text(stmt, 1, "fs_blob_size_mb", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, size_str, -1, SQLITE_TRANSIENT);
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);
  }
  
  sqlite3_close(db);
  return 0;
}

// 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(g_db_path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 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;

  fprintf(stderr, "get_blob_metadata: Looking for %s in database %s\n", sha256, g_db_path);

  rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READONLY, NULL);
  if (rc) {
    fprintf(stderr, "get_blob_metadata: Can't open database %s: %s\n", g_db_path, 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, "get_blob_metadata: 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);
  fprintf(stderr, "get_blob_metadata: sqlite3_step returned %d (SQLITE_ROW=%d, SQLITE_DONE=%d)\n", rc, SQLITE_ROW, SQLITE_DONE);

  if (rc == SQLITE_ROW) {
    fprintf(stderr, "get_blob_metadata: Found blob in database\n");
    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);

  // Construct path safely
  size_t dir_len = strlen(g_storage_dir);
  size_t sha_len = strlen(sha256);
  size_t ext_len = strlen(extension);
  size_t total_len = dir_len + 1 + sha_len + ext_len + 1; // +1 for /, +1 for null

  if (total_len > sizeof(filepath)) {
    fprintf(stderr, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256, extension);
    return 0;
  }

  // Build path manually to avoid compiler warnings
  memcpy(filepath, g_storage_dir, dir_len);
  filepath[dir_len] = '/';
  memcpy(filepath + dir_len + 1, sha256, sha_len);
  memcpy(filepath + dir_len + 1 + sha_len, extension, ext_len);
  filepath[total_len - 1] = '\0';

  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
}

// Handle GET request for blob - serve the actual file
void handle_get_request(const char *sha256) {
  blob_metadata_t metadata = {0};

  // Debug logging
  fprintf(stderr, "GET: Requesting blob %s\n", sha256);
  fprintf(stderr, "GET: Database path: %s\n", g_db_path);
  fprintf(stderr, "GET: Storage dir: %s\n", g_storage_dir);

  // 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
  if (!get_blob_metadata(sha256, &metadata)) {
    fprintf(stderr, "GET: Blob not found in database\n");
    printf("Status: 404 Not Found\r\n");
    printf("Content-Type: text/plain\r\n\r\n");
    printf("Blob not found\n");
    return;
  }

  // Construct file path
  char filepath[4096];
  const char *extension = mime_to_extension(metadata.type);
  snprintf(filepath, sizeof(filepath), "%s/%s%s", g_storage_dir, sha256, extension);

  // Open and serve the file
  FILE *file = fopen(filepath, "rb");
  if (!file) {
    printf("Status: 404 Not Found\r\n");
    printf("Content-Type: text/plain\r\n\r\n");
    printf("Blob file not found on disk\n");
    return;
  }

  // Send headers
  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);
  printf("X-Ginxsom-Server: FastCGI\r\n");
  
  if (strlen(metadata.filename) > 0) {
    printf("X-Original-Filename: %s\r\n", metadata.filename);
  }
  
  printf("\r\n");

  // Send file content
  char buffer[8192];
  size_t bytes_read;
  while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
    fwrite(buffer, 1, bytes_read, stdout);
  }

  fclose(file);
}

// 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-01 CORS Compliance System (Now handled by nginx)
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

// 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 403:
    status_text = "Forbidden";
    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);
}

// 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);
    }
  }

  // Authentication is handled by centralized validation system
  // Check if auth header was provided to set appropriate status
  const char *auth_header = getenv("HTTP_AUTHORIZATION");
  const char *auth_status = auth_header ? "authenticated" : "none";

  // Query database for blobs uploaded by this pubkey
  sqlite3 *db;
  sqlite3_stmt *stmt;
  int rc;

  rc = sqlite3_open_v2(g_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);
}

// Handle DELETE /<sha256> requests with validation result
void handle_delete_request_with_validation(const char *sha256, nostr_request_result_t *validation_result) {

  // 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;
    }
  }

  // Get authenticated pubkey from centralized validation result
  const char *auth_pubkey = NULL;
  if (validation_result && validation_result->valid && strlen(validation_result->pubkey) == 64) {
    auth_pubkey = validation_result->pubkey;
  } else {
    // No valid authentication - this should have been caught by centralized validation
    send_error_response(401, "unauthorized", "Authentication required for delete operations",
                        "Valid authentication is required to delete blobs");
    log_request("DELETE", "/delete", "unauthenticated", 401);
    return;
  }

  // Check if blob exists in database
  sqlite3 *db;
  sqlite3_stmt *stmt;
  int rc;

  rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 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];
  // Construct path safely
  size_t dir_len = strlen(g_storage_dir);
  size_t sha_len = strlen(sha256);
  size_t ext_len = strlen(extension);
  size_t total_len = dir_len + 1 + sha_len + ext_len + 1; // +1 for /, +1 for null

  if (total_len > sizeof(filepath)) {
    fprintf(stderr, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256, extension);
    // Continue anyway - unlink will fail gracefully
  } else {
    // Build path manually to avoid compiler warnings
    memcpy(filepath, g_storage_dir, dir_len);
    filepath[dir_len] = '/';
    memcpy(filepath + dir_len + 1, sha256, sha_len);
    memcpy(filepath + dir_len + 1 + sha_len, extension, ext_len);
    filepath[total_len - 1] = '\0';
  }

  // 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);


  // 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 storage directory with SHA-256 + extension
  char filepath[MAX_PATH_LEN];
  // Construct path safely
  size_t dir_len = strlen(g_storage_dir);
  size_t sha_len = strlen(sha256_hex);
  size_t ext_len = strlen(extension);
  size_t total_len = dir_len + 1 + sha_len + ext_len + 1; // +1 for /, +1 for null

  if (total_len > sizeof(filepath)) {
    fprintf(stderr, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256_hex, extension);
    printf("Status: 500 Internal Server Error\r\n");

    printf("Content-Type: text/plain\r\n\r\n");
    printf("File path too long\n");
    return;
  }

  // Build path manually to avoid compiler warnings
  memcpy(filepath, g_storage_dir, dir_len);
  filepath[dir_len] = '/';
  memcpy(filepath + dir_len + 1, sha256_hex, sha_len);
  memcpy(filepath + dir_len + 1 + sha_len, extension, ext_len);
  filepath[total_len - 1] = '\0';

  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");
}

// Handle PUT /upload requests with pre-validated file data from validator
void handle_upload_request_with_validation(nostr_request_result_t* validation_result) {

  // 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");
  
  // Extract uploader pubkey from validation result
  const char *uploader_pubkey = NULL;
  if (validation_result && strlen(validation_result->pubkey) == 64) {
    uploader_pubkey = validation_result->pubkey;
    log_request("PUT", "/upload", "authenticated", 0);
  } else if (auth_header) {
    log_request("PUT", "/upload", "auth_provided", 0);
  } else {
    log_request("PUT", "/upload", "anonymous", 0);
  }

  // Use file data from validator if available, otherwise read from stdin
  unsigned char *file_data = NULL;
  size_t file_size = 0;
  int should_free_file_data = 0;
  
  if (validation_result && validation_result->file_data && validation_result->file_size > 0) {
    // Use file data provided by validator
    file_data = validation_result->file_data;
    file_size = validation_result->file_size;
    should_free_file_data = 0; // Validator owns the memory
    fprintf(stderr, "UPLOAD: Using file data from validator (%zu bytes)\n", file_size);
  } else {
    // Fallback: read from stdin (for non-Blossom uploads or when validation didn't provide file data)
    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;
    }
    
    file_size = bytes_read;
    should_free_file_data = 1; // We own the memory
    fprintf(stderr, "UPLOAD: Read file data from stdin (%zu bytes)\n", file_size);
  }

  // Calculate SHA-256 hash using nostr_core function
  unsigned char hash[32];
  if (nostr_sha256(file_data, file_size, hash) != NOSTR_SUCCESS) {
    if (should_free_file_data) 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);


  // Get dimensions from in-memory buffer before saving file
  int width = 0, height = 0;
  nip94_get_dimensions(file_data, file_size, content_type, &width,
                       &height);

  // Determine file extension from Content-Type using centralized mapping
  const char *extension = mime_to_extension(content_type);

  // Save file to storage directory with SHA-256 + extension
  char filepath[MAX_PATH_LEN];
  // Construct path safely
  size_t dir_len = strlen(g_storage_dir);
  size_t sha_len = strlen(sha256_hex);
  size_t ext_len = strlen(extension);
  size_t total_len = dir_len + 1 + sha_len + ext_len + 1; // +1 for /, +1 for null

  if (total_len > sizeof(filepath)) {
    fprintf(stderr, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256_hex, extension);
    printf("Status: 500 Internal Server Error\r\n");

    printf("Content-Type: text/plain\r\n\r\n");
    printf("File path too long\n");
    return;
  }

  // Build path manually to avoid compiler warnings
  memcpy(filepath, g_storage_dir, dir_len);
  filepath[dir_len] = '/';
  memcpy(filepath + dir_len + 1, sha256_hex, sha_len);
  memcpy(filepath + dir_len + 1 + sha_len, extension, ext_len);
  filepath[total_len - 1] = '\0';

  FILE *outfile = fopen(filepath, "wb");
  if (!outfile) {
    if (should_free_file_data) 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, file_size, 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
  }

  // Don't free file_data here if validator owns it - main() will handle cleanup
  if (should_free_file_data) {
    free(file_data);
  }

  if (bytes_written != file_size) {
    // 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 {
        // 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;
        }
      }
    }
  }

  // Store blob metadata in database
  time_t uploaded_time = time(NULL);
  if (!insert_blob_metadata(sha256_hex, file_size, 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;
  }

  // Update system metrics after successful blob upload
  update_system_metrics();

  // 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", (long)file_size);
  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, file_size, width,
                     height);
  }

  printf("\n}\n");
}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             NIP-42 Authentication Support
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//             OPTIONS Preflight Request Handler (BUD-01 CORS)
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

// Handle OPTIONS requests for CORS preflight
void handle_options_request(void) {
  log_request("OPTIONS", "*", "cors_preflight", 0);
  
  printf("Status: 200 OK\r\n");
  
  printf("Content-Length: 0\r\n");
  printf("\r\n");
  // No body for OPTIONS response
  
  log_request("OPTIONS", "*", "cors_preflight", 200);
}

// Handle GET /auth requests to provide NIP-42 challenges
void handle_auth_challenge_request(void) {
  // Log the incoming request
  log_request("GET", "/auth", "challenge_request", 0);

  // Use the validator's challenge generation system
  char challenge_buffer[65];
  const char *client_ip = getenv("REMOTE_ADDR");
  
  // Generate and store challenge using validator system
  int result = nostr_generate_nip42_challenge(challenge_buffer, sizeof(challenge_buffer), client_ip);
  
  if (result != NOSTR_SUCCESS) {
    printf("Status: 500 Internal Server Error\r\n");
    
    printf("Content-Type: application/json\r\n\r\n");
    printf("{\n");
    printf("  \"error\": \"challenge_generation_failed\",\n");
    printf("  \"message\": \"Failed to generate authentication challenge\"\n");
    printf("}\n");
    log_request("GET", "/auth", "challenge_failed", 500);
    return;
  }

  // Calculate expiration (current time + challenge timeout)
  // Default timeout is 600 seconds (10 minutes) as per NIP-42 challenge manager
  time_t expires_at = time(NULL) + 600;

  // Return the challenge as JSON
  printf("Status: 200 OK\r\n");
  
  printf("Content-Type: application/json\r\n\r\n");
  printf("{\n");
  printf("  \"challenge\": \"%s\",\n", challenge_buffer);
  printf("  \"relay\": \"ginxsom\",\n");
  printf("  \"expires\": %ld\n", expires_at);
  printf("}\n");

  log_request("GET", "/auth", "challenge_generated", 200);
}

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

int main(int argc, char *argv[]) {
  // Initialize application logging
  app_log(LOG_INFO, "=== Ginxsom FastCGI Application Starting ===");
  app_log(LOG_INFO, "Process ID: %d", getpid());

  // Parse command line arguments
  int use_test_keys = 0;
  char test_server_privkey[65] = "";
  char specified_db_path[MAX_PATH_LEN] = "";
  int db_path_specified = 0;
  
  for (int i = 1; i < argc; i++) {
    if (strcmp(argv[i], "--db-path") == 0 && i + 1 < argc) {
      strncpy(specified_db_path, argv[i + 1], sizeof(specified_db_path) - 1);
      db_path_specified = 1;
      i++; // Skip next argument
    } else if (strcmp(argv[i], "--storage-dir") == 0 && i + 1 < argc) {
      strncpy(g_storage_dir, argv[i + 1], sizeof(g_storage_dir) - 1);
      i++; // Skip next argument
    } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
      // Use write() directly to avoid FCGI printf redefinition
      const char *help_text =
        "Usage: ginxsom-fcgi [options]\n"
        "Options:\n"
        "  --db-path PATH          Database file path (must match pubkey if keys exist)\n"
        "  --storage-dir DIR       Storage directory for files (default: .)\n"
        "  --admin-pubkey KEY      Admin public key (only used when creating new database)\n"
        "  --server-privkey KEY    Server private key (creates new DB or validates existing)\n"
        "  --test-keys             Use test keys from .test_keys file\n"
        "  --generate-keys         Generate new keypair and create database (deprecated)\n"
        "  --help, -h              Show this help message\n"
        "\n"
        "Database Naming:\n"
        "  Databases are named after the server's public key: db/<pubkey>.db\n"
        "  This ensures database-key consistency and prevents mismatches.\n"
        "\n"
        "Startup Scenarios:\n"
        "  1. No arguments          → Generate new keys, create db/<new_pubkey>.db\n"
        "  2. --db-path <path>      → Open existing database, validate keys match filename\n"
        "  3. --server-privkey <key>→ Create new database with those keys\n"
        "  4. --test-keys           → Use test keys, create/overwrite test database\n"
        "  5. --db-path + --server-privkey → Validate keys match or error\n";
      ssize_t written = write(STDOUT_FILENO, help_text, strlen(help_text));
      (void)written; // Suppress unused variable warning
      return 0;
    } else if (strcmp(argv[i], "--admin-pubkey") == 0 && i + 1 < argc) {
      strncpy(g_admin_pubkey, argv[i + 1], sizeof(g_admin_pubkey) - 1);
      i++; // Skip next argument
    } else if (strcmp(argv[i], "--server-privkey") == 0 && i + 1 < argc) {
      strncpy(test_server_privkey, argv[i + 1], sizeof(test_server_privkey) - 1);
      i++; // Skip next argument
    } else if (strcmp(argv[i], "--test-keys") == 0) {
      use_test_keys = 1;
    } else if (strcmp(argv[i], "--generate-keys") == 0) {
      g_generate_keys = 1;
      fprintf(stderr, "WARNING: --generate-keys is deprecated. Keys are generated automatically when needed.\n");
    } else {
      fprintf(stderr, "Unknown option: %s\n", argv[i]);
      fprintf(stderr, "Use --help for usage information\n");
      return 1;
    }
  }
  
  app_log(LOG_INFO, "Storage directory: %s", g_storage_dir);

  // CRITICAL: Initialize nostr crypto system BEFORE key operations
  app_log(LOG_INFO, "Initializing nostr crypto system...");
  int crypto_init_result = nostr_crypto_init();
  if (crypto_init_result != 0) {
    app_log(LOG_ERROR, "Failed to initialize nostr crypto system (result: %d)", crypto_init_result);
    return 1;
  }
  app_log(LOG_INFO, "Nostr crypto system initialized successfully");

  // ========================================================================
  // DATABASE AND KEY INITIALIZATION - 5 SCENARIOS
  // ========================================================================
  
  // Scenario 4: Test Mode (--test-keys)
  if (use_test_keys) {
    app_log(LOG_INFO, "=== SCENARIO 4: TEST MODE ===");
    
    // Load test keys from .test_keys file
    FILE *keys_file = fopen(".test_keys", "r");
    if (!keys_file) {
      app_log(LOG_ERROR, "Cannot open .test_keys file");
      return 1;
    }
    
    char line[256];
    while (fgets(line, sizeof(line), keys_file)) {
      if (strncmp(line, "ADMIN_PUBKEY='", 14) == 0) {
        char *start = line + 14;
        char *end = strchr(start, '\'');
        if (end && (end - start) == 64) {
          strncpy(g_admin_pubkey, start, 64);
          g_admin_pubkey[64] = '\0';
        }
      }
      else if (strncmp(line, "SERVER_PRIVKEY='", 16) == 0) {
        char *start = line + 16;
        char *end = strchr(start, '\'');
        if (end && (end - start) == 64) {
          strncpy(test_server_privkey, start, 64);
          test_server_privkey[64] = '\0';
          app_log(LOG_DEBUG, "Parsed SERVER_PRIVKEY from .test_keys");
        } else {
          app_log(LOG_ERROR, "Failed to parse SERVER_PRIVKEY (length: %ld)", end ? (long)(end - start) : -1L);
        }
      }
    }
    fclose(keys_file);
    
    app_log(LOG_INFO, "Loaded keys from .test_keys");
    app_log(LOG_INFO, "Admin pubkey: %s", g_admin_pubkey);
    
    // Derive pubkey from test privkey
    if (derive_pubkey_from_privkey(test_server_privkey, g_blossom_pubkey) != 0) {
      app_log(LOG_ERROR, "Failed to derive pubkey from test privkey");
      return 1;
    }
    
    app_log(LOG_INFO, "Server pubkey: %s", g_blossom_pubkey);
    
    // Set database path based on test pubkey
    if (set_db_path_from_pubkey(g_blossom_pubkey) != 0) {
      app_log(LOG_ERROR, "Failed to set database path");
      return 1;
    }
    
    // Test mode ALWAYS overwrites database for clean testing
    app_log(LOG_INFO, "Creating/overwriting test database: %s", g_db_path);
    unlink(g_db_path); // Remove if exists
    
    // Initialize new database
    if (initialize_database(g_db_path) != 0) {
      app_log(LOG_ERROR, "Failed to initialize test database");
      return 1;
    }
    
    // Store test keys
    strncpy(g_blossom_seckey, test_server_privkey, sizeof(g_blossom_seckey) - 1);
    g_blossom_seckey[64] = '\0';
    
    if (store_blossom_private_key(test_server_privkey) != 0) {
      app_log(LOG_ERROR, "Failed to store test private key");
      return 1;
    }
    
    // Store pubkey and admin pubkey in config
    sqlite3 *db;
    sqlite3_stmt *stmt;
    int rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE, NULL);
    if (rc == SQLITE_OK) {
      const char *sql = "INSERT OR REPLACE INTO config (key, value, description) VALUES (?, ?, ?)";
      rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
      if (rc == SQLITE_OK) {
        sqlite3_bind_text(stmt, 1, "blossom_pubkey", -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 2, g_blossom_pubkey, -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 3, "Blossom server's public key (TEST MODE)", -1, SQLITE_STATIC);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);
      }
      
      if (strlen(g_admin_pubkey) > 0) {
        rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
        if (rc == SQLITE_OK) {
          sqlite3_bind_text(stmt, 1, "admin_pubkey", -1, SQLITE_STATIC);
          sqlite3_bind_text(stmt, 2, g_admin_pubkey, -1, SQLITE_STATIC);
          sqlite3_bind_text(stmt, 3, "Admin public key (TEST MODE)", -1, SQLITE_STATIC);
          sqlite3_step(stmt);
          sqlite3_finalize(stmt);
        }
      }
      sqlite3_close(db);
    }
    
    app_log(LOG_INFO, "Test database initialized successfully");
  }
  
  // Scenario 3: Keys Specified (--server-privkey)
  else if (test_server_privkey[0] != '\0') {
    app_log(LOG_INFO, "=== SCENARIO 3: KEYS SPECIFIED ===");
    
    // Derive pubkey from provided privkey
    if (derive_pubkey_from_privkey(test_server_privkey, g_blossom_pubkey) != 0) {
      fprintf(stderr, "ERROR: Invalid server private key\n");
      return 1;
    }
    
    fprintf(stderr, "KEYS: Derived pubkey: %s\n", g_blossom_pubkey);
    
    // Scenario 5: Both database path and keys specified
    if (db_path_specified) {
      fprintf(stderr, "\n=== SCENARIO 5: DATABASE PATH + KEYS ===\n");
      
      // Check if specified path is a directory or file
      struct stat st;
      int is_directory = 0;
      
      if (stat(specified_db_path, &st) == 0) {
        is_directory = S_ISDIR(st.st_mode);
      } else {
        // Path doesn't exist - assume it's meant to be a directory
        is_directory = (specified_db_path[strlen(specified_db_path) - 1] == '/' ||
                       strstr(specified_db_path, ".db") == NULL);
      }
      
      if (is_directory) {
        // Build database path from directory + derived pubkey
        snprintf(g_db_path, sizeof(g_db_path), "%s/%s.db", specified_db_path, g_blossom_pubkey);
        fprintf(stderr, "DATABASE: Using directory path, derived database: %s\n", g_db_path);
      } else {
        // Use specified file path directly
        strncpy(g_db_path, specified_db_path, sizeof(g_db_path) - 1);
        g_db_path[sizeof(g_db_path) - 1] = '\0';
        fprintf(stderr, "DATABASE: Using file path: %s\n", g_db_path);
      }
      
      // Check if database exists
      if (stat(g_db_path, &st) == 0) {
        // Database exists - validate keys match
        fprintf(stderr, "DATABASE: Found existing database, validating keys...\n");
        
        // Load keys from database
        if (get_blossom_private_key(g_blossom_seckey, sizeof(g_blossom_seckey)) != 0) {
          fprintf(stderr, "ERROR: Invalid database: missing server keys\n");
          return 1;
        }
        
        // Compare with provided key
        if (strcmp(g_blossom_seckey, test_server_privkey) != 0) {
          fprintf(stderr, "ERROR: Server private key doesn't match database\n");
          fprintf(stderr, "  Provided key and database keys are different\n");
          return 1;
        }
        
        fprintf(stderr, "VALIDATION: Keys match database - continuing\n");
        
        // Validate pubkey matches filename
        if (validate_database_pubkey_match(g_db_path, g_blossom_pubkey) != 0) {
          return 1;
        }
      } else {
        // Database doesn't exist - create it with provided keys
        fprintf(stderr, "DATABASE: No existing database, creating new one...\n");
        
        // Initialize new database
        if (initialize_database(g_db_path) != 0) {
          fprintf(stderr, "ERROR: Failed to initialize database\n");
          return 1;
        }
        
        // Store keys
        strncpy(g_blossom_seckey, test_server_privkey, sizeof(g_blossom_seckey) - 1);
        g_blossom_seckey[64] = '\0';
        
        if (store_blossom_private_key(test_server_privkey) != 0) {
          fprintf(stderr, "ERROR: Failed to store private key\n");
          return 1;
        }
        
        // Store pubkey in config
        sqlite3 *db;
        sqlite3_stmt *stmt;
        int rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE, NULL);
        if (rc == SQLITE_OK) {
          const char *sql = "INSERT OR REPLACE INTO config (key, value, description) VALUES (?, ?, ?)";
          rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
          if (rc == SQLITE_OK) {
            sqlite3_bind_text(stmt, 1, "blossom_pubkey", -1, SQLITE_STATIC);
            sqlite3_bind_text(stmt, 2, g_blossom_pubkey, -1, SQLITE_STATIC);
            sqlite3_bind_text(stmt, 3, "Blossom server's public key", -1, SQLITE_STATIC);
            sqlite3_step(stmt);
            sqlite3_finalize(stmt);
          }
          
          if (strlen(g_admin_pubkey) > 0) {
            rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
            if (rc == SQLITE_OK) {
              sqlite3_bind_text(stmt, 1, "admin_pubkey", -1, SQLITE_STATIC);
              sqlite3_bind_text(stmt, 2, g_admin_pubkey, -1, SQLITE_STATIC);
              sqlite3_bind_text(stmt, 3, "Admin public key", -1, SQLITE_STATIC);
              sqlite3_step(stmt);
              sqlite3_finalize(stmt);
            }
          }
          sqlite3_close(db);
        }
        
        fprintf(stderr, "DATABASE: New database created successfully\n");
      }
    }
    // Scenario 3 continued: Create new database with provided keys
    else {
      // Set database path based on derived pubkey
      if (set_db_path_from_pubkey(g_blossom_pubkey) != 0) {
        fprintf(stderr, "ERROR: Failed to set database path\n");
        return 1;
      }
      
      // Check if database already exists
      struct stat st;
      if (stat(g_db_path, &st) == 0) {
        fprintf(stderr, "ERROR: Database already exists for this pubkey: %s\n", g_db_path);
        fprintf(stderr, "  Use --db-path to open existing database or use different keys\n");
        return 1;
      }
      
      // Initialize new database
      if (initialize_database(g_db_path) != 0) {
        fprintf(stderr, "ERROR: Failed to initialize database\n");
        return 1;
      }
      
      // Store keys
      strncpy(g_blossom_seckey, test_server_privkey, sizeof(g_blossom_seckey) - 1);
      g_blossom_seckey[64] = '\0';
      
      if (store_blossom_private_key(test_server_privkey) != 0) {
        fprintf(stderr, "ERROR: Failed to store private key\n");
        return 1;
      }
      
      // Store pubkey in config
      sqlite3 *db;
      sqlite3_stmt *stmt;
      int rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE, NULL);
      if (rc == SQLITE_OK) {
        const char *sql = "INSERT OR REPLACE INTO config (key, value, description) VALUES (?, ?, ?)";
        rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
        if (rc == SQLITE_OK) {
          sqlite3_bind_text(stmt, 1, "blossom_pubkey", -1, SQLITE_STATIC);
          sqlite3_bind_text(stmt, 2, g_blossom_pubkey, -1, SQLITE_STATIC);
          sqlite3_bind_text(stmt, 3, "Blossom server's public key", -1, SQLITE_STATIC);
          sqlite3_step(stmt);
          sqlite3_finalize(stmt);
        }
        
        if (strlen(g_admin_pubkey) > 0) {
          rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
          if (rc == SQLITE_OK) {
            sqlite3_bind_text(stmt, 1, "admin_pubkey", -1, SQLITE_STATIC);
            sqlite3_bind_text(stmt, 2, g_admin_pubkey, -1, SQLITE_STATIC);
            sqlite3_bind_text(stmt, 3, "Admin public key", -1, SQLITE_STATIC);
            sqlite3_step(stmt);
            sqlite3_finalize(stmt);
          }
        }
        sqlite3_close(db);
      }
      
      fprintf(stderr, "KEYS: New database created successfully\n");
    }
  }
  
  // Scenario 2: Database Path Specified (--db-path)
  // Note: --db-path should specify a DIRECTORY, not a full file path
  // The actual database filename will be derived from the server's pubkey
  else if (db_path_specified) {
    fprintf(stderr, "\n=== SCENARIO 2: DATABASE DIRECTORY SPECIFIED ===\n");
    
    // Check if specified path is a directory or file
    struct stat st;
    int is_directory = 0;
    
    if (stat(specified_db_path, &st) == 0) {
      is_directory = S_ISDIR(st.st_mode);
    } else {
      // Path doesn't exist - assume it's meant to be a directory
      is_directory = (specified_db_path[strlen(specified_db_path) - 1] == '/' ||
                     strstr(specified_db_path, ".db") == NULL);
    }
    
    if (is_directory) {
      // Treat as directory - will derive filename from pubkey after loading keys
      fprintf(stderr, "DATABASE: Directory specified: %s\n", specified_db_path);
      fprintf(stderr, "DATABASE: Will derive filename from server pubkey\n");
      
      // Look for any .db file that matches the pubkey pattern
      DIR *dir = opendir(specified_db_path);
      int found_db = 0;
      
      if (dir) {
        struct dirent *entry;
        while ((entry = readdir(dir)) != NULL) {
          // Check if filename matches pattern: <64-hex-chars>.db
          size_t name_len = strlen(entry->d_name);
          if (name_len == 67 && strcmp(entry->d_name + 64, ".db") == 0) {
            // Found a potential database file
            snprintf(g_db_path, sizeof(g_db_path), "%s/%s", specified_db_path, entry->d_name);
            found_db = 1;
            fprintf(stderr, "DATABASE: Found existing database: %s\n", g_db_path);
            break;
          }
        }
        closedir(dir);
      }
      
      if (!found_db) {
        // No database found - this is OK, we'll create one if we have keys
        fprintf(stderr, "DATABASE: No existing database found in directory\n");
        // g_db_path will be set later based on pubkey
      }
    } else {
      // Treat as full file path (legacy behavior)
      strncpy(g_db_path, specified_db_path, sizeof(g_db_path) - 1);
      g_db_path[sizeof(g_db_path) - 1] = '\0';
    }
    
    // If we found a database file, try to load it
    if (g_db_path[0] != '\0' && stat(g_db_path, &st) == 0) {
      fprintf(stderr, "DATABASE: Opening existing database: %s\n", g_db_path);
      
      // Load keys from database
      if (load_server_keys() != 0) {
        fprintf(stderr, "ERROR: Failed to load keys from database\n");
        fprintf(stderr, "  → Database may be corrupted or not a valid ginxsom database\n");
        return 1;
      }
      
      fprintf(stderr, "DATABASE: Keys loaded and validated successfully\n");
    } else {
      // No database file exists - we need keys to create one
      fprintf(stderr, "ERROR: No database found and no --server-privkey provided\n");
      fprintf(stderr, "  → Use --server-privkey to create a new database\n");
      return 1;
    }
  }
  
  // Scenario 1: No Arguments (Fresh Start)
  else {
    fprintf(stderr, "\n=== SCENARIO 1: FRESH START (NO ARGUMENTS) ===\n");
    fprintf(stderr, "FRESH START: Generating new server keypair...\n");
    
    // Generate new keypair (this will set g_db_path based on pubkey)
    if (generate_server_keypair() != 0) {
      fprintf(stderr, "ERROR: Failed to generate server keypair\n");
      return 1;
    }
    
    fprintf(stderr, "FRESH START: New instance created successfully\n");
  }
  
  // ========================================================================
  // END DATABASE AND KEY INITIALIZATION
  // ========================================================================
  
  app_log(LOG_INFO, "=== FINAL CONFIGURATION ===");
  app_log(LOG_INFO, "Database path:    %s", g_db_path);
  app_log(LOG_INFO, "Storage directory: %s", g_storage_dir);
  app_log(LOG_INFO, "Server pubkey:    %s", g_blossom_pubkey);
  if (strlen(g_admin_pubkey) > 0) {
    app_log(LOG_INFO, "Admin pubkey:     %s", g_admin_pubkey);
  }
  app_log(LOG_INFO, "===========================");

  // If --generate-keys was specified, exit after key generation
  if (g_generate_keys) {
    app_log(LOG_INFO, "Key generation completed, exiting");
    return 0;
  }

// Initialize server configuration and identity
// Try file-based config first, then fall back to database config
int config_loaded = 0;

// 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

} else if (!config_loaded) {
 fprintf(stderr, "STARTUP: Database configuration loaded successfully\n");
}

  // Initialize request validator system
  app_log(LOG_INFO, "Initializing request validator system...");
  int validator_init_result =
      ginxsom_request_validator_init(g_db_path, "ginxsom");
  if (validator_init_result != NOSTR_SUCCESS) {
    app_log(LOG_ERROR, "Failed to initialize request validator system (result: %d)", validator_init_result);
    return 1;
  }
  app_log(LOG_INFO, "Request validator system initialized successfully");

  // Initialize relay client system
  app_log(LOG_INFO, "Initializing relay client system...");
  int relay_init_result = relay_client_init(g_db_path);
  if (relay_init_result != 0) {
    app_log(LOG_WARN, "Failed to initialize relay client system (result: %d)", relay_init_result);
    app_log(LOG_WARN, "Continuing without relay client functionality");
  } else {
    app_log(LOG_INFO, "Relay client system initialized successfully");
    
    // Start relay connections (this will check enable_relay_connect config)
    app_log(LOG_INFO, "Starting relay client connections...");
    int relay_start_result = relay_client_start();
    if (relay_start_result != 0) {
      app_log(LOG_WARN, "Failed to start relay client (result: %d)", relay_start_result);
      app_log(LOG_WARN, "Relay client disabled - check configuration");
    } else {
      app_log(LOG_INFO, "Relay client started successfully");
    }
  }

  // Initialize admin commands system
  app_log(LOG_INFO, "Initializing admin commands system...");
  int admin_cmd_result = admin_commands_init(g_db_path);
  if (admin_cmd_result != 0) {
    app_log(LOG_WARN, "Failed to initialize admin commands system (result: %d)", admin_cmd_result);
    app_log(LOG_WARN, "Continuing without admin commands functionality");
  } else {
    app_log(LOG_INFO, "Admin commands system initialized successfully");
  }

  // Initialize system metrics at startup
  app_log(LOG_INFO, "Initializing system metrics...");
  if (update_system_metrics() == 0) {
    app_log(LOG_INFO, "System metrics initialized successfully");
  } else {
    app_log(LOG_WARN, "Failed to initialize system metrics");
  }

  /////////////////////////////////////////////////////////////////////
  // THIS IS WHERE THE REQUESTS ENTER THE FastCGI
  /////////////////////////////////////////////////////////////////////
  app_log(LOG_INFO, "FastCGI request loop starting - ready to accept requests");
  
  int first_request = 1;
  while (FCGI_Accept() >= 0) {
    // Test stderr capture on first request
    if (first_request) {
      fprintf(stderr, "FCGI: First request received - testing nginx stderr capture\n");
      fflush(stderr);
      first_request = 0;
    }
    const char *request_method = getenv("REQUEST_METHOD");
    const char *request_uri = getenv("REQUEST_URI");
    const char *auth_header = getenv("HTTP_AUTHORIZATION");

    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;
    }

    /////////////////////////////////////////////////////////////////////
    // HANDLE OPTIONS PREFLIGHT REQUESTS (BUD-01 CORS)
    /////////////////////////////////////////////////////////////////////
    
    if (strcmp(request_method, "OPTIONS") == 0) {
      handle_options_request();
      continue;
    }

    /////////////////////////////////////////////////////////////////////
    // CENTRALIZED REQUEST VALIDATION SYSTEM
    /////////////////////////////////////////////////////////////////////

    // Special case: Root endpoint is public and doesn't require authentication
    if (strcmp(request_method, "GET") == 0 && strcmp(request_uri, "/") == 0) {
      // Handle GET / requests - Server info endpoint (NIP-11)
      printf("Status: 200 OK\r\n");
      printf("Content-Type: application/nostr+json\r\n\r\n");
      printf("{\n");
      printf("  \"server\": \"ginxsom\",\n");
      printf("  \"version\": \"%s\",\n", VERSION);
      printf("  \"description\": \"Ginxsom Blossom Server\",\n");
      printf("  \"pubkey\": \"%s\",\n", g_blossom_pubkey);
      printf("  \"endpoints\": {\n");
      printf("    \"blob_get\": \"GET /<sha256>\",\n");
      printf("    \"blob_head\": \"HEAD /<sha256>\",\n");
      printf("    \"upload\": \"PUT /upload\",\n");
      printf("    \"upload_requirements\": \"HEAD /upload\",\n");
      printf("    \"list\": \"GET /list/<pubkey>\",\n");
      printf("    \"delete\": \"DELETE /<sha256>\",\n");
      printf("    \"mirror\": \"PUT /mirror\",\n");
      printf("    \"report\": \"PUT /report\",\n");
      printf("    \"health\": \"GET /health\",\n");
      printf("    \"auth\": \"GET /auth\"\n");
      printf("  },\n");
      printf("  \"supported_buds\": [\n");
      printf("    \"BUD-01\",\n");
      printf("    \"BUD-02\",\n");
      printf("    \"BUD-04\",\n");
      printf("    \"BUD-06\",\n");
      printf("    \"BUD-08\",\n");
      printf("    \"BUD-09\"\n");
      printf("  ],\n");
      printf("  \"limits\": {\n");
      printf("    \"max_upload_size\": 104857600,\n");
      printf("    \"supported_mime_types\": [\n");
      printf("      \"image/jpeg\",\n");
      printf("      \"image/png\",\n");
      printf("      \"image/webp\",\n");
      printf("      \"image/gif\",\n");
      printf("      \"video/mp4\",\n");
      printf("      \"video/webm\",\n");
      printf("      \"audio/mpeg\",\n");
      printf("      \"audio/ogg\",\n");
      printf("      \"text/plain\",\n");
      printf("      \"application/pdf\"\n");
      printf("    ]\n");
      printf("  },\n");
      printf("  \"authentication\": {\n");
      printf("    \"required_for_upload\": false,\n");
      printf("    \"required_for_delete\": true,\n");
      printf("    \"required_for_list\": false,\n");
      printf("    \"nip42_enabled\": true\n");
      printf("  }\n");
      printf("}\n");
      log_request("GET", "/", "server_info", 200);
      continue;
    }

    // Determine operation from request method and URI
    const char *operation = "unknown";
    const char *resource_hash = NULL;

    if (strcmp(request_method, "HEAD") == 0 && strcmp(request_uri, "/upload") == 0) {
      operation = "head_upload";
    } else if (strcmp(request_method, "HEAD") == 0) {
      operation = "head";
      resource_hash = extract_sha256_from_uri(request_uri);
    } else if (strcmp(request_method, "PUT") == 0 && strcmp(request_uri, "/upload") == 0) {
      operation = "upload";
    } else if (strcmp(request_method, "PUT") == 0 && strcmp(request_uri, "/mirror") == 0) {
      operation = "mirror";
    } else if (strcmp(request_method, "PUT") == 0 && strcmp(request_uri, "/report") == 0) {
      operation = "report";
    } else if (strncmp(request_uri, "/admin", 6) == 0) {
      operation = "admin_interface";  // Public static files - no auth required
    } else if (strncmp(request_uri, "/api/", 5) == 0) {
      // Check if this is a static file request or API request
      const char *path = request_uri + 5; // Skip "/api/"
      int is_static_file = 0;
      
      // Check for static file extensions or root /api path
      if (strstr(path, ".html") || strstr(path, ".css") || strstr(path, ".js") ||
          strlen(path) == 0 || strcmp(path, "/") == 0) {
        is_static_file = 1;
      }
      
      if (is_static_file) {
        operation = "admin_interface";  // Public static files - no auth required
      } else {
        operation = "admin";
        // Special case: POST /api/admin uses Kind 23458 events for authentication
        // Skip centralized validation for these requests
        if (strcmp(request_method, "POST") == 0 && strcmp(request_uri, "/api/admin") == 0) {
          operation = "admin_event";  // Mark as special case
        }
      }
    } else if (strcmp(request_method, "GET") == 0 && strncmp(request_uri, "/list/", 6) == 0) {
      operation = "list";
    } else if (strcmp(request_method, "GET") == 0 && strcmp(request_uri, "/auth") == 0) {
      operation = "challenge";
    } else if (strcmp(request_method, "GET") == 0 && extract_sha256_from_uri(request_uri) != NULL) {
      operation = "get_blob";  // Public blob retrieval - no auth required
      resource_hash = extract_sha256_from_uri(request_uri);
    } else if (strcmp(request_method, "DELETE") == 0) {
      operation = "delete";
      resource_hash = extract_sha256_from_uri(request_uri);
    }
    
    // Declare result structure
    nostr_request_result_t result;
    
    // Create unified request structure
    nostr_unified_request_t request = {
        .operation = operation,
        .auth_header = auth_header,
        .event = NULL,
        .resource_hash = resource_hash,
        .mime_type = getenv("CONTENT_TYPE"),
        .file_size = getenv("CONTENT_LENGTH") ? atol(getenv("CONTENT_LENGTH")) : 0,
        .request_url = "ginxsom",
        .challenge_id = NULL,           // Validator will extract from NIP-42 event tags
        .nip42_enabled = 1,             // Let validator check actual config
        .client_ip = getenv("REMOTE_ADDR"),
        .app_context = NULL
    };

    // Validate the request through unified system
    int validation_result = nostr_validate_unified_request(&request, &result);

    // Handle validation failures (except for certain cases)
    if (validation_result != NOSTR_SUCCESS || !result.valid) {
      // Special case: challenge generation failure should be handled by the endpoint
      if (strcmp(operation, "challenge") == 0) {
        // Let the /auth endpoint handle this - it will generate its own error response
      } else if (strcmp(operation, "admin_interface") == 0) {
        // Admin interface serves public static files - no auth required
      } else if (strcmp(operation, "head") == 0 || strcmp(operation, "head_upload") == 0) {
        // HEAD requests might not require auth depending on config - let handler decide
      } else if (strcmp(operation, "list") == 0) {
        // List operation might be optional auth - let handler decide
      } else if (strcmp(operation, "admin") == 0 && strcmp(request_uri, "/api/health") == 0) {
        // Health endpoint is public and doesn't require authentication - let handler decide
      } else if (strcmp(operation, "admin_event") == 0) {
        // POST /api/admin uses Kind 23458 events - authentication handled by admin_event.c
        // Skip centralized validation and let the handler validate the event
      } else {
        // For other operations, validation failure means auth failure
        const char *message = result.reason[0] ? result.reason : "Authentication failed";
        const char *details = "Authentication validation failed";
        
        // Determine appropriate status code based on violation type
        int status_code = 401; // Default: Unauthorized (no auth or invalid auth)
        const char *violation_type = nostr_request_validator_get_last_violation_type();
        
        // If auth rules denied the request, use 403 Forbidden instead of 401 Unauthorized
        if (violation_type && (
            strcmp(violation_type, "pubkey_blacklist") == 0 ||
            strcmp(violation_type, "hash_blacklist") == 0 ||
            strcmp(violation_type, "whitelist_violation") == 0 ||
            strcmp(violation_type, "mime_blacklist") == 0 ||
            strcmp(violation_type, "mime_whitelist_violation") == 0)) {
          status_code = 403; // Forbidden: authenticated but not authorized
        }

        // Always include event JSON in details when auth header is provided for debugging
        if (auth_header) {
          // Parse event JSON from auth header to show in details for debugging
          static char event_json[8192];  // Increased buffer size
          if (strncasecmp(auth_header, "nostr ", 6) == 0) {
            // Decode base64 to get event JSON
            const char *base64_event = auth_header + 6;
            unsigned char decoded_buffer[8192];  // Increased buffer size
            size_t decoded_len = base64_decode(base64_event, decoded_buffer);
            if (decoded_len > 0 && decoded_len < sizeof(event_json)) {
              memcpy(event_json, decoded_buffer, decoded_len);
              event_json[decoded_len] = '\0';
              details = event_json;  // Use the event JSON as details for debugging
            }
          }
        }

        send_error_response(status_code, "authentication_failed", message, details);
        log_request(request_method, request_uri, "auth_failed", status_code);
        continue;
      }
    }


    /////////////////////////////////////////////////////////////////////
    // ROUTE THE NOW VALID REQUEST TO HANDLERS
    /////////////////////////////////////////////////////////////////////

    // 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, "public", 200);
      } 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 pre-validated auth
      handle_upload_request_with_validation(&result);
      // Clean up file data allocated by validator
      nostr_request_result_free_file_data(&result);



    } 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 (strcmp(request_method, "POST") == 0 &&
               strcmp(request_uri, "/api/admin") == 0) {
      // Handle POST /api/admin requests (Kind 23458 admin events)
      handle_admin_event_request();

    } else if (strncmp(request_uri, "/admin", 6) == 0) {
      // Handle admin web interface requests (embedded files)
      handle_admin_interface_request(request_uri);

    } else if (strncmp(request_uri, "/api/", 5) == 0) {
      // Check if this is a static file request (no auth required) or API request (auth required)
      const char *path = request_uri + 5; // Skip "/api/"
      int is_static_file = 0;
      
      // Check for static file extensions
      if (strstr(path, ".html") || strstr(path, ".css") || strstr(path, ".js") ||
          strcmp(request_uri, "/api") == 0 || strcmp(request_uri, "/api/") == 0) {
        is_static_file = 1;
      }
      
      if (is_static_file) {
        // Serve static files without authentication
        handle_admin_interface_request(request_uri);
      } else {
        // Handle admin API requests with pre-validated auth
        const char *validated_pubkey = (result.valid && strlen(result.pubkey) == 64) ? result.pubkey : NULL;
        handle_admin_api_request(request_method, request_uri, validated_pubkey, result.valid);
      }

      
    } else if (strcmp(request_method, "GET") == 0 &&
               strncmp(request_uri, "/list/", 6) == 0) {
      // Handle GET /list/<pubkey> requests with pre-validated auth
      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';
        // TODO: Pass validated result to existing handler
        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, "GET") == 0 &&
               strcmp(request_uri, "/") == 0) {
      // Handle GET / requests - Server info endpoint (NIP-11)
      printf("Status: 200 OK\r\n");
      printf("Content-Type: application/nostr+json\r\n\r\n");
      printf("{\n");
      printf("  \"server\": \"ginxsom\",\n");
      printf("  \"version\": \"%s\",\n", VERSION);
      printf("  \"description\": \"Ginxsom Blossom Server\",\n");
      printf("  \"pubkey\": \"%s\",\n", g_blossom_pubkey);
      printf("  \"endpoints\": {\n");
      printf("    \"blob_get\": \"GET /<sha256>\",\n");
      printf("    \"blob_head\": \"HEAD /<sha256>\",\n");
      printf("    \"upload\": \"PUT /upload\",\n");
      printf("    \"upload_requirements\": \"HEAD /upload\",\n");
      printf("    \"list\": \"GET /list/<pubkey>\",\n");
      printf("    \"delete\": \"DELETE /<sha256>\",\n");
      printf("    \"mirror\": \"PUT /mirror\",\n");
      printf("    \"report\": \"PUT /report\",\n");
      printf("    \"health\": \"GET /health\",\n");
      printf("    \"auth\": \"GET /auth\"\n");
      printf("  },\n");
      printf("  \"supported_buds\": [\n");
      printf("    \"BUD-01\",\n");
      printf("    \"BUD-02\",\n");
      printf("    \"BUD-04\",\n");
      printf("    \"BUD-06\",\n");
      printf("    \"BUD-08\",\n");
      printf("    \"BUD-09\"\n");
      printf("  ],\n");
      printf("  \"limits\": {\n");
      printf("    \"max_upload_size\": 104857600,\n");
      printf("    \"supported_mime_types\": [\n");
      printf("      \"image/jpeg\",\n");
      printf("      \"image/png\",\n");
      printf("      \"image/webp\",\n");
      printf("      \"image/gif\",\n");
      printf("      \"video/mp4\",\n");
      printf("      \"video/webm\",\n");
      printf("      \"audio/mpeg\",\n");
      printf("      \"audio/ogg\",\n");
      printf("      \"text/plain\",\n");
      printf("      \"application/pdf\"\n");
      printf("    ]\n");
      printf("  },\n");
      printf("  \"authentication\": {\n");
      printf("    \"required_for_upload\": false,\n");
      printf("    \"required_for_delete\": true,\n");
      printf("    \"required_for_list\": false,\n");
      printf("    \"nip42_enabled\": true\n");
      printf("  }\n");
      printf("}\n");
      log_request("GET", "/", "server_info", 200);
    } else if (strcmp(request_method, "GET") == 0 &&
               strcmp(request_uri, "/auth") == 0) {
      // Handle GET /auth requests using the existing handler
      handle_auth_challenge_request();
    } else if (strcmp(request_method, "GET") == 0) {
      // Handle GET /<sha256> requests for blob retrieval
      const char *sha256 = extract_sha256_from_uri(request_uri);
      if (sha256) {
        handle_get_request(sha256);
        log_request("GET", request_uri, "public", 200);
      } else {
        send_error_response(400, "invalid_hash", "Invalid SHA-256 hash in URI",
                            "URI must contain a valid 64-character hex hash");
        log_request("GET", request_uri, "none", 400);
      }
    } else if (strcmp(request_method, "DELETE") == 0) {
      // Handle DELETE /<sha256> requests with pre-validated auth
      const char *sha256 = extract_sha256_from_uri(request_uri);
      if (sha256) {
        // Pass validated result to handler for ownership check
        handle_delete_request_with_validation(sha256, &result);
      } 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;
}
// ===== END UNUSED CODE =====
