var Quota = require('libresignage/user/Quota');
var APIEndpoints = require('libresignage/api/APIEndpoints');
var Assert = require('assert');

/**
* Class representing LibreSignage user data.
*
* @memberof module:libresignage/user
*/
class User {
	/**
	* Construct a User object.
	*
	* @param {APIInterface} api An initialized APIInterface object.
	*
	* @throws {AssertionError} If api == null.
	*/
	constructor(api) {
		Assert.ok(api != null, 'An API object must be supplied.');
		this.api = api;
		this.data = null;
	}

	/**
	* Create a new user.
	*
	* @param {string}  user         The username of the user.
	* @param {boolean} passwordless Enable passwordless login for the user.
	*/
	async create(user, passwordless) {
		this.data = {};
		let resp = await this.api.call(
			APIEndpoints.USER_CREATE,
			{'user': user, 'passwordless': passwordless}
		);

		this.data = Object.assign(this.data, resp.user);
		this.data.pass = resp.pass;
	}

	/**
	* Load a user from via the API.
	*
	* @param {string} user The username of the user.
	*/
	async load(user) {
		let resp = null;
		this.data = {};

		if (user == null) {
			resp = await this.api.call(
				APIEndpoints.USER_GET_CURRENT,
				null
			);
		} else {
			resp = await this.api.call(
				APIEndpoints.USER_GET,
				{'user': user}
			);
		}

		/*
		*  Create a separate Quota object from the quota data
		*  if the data exists in the response. This check is
		*  needed because quota data is only returned if the
		*  user is requesting their own userdata.
		*/
		if ('quota' in resp.user) {
			this.data = { quota: new Quota(resp.user.quota) };
			delete resp.user.quota;
		}

		// Add rest of the fetched data as-is.
		this.data = Object.assign(this.data, resp.user);
	}

	/**
	* Save a user via the API.
	*
	* @throws {AssertionError} If a user is not loaded.
	*/
	async save() {
		Assert.ok(this.data != null, "User not loaded.");
		await this.api.call(
			APIEndpoints.USER_SAVE,
			{
				'user':   this.data.user,
				'groups': this.data.groups,
				'pass':   this.data.pass ? this.data.pass : null
			}
		);
	}

	/**
	* Remove a user via the API.
	*
	* @throws {AssertionError} If a user is not loaded.
	*/
	async remove() {
		Assert.ok(this.data != null, "User not loaded.");
		await this.api.call(
			APIEndpoints.USER_REMOVE,
			{user: this.data.user}
		);
		this.data = null;
	}

	/**
	* Set the password of a user.
	*
	* @param {string} pass The new password.
	*/
	set_password(pass) { this.data.pass = pass;     }

	/**
	* Set the groups of a user.
	*
	* @param {string[]} groups The new groups array.
	*/
	set_groups(groups) { this.data.groups = groups; }

	/**
	* Get the username of a user.
	*
	* @return {string|null} The username.
	*/
	get_user() { return this.data.user; }

	/**
	* Get the password of a user.

	* Note that the API only gives out the password when a new user
	* is created. This function consequently only returns the password
	* if the loaded user has just been created. Null is returned for all
	* other users.
	*
	* @return {string|null} The password.
	*/
	get_password() { return this.data.pass; }

	/**
	* Get the groups of a user.
	*
	* @return {string[]} An array of groups.
	*/
	get_groups() { return this.data.groups; }

	/**
	* Get the sessions of a user.
	*
	* @return {object} An object with session data as received from the API.
	*/
	get_sessions() { return this.data.sessions; }

	/**
	* Get the quota of a user.
	*
	* @return {Quota} A quota object.
	*/
	get_quota() { return this.data.quota; }

	/**
	* Check whether a user is in a group. If 'group' is an array,
	* this function checks whether a user is in any of the groups
	* in 'group'.
	*
	* @param {string|string[]} group The group(s) to check.
	*
	* @return True if the user is in the group(s) and false otherwise.
	*/
	is_in_group(group) {
		if (Array.isArray(group)) {
			for (let g in group) {
				if (this.data.groups.includes(g)) { return true; }
			}
			return false;
		} else {
			return this.data.groups.includes(group);
		}
	}

	/**
	* Check whether a user is passwordless.
	*
	* @return {boolean} True if the user is passwordless, false otherwise.
	*/
	is_passwordless() {
		return this.data.passwordless;
	}

	/**
	* Get an array of all existing usernames via the API.
	*
	* @param {APIInterface} api An APIInterface object.
	*
	* @return {string[]} An array of usernames.
	*/
	static async list_all(api) {
		Assert.ok(api != null, 'An API object must be supplied.');
		let resp = await api.call(
			APIEndpoints.USERS_LIST,
			null
		);
		return resp.users;
	}

	/**
	* Get all existing User objects via the API.
	*
	* @param {APIInterface} api An APIInterface object.
	*
	* @return {User[]} An array of user objects.
	*/
	static async get_all(api) {
		Assert.ok(api != null, 'An API object must be supplied.');
		let users = {};
		let names = await User.list_all(api);
		for (let n of names ) {
			users[n] = new User(api);
			await users[n].load(n);
		}
		return users;
	}
}
module.exports = User;
