const std = @import("std");
const Allocator = std.mem.Allocator;

const hotfixInfo = struct {
    clientVersion: []const u8,
    assetBundleUrl: []const u8,
    exResourceUrl: []const u8,
    luaUrl: []const u8,
    iFixUrl: []const u8,
};

pub fn Parser(allocator: Allocator, filename: []const u8, version: []const u8) !hotfixInfo {
    const file = try std.fs.cwd().openFile(filename, .{});
    defer file.close();

    const file_size = try file.getEndPos();
    const buffer = try file.readToEndAlloc(allocator, file_size);
    defer allocator.free(buffer);

    var json_tree = try std.json.parseFromSlice(std.json.Value, allocator, buffer, .{ .ignore_unknown_fields = true });
    defer json_tree.deinit();

    const version_node = json_tree.value.object.get(version) orelse {
        return hotfixInfo{
            .clientVersion = version,
            .assetBundleUrl = "",
            .exResourceUrl = "",
            .luaUrl = "",
            .iFixUrl = "",
        };
    };

    return getValue(version_node, version);
}

fn getValue(node: std.json.Value, client_version: []const u8) !hotfixInfo {
    if (node != .object) return error.InvalidJsonStructure;

    const obj = node.object;

    const assetBundleUrl = obj.get("asset_bundle_url") orelse return error.MissingAssetBundleUrl;
    const exResourceUrl = obj.get("ex_resource_url") orelse return error.MissingExResourceUrl;
    const luaUrl = obj.get("lua_url") orelse return error.MissingLuaUrl;
    const iFixUrl = obj.get("ifix_url") orelse return error.MissingIFixUrl;

    if (assetBundleUrl != .string or
        exResourceUrl != .string or
        luaUrl != .string or
        iFixUrl != .string) return error.InvalidUrlFormat;

    return hotfixInfo{
        .clientVersion = client_version,
        .assetBundleUrl = assetBundleUrl.string,
        .exResourceUrl = exResourceUrl.string,
        .luaUrl = luaUrl.string,
        .iFixUrl = iFixUrl.string,
    };
}

pub fn putValue(version: []const u8, assetBundleUrl: []const u8, exResourceUrl: []const u8, luaUrl: []const u8, iFixUrl: []const u8) !void {
    const file = try std.fs.cwd().openFile("hotfix.json", .{ .mode = .read_write });
    defer file.close();

    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    const file_size = try file.getEndPos();
    const buffer0 = try file.readToEndAlloc(allocator, file_size);
    defer allocator.free(buffer0);

    var json_tree = try std.json.parseFromSlice(std.json.Value, allocator, buffer0, .{ .ignore_unknown_fields = true });
    defer json_tree.deinit();

    var root = json_tree.value.object;

    var new_version = std.json.ObjectMap.init(allocator);
    try new_version.put("asset_bundle_url", .{ .string = assetBundleUrl });
    try new_version.put("ex_resource_url", .{ .string = exResourceUrl });
    try new_version.put("ifix_url", .{ .string = iFixUrl });
    try new_version.put("ifix_version", .{ .string = "0" });
    try new_version.put("lua_url", .{ .string = luaUrl });
    try new_version.put("lua_version", .{ .string = "" });
    try root.put(version, .{ .object = new_version });

    const json_value = std.json.Value{ .object = root };

    var buffer = std.ArrayList(u8).init(allocator);
    try std.json.stringify(json_value, .{ .whitespace = .indent_4 }, buffer.writer());

    const new_file = try std.fs.cwd().createFile("hotfix.json", .{ .truncate = true });
    defer new_file.close();

    try new_file.writeAll(buffer.items);
}
