const std = @import("std");

const ErrorDefinition = struct {
    name: [:0]const u8,
    code: ?u10,

    fn init(name: [:0]const u8, code: ?u10) ErrorDefinition {
        return ErrorDefinition{ .name = name, .code = code };
    }
};

fn ErrorDefinitionList(errors: []const ErrorDefinition) type {
    var errorNames: [errors.len]std.builtin.Type.Error = undefined;
    for (0.., errors) |i, def| {
        errorNames[i] = .{ .name = def.name };
    }

    const error_set = @Type(.{ .ErrorSet = &errorNames });

    return struct {
        const ErrorSet = error_set;

        fn parse(status: std.http.Status) ?ErrorSet {
            inline for (errors) |err| {
                if (err.code == @intFromEnum(status)) {
                    return @field(ErrorSet, err.name);
                }
            }
            return null;
        }
    };
}

// zig fmt: off

pub const NotAuthenticated  = ErrorDefinition.init("NotAuthenticated", 403);
pub const ServerUnavailable = ErrorDefinition.init("ServerUnavailable", 503);
pub const RequestFailed     = ErrorDefinition.init("RequestFailed", null);
pub const ParseFailed       = ErrorDefinition.init("ParseFailed", null);
pub const OutOfMemory       = ErrorDefinition.init("OutOfMemory", null);

// List of error codes gotten from https://docs.artifactsmmo.com/api_guide/response_codes
// General
pub const InvalidPayload  = ErrorDefinition.init("InvalidPayload", 422);
pub const TooManyRequests = ErrorDefinition.init("TooManyRequests", 429);
pub const NotFound        = ErrorDefinition.init("NotFound", 404);
pub const FatalError      = ErrorDefinition.init("FatalError", 500);

// Account Error Codes
const TokenInvalid           = ErrorDefinition.init("TokenInvalid", 452);
const TokenExpired           = ErrorDefinition.init("TokenExpired", 453);
const TokenMissing           = ErrorDefinition.init("TokenMissing", 454);
const TokenGenerationFail    = ErrorDefinition.init("TokenGenerationFail", 455);
const UsernameAlreadyUsed    = ErrorDefinition.init("UsernameAlreadyUsed", 456);
const EmailAlreadyUsed       = ErrorDefinition.init("EmailAlreadyUsed", 457);
const SamePassword           = ErrorDefinition.init("SamePassword", 458);
const CurrentPasswordInvalid = ErrorDefinition.init("CurrentPasswordInvalid", 459);

// Character Error Codes
const CharacterNotEnoughHp            = ErrorDefinition.init("CharacterNotEnoughHp", 483);
const CharacterMaximumUtilitesEquiped = ErrorDefinition.init("CharacterMaximumUtilitesEquiped", 484);
const CharacterItemAlreadyEquiped     = ErrorDefinition.init("CharacterItemAlreadyEquiped", 485);
const CharacterLocked                 = ErrorDefinition.init("CharacterLocked", 486);
const CharacterNotThisTask            = ErrorDefinition.init("CharacterNotThisTask", 474);
const CharacterTooManyItemsTask       = ErrorDefinition.init("CharacterTooManyItemsTask", 475);
const CharacterNoTask                 = ErrorDefinition.init("CharacterNoTask", 487);
const CharacterTaskNotCompleted       = ErrorDefinition.init("CharacterTaskNotCompleted", 488);
const CharacterAlreadyTask            = ErrorDefinition.init("CharacterAlreadyTask", 489);
const CharacterAlreadyMap             = ErrorDefinition.init("CharacterAlreadyMap", 490);
const CharacterSlotEquipmentError     = ErrorDefinition.init("CharacterSlotEquipmentError", 491);
const CharacterGoldInsufficient       = ErrorDefinition.init("CharacterGoldInsufficient", 492);
const CharacterNotSkillLevelRequired  = ErrorDefinition.init("CharacterNotSkillLevelRequired", 493);
const CharacterNameAlreadyUsed        = ErrorDefinition.init("CharacterNameAlreadyUsed", 494);
const MaxCharactersReached            = ErrorDefinition.init("MaxCharactersReached", 495);
const CharacterNotLevelRequired       = ErrorDefinition.init("CharacterNotLevelRequired", 496);
const CharacterInventoryFull          = ErrorDefinition.init("CharacterInventoryFull", 497);
const CharacterNotFound               = ErrorDefinition.init("CharacterNotFound", 498);
const CharacterInCooldown             = ErrorDefinition.init("CharacterInCooldown", 499);

// Item Error Codes
const ItemInsufficientQuantity = ErrorDefinition.init("ItemInsufficientQuantity", 471);
const ItemInvalidEquipment     = ErrorDefinition.init("ItemInvalidEquipment", 472);
const ItemRecyclingInvalidItem = ErrorDefinition.init("ItemRecyclingInvalidItem", 473);
const ItemInvalidConsumable    = ErrorDefinition.init("ItemInvalidConsumable", 476);
const MissingItem              = ErrorDefinition.init("MissingItem", 478);

// Grand Exchange Error Codes
const GeMaxQuantity           = ErrorDefinition.init("GeMaxQuantity", 479);
const GeNotInStock            = ErrorDefinition.init("GeNotInStock", 480);
const GeNotThePrice           = ErrorDefinition.init("GeNotThePrice", 482);
const GeTransactionInProgress = ErrorDefinition.init("GeTransactionInProgress", 436);
const GeNoOrders              = ErrorDefinition.init("GeNoOrders", 431);
const GeMaxOrders             = ErrorDefinition.init("GeMaxOrders", 433);
const GeTooManyItems          = ErrorDefinition.init("GeTooManyItems", 434);
const GeSameAccount           = ErrorDefinition.init("GeSameAccount", 435);
const GeInvalidItem           = ErrorDefinition.init("GeInvalidItem", 437);
const GeNotYourOrder          = ErrorDefinition.init("GeNotYourOrder", 438);

// Bank Error Codes
const BankInsufficientGold      = ErrorDefinition.init("BankInsufficientGold", 460);
const BankTransactionInProgress = ErrorDefinition.init("BankTransactionInProgress", 461);
const BankFull                  = ErrorDefinition.init("BankFull", 462);

// Maps Error Codes
const MapNotFound        = ErrorDefinition.init("MapNotFound", 597);
const MapContentNotFound = ErrorDefinition.init("MapContentNotFound", 598);

pub const FetchError = ErrorDefinitionList(&[_]ErrorDefinition{
    NotAuthenticated,
    TooManyRequests,
    FatalError,
    InvalidPayload,
    ServerUnavailable,
    RequestFailed,
    ParseFailed,
    OutOfMemory,
}).ErrorSet;

const MoveErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    MapNotFound,
    CharacterLocked,
    CharacterAlreadyMap,
    CharacterNotFound,
    CharacterInCooldown
});
pub const MoveError = FetchError || MoveErrorDef.ErrorSet;
pub const parseMoveError = MoveErrorDef.parse;

const FightErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    CharacterLocked,
    CharacterInventoryFull,
    CharacterNotFound,
    CharacterInCooldown,
    MapContentNotFound,
});
pub const FightError = FetchError || FightErrorDef.ErrorSet;
pub const parseFightError = FightErrorDef.parse;

const GatherErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    CharacterLocked,
    CharacterNotSkillLevelRequired,
    CharacterInventoryFull,
    CharacterNotFound,
    CharacterInCooldown,
    MapContentNotFound
});
pub const GatherError = FetchError || GatherErrorDef.ErrorSet;
pub const parseGatherError = GatherErrorDef.parse;

const BankDepositItemErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    NotFound,
    BankTransactionInProgress,
    MissingItem,
    CharacterLocked,
    CharacterNotFound,
    CharacterInCooldown,
    MapContentNotFound
});
pub const BankDepositItemError = FetchError || BankDepositItemErrorDef.ErrorSet;
pub const parseBankDepositItemError = BankDepositItemErrorDef.parse;

const BankDepositGoldErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    BankTransactionInProgress,
    BankInsufficientGold,
    CharacterLocked,
    CharacterNotFound,
    CharacterInCooldown,
    MapContentNotFound
});
pub const BankDepositGoldError = FetchError || BankDepositGoldErrorDef.ErrorSet;
pub const parseBankDepositGoldError = BankDepositGoldErrorDef.parse;

const BankWithdrawGoldErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    BankTransactionInProgress,
    BankInsufficientGold,
    CharacterLocked,
    CharacterNotFound,
    CharacterInCooldown,
    MapContentNotFound
});
pub const BankWithdrawGoldError = FetchError || BankWithdrawGoldErrorDef.ErrorSet;
pub const parseBankWithdrawGoldError = BankWithdrawGoldErrorDef.parse;

const BankWithdrawItemErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    NotFound,
    BankTransactionInProgress,
    MissingItem,
    CharacterLocked,
    CharacterInventoryFull,
    CharacterNotFound,
    CharacterInCooldown,
    MapContentNotFound
});
pub const BankWithdrawItemError = FetchError || BankWithdrawItemErrorDef.ErrorSet;
pub const parseBankWithdrawItemError = BankWithdrawItemErrorDef.parse;

const CraftErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    NotFound,
    MissingItem,
    CharacterLocked,
    CharacterNotSkillLevelRequired,
    CharacterInventoryFull,
    CharacterNotFound,
    CharacterInCooldown,
    MapContentNotFound
});
pub const CraftError = FetchError || CraftErrorDef.ErrorSet;
pub const parseCraftError = CraftErrorDef.parse;

const UnequipErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    NotFound,
    CharacterLocked,
    CharacterSlotEquipmentError,
    CharacterInventoryFull,
    CharacterNotFound,
    CharacterInCooldown,
});
pub const UnequipError = FetchError || UnequipErrorDef.ErrorSet;
pub const parseUnequipError = UnequipErrorDef.parse;

const EquipErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    NotFound,
    CharacterItemAlreadyEquiped,
    CharacterLocked,
    CharacterNotSkillLevelRequired,
    CharacterNotFound,
    CharacterInCooldown,
});
pub const EquipError = FetchError || EquipErrorDef.ErrorSet;
pub const parseEquipError = EquipErrorDef.parse;

const AcceptTaskErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    CharacterLocked,
    CharacterNotFound,
    CharacterInCooldown,
    MapContentNotFound
});
pub const AcceptTaskError = FetchError || AcceptTaskErrorDef.ErrorSet;
pub const parseAcceptTaskError = AcceptTaskErrorDef.parse;

const TaskCompleteErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    CharacterLocked,
    CharacterNoTask,
    CharacterTaskNotCompleted,
    CharacterInventoryFull,
    CharacterNotFound,
    CharacterInCooldown,
    MapContentNotFound
});
pub const TaskCompleteError = FetchError || TaskCompleteErrorDef.ErrorSet;
pub const parseTaskCompleteError = TaskCompleteErrorDef.parse;

const CreateCharacterErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    CharacterNameAlreadyUsed,
    MaxCharactersReached
});
pub const CreateCharacterError = FetchError || CreateCharacterErrorDef.ErrorSet;
pub const parseCreateCharacterError = CreateCharacterErrorDef.parse;

const DeleteCharacterErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
    CharacterNotFound,
});
pub const DeleteCharacterError = FetchError || DeleteCharacterErrorDef.ErrorSet;
pub const parseDeleteCharacterError = DeleteCharacterErrorDef.parse;
