/**
 * BotHelpers helps bots
 * "private" properties use meteor collection cursors, so they stay reactive
 * "public" properties use getters to fetch and filter collections as array
 */
class BotHelpers {
	constructor() {
		this.queries = {
			online: { 'status': { $ne: 'offline' } },
			users: { 'roles': { $not: { $all: ['bot'] } } }
		};
	}

	// setup collection cursors with array of fields from setting
	setupCursors(fieldsSetting) {
		this.userFields = {};
		if (typeof fieldsSetting === 'string') {
			fieldsSetting = fieldsSetting.split(',');
		}
		fieldsSetting.forEach((n) => {
			this.userFields[n.trim()] = 1;
		});
		this._allUsers = RocketChat.models.Users.find(this.queries.users, { fields: this.userFields });
		this._onlineUsers = RocketChat.models.Users.find({ $and: [this.queries.users, this.queries.online] }, { fields: this.userFields });
	}

	// request methods or props as arguments to Meteor.call
	request(prop, ...params) {
		if (typeof this[prop] === 'undefined') {
			return null;
		} else if (typeof this[prop] === 'function') {
			return this[prop](...params);
		} else {
			return this[prop];
		}
	}

	addUserToRole(userName, roleName) {
		Meteor.call('authorization:addUserToRole', roleName, userName);
	}

	removeUserFromRole(userName, roleName) {
		Meteor.call('authorization:removeUserFromRole', roleName, userName);
	}

	addUserToRoom(userName, room) {
		const foundRoom = RocketChat.models.Rooms.findOneByIdOrName(room);

		if (!_.isObject(foundRoom)) {
			throw new Meteor.Error('invalid-channel');
		}

		const data = {};
		data.rid = foundRoom._id;
		data.username = userName;
		Meteor.call('addUserToRoom', data);
	}

	removeUserFromRoom(userName, room) {
		const foundRoom = RocketChat.models.Rooms.findOneByIdOrName(room);

		if (!_.isObject(foundRoom)) {
			throw new Meteor.Error('invalid-channel');
		}
		const data = {};
		data.rid = foundRoom._id;
		data.username = userName;
		Meteor.call('removeUserFromRoom', data);
	}

	// generic error whenever property access insufficient to fill request
	requestError() {
		throw new Meteor.Error('error-not-allowed', 'Bot request not allowed', { method: 'botRequest', action: 'bot_request' });
	}

	// "public" properties accessed by getters
	// allUsers / onlineUsers return whichever properties are enabled by settings
	get allUsers() {
		if (!Object.keys(this.userFields).length) {
			this.requestError();
			return false;
		} else {
			return this._allUsers.fetch();
		}
	}
	get onlineUsers() {
		if (!Object.keys(this.userFields).length) {
			this.requestError();
			return false;
		} else {
			return this._onlineUsers.fetch();
		}
	}
	get allUsernames() {
		if (!this.userFields.hasOwnProperty('username')) {
			this.requestError();
			return false;
		} else {
			return this._allUsers.fetch().map((user) => user.username);
		}
	}
	get onlineUsernames() {
		if (!this.userFields.hasOwnProperty('username')) {
			this.requestError();
			return false;
		} else {
			return this._onlineUsers.fetch().map((user) => user.username);
		}
	}
	get allNames() {
		if (!this.userFields.hasOwnProperty('name')) {
			this.requestError();
			return false;
		} else {
			return this._allUsers.fetch().map((user) => user.name);
		}
	}
	get onlineNames() {
		if (!this.userFields.hasOwnProperty('name')) {
			this.requestError();
			return false;
		} else {
			return this._onlineUsers.fetch().map((user) => user.name);
		}
	}
	get allIDs() {
		if (!this.userFields.hasOwnProperty('_id') || !this.userFields.hasOwnProperty('username')) {
			this.requestError();
			return false;
		} else {
			return this._allUsers.fetch().map((user) => {
				return { 'id': user._id, 'name': user.username };
			});
		}
	}
	get onlineIDs() {
		if (!this.userFields.hasOwnProperty('_id') || !this.userFields.hasOwnProperty('username')) {
			this.requestError();
			return false;
		} else {
			return this._onlineUsers.fetch().map((user) => {
				return { 'id': user._id, 'name': user.username };
			});
		}
	}
}

// add class to meteor methods
const botHelpers = new BotHelpers();

// init cursors with fields setting and update on setting change
RocketChat.settings.get('BotHelpers_userFields', function(settingKey, settingValue) {
	botHelpers.setupCursors(settingValue);
});

Meteor.methods({
	botRequest: (...args) => {
		const userID = Meteor.userId();
		if (userID && RocketChat.authz.hasRole(userID, 'bot')) {
			return botHelpers.request(...args);
		} else {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'botRequest' });
		}
	}
});
