const std = @import("std");
const Api = @import("artifacts-api");
const Server = Api.Server;
const Position = Api.Position;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;

const CharacterTask = @import("./task.zig").Task;
const QueuedAction = @import("./action.zig").Action;
const QueuedActionResult = @import("./action.zig").ActionResult;

const Brain = @This();

name: []const u8,
action_queue: std.ArrayList(QueuedAction),
task: ?CharacterTask = null,
paused_until: ?i64 = null, // ms

pub fn init(allocator: Allocator, name: []const u8) !Brain {
    return Brain{
        .name = try allocator.dupe(u8, name),
        .action_queue = std.ArrayList(QueuedAction).init(allocator),
    };
}

pub fn deinit(self: Brain) void {
    const allocator = self.action_queue.allocator;
    allocator.free(self.name);
    self.action_queue.deinit();
}

pub fn performNextAction(self: *Brain, api: *Server) !void {
    const log = std.log.default;
    assert(self.action_queue.items.len > 0);

    const retry_delay = 500; // 500ms

    const next_action = self.action_queue.items[0];
    const action_result =  try next_action.perform(api, self.name);

    if (action_result.getErrorResponse()) |error_response| {
        switch (error_response) {
            .retry => {
                self.paused_until = std.time.milliTimestamp() + retry_delay;
                log.warn("[{s}] retry action", .{self.name});
                return;
            },
            .restart => {
                log.warn("[{s}] clear action queue", .{self.name});
                self.action_queue.clearAndFree();
                return;
            },
            .abort => {
                log.warn("[{s}] abort action {s}", .{ self.name, @tagName(next_action) });
                try action_result.getError();

                // The error above should always return
                unreachable;
            },
            .ignore => { },
        }
    }

    _ = self.action_queue.orderedRemove(0);

    if (self.task) |*task| {
        task.onActionCompleted(action_result);
    }
}

pub fn step(self: *Brain, api: *Api.Server) !void {
    if (self.paused_until) |paused_until| {
        if (std.time.milliTimestamp() < paused_until) {
            return;
        }
        self.paused_until = null;
    }

    if (self.action_queue.items.len > 0) return;

    if (self.task) |task| {
        if (task.isComplete()) {
            // if (try brain.depositItemsToBank(&self.server)) {
            //     continue;
            // }
            self.task = null;
        }
    }

    if (self.task) |task| {
        try task.queueActions(api, self.name, &self.action_queue);
    }
}

pub fn cooldown(self: *Brain, api: *Server) i64 {
    const character = api.store.getCharacter(self.name).?;
    const cooldown_expiration: i64 = @intFromFloat(character.cooldown_expiration * std.time.ms_per_s);

    if (self.paused_until) |pause_until| {
        return @max(cooldown_expiration, pause_until);
    } else {
        return cooldown_expiration;
    }
}
