// zig fmt: off
const std = @import("std");
const Store = @import("../store.zig");
const json_utils = @import("../json_utils.zig");
const parseDateTime = @import("../date_time/parse.zig").parseDateTime;
const json = std.json;
const Allocator = std.mem.Allocator;

const ServerStatus = @This();

pub const Status = std.BoundedArray(u8, 32);
pub const Version = std.BoundedArray(u8, 16);
pub const Date = std.BoundedArray(u8, 10);
pub const Announcement = struct {
    pub const Message = std.BoundedArray(u8, 64);

    message: Message,
    created_at: f64
};
pub const Announcements = std.BoundedArray(Announcement, 16);

status: Status,
version: Version,
characters_online: u64,
max_level: u64,
server_time: f64,
last_wipe: Date,
next_wipe: Date,
announcements: Announcements,

fn parseAnnouncements(array: json.Array) !Announcements {
    var announcements = Announcements.init(0) catch unreachable;

    for (array.items) |item| {
        const obj = json_utils.asObject(item) orelse return error.InvalidAnnouncement;
        const message = try json_utils.getStringRequired(obj, "message");
        const created_at = try json_utils.getStringRequired(obj, "created_at");

        try announcements.append(Announcement{
            .message = try Announcement.Message.fromSlice(message),
            .created_at = parseDateTime(created_at) orelse return error.InvalidDataTime,
        });
    }

    return announcements;
}

pub fn parse(store: *Store, obj: json.ObjectMap) !ServerStatus {
    _ = store;

    const characters_online = json_utils.getInteger(obj, "characters_online") orelse return error.MissingProperty;
    if (characters_online < 0) {
        return error.InvalidCharactersOnline;
    }

    const max_level = try json_utils.getIntegerRequired(obj, "max_level");
    if (max_level < 0) {
        return error.InvalidMaxLevel;
    }

    const status = try json_utils.getStringRequired(obj, "status");
    const version = try json_utils.getStringRequired(obj, "version");
    const last_wipe = try json_utils.getStringRequired(obj, "last_wipe");
    const next_wipe = try json_utils.getStringRequired(obj, "next_wipe");
    const server_time = try json_utils.getStringRequired(obj, "server_time");

    const announcements = json_utils.getArray(obj, "announcements") orelse return error.MissingProperty;

    return ServerStatus{
        .characters_online = @intCast(characters_online),
        .status = try Status.fromSlice(status),
        .version = try Version.fromSlice(version),
        .next_wipe = try Date.fromSlice(next_wipe),
        .last_wipe = try Date.fromSlice(last_wipe),
        .server_time = parseDateTime(server_time) orelse return error.InvalidaDateTime,
        .announcements = try parseAnnouncements(announcements),
        .max_level = @intCast(max_level)
    };
}

pub fn format(
    self: ServerStatus,
    comptime fmt: []const u8,
    options: std.fmt.FormatOptions,
    writer: anytype,
) !void {
    _ = fmt;
    _ = options;

    try writer.print("{s}{{ ", .{ @typeName(ServerStatus) });

    try writer.print(".status = \"{s}\", ", .{ self.status.slice() });
    try writer.print(".version = \"{s}\", ", .{ self.version.slice() });
    try writer.print(".characters_online = {}, ", .{ self.characters_online });
    try writer.print(".max_level = {}, ", .{ self.max_level });
    try writer.print(".server_time = {}, ", .{ self.server_time });
    try writer.print(".last_wipe = \"{s}\", ", .{ self.last_wipe.slice() });
    try writer.print(".next_wipe = \"{s}\", ", .{ self.next_wipe.slice() });
    try writer.writeAll(".announcements = .{ ... }, ");

    try writer.writeAll("}");
}
