/*
 * BUD-06 Upload Requirements (Pre-flight Validation)
 * Handles HEAD /upload requests for upload requirement validation
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcgi_stdio.h>
#include "ginxsom.h"

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

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

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

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

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

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

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

// Main BUD-06 handler function
void handle_head_upload_request(void) {
    // Log the incoming request
    log_request("HEAD", "/upload", "pending", 0);
    
    // Validate upload headers
    const char* sha256 = NULL;
    const char* content_type = NULL;
    long content_length = 0;
    char error_reason[256];
    
    int validation_result = validate_upload_headers(&sha256, &content_type,
                                                   &content_length, error_reason, sizeof(error_reason));
    
    if (validation_result != 200) {
        // Header validation failed
        const char* error_type;
        switch (validation_result) {
            case 400: error_type = "invalid_headers"; break;
            case 411: error_type = "length_required"; break;
            case 413: error_type = "payload_too_large"; break;
            default: error_type = "validation_error"; break;
        }
        
        send_upload_error_response(validation_result, error_type, error_reason, error_reason);
        log_request("HEAD", "/upload", "none", validation_result);
        return;
    }
    
    // Check for authorization first (before duplicate detection)
    const char* auth_header = getenv("HTTP_AUTHORIZATION");
    const char* auth_status = "none";
    
    if (auth_header) {
        // NOTE: Authorization validation now handled by centralized validation system in main.c
        // This handler receives pre-validated requests, so if we reach here with auth_header,
        // the authentication was already successful
        auth_status = "authenticated";
    } else {
        // Check if server requires authorization for uploads
        // If auth is required but not provided, return 401 before checking for duplicates
        // TODO: This should check server configuration for auth requirements
        // For now, assume auth is optional unless configured otherwise
    }
    
    // Check if blob already exists (duplicate detection - after auth validation)
    if (check_blob_exists(sha256)) {
        send_upload_error_response(409, "blob_exists", "Blob with this hash already exists", XREASON_BLOB_EXISTS);
        log_request("HEAD", "/upload", auth_status, 409);
        return;
    }
    
    // All validations passed - return success
    send_upload_success_response(sha256, content_type, content_length);
    log_request("HEAD", "/upload", auth_status, 200);
}