//! Experiment core module - shared validation and formatting logic
//!
//! This module provides common utilities used by both local and server
//! experiment operations, reducing code duplication between modes.

const std = @import("std");
const core = @import("../core.zig");

/// Experiment name validation
pub fn validateExperimentName(name: []const u8) bool {
    if (name.len == 0 or name.len > 128) return false;
    
    for (name) |c| {
        if (!std.ascii.isAlphanumeric(c) and c != '_' and c != '-' and c != '.') {
            return false;
        }
    }
    return true;
}

/// Generate a UUID for experiment IDs
pub fn generateExperimentId(allocator: std.mem.Allocator) ![]const u8 {
    // Simple UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
    var buf: [36]u8 = undefined;
    const hex_chars = "0123456789abcdef";
    
    var i: usize = 0;
    while (i < 36) : (i += 1) {
        if (i == 8 or i == 13 or i == 18 or i == 23) {
            buf[i] = '-';
        } else if (i == 14) {
            buf[i] = '4'; // Version 4
        } else if (i == 19) {
            // Variant: 8, 9, a, or b
            const rand = std.crypto.random.int(u8);
            buf[i] = hex_chars[(rand & 0x03) + 8];
        } else {
            const rand = std.crypto.random.int(u8);
            buf[i] = hex_chars[rand & 0x0f];
        }
    }
    
    return try allocator.dupe(u8, &buf);
}

/// Format experiment for JSON output
pub fn formatExperimentJson(
    allocator: std.mem.Allocator,
    id: []const u8,
    name: []const u8,
    lifecycle: []const u8,
    created: []const u8,
) ![]const u8 {
    var buf = std.ArrayList(u8).init(allocator);
    defer buf.deinit();
    
    const writer = buf.writer();
    try writer.print(
        "{{\"id\":\"{s}\",\"name\":\"{s}\",\"lifecycle\":\"{s}\",\"created\":\"{s}\"}}",
        .{ id, name, lifecycle, created },
    );
    
    return buf.toOwnedSlice();
}

/// Format experiment list for JSON output
pub fn formatExperimentListJson(
    allocator: std.mem.Allocator,
    experiments: []const Experiment,
) ![]const u8 {
    var buf = std.ArrayList(u8).init(allocator);
    defer buf.deinit();
    
    const writer = buf.writer();
    try writer.writeAll("[");
    
    for (experiments, 0..) |exp, i| {
        if (i > 0) try writer.writeAll(",");
        try writer.print(
            "{{\"id\":\"{s}\",\"name\":\"{s}\",\"lifecycle\":\"{s}\",\"created\":\"{s}\"}}",
            .{ exp.id, exp.name, exp.lifecycle, exp.created },
        );
    }
    
    try writer.writeAll("]");
    return buf.toOwnedSlice();
}

/// Experiment struct for shared use
pub const Experiment = struct {
    id: []const u8,
    name: []const u8,
    lifecycle: []const u8,
    created: []const u8,
};

/// Print experiment in text format
pub fn printExperimentText(exp: Experiment) void {
    core.output.info("  {s} | {s} | {s} | {s}", .{
        exp.id,
        exp.name,
        exp.lifecycle,
        exp.created,
    });
}

/// Format metric for JSON output
pub fn formatMetricJson(
    allocator: std.mem.Allocator,
    name: []const u8,
    value: f64,
    step: u32,
) ![]const u8 {
    var buf = std.ArrayList(u8).init(allocator);
    defer buf.deinit();
    
    const writer = buf.writer();
    try writer.print(
        "{{\"name\":\"{s}\",\"value\":{d:.6},\"step\":{d}}}",
        .{ name, value, step },
    );
    
    return buf.toOwnedSlice();
}

/// Validate metric value
pub fn validateMetricValue(value: f64) bool {
    return !std.math.isNan(value) and !std.math.isInf(value);
}

/// Format run ID
pub fn formatRunId(allocator: std.mem.Allocator, experiment_id: []const u8, timestamp: i64) ![]const u8 {
    var buf: [64]u8 = undefined;
    const formatted = try std.fmt.bufPrint(&buf, "{s}_{d}", .{ experiment_id, timestamp });
    return try allocator.dupe(u8, formatted);
}
