var User = require('libresignage/user/User');
var APIEndpoints = require('libresignage/api/APIEndpoints');
var APIError = require('libresignage/api/APIError');
var HTTPStatus = require('libresignage/api/HTTPStatus');
var Assert = require('assert');

/**
* An object for handling API sessions.
*
* @memberof module:libresignage/api
*/
class APISession {
	/**
	* Create a new APISession object.
	*
	* @constructor
	*
	* @param {APIInterface} api An initialized APIInterface object.
	*/
	constructor(api) {
		this.MIN_MAX_AGE = 30;
		this.MAX_AGE_HEADROOM = 10;

		this.api = api;
		this.user = null;
		this.data = null;
		this.token = null;
		this.renew_interval_id = null;
	}

	/**
	* Log out from the API.
	*/
	async logout() {
		Assert.ok(this.user !== null);
		Assert.ok(this.data !== null);

		await this.api.call(APIEndpoints.AUTH_LOGOUT, null);
		this.clear();
	}

	/**
	* Log in to the API.
	*
	* @param {string}  user      The username to use.
	* @param {string}  pass      The password to use.
	* @param {boolean} permanent Whether to create a permanent session.
	*/
	async login(user, pass, permanent) {
		let resp = await this.api.call(
			APIEndpoints.AUTH_LOGIN,
			{
				username: user,
				password: pass,
				permanent: permanent,
				who: 'LibreSignage-Web-Interface'
			}
		);
		this.data = resp.session;
		this.token = resp.token;
		this.user = new User(this.api);

		await this.user.load(null);
		await this.start_renewal();
	}

	/**
	* Clear all session related data.
	*
	* @private
	*/
	clear() {
		this.user = null;
		this.data = null;
	}

	/**
	* Attempt to reload an APISession from the server using a token.
	*
	* @param {string} token A token to use for authentication.
	*
	* @return {boolean} True on success and false otherwise.
	*/
	async reload(token) {
		let resp = null;

		this.token = token;
		try {
			resp = await this.api.call(APIEndpoints.AUTH_GET_DATA, null);
		} catch (e) {
			if (e instanceof APIError && e.is(HTTPStatus.UNAUTHORIZED)) {
				return false;
			} else {
				throw e;
			}
		}

		this.user = new User(this.api);
		await this.user.load(null);

		this.data = resp.session;
		await this.start_renewal();

		return true;
	}

	/**
	* Initialize a new session renewal loop. This function
	* automatically stops any previous loops to make sure
	* multiple simultaneous loops are never running.
	*
	* @private
	*/
	async start_renewal() {
		let t = this.data.max_age
				- this.MIN_MAX_AGE
				+ this.MAX_AGE_HEADROOM;

		if (this.data.permanent === true) { return; }
		if (this.data.max_age < this.MIN_MAX_AGE) {
			throw new Error(
				`Maximum session age too low! ` +
				`(MAX_AGE < ${this.MIN_MAX_AGE}s)`
			);
		}
		console.log(`APIInterface: Init session renewal loop. (${t}s)`);

		this.stop_renewal();
		await this.renew();

		this.renew_interval_id = setInterval(
			async () => await this.renew(),
			t*1000
		);
	}

	/**
	* Stop the currently running session renewal loop.
	*
	* @private
	*/
	stop_renewal() {
		if (this.renew_interval_id != null) {
			console.log('APISession: Stop session renewal loop.');
			clearInterval(this.renew_interval_id);
			this.renew_interval_id = null;
		}
	}

	/**
	* Renew an APISession.
	*
	* @private
	*/
	async renew() {
		let resp = null;
		console.log('APIInterface: Renew session.');
		try {
			resp = await this.api.call(APIEndpoints.AUTH_SESSION_RENEW, null);
		} catch (e) {
			this.clear();
			throw e;
		}
		this.data = resp.session;
	}

	/**
	* Get the User object of an APISession.
	*
	* @return {User|null} A User object.
	*/
	get_user() { return this.user; }

	/**
	* Get the authentication token of an APISession.
	*
	* @return {string|null} An authentication token.
	*/
	get_token() { return this.token; }

	/**
	* Return the ID of an APISession.
	*
	* @return {string|null} An ID string.
	*/
	get_id() { return this.data.id; }

	/**
	* Get the 'who' description value of an APISession.
	*
	* @return {string|null} A description string.
	*/
	get_who() { return this.data.who; }

	/**
	* Get the caller IP address of an APISession.
	* You shouldn't rely on this being correct.
	*
	* @return {string|null} An IP address.
	*/
	get_from() { return this.data.from; }

	/**
	* Get the unix timestamp when an APISession was created.
	*
	* @return {number|null} A unix timestamp in seconds.
	*/
	get_created() { return this.data.created; }

	/**
	* Get the maximum age of an APISession.
	*
	* @return {number|null} An age in seconds.
	*/
	get_max_age() { return this.data.max_age; }

	/**
	* Get a boolean value representing whether an APISession is permanent.
	*
	* @return {boolean} True if APISession is permanent, false otherwise.
	*/
	get_permanent() { return this.data.permanent; }
}
module.exports = APISession;
