const std = @import("std");
const aws = @import("aws.zig");
const json = @import("json.zig");

pub fn log(
    comptime level: std.log.Level,
    comptime scope: @TypeOf(.EnumLiteral),
    comptime format: []const u8,
    args: anytype,
) void {
    // Ignore awshttp messages
    if (scope == .awshttp and @enumToInt(level) >= @enumToInt(std.log.Level.debug))
        return;
    const scope_prefix = "(" ++ @tagName(scope) ++ "): ";
    const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;

    // Print the message to stderr, silently ignoring any errors
    const held = std.debug.getStderrMutex().acquire();
    defer held.release();
    const stderr = std.io.getStdErr().writer();
    nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
}

pub fn main() anyerror!void {
    // Uncomment if you want to log allocations
    // const file = try std.fs.cwd().createFile("/tmp/allocations.log", .{ .truncate = true });
    // defer file.close();
    // var child_allocator = std.heap.c_allocator;
    // const allocator = &std.heap.loggingAllocator(child_allocator, file.writer()).allocator;

    // Flip to true to run a second time. This will help debug
    // allocation/deallocation issues
    const test_twice = false;

    // Flip to true to run through the json parsing changes made to stdlib
    const test_json = false;
    if (test_json) try jsonFun();

    const allocator = std.heap.c_allocator;

    const options = aws.Options{
        .region = "us-west-2",
    };
    std.log.info("Start", .{});

    var client = aws.Aws.init(allocator);
    defer client.deinit();
    const resp = try client.call(aws.services.sts.get_caller_identity.Request{}, options);
    // TODO: This is a bit wonky. Root cause is lack of declarations in
    //       comptime-generated types
    defer resp.deinit();

    if (test_twice) {
        std.time.sleep(1000 * std.time.ns_per_ms);
        std.log.info("second request", .{});

        var client2 = aws.Aws.init(allocator);
        defer client2.deinit();
        const resp2 = try client2.call(aws.services.sts.get_caller_identity.Request{}, options); // catch here and try alloc?
        defer resp2.deinit();
    }

    std.log.info("arn: {s}", .{resp.response.arn});
    std.log.info("id: {s}", .{resp.response.user_id});
    std.log.info("account: {s}", .{resp.response.account});
    std.log.info("requestId: {s}", .{resp.response_metadata.request_id});

    std.log.info("Departing main", .{});
}

pub fn jsonFun() !void {
    // Standard behavior
    const payload =
        \\{"GetCallerIdentityResponse":{"GetCallerIdentityResult":{"Account":"0123456789","Arn":"arn:aws:iam::0123456789:user/test","UserId":"MYUSERID"},"ResponseMetadata":{"RequestId":"3b80a99b-7df8-4bcb-96ee-b2759878a5f2"}}}
    ;
    const Ret3 = struct {
        getCallerIdentityResponse: struct { getCallerIdentityResult: struct { account: []u8, arn: []u8, user_id: []u8 }, responseMetadata: struct { requestId: []u8 } },
    };
    var stream3 = json.TokenStream.init(payload);
    const res3 = json.parse(Ret3, &stream3, .{
        .allocator = std.heap.c_allocator,
        .allow_camel_case_conversion = true, // new option
        .allow_snake_case_conversion = true, // new option
        .allow_unknown_fields = true, // new option
    }) catch unreachable;
    std.log.info("{}", .{res3});
    std.log.info("{s}", .{res3.getCallerIdentityResponse.getCallerIdentityResult.user_id});
}
