const std = @import("std");
const build_options = @import("build_options");

pub const HashError = error{
    ContextInitFailed,
    HashFailed,
    InvalidPath,
    OutOfMemory,
    NotAvailable,
};

// Conditionally compile C imports only when not cross-compiling
const c = if (build_options.is_cross_compiling)
    struct {
        pub const fh_context_t = opaque {};
        pub fn fh_init(_: i32) ?*fh_context_t {
            return null;
        }
        pub fn fh_hash_directory_combined(_: *fh_context_t, _: [*c]const u8) [*c]u8 {
            return null;
        }
        pub fn fh_free_string(_: [*c]u8) void {}
        pub fn fh_has_simd_sha256() i32 {
            return 0;
        }
        pub fn fh_get_simd_impl_name() [*c]const u8 {
            return @ptrCast(@alignCast("none"));
        }
    }
else
    @cImport({
        @cInclude("dataset_hash.h");
    });

// Global context for reuse across multiple hash operations
var global_ctx: ?*c.fh_context_t = null;
var ctx_initialized = std.atomic.Value(bool).init(false);
var init_mutex = std.Thread.Mutex{};

/// Initialize global hash context once (thread-safe)
pub fn init() !void {
    if (build_options.is_cross_compiling) {
        return HashError.NotAvailable;
    }

    if (ctx_initialized.load(.seq_cst)) return;

    init_mutex.lock();
    defer init_mutex.unlock();

    if (ctx_initialized.load(.seq_cst)) return; // Double-check

    const start = std.time.milliTimestamp();
    global_ctx = c.fh_init(0); // 0 = auto-detect threads
    const elapsed = std.time.milliTimestamp() - start;

    if (global_ctx == null) {
        return HashError.ContextInitFailed;
    }

    ctx_initialized.store(true, .seq_cst);
    std.log.info("[native] hash context initialized: {}ms", .{elapsed});
}

/// Hash a directory using the native library (reuses global context)
/// Returns the hex-encoded SHA256 hash string
pub fn hashDirectory(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
    if (build_options.is_cross_compiling) {
        return HashError.NotAvailable;
    }

    try init(); // Idempotent initialization

    const ctx = global_ctx.?; // Safe: init() guarantees non-null

    // Convert path to null-terminated C string
    const c_path = try allocator.dupeZ(u8, path);
    defer allocator.free(c_path);

    // Call native function
    const result = c.fh_hash_directory_combined(ctx, c_path);
    if (result == null) {
        return HashError.HashFailed;
    }
    defer c.fh_free_string(result);

    // Convert result to Zig string
    const result_slice = std.mem.span(result);
    return try allocator.dupe(u8, result_slice);
}

/// Check if SIMD SHA256 is available
pub fn hasSimdSha256() bool {
    if (build_options.is_cross_compiling) return false;
    return c.fh_has_simd_sha256() == 1;
}

/// Get the name of the SIMD implementation being used
pub fn getSimdImplName() []const u8 {
    if (build_options.is_cross_compiling) return "none";
    const name = c.fh_get_simd_impl_name();
    return std.mem.span(name);
}
