/*
 * BUD-08 NIP-94 File Metadata Tags Implementation
 * Handles NIP-94 metadata generation for blob descriptors
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#include <stdint.h>
#include "ginxsom.h"

// Use global database path from main.c
extern char g_db_path[];

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

// Get CDN origin for blob URLs
int nip94_get_origin(char* out, size_t out_size) {
    if (!out || out_size == 0) {
        return 0;
    }

    // Check database config first for custom origin
    sqlite3* db;
    sqlite3_stmt* stmt;
    int rc;

    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 = 'cdn_origin'";
        rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
        if (rc == SQLITE_OK) {
            rc = sqlite3_step(stmt);
            if (rc == SQLITE_ROW) {
                const char* value = (const char*)sqlite3_column_text(stmt, 0);
                if (value) {
                    strncpy(out, value, out_size - 1);
                    out[out_size - 1] = '\0';
                    sqlite3_finalize(stmt);
                    sqlite3_close(db);
                    return 1;
                }
            }
            sqlite3_finalize(stmt);
        }
        sqlite3_close(db);
    }

    // Check if request came over HTTPS (nginx sets HTTPS=on for SSL requests)
    const char* https_env = getenv("HTTPS");
    const char* server_name = getenv("SERVER_NAME");
    
    // Use production domain if SERVER_NAME is set and not localhost
    if (server_name && strcmp(server_name, "localhost") != 0) {
        if (https_env && strcmp(https_env, "on") == 0) {
            snprintf(out, out_size, "https://%s", server_name);
        } else {
            snprintf(out, out_size, "http://%s", server_name);
        }
        return 1;
    }
    
    // Fallback to localhost for development
    if (https_env && strcmp(https_env, "on") == 0) {
        strncpy(out, "https://localhost:9443", out_size - 1);
    } else {
        strncpy(out, "http://localhost:9001", out_size - 1);
    }
    out[out_size - 1] = '\0';
    return 1;
}

#define GINXSOM_MIME_EXTENSION_ENTRIES(X) \
    X("image/jpeg", ".jpg") \
    X("image/webp", ".webp") \
    X("image/png", ".png") \
    X("image/gif", ".gif") \
    X("video/mp4", ".mp4") \
    X("video/webm", ".webm") \
    X("audio/mpeg", ".mp3") \
    X("audio/ogg", ".ogg") \
    X("text/plain", ".txt") \
    X("text/html", ".html") \
    X("text/css", ".css") \
    X("application/javascript", ".js") \
    X("text/javascript", ".mjs") \
    X("application/pdf", ".pdf")

typedef struct {
    const char* mime;
    const char* extension;
} mime_extension_entry_t;

static const mime_extension_entry_t g_mime_extension_map[] = {
#define MAKE_MIME_ENTRY(mime, ext) {mime, ext},
    GINXSOM_MIME_EXTENSION_ENTRIES(MAKE_MIME_ENTRY)
#undef MAKE_MIME_ENTRY
};

static const char* g_supported_mime_types[] = {
#define MAKE_MIME_ONLY(mime, ext) mime,
    GINXSOM_MIME_EXTENSION_ENTRIES(MAKE_MIME_ONLY)
#undef MAKE_MIME_ONLY
};

const char* const* ginxsom_supported_mime_types(size_t* count) {
    if (count) {
        *count = sizeof(g_supported_mime_types) / sizeof(g_supported_mime_types[0]);
    }
    return g_supported_mime_types;
}

// Centralized MIME type to file extension mapping
const char* mime_to_extension(const char* mime_type) {
    if (!mime_type) {
        return ".bin";
    }

    size_t count = sizeof(g_mime_extension_map) / sizeof(g_mime_extension_map[0]);
    for (size_t i = 0; i < count; i++) {
        if (strstr(mime_type, g_mime_extension_map[i].mime)) {
            return g_mime_extension_map[i].extension;
        }
    }

    return ".bin";
}

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

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

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

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

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

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