const UINetRequest = require('../net/request.js');
const IP = require('ip');

/**
 * License manager.
 */
class UILicense {
	/**
	 * Class constructor.
	 */
	constructor(config) {
		// keep a reference of the configuration
		this.config = config;

		// pool used to cache the validated licenses
		this.pool = {};
	}

	/**
	 * Activates the software.
	 * Applies only at the first run.
	 *
	 * @return 	void
	 */
	activate() {
		let now = new Date().getTime();

		// get activation date registered within the configuration
		let activationDate = this.config.get('activationdate', now);
		let creationDate;

		try {
			// get creation date of the configuration file
			let stat = this.config.handler.statSync(this.config.file);

			// register birth date of configuration file
			creationDate = stat.birthtimeMs;
		} catch (err) {
			// missing configuration file, use the current date
			creationDate = now;
		}

		// Register the lowest timestamp as activation date.
		// Consider also the current date and time in order to
		// ignore an activation date in the future, which might
		// have been manually introduced by hacking the creation
		// date of the configuration file and its contents.
		this.activationDate = new Date(Math.min(now, activationDate, creationDate));
	}

	/**
	 * Returns the activation date of the software.
	 *
	 * @return 	Date
	 */
	getActivationDate() {
		if (!this.activationDate) {
			// activate the program first
			this.activate();
		}

		return this.activationDate;
	}

	/**
	 * Checks whether the software owns a PRO license.
	 *
	 * @return 	Promise
	 */
	isPro() {
		return new Promise((resolve, reject) => {
			// retrieve license key stored within the configuration
			let key = this.config.get('licensekey', '');

			// check if we already validated the license key
			if (key && this.pool.hasOwnProperty(key)) {
				// yep, lets retrieve the cached status
				if (this.pool[key]) {
					// valid license key
					resolve();
				} else {
					// invalid license key
					reject();
				}
			} else {
				// nope, lets validate it for the first time
				this.validateLicense(key).then(() => {
					// license key is valid
					resolve();
				}).catch(() => {
					// license key is not valid
					reject();
				});
			}
		});
	}

	/**
	 * Checks whether the TRIAL version is expired.
	 * Will never result as expired after registering
	 * a valid license key.
	 *
	 * @return 	Promise
	 */
	isTrialExpired() {
		return new Promise((resolve, reject) => {
			// look for an active PRO version
			this.isPro().then(() => {
				// yep, trial is not expired
				reject();
			}).catch(() => {
				// make sure there is still a day
				if (this.getRemainingDays() > 0) {
					// still active
					reject();
				} else {
					// expired...
					resolve();
				}
			});
		});
	}

	/**
	 * Returns the remaining days to the TRIAL expiration.
	 *
	 * @return 	integer
	 */
	getRemainingDays() {
		// keep TRIAL valid for 14 days
		let threshold = new Date(this.getActivationDate());
		threshold.setDate(threshold.getDate() + 14);

		// calculate the difference between the expiration date
		// and the current one, then divide by milliseconds,
		// seconds, minutes and hours to obtain the difference
		// of solar days
		return Math.ceil((threshold - new Date()) / 1000 / 60 / 60 / 24);
	}

	/**
	 * Validates the specified license key.
	 *
	 * @param 	string 	key  The license key.
	 *
	 * @return 	Promise
	 */
	validateLicense(key) {
		return new Promise((resolve, reject) => {
			if (!key || typeof key !== 'string') {
				// definitely not valid...
				reject('error.license.emptykey');
				return false;
			}

			// validate license key format
			if (!key.match(/[a-zA-Z0-9]{16,16}$/)) {
				// license key format is not accepted, so
				// we can avoid to validate the license...
				reject('error.license.invalid.base');
				return false;
			}

			// get domain from configuration
			let domain = this.config.get('baseurl', '');

			if (!domain) {
				// no specified domain yet
				reject('error.license.nohost');
				return false;
			}

			// create unique hash
			let hash = Buffer.from(domain + new Date().getUTCMilliseconds()).toString('base64');

			// create net communication handler
			let net = new UINetRequest('https://vikwp.com/api?task=licenses.validate', {
				// do not validate SSL certificate
				rejectUnauthorized: false,
			});

			// set parameters
			net.addParam('key', key);
			net.addParam('domain', domain);
			net.addParam('ip', IP.address());
			net.addParam('hash', hash);
			net.addParam('application', 'vreprint');

			// make connection
			net.post((error, response, body) => {
				// mark license key as invalid by default
				this.pool[key] = 0;

				if (error) {
					// an error occurred
					reject(['error.license.invalid.extended', error]);
					return false;
				}

				if (typeof body === 'string') {
					try {
						body = JSON.parse(body);
					} catch (err) {
						// unable to decode JSON
						reject(err);
						return false;
					}
				}

				if (body.status != 1) {
					// unexpected response...
					reject('error.license.invalid.base');
					return false;	
				}

				// the license key is correct, mark it as valid
				this.pool[key] = 1;
				
				resolve();
			});
		});
	}
}

// export for external usage
module.exports = UILicense;
