const std = @import("std");
const Config = @import("../config.zig").Config;
const io = @import("../utils/io.zig");
const json = @import("../utils/json.zig");
const manifest = @import("../utils/manifest.zig");
const core = @import("../core.zig");

pub const Options = struct {
    json: bool = false,
    base: ?[]const u8 = null,
};

pub fn run(allocator: std.mem.Allocator, args: []const []const u8) !void {
    var flags = core.flags.CommonFlags{};
    var base: ?[]const u8 = null;
    var target_path: ?[]const u8 = null;

    var i: usize = 0;
    while (i < args.len) : (i += 1) {
        const arg = args[i];
        if (std.mem.eql(u8, arg, "--json")) {
            flags.json = true;
        } else if (std.mem.eql(u8, arg, "--base") and i + 1 < args.len) {
            base = args[i + 1];
            i += 1;
        } else if (std.mem.startsWith(u8, arg, "--help")) {
            return printUsage();
        } else if (std.mem.startsWith(u8, arg, "--")) {
            core.output.err("Unknown option");
            return error.InvalidArgs;
        } else {
            target_path = arg;
        }
    }

    core.output.setMode(if (flags.json) .json else .text);

    if (target_path == null) {
        core.output.err("No target path specified");
        return printUsage();
    }

    const manifest_path = manifest.resolvePathWithBase(allocator, target_path.?, base) catch |err| {
        if (err == error.FileNotFound) {
            core.output.err("Manifest not found");
        }
        return err;
    };

    const data = try manifest.readFileAlloc(allocator, manifest_path);
    defer {
        allocator.free(manifest_path);
        allocator.free(data);
    }

    if (flags.json) {
        var out = io.stdoutWriter();
        try out.print("{s}\n", .{data});
        return;
    }

    const parsed = try std.json.parseFromSlice(std.json.Value, allocator, data, .{});
    defer parsed.deinit();

    if (parsed.value != .object) {
        core.output.err("run manifest is not a JSON object");
        return error.InvalidManifest;
    }

    const root = parsed.value.object;

    const run_id = json.getString(root, "run_id") orelse "";
    const task_id = json.getString(root, "task_id") orelse "";
    const job_name = json.getString(root, "job_name") orelse "";

    const commit_id = json.getString(root, "commit_id") orelse "";
    const worker_version = json.getString(root, "worker_version") orelse "";
    const podman_image = json.getString(root, "podman_image") orelse "";

    const snapshot_id = json.getString(root, "snapshot_id") orelse "";
    const snapshot_sha = json.getString(root, "snapshot_sha256") orelse "";

    const command = json.getString(root, "command") orelse "";
    const cmd_args = json.getString(root, "args") orelse "";

    const exit_code = json.getInt(root, "exit_code");
    const err_msg = json.getString(root, "error") orelse "";

    const created_at = json.getString(root, "created_at") orelse "";
    const started_at = json.getString(root, "started_at") orelse "";
    const ended_at = json.getString(root, "ended_at") orelse "";

    const staging_ms = json.getInt(root, "staging_duration_ms") orelse 0;
    const exec_ms = json.getInt(root, "execution_duration_ms") orelse 0;
    const finalize_ms = json.getInt(root, "finalize_duration_ms") orelse 0;
    const total_ms = json.getInt(root, "total_duration_ms") orelse 0;

    std.debug.print("run_manifest\t{s}\n", .{manifest_path});

    if (job_name.len > 0) std.debug.print("job_name\t{s}\n", .{job_name});
    if (run_id.len > 0) std.debug.print("run_id\t{s}\n", .{run_id});
    if (task_id.len > 0) std.debug.print("task_id\t{s}\n", .{task_id});

    if (commit_id.len > 0) std.debug.print("commit_id\t{s}\n", .{commit_id});
    if (worker_version.len > 0) std.debug.print("worker_version\t{s}\n", .{worker_version});
    if (podman_image.len > 0) std.debug.print("podman_image\t{s}\n", .{podman_image});

    if (snapshot_id.len > 0) std.debug.print("snapshot_id\t{s}\n", .{snapshot_id});
    if (snapshot_sha.len > 0) std.debug.print("snapshot_sha256\t{s}\n", .{snapshot_sha});

    if (command.len > 0) {
        if (cmd_args.len > 0) {
            std.debug.print("command\t{s} {s}\n", .{ command, cmd_args });
        } else {
            std.debug.print("command\t{s}\n", .{command});
        }
    }

    if (created_at.len > 0) std.debug.print("created_at\t{s}\n", .{created_at});
    if (started_at.len > 0) std.debug.print("started_at\t{s}\n", .{started_at});
    if (ended_at.len > 0) std.debug.print("ended_at\t{s}\n", .{ended_at});

    if (total_ms > 0 or staging_ms > 0 or exec_ms > 0 or finalize_ms > 0) {
        std.debug.print("durations_ms\ttotal={d}\tstaging={d}\texecution={d}\tfinalize={d}\n", .{ total_ms, staging_ms, exec_ms, finalize_ms });
    }

    if (exit_code) |ec| {
        if (ec == 0 and err_msg.len == 0) {
            std.debug.print("exit_code\t0\n", .{});
        } else {
            std.debug.print("exit_code\t{d}\n", .{ec});
        }
    }

    if (err_msg.len > 0) {
        std.debug.print("error\t{s}\n", .{err_msg});
    }
}

fn printUsage() !void {
    std.debug.print("Usage:\n", .{});
    std.debug.print("\tml info <run_dir_or_manifest_path_or_id> [--json] [--base <path>]\n", .{});
}

test "resolveManifestPath uses run_manifest.json for directories" {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup();

    try tmp.dir.makeDir("run");
    const run_abs = try tmp.dir.realpathAlloc(allocator, "run");
    defer allocator.free(run_abs);
    const got = try manifest.resolvePathWithBase(allocator, run_abs, null);
    try std.testing.expect(std.mem.endsWith(u8, got, "run/run_manifest.json"));
}

test "resolveManifestPath resolves by task id when base is provided" {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup();

    try tmp.dir.makePath("finished/run-a");
    var file = try tmp.dir.createFile("finished/run-a/run_manifest.json", .{});
    defer file.close();
    try file.writeAll(
        "{\n" ++
            "  \"run_id\": \"run-a\",\n" ++
            "  \"task_id\": \"task-123\",\n" ++
            "  \"job_name\": \"job\"\n" ++
            "}\n",
    );

    const base_abs = try tmp.dir.realpathAlloc(allocator, ".");
    defer allocator.free(base_abs);

    const got = try manifest.resolvePathWithBase(allocator, "task-123", base_abs);
    try std.testing.expect(std.mem.endsWith(u8, got, "finished/run-a/run_manifest.json"));
}
