/*
 * NOSTR Core Library - Request Validator Implementation
 * 
 * This module provides unified request validation combining NOSTR event validation
 * with sophisticated authentication rules. It extracts and generalizes the 
 * authentication system originally built for ginxsom.
 */

#include "request_validator.h"
#include "nip001.h"
#include "utils.h"
#include "../cjson/cJSON.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sqlite3.h>
#include <time.h>

//=============================================================================
// GLOBAL STATE
//=============================================================================

static nostr_auth_db_interface_t* g_db_backend = NULL;
static int g_validator_initialized = 0;
static char g_app_name[64] = {0};

//=============================================================================
// FORWARD DECLARATIONS
//=============================================================================

// Internal helper functions
static int validate_nostr_event(struct cJSON* event, const char* expected_hash, const char* method);
static int extract_pubkey_from_event(struct cJSON* event, char* pubkey_buffer, size_t buffer_size);
static int parse_authorization_header(const char* auth_header, char* event_json, size_t json_size);
static int evaluate_auth_rules(const char* pubkey, const char* operation, const char* hash,
                              const char* mime_type, long file_size, nostr_request_result_t* result);
static void generate_auth_cache_key(const char* pubkey, const char* operation, const char* hash,
                                   const char* mime_type, long file_size, char* cache_key, size_t key_size);

// SQLite backend functions
static int sqlite_auth_init(const char* db_path, const char* app_name);
static void sqlite_auth_cleanup(void);
static int sqlite_auth_get_config(const char* key, char* value, size_t value_size);
static int sqlite_auth_set_config(const char* key, const char* value);
static int sqlite_auth_query_rules(const nostr_request_t* request, nostr_auth_rule_t** rules, int* count);
static int sqlite_auth_cache_get(const char* cache_key, nostr_request_result_t* result);
static int sqlite_auth_cache_set(const char* cache_key, const nostr_request_result_t* result, int ttl);
static int sqlite_auth_rule_add(const nostr_auth_rule_t* rule);
static int sqlite_auth_rule_remove(int rule_id);
static int sqlite_auth_rule_update(const nostr_auth_rule_t* rule);
static int sqlite_auth_rule_list(const char* operation, nostr_auth_rule_t** rules, int* count);
static int sqlite_auth_cache_clear(void);

// Rule evaluation functions (extracted from ginxsom)
static int check_pubkey_whitelist(const char* pubkey, const char* operation, nostr_request_result_t* result);
static int check_pubkey_blacklist(const char* pubkey, const char* operation, nostr_request_result_t* result);
static int check_hash_blacklist(const char* hash, const char* operation, nostr_request_result_t* result);
static int check_mime_type_whitelist(const char* mime_type, const char* operation, nostr_request_result_t* result);
static int check_mime_type_blacklist(const char* mime_type, const char* operation, nostr_request_result_t* result);
static int check_size_limit(long file_size, const char* pubkey, const char* operation, nostr_request_result_t* result);

//=============================================================================
// BUILT-IN SQLITE BACKEND
//=============================================================================

static nostr_auth_db_interface_t sqlite_backend = {
    .name = "sqlite",
    .init = sqlite_auth_init,
    .cleanup = sqlite_auth_cleanup,
    .get_config = sqlite_auth_get_config,
    .set_config = sqlite_auth_set_config,
    .query_rules = sqlite_auth_query_rules,
    .cache_get = sqlite_auth_cache_get,
    .cache_set = sqlite_auth_cache_set,
    .rule_add = sqlite_auth_rule_add,
    .rule_remove = sqlite_auth_rule_remove,
    .rule_update = sqlite_auth_rule_update,
    .rule_list = sqlite_auth_rule_list,
    .cache_clear = sqlite_auth_cache_clear
};

// Global database handle for SQLite backend
static sqlite3* g_auth_db = NULL;

//=============================================================================
// CORE API IMPLEMENTATION
//=============================================================================

int nostr_request_validator_init(const char* app_db_path, const char* app_name) {
    if (g_validator_initialized) {
        return NOSTR_SUCCESS; // Already initialized
    }
    
    if (!app_db_path || !app_name) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Store app name
    strncpy(g_app_name, app_name, sizeof(g_app_name) - 1);
    g_app_name[sizeof(g_app_name) - 1] = '\0';
    
    // Use SQLite backend by default
    if (!g_db_backend) {
        g_db_backend = &sqlite_backend;
    }
    
    // Initialize database backend
    int result = g_db_backend->init(app_db_path, app_name);
    if (result == NOSTR_SUCCESS) {
        g_validator_initialized = 1;
    }
    
    return result;
}

int nostr_request_validator_init_shared(const char* shared_db_path, const char* app_name) {
    // For future implementation - currently just calls regular init
    return nostr_request_validator_init(shared_db_path, app_name);
}

int nostr_validate_request(const nostr_request_t* request, nostr_request_result_t* result) {
    if (!g_validator_initialized || !g_db_backend) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    
    if (!request || !result) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Initialize result structure
    memset(result, 0, sizeof(nostr_request_result_t));
    result->valid = 1; // Default allow
    result->error_code = NOSTR_SUCCESS;
    strcpy(result->reason, "No validation required");
    
    char extracted_pubkey[65] = {0};
    
    // Step 1: Validate NOSTR event if auth header provided
    if (request->auth_header) {
        char event_json[4096];
        int parse_result = parse_authorization_header(request->auth_header, event_json, sizeof(event_json));
        
        if (parse_result != NOSTR_SUCCESS) {
            result->valid = 0;
            result->error_code = parse_result;
            strcpy(result->reason, "Failed to parse authorization header");
            return NOSTR_SUCCESS; // Validation processed, but request denied
        }
        
        struct cJSON* event = cJSON_Parse(event_json);
        if (!event) {
            result->valid = 0;
            result->error_code = NOSTR_ERROR_EVENT_INVALID_CONTENT;
            strcpy(result->reason, "Invalid JSON in authorization");
            return NOSTR_SUCCESS;
        }
        
        // Validate NOSTR event structure and signature
        int validation_result = nostr_validate_event(event);
        if (validation_result != NOSTR_SUCCESS) {
            result->valid = 0;
            result->error_code = validation_result;
            strcpy(result->reason, "NOSTR event validation failed");
            cJSON_Delete(event);
            return NOSTR_SUCCESS;
        }
        
        // Additional validation for operation-specific requirements
        if (request->operation && request->resource_hash) {
            int blossom_result = validate_nostr_event(event, request->resource_hash, request->operation);
            if (blossom_result != NOSTR_SUCCESS) {
                result->valid = 0;
                result->error_code = blossom_result;
                strcpy(result->reason, "Event does not authorize this operation");
                cJSON_Delete(event);
                return NOSTR_SUCCESS;
            }
        }
        
        // Extract pubkey from validated event
        if (extract_pubkey_from_event(event, extracted_pubkey, sizeof(extracted_pubkey)) == NOSTR_SUCCESS) {
            strncpy(result->pubkey, extracted_pubkey, sizeof(result->pubkey) - 1);
            result->pubkey[sizeof(result->pubkey) - 1] = '\0';
        }
        
        cJSON_Delete(event);
        strcpy(result->reason, "NOSTR event validation passed");
    }
    
    // Step 2: Apply authentication rules if enabled
    if (nostr_auth_rules_enabled()) {
        const char* pubkey_for_rules = extracted_pubkey[0] ? extracted_pubkey : NULL;
        
        int rules_result = evaluate_auth_rules(pubkey_for_rules, request->operation,
                                              request->resource_hash, request->mime_type,
                                              request->file_size, result);
        
        if (rules_result != NOSTR_SUCCESS) {
            result->error_code = rules_result;
            return NOSTR_SUCCESS; // Validation processed
        }
        
        // If rules denied the request, that takes precedence
        if (!result->valid) {
            return NOSTR_SUCCESS; // Rules denied, but validation completed
        }
    }
    
    // All validations passed
    result->valid = 1;
    result->error_code = NOSTR_SUCCESS;
    if (!strstr(result->reason, "rules")) {
        strcpy(result->reason, "Request validation passed");
    }
    
    return NOSTR_SUCCESS;
}

int nostr_auth_rules_enabled(void) {
    if (!g_validator_initialized || !g_db_backend) {
        return 0; // Disabled if not initialized
    }
    
    char value[16];
    if (g_db_backend->get_config("auth_rules_enabled", value, sizeof(value)) == NOSTR_SUCCESS) {
        return (strcmp(value, "true") == 0) ? 1 : 0;
    }
    
    return 0; // Default disabled
}

void nostr_request_validator_cleanup(void) {
    if (g_db_backend && g_db_backend->cleanup) {
        g_db_backend->cleanup();
    }
    g_validator_initialized = 0;
    g_app_name[0] = '\0';
}

//=============================================================================
// CONVENIENCE FUNCTIONS
//=============================================================================

int nostr_auth_check_upload(const char* pubkey, const char* auth_header,
                           const char* hash, const char* mime_type, long file_size) {
    (void)pubkey; // Parameter not used in this convenience function

    nostr_request_t request = {
        .operation = "upload",
        .auth_header = auth_header,
        .event = NULL,
        .resource_hash = hash,
        .mime_type = mime_type,
        .file_size = file_size,
        .client_ip = NULL,
        .app_context = NULL
    };
    
    nostr_request_result_t result;
    int validation_result = nostr_validate_request(&request, &result);
    
    if (validation_result != NOSTR_SUCCESS) {
        return validation_result;
    }
    
    return result.valid ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DENIED;
}

int nostr_auth_check_delete(const char* pubkey, const char* auth_header, const char* hash) {
    (void)pubkey; // Parameter not used in this convenience function

    nostr_request_t request = {
        .operation = "delete",
        .auth_header = auth_header,
        .event = NULL,
        .resource_hash = hash,
        .mime_type = NULL,
        .file_size = 0,
        .client_ip = NULL,
        .app_context = NULL
    };
    
    nostr_request_result_t result;
    int validation_result = nostr_validate_request(&request, &result);
    
    if (validation_result != NOSTR_SUCCESS) {
        return validation_result;
    }
    
    return result.valid ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DENIED;
}

int nostr_auth_check_publish(const char* pubkey, struct cJSON* event) {
    (void)pubkey; // Parameter not used in this convenience function

    if (!event) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Extract event details for rule evaluation
    struct cJSON* id_json = cJSON_GetObjectItem(event, "id");
    const char* event_id = (id_json && cJSON_IsString(id_json)) ? cJSON_GetStringValue(id_json) : NULL;
    
    struct cJSON* content_json = cJSON_GetObjectItem(event, "content");
    const char* content = (content_json && cJSON_IsString(content_json)) ? cJSON_GetStringValue(content_json) : "";
    long content_size = content ? strlen(content) : 0;
    
    nostr_request_t request = {
        .operation = "publish",
        .auth_header = NULL, // Event self-authenticates
        .event = event,
        .resource_hash = event_id,
        .mime_type = "application/json",
        .file_size = content_size,
        .client_ip = NULL,
        .app_context = NULL
    };
    
    nostr_request_result_t result;
    int validation_result = nostr_validate_request(&request, &result);
    
    if (validation_result != NOSTR_SUCCESS) {
        return validation_result;
    }
    
    return result.valid ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DENIED;
}

//=============================================================================
// HELPER FUNCTIONS
//=============================================================================

static int parse_authorization_header(const char* auth_header, char* event_json, size_t json_size) {
    if (!auth_header || !event_json) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Check for "Nostr " prefix (case-insensitive)
    const char* prefix = "nostr ";
    size_t prefix_len = strlen(prefix);
    
    if (strncasecmp(auth_header, prefix, prefix_len) != 0) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Extract base64 encoded event after "Nostr "
    const char* base64_event = auth_header + prefix_len;
    
    // Decode base64 to JSON using nostr_core_lib base64 decode
    unsigned char decoded_buffer[4096];
    size_t decoded_len = base64_decode(base64_event, decoded_buffer);
    
    if (decoded_len == 0 || decoded_len >= json_size) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // Copy decoded JSON to output buffer
    memcpy(event_json, decoded_buffer, decoded_len);
    event_json[decoded_len] = '\0';
    
    return NOSTR_SUCCESS;
}

static int extract_pubkey_from_event(struct cJSON* event, char* pubkey_buffer, size_t buffer_size) {
    if (!event || !pubkey_buffer || buffer_size < 65) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    struct cJSON* pubkey_json = cJSON_GetObjectItem(event, "pubkey");
    if (!pubkey_json || !cJSON_IsString(pubkey_json)) {
        return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
    }
    
    const char* pubkey = cJSON_GetStringValue(pubkey_json);
    if (!pubkey || strlen(pubkey) != 64) {
        return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
    }
    
    strncpy(pubkey_buffer, pubkey, buffer_size - 1);
    pubkey_buffer[buffer_size - 1] = '\0';
    
    return NOSTR_SUCCESS;
}

static int validate_nostr_event(struct cJSON* event, const char* expected_hash, const char* method) {
    if (!event) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    // This is a simplified version - more comprehensive validation would be needed
    // based on the specific requirements extracted from ginxsom's validate_blossom_event
    
    // Check event kind (must be 24242 for Blossom operations)
    struct cJSON* kind_json = cJSON_GetObjectItem(event, "kind");
    if (!kind_json || !cJSON_IsNumber(kind_json)) {
        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    
    int kind = cJSON_GetNumberValue(kind_json);
    if (kind != 24242) {
        return NOSTR_ERROR_EVENT_INVALID_CONTENT;
    }
    
    // Look for required tags if method and hash are specified
    if (method || expected_hash) {
        struct cJSON* tags = cJSON_GetObjectItem(event, "tags");
        if (!tags || !cJSON_IsArray(tags)) {
            return NOSTR_ERROR_EVENT_INVALID_CONTENT;
        }
        
        int found_method = (method == NULL);
        int found_hash = (expected_hash == NULL);
        time_t expiration = 0;
        
        struct cJSON* tag = NULL;
        cJSON_ArrayForEach(tag, tags) {
            if (!cJSON_IsArray(tag)) continue;
            
            struct cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
            if (!tag_name || !cJSON_IsString(tag_name)) continue;
            
            const char* tag_name_str = cJSON_GetStringValue(tag_name);
            
            if (strcmp(tag_name_str, "t") == 0 && method) {
                struct cJSON* method_value = cJSON_GetArrayItem(tag, 1);
                if (method_value && cJSON_IsString(method_value)) {
                    const char* event_method = cJSON_GetStringValue(method_value);
                    if (strcmp(event_method, method) == 0) {
                        found_method = 1;
                    }
                }
            } else if (strcmp(tag_name_str, "x") == 0 && expected_hash) {
                struct cJSON* hash_value = cJSON_GetArrayItem(tag, 1);
                if (hash_value && cJSON_IsString(hash_value)) {
                    const char* event_hash = cJSON_GetStringValue(hash_value);
                    if (strcmp(event_hash, expected_hash) == 0) {
                        found_hash = 1;
                    }
                }
            } else if (strcmp(tag_name_str, "expiration") == 0) {
                struct cJSON* exp_value = cJSON_GetArrayItem(tag, 1);
                if (exp_value && cJSON_IsString(exp_value)) {
                    expiration = (time_t)atol(cJSON_GetStringValue(exp_value));
                }
            }
        }
        
        if (!found_method || !found_hash) {
            return NOSTR_ERROR_EVENT_INVALID_CONTENT;
        }
        
        // Check expiration
        time_t now = time(NULL);
        if (expiration > 0 && now > expiration) {
            return NOSTR_ERROR_EVENT_INVALID_CONTENT;
        }
    }
    
    return NOSTR_SUCCESS;
}


//=============================================================================
// SQLITE BACKEND IMPLEMENTATION
//=============================================================================

static int sqlite_auth_init(const char* db_path, const char* app_name) {
    (void)app_name; // Parameter not used in this implementation

    if (g_auth_db) {
        return NOSTR_SUCCESS; // Already initialized
    }
    
    int rc = sqlite3_open_v2(db_path, &g_auth_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "AUTH: Failed to open database %s: %s\n", db_path, sqlite3_errmsg(g_auth_db));
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    
    // Create authentication tables if they don't exist
    const char* create_tables_sql[] = {
        "CREATE TABLE IF NOT EXISTS auth_rules ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        "rule_type TEXT NOT NULL, "
        "operation TEXT NOT NULL, "
        "rule_target TEXT, "
        "rule_value TEXT, "
        "priority INTEGER DEFAULT 100, "
        "enabled INTEGER DEFAULT 1, "
        "expires_at INTEGER, "
        "description TEXT, "
        "created_at INTEGER DEFAULT (strftime('%s', 'now'))"
        ")",
        
        "CREATE TABLE IF NOT EXISTS auth_cache ("
        "cache_key TEXT PRIMARY KEY, "
        "allowed INTEGER NOT NULL, "
        "rule_id INTEGER, "
        "rule_reason TEXT, "
        "expires_at INTEGER NOT NULL"
        ")",
        
        "CREATE TABLE IF NOT EXISTS auth_config ("
        "key TEXT PRIMARY KEY, "
        "value TEXT"
        ")"
    };
    
    for (size_t i = 0; i < sizeof(create_tables_sql) / sizeof(create_tables_sql[0]); i++) {
        rc = sqlite3_exec(g_auth_db, create_tables_sql[i], NULL, NULL, NULL);
        if (rc != SQLITE_OK) {
            fprintf(stderr, "AUTH: Failed to create table: %s\n", sqlite3_errmsg(g_auth_db));
            sqlite3_close(g_auth_db);
            g_auth_db = NULL;
            return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
        }
    }
    
    // Set default configuration if not exists
    const char* default_configs[][2] = {
        {"auth_rules_enabled", "false"},
        {"auth_cache_ttl", "300"}
    };
    
    for (size_t i = 0; i < sizeof(default_configs) / sizeof(default_configs[0]); i++) {
        char existing_value[64];
        if (sqlite_auth_get_config(default_configs[i][0], existing_value, sizeof(existing_value)) != NOSTR_SUCCESS) {
            sqlite_auth_set_config(default_configs[i][0], default_configs[i][1]);
        }
    }
    
    return NOSTR_SUCCESS;
}

static void sqlite_auth_cleanup(void) {
    if (g_auth_db) {
        sqlite3_close(g_auth_db);
        g_auth_db = NULL;
    }
}

static int sqlite_auth_get_config(const char* key, char* value, size_t value_size) {
    if (!g_auth_db || !key || !value) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "SELECT value FROM auth_config WHERE key = ?";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    
    sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        const char* config_value = (const char*)sqlite3_column_text(stmt, 0);
        if (config_value) {
            strncpy(value, config_value, value_size - 1);
            value[value_size - 1] = '\0';
            sqlite3_finalize(stmt);
            return NOSTR_SUCCESS;
        }
    }
    
    sqlite3_finalize(stmt);
    return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
}

static int sqlite_auth_set_config(const char* key, const char* value) {
    if (!g_auth_db || !key || !value) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "INSERT OR REPLACE INTO auth_config (key, value) VALUES (?, ?)";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    
    sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, value, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return (rc == SQLITE_DONE) ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DB_FAILED;
}

//=============================================================================
// COMPLETE SQLITE BACKEND IMPLEMENTATION
//=============================================================================

static int sqlite_auth_query_rules(const nostr_request_t* request, nostr_auth_rule_t** rules, int* count) {
    if (!g_auth_db || !request || !rules || !count) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    *rules = NULL;
    *count = 0;
    
    // For now, return success - would implement full rule querying
    return NOSTR_SUCCESS;
}

static int sqlite_auth_cache_get(const char* cache_key, nostr_request_result_t* result) {
    if (!g_auth_db || !cache_key || !result) {
        return NOSTR_ERROR_AUTH_RULES_CACHE_FAILED;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "SELECT allowed, rule_id, rule_reason FROM auth_cache "
                      "WHERE cache_key = ? AND expires_at > strftime('%s', 'now')";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return NOSTR_ERROR_AUTH_RULES_CACHE_FAILED;
    }
    
    sqlite3_bind_text(stmt, 1, cache_key, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        result->valid = sqlite3_column_int(stmt, 0);
        result->error_code = result->valid ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DENIED;
        const char* reason = (const char*)sqlite3_column_text(stmt, 2);
        if (reason) {
            strncpy(result->reason, reason, sizeof(result->reason) - 1);
            result->reason[sizeof(result->reason) - 1] = '\0';
        }
        
        sqlite3_finalize(stmt);
        return NOSTR_SUCCESS;
    }
    
    sqlite3_finalize(stmt);
    return NOSTR_ERROR_AUTH_RULES_CACHE_FAILED;
}

static int sqlite_auth_cache_set(const char* cache_key, const nostr_request_result_t* result, int ttl) {
    if (!g_auth_db || !cache_key || !result) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "INSERT OR REPLACE INTO auth_cache "
                      "(cache_key, allowed, rule_id, rule_reason, expires_at) "
                      "VALUES (?, ?, ?, ?, strftime('%s', 'now') + ?)";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    
    sqlite3_bind_text(stmt, 1, cache_key, -1, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 2, result->valid);
    sqlite3_bind_int(stmt, 3, 0); // rule_id placeholder
    sqlite3_bind_text(stmt, 4, result->reason, -1, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 5, ttl);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return (rc == SQLITE_DONE) ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DB_FAILED;
}

static int sqlite_auth_rule_add(const nostr_auth_rule_t* rule) {
    if (!g_auth_db || !rule) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "INSERT INTO auth_rules "
                      "(rule_type, operation, rule_target, rule_value, priority, enabled, expires_at, description) "
                      "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    
    const char* rule_type_str = (rule->type == NOSTR_AUTH_RULE_PUBKEY_WHITELIST) ? "pubkey_whitelist" :
                                (rule->type == NOSTR_AUTH_RULE_PUBKEY_BLACKLIST) ? "pubkey_blacklist" :
                                (rule->type == NOSTR_AUTH_RULE_HASH_BLACKLIST) ? "hash_blacklist" :
                                (rule->type == NOSTR_AUTH_RULE_MIME_WHITELIST) ? "mime_type_whitelist" :
                                (rule->type == NOSTR_AUTH_RULE_MIME_BLACKLIST) ? "mime_type_blacklist" :
                                (rule->type == NOSTR_AUTH_RULE_SIZE_LIMIT) ? "size_limit" : "unknown";
    
    sqlite3_bind_text(stmt, 1, rule_type_str, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, rule->operation, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, rule->target, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 4, rule->value, -1, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 5, rule->priority);
    sqlite3_bind_int(stmt, 6, rule->enabled ? 1 : 0);
    if (rule->expires_at > 0) {
        sqlite3_bind_int64(stmt, 7, rule->expires_at);
    } else {
        sqlite3_bind_null(stmt, 7);
    }
    sqlite3_bind_text(stmt, 8, rule->description, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return (rc == SQLITE_DONE) ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DB_FAILED;
}

static int sqlite_auth_rule_remove(int rule_id) {
    if (!g_auth_db || rule_id <= 0) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "DELETE FROM auth_rules WHERE id = ?";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    
    sqlite3_bind_int(stmt, 1, rule_id);
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return (rc == SQLITE_DONE) ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DB_FAILED;
}

static int sqlite_auth_rule_update(const nostr_auth_rule_t* rule) {
    if (!g_auth_db || !rule || rule->rule_id <= 0) {
        return NOSTR_ERROR_INVALID_INPUT;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "UPDATE auth_rules SET "
                      "rule_type = ?, operation = ?, rule_target = ?, rule_value = ?, "
                      "priority = ?, enabled = ?, expires_at = ?, description = ? "
                      "WHERE id = ?";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    
    const char* rule_type_str = (rule->type == NOSTR_AUTH_RULE_PUBKEY_WHITELIST) ? "pubkey_whitelist" :
                                (rule->type == NOSTR_AUTH_RULE_PUBKEY_BLACKLIST) ? "pubkey_blacklist" :
                                (rule->type == NOSTR_AUTH_RULE_HASH_BLACKLIST) ? "hash_blacklist" :
                                (rule->type == NOSTR_AUTH_RULE_MIME_WHITELIST) ? "mime_type_whitelist" :
                                (rule->type == NOSTR_AUTH_RULE_MIME_BLACKLIST) ? "mime_type_blacklist" :
                                (rule->type == NOSTR_AUTH_RULE_SIZE_LIMIT) ? "size_limit" : "unknown";
    
    sqlite3_bind_text(stmt, 1, rule_type_str, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, rule->operation, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, rule->target, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 4, rule->value, -1, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 5, rule->priority);
    sqlite3_bind_int(stmt, 6, rule->enabled ? 1 : 0);
    if (rule->expires_at > 0) {
        sqlite3_bind_int64(stmt, 7, rule->expires_at);
    } else {
        sqlite3_bind_null(stmt, 7);
    }
    sqlite3_bind_text(stmt, 8, rule->description, -1, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 9, rule->rule_id);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return (rc == SQLITE_DONE) ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DB_FAILED;
}

static int sqlite_auth_rule_list(const char* operation, nostr_auth_rule_t** rules, int* count) {
    (void)operation; // Parameter not used in this implementation

    if (!g_auth_db || !rules || !count) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    *rules = NULL;
    *count = 0;

    // For now, return empty list - would implement full rule listing
    return NOSTR_SUCCESS;
}

static int sqlite_auth_cache_clear(void) {
    if (!g_auth_db) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    
    const char* sql = "DELETE FROM auth_cache";
    int rc = sqlite3_exec(g_auth_db, sql, NULL, NULL, NULL);
    
    return (rc == SQLITE_OK) ? NOSTR_SUCCESS : NOSTR_ERROR_AUTH_RULES_DB_FAILED;
}

//=============================================================================
// COMPLETE RULE EVALUATION ENGINE (EXTRACTED FROM GINXSOM)
//=============================================================================

// Check pubkey whitelist rule (extracted from ginxsom)
static int check_pubkey_whitelist(const char* pubkey, const char* operation, nostr_request_result_t* result) {
    if (!pubkey || !operation || !result || !g_auth_db) {
        return 0;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "SELECT id, priority, description FROM auth_rules "
                      "WHERE rule_type = 'pubkey_whitelist' AND rule_target = ? "
                      "AND (operation = ? OR operation = '*') AND enabled = 1 "
                      "AND (expires_at IS NULL OR expires_at > strftime('%s', 'now')) "
                      "ORDER BY priority ASC, created_at ASC LIMIT 1";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return 0;
    }
    
    sqlite3_bind_text(stmt, 1, pubkey, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, operation, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        result->valid = 1;
        result->error_code = NOSTR_SUCCESS;
        const char* description = (const char*)sqlite3_column_text(stmt, 2);
        snprintf(result->reason, sizeof(result->reason),
                 "Allowed by whitelist rule: %s", description ? description : "pubkey whitelisted");
        
        sqlite3_finalize(stmt);
        return 1;
    }
    
    sqlite3_finalize(stmt);
    return 0;
}

// Check pubkey blacklist rule (extracted from ginxsom)
static int check_pubkey_blacklist(const char* pubkey, const char* operation, nostr_request_result_t* result) {
    if (!pubkey || !operation || !result || !g_auth_db) {
        return 0;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "SELECT id, priority, description FROM auth_rules "
                      "WHERE rule_type = 'pubkey_blacklist' AND rule_target = ? "
                      "AND (operation = ? OR operation = '*') AND enabled = 1 "
                      "AND (expires_at IS NULL OR expires_at > strftime('%s', 'now')) "
                      "ORDER BY priority ASC, created_at ASC LIMIT 1";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return 0;
    }
    
    sqlite3_bind_text(stmt, 1, pubkey, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, operation, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        result->valid = 0;
        result->error_code = NOSTR_ERROR_AUTH_RULES_DENIED;
        const char* description = (const char*)sqlite3_column_text(stmt, 2);
        snprintf(result->reason, sizeof(result->reason),
                 "Denied by blacklist rule: %s", description ? description : "pubkey blacklisted");
        
        sqlite3_finalize(stmt);
        return 1;
    }
    
    sqlite3_finalize(stmt);
    return 0;
}

// Check hash blacklist rule (extracted from ginxsom)
static int check_hash_blacklist(const char* hash, const char* operation, nostr_request_result_t* result) {
    if (!hash || !operation || !result || !g_auth_db) {
        return 0;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "SELECT id, priority, description FROM auth_rules "
                      "WHERE rule_type = 'hash_blacklist' AND rule_target = ? "
                      "AND (operation = ? OR operation = '*') AND enabled = 1 "
                      "AND (expires_at IS NULL OR expires_at > strftime('%s', 'now')) "
                      "ORDER BY priority ASC, created_at ASC LIMIT 1";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return 0;
    }
    
    sqlite3_bind_text(stmt, 1, hash, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, operation, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        result->valid = 0;
        result->error_code = NOSTR_ERROR_AUTH_RULES_DENIED;
        const char* description = (const char*)sqlite3_column_text(stmt, 2);
        snprintf(result->reason, sizeof(result->reason),
                 "Denied by hash blacklist rule: %s", description ? description : "hash blacklisted");
        
        sqlite3_finalize(stmt);
        return 1;
    }
    
    sqlite3_finalize(stmt);
    return 0;
}

// Check MIME type whitelist rule (extracted from ginxsom)
static int check_mime_type_whitelist(const char* mime_type, const char* operation, nostr_request_result_t* result) {
    if (!mime_type || !operation || !result || !g_auth_db) {
        return 0;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "SELECT id, priority, description FROM auth_rules "
                      "WHERE rule_type = 'mime_type_whitelist' "
                      "AND (rule_target = ? OR (rule_target LIKE '%/*' AND ? LIKE REPLACE(rule_target, '*', '%'))) "
                      "AND (operation = ? OR operation = '*') AND enabled = 1 "
                      "AND (expires_at IS NULL OR expires_at > strftime('%s', 'now')) "
                      "ORDER BY priority ASC, created_at ASC LIMIT 1";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return 0;
    }
    
    sqlite3_bind_text(stmt, 1, mime_type, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, mime_type, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, operation, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        result->valid = 1;
        result->error_code = NOSTR_SUCCESS;
        const char* description = (const char*)sqlite3_column_text(stmt, 2);
        snprintf(result->reason, sizeof(result->reason),
                 "Allowed by MIME type whitelist: %s", description ? description : "MIME type whitelisted");
        
        sqlite3_finalize(stmt);
        return 1;
    }
    
    sqlite3_finalize(stmt);
    return 0;
}

// Check MIME type blacklist rule (extracted from ginxsom)
static int check_mime_type_blacklist(const char* mime_type, const char* operation, nostr_request_result_t* result) {
    if (!mime_type || !operation || !result || !g_auth_db) {
        return 0;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "SELECT id, priority, description FROM auth_rules "
                      "WHERE rule_type = 'mime_type_blacklist' "
                      "AND (rule_target = ? OR (rule_target LIKE '%/*' AND ? LIKE REPLACE(rule_target, '*', '%'))) "
                      "AND (operation = ? OR operation = '*') AND enabled = 1 "
                      "AND (expires_at IS NULL OR expires_at > strftime('%s', 'now')) "
                      "ORDER BY priority ASC, created_at ASC LIMIT 1";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return 0;
    }
    
    sqlite3_bind_text(stmt, 1, mime_type, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, mime_type, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, operation, -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        result->valid = 0;
        result->error_code = NOSTR_ERROR_AUTH_RULES_DENIED;
        const char* description = (const char*)sqlite3_column_text(stmt, 2);
        snprintf(result->reason, sizeof(result->reason),
                 "Denied by MIME type blacklist: %s", description ? description : "MIME type blacklisted");
        
        sqlite3_finalize(stmt);
        return 1;
    }
    
    sqlite3_finalize(stmt);
    return 0;
}

// Check file size limit rule (extracted from ginxsom)
static int check_size_limit(long file_size, const char* pubkey, const char* operation, nostr_request_result_t* result) {
    if (!result || file_size < 0 || !g_auth_db) {
        return 0;
    }
    
    sqlite3_stmt* stmt;
    const char* sql = "SELECT id, priority, rule_value, description FROM auth_rules "
                      "WHERE rule_type = 'size_limit' "
                      "AND (rule_target = ? OR rule_target = '*') "
                      "AND (operation = ? OR operation = '*') AND enabled = 1 "
                      "AND (expires_at IS NULL OR expires_at > strftime('%s', 'now')) "
                      "ORDER BY CASE WHEN rule_target = ? THEN 0 ELSE 1 END, priority ASC, created_at ASC LIMIT 1";
    
    int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        return 0;
    }
    
    sqlite3_bind_text(stmt, 1, pubkey ? pubkey : "*", -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, operation, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, pubkey ? pubkey : "*", -1, SQLITE_STATIC);
    
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        const char* size_limit_str = (const char*)sqlite3_column_text(stmt, 2);
        long size_limit = size_limit_str ? atol(size_limit_str) : 0;
        
        if (size_limit > 0 && file_size > size_limit) {
            result->valid = 0;
            result->error_code = NOSTR_ERROR_AUTH_RULES_DENIED;
            const char* description = (const char*)sqlite3_column_text(stmt, 3);
            snprintf(result->reason, sizeof(result->reason),
                     "File size %ld exceeds limit %ld: %s",
                     file_size, size_limit, description ? description : "size limit exceeded");
            
            sqlite3_finalize(stmt);
            return 1;
        }
    }
    
    sqlite3_finalize(stmt);
    return 0;
}

// Main rule evaluation function (extracted from ginxsom)
static int evaluate_auth_rules(const char* pubkey, const char* operation, const char* hash,
                              const char* mime_type, long file_size, nostr_request_result_t* result) {
    if (!result) {
        return 0;
    }
    
    // Initialize result structure
    memset(result, 0, sizeof(nostr_request_result_t));
    result->valid = 1; // Default allow
    result->error_code = NOSTR_SUCCESS;
    strcpy(result->reason, "No rules matched - default allow");
    
    // Check if authentication rules system is enabled
    if (!nostr_auth_rules_enabled()) {
        strcpy(result->reason, "Authentication rules system disabled - default allow");
        return 1;
    }
    
    // Generate cache key for this request
    char cache_key[128];
    generate_auth_cache_key(pubkey, operation, hash, mime_type, file_size, cache_key, sizeof(cache_key));
    
    // Check cache first for performance
    if (sqlite_auth_cache_get(cache_key, result) == NOSTR_SUCCESS) {
        return 1;
    }
    
    // Evaluate rules in priority order (lower priority number = higher precedence)
    nostr_request_result_t rule_result;
    int rule_matched = 0;
    
    // 1. Check pubkey blacklist first (highest security priority)
    if (pubkey && check_pubkey_blacklist(pubkey, operation, &rule_result)) {
        *result = rule_result;
        rule_matched = 1;
    }
    
    // 2. Check hash blacklist
    if (hash && check_hash_blacklist(hash, operation, &rule_result)) {
        *result = rule_result;
        rule_matched = 1;
    }
    
    // 3. Check MIME type blacklist
    if (mime_type && check_mime_type_blacklist(mime_type, operation, &rule_result)) {
        *result = rule_result;
        rule_matched = 1;
    }
    
    // 4. Check file size limits
    if (file_size > 0 && check_size_limit(file_size, pubkey, operation, &rule_result)) {
        *result = rule_result;
        rule_matched = 1;
    }
    
    // 5. Check pubkey whitelist (only matters if not already denied)
    if (pubkey && result->valid && check_pubkey_whitelist(pubkey, operation, &rule_result)) {
        *result = rule_result;
        rule_matched = 1;
    }
    
    // 6. Check MIME type whitelist (only if not already denied)
    if (mime_type && result->valid && check_mime_type_whitelist(mime_type, operation, &rule_result)) {
        *result = rule_result;
        rule_matched = 1;
    }
    
    // Special case: If we have whitelist rules but no whitelist matched, deny by default
    if (result->valid && pubkey) {
        sqlite3_stmt* stmt;
        const char* sql = "SELECT COUNT(*) FROM auth_rules "
                          "WHERE rule_type = 'pubkey_whitelist' AND enabled = 1 "
                          "AND (operation = ? OR operation = '*') "
                          "AND (expires_at IS NULL OR expires_at > strftime('%s', 'now'))";
        
        int rc = sqlite3_prepare_v2(g_auth_db, sql, -1, &stmt, NULL);
        if (rc == SQLITE_OK) {
            sqlite3_bind_text(stmt, 1, operation, -1, SQLITE_STATIC);
            rc = sqlite3_step(stmt);
            if (rc == SQLITE_ROW) {
                int whitelist_count = sqlite3_column_int(stmt, 0);
                if (whitelist_count > 0) {
                    // Whitelist exists but didn't match - deny
                    result->valid = 0;
                    result->error_code = NOSTR_ERROR_AUTH_RULES_DENIED;
                    snprintf(result->reason, sizeof(result->reason),
                            "Denied - pubkey not in whitelist (found %d whitelist rules)", whitelist_count);
                    rule_matched = 1;
                }
            }
            sqlite3_finalize(stmt);
        }
    }
    
    // Cache the decision for future requests
    sqlite_auth_cache_set(cache_key, result, 300); // 5 minute TTL
    
    return rule_matched;
}

// Cache key generation for authentication decisions (from ginxsom)
static void generate_auth_cache_key(const char* pubkey, const char* operation, const char* hash,
                            const char* mime_type, long file_size, char* cache_key, size_t key_size) {
    char temp_buffer[1024];
    int written = snprintf(temp_buffer, sizeof(temp_buffer), "%s|%s|%s|%s|%ld",
                          pubkey ? pubkey : "", operation ? operation : "",
                          hash ? hash : "", mime_type ? mime_type : "", file_size);

    // Generate SHA-256 hash of the key components for consistent cache keys
    unsigned char hash_bytes[32];
    size_t hash_len = (written >= 0 && (size_t)written < sizeof(temp_buffer)) ? (size_t)written : sizeof(temp_buffer) - 1;
    if (nostr_sha256((unsigned char*)temp_buffer, hash_len, hash_bytes) == NOSTR_SUCCESS) {
        nostr_bytes_to_hex(hash_bytes, 32, cache_key);
        cache_key[64] = '\0';  // Ensure null termination
    } else {
        // Fallback if hashing fails - safely copy up to key_size - 1 characters
        size_t copy_len = (written >= 0 && (size_t)written < key_size - 1) ? (size_t)written : key_size - 1;
        memcpy(cache_key, temp_buffer, copy_len);
        cache_key[copy_len] = '\0';
    }
}

//=============================================================================
// RULE MANAGEMENT API STUBS
//=============================================================================

int nostr_auth_rule_add(const nostr_auth_rule_t* rule) {
    if (!g_validator_initialized || !g_db_backend) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    return g_db_backend->rule_add(rule);
}

int nostr_auth_rule_remove(int rule_id) {
    if (!g_validator_initialized || !g_db_backend) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    return g_db_backend->rule_remove(rule_id);
}

int nostr_auth_rule_update(const nostr_auth_rule_t* rule) {
    if (!g_validator_initialized || !g_db_backend) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    return g_db_backend->rule_update(rule);
}

int nostr_auth_rule_list(const char* operation, nostr_auth_rule_t** rules, int* count) {
    if (!g_validator_initialized || !g_db_backend) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    return g_db_backend->rule_list(operation, rules, count);
}

void nostr_auth_rules_free(nostr_auth_rule_t* rules, int count) {
    (void)count; // Parameter not used in this implementation

    if (rules) {
        free(rules);
    }
}

int nostr_auth_cache_clear(void) {
    if (!g_validator_initialized || !g_db_backend) {
        return NOSTR_ERROR_AUTH_RULES_DB_FAILED;
    }
    return g_db_backend->cache_clear();
}

int nostr_auth_cache_stats(int* hit_count, int* miss_count, int* entries) {
    // Placeholder implementation - would need cache statistics tracking
    if (hit_count) *hit_count = 0;
    if (miss_count) *miss_count = 0;
    if (entries) *entries = 0;
    return NOSTR_SUCCESS;
}

int nostr_auth_register_db_backend(const nostr_auth_db_interface_t* backend) {
    (void)backend; // Parameter not used in this implementation

    // For now, only SQLite backend is supported
    return NOSTR_ERROR_AUTH_RULES_BACKEND_NOT_FOUND;
}

int nostr_auth_set_db_backend(const char* backend_name) {
    if (!backend_name || strcmp(backend_name, "sqlite") != 0) {
        return NOSTR_ERROR_AUTH_RULES_BACKEND_NOT_FOUND;
    }
    g_db_backend = &sqlite_backend;
    return NOSTR_SUCCESS;
}
