const MamiUserPermsInfo = function(rank = 0, canKick = false, canSetNick = false, canCreateChannels = false) {
    if(typeof rank !== 'number')
        throw 'rank must be a number';
    if(typeof canKick !== 'boolean')
        throw 'canKick must be a boolean';
    if(typeof canSetNick !== 'boolean')
        throw 'canSetNick must be a boolean';
    if(typeof canCreateChannels !== 'boolean')
        throw 'canCreateChannels must be a boolean';

    return {
        get rank() { return rank; },
        get canKick() { return canKick; },
        get canSetNick() { return canSetNick; },
        get canCreateChannels() { return canCreateChannels; },
    };
};

const MamiUserStatusInfo = function(isAway = false, message = '') {
    if(typeof isAway !== 'boolean')
        throw 'isAway must be a boolean';
    if(typeof message !== 'string')
        throw 'message must be a string';

    return {
        get isAway() { return isAway; },
        get message() { return message; },
    };
};

const MamiUserInfo = function(id, name, colour = 'inherit', status = null, perms = null) {
    if(typeof id !== 'string')
        throw 'id must be a string';
    if(typeof name !== 'string')
        throw 'name must be a string';
    if(typeof colour !== 'string') // should be like, object or something maybe
        throw 'colour must be a string';
    if(status === null)
        status = new MamiUserStatusInfo;
    else if(typeof status !== 'object')
        throw 'status must be an object';
    if(perms === null)
        perms = new MamiUserPermsInfo;
    else if(typeof perms !== 'object')
        throw 'perms must be an object';

    let avatarChangeTime = Date.now();

    return {
        get id() { return id; },

        get name() { return name; },
        set name(value) {
            if(typeof value !== 'string')
                throw 'value must be a string';
            name = value;
        },

        get colour() { return colour; },
        set colour(value) {
            if(typeof value !== 'string') // ^
                throw 'value must be a string';
            colour = value;
        },

        get status() { return status; },
        set status(value) {
            if(typeof value !== 'object' || value === null)
                throw 'value must be an object';
            status = value;
        },

        get perms() { return perms; },
        set perms(value) {
            if(typeof value !== 'object' || value === null)
                throw 'value must be an object';
            perms = value;
        },

        get avatarChangeTime() { return avatarChangeTime; },
        set avatarChangeTime(value) {
            if(typeof value !== 'number')
                throw 'value must be a number';
            avatarChangeTime = value;
        },
    };
};

const MamiConvertUserInfoToUmi = info => {
    return {
        getId: () => info.id,
        getIdInt: () => parseInt(info.id),

        getName: () => {
            let name = info.name;
            if(info.status.isAway)
                name = `<${info.status.message.substring(0, 5).toUpperCase()}>_${name}`;

            return name;
        },
        setName: () => {},

        getColour: () => info.colour,
        setColour: () => {},

        setPermissions: () => {},

        getRank: () => info.perms.rank,
        canBan: () => info.perms.canKick,
        canSilence: () => false,
        canCreateChannel: () => info.perms.canCreateChannels,
        canSetNickName: () => info.perms.canSetNick,
        getAvatarTime: () => info.avatarChangeTime,
        bumpAvatarTime: () => {
            info.avatarChangeTime = Date.now();
        },

        isBot: () => info.id === '-1',
    };
};

Umi.User = (() => {
    let userInfo;

    return {
        hasCurrentUser: () => userInfo !== undefined,
        getCurrentUser: () => userInfo,
        setCurrentUser: value => { userInfo = value; },
        isCurrentUser: otherInfo => otherInfo !== null && typeof otherInfo === 'object' && typeof otherInfo.id === 'string'
            && userInfo !== null && typeof userInfo === 'object'
            && typeof userInfo.id === 'string'
            && (userInfo === otherInfo || userInfo.id === otherInfo.id),
    };
})();

Umi.Users = (function() {
    const users = new Map;

    const onAdd = [];
    const onRemove = [];
    const onClear = [];
    const onUpdate = [];

    return {
        OnAdd: onAdd,
        OnRemove: onRemove,
        OnClear: onClear,
        OnUpdate: onUpdate,
        Add: function(user) {
            const userId = user.id;
            if(!users.has(userId)) {
                users.set(userId, user);

                for(const i in onAdd)
                    onAdd[i](user);
            }
        },
        Remove: function(user) {
            const userId = user.id;
            if(users.has(userId)) {
                users.delete(userId);

                for(const i in onRemove)
                    onRemove[i](user);
            }
        },
        Clear: function() {
            users.clear();

            for(const i in onClear)
                onClear[i]();
        },
        All: function() {
            return Array.from(users.values());
        },
        Get: function(userId) {
            userId = userId.toString();
            if(users.has(userId))
                return users.get(userId);
            return null;
        },
        Find: function(userName) {
            const found = [];
            userName = userName.toLowerCase();

            users.forEach(function(user) {
                if(user.name.toLowerCase().includes(userName))
                    found.push(user);
            });

            return found;
        },
        FindExact: function(userName) {
            userName = userName.toLowerCase();

            for(const user of users.values())
                if(user.name.toLowerCase() === userName)
                    return user;

            return null;
        },
        Update: function(userId, user) {
            userId = userId.toString();
            users.set(userId, user);

            for(const i in onUpdate)
                onUpdate[i](userId, user);
        },
    };
})();
