/**
 * Class used to handle abstract connections.
 */
class UINetRequest {
	/**
	 * Class constructor.
	 *
	 * @param 	string  url  The end-point URL.
	 */
	constructor(url, options) {
		this.setUrl(url);

		this.params = {};

		this.request = require('request').defaults(typeof options === 'object' ? options : {});
	}

	/**
	 * Sets the complete end-point URL.
	 *
	 * @param 	string  url  The end-point URL.
	 *
	 * @return 	self  This object to support chaining.
	 */
	setUrl(url) {
		this.url = url;

		return this;
	}

	/**
	 * Returns the complete end-point URL.
	 *
	 * @return 	string
	 */
	getUrl() {
		return this.url;
	}

	/**
	 * Sets a new parameters within the body.
	 *
	 * @param 	string  key  The query key.
	 * @param 	mixed   val  The query value.
	 *
	 * @return 	self    This object to support chaining.
	 */
	addParam(key, val) {
		this.params[key] = val;

		return this;
	}

	/**
	 * Makes a POST request to the end-point.
	 *
	 * @param  function  onComplete  An optional callback to invoke
	 * 								 after completing the request.
	 *
	 * @return 	self     This object to support chaining.
	 */
	post(onComplete) {
		// get end-point URL
		let uri = this.getUrl();

		let payload = [];

		// serialize the parameters
		for (let k in this.params) {
			if (this.params.hasOwnProperty(k)) {
				payload.push(encodeURIComponent(k) + '=' + encodeURIComponent(this.params[k]));
			}
		}

		payload = payload.join('&');

		// init the request
		const request = this.request({
			method: 'POST',
			url: uri,
			headers: {
				// pass the payload within POST
				'Content-Type': 'application/x-www-form-urlencoded',
				'Content-Length': payload.length,
			},
		});

		// catch connection response
		request.on('response', (response) => {
			let body  = '';
			let error = null;

			// catch response body
			response.on('data', (data) => {
				// concat because the event might be called
				// multiple times for large responses
				body += data;
			});

			// fetch error message
			if (response.statusCode >= 400) {
				error = [response.statusCode, response.statusMessage].join(' ');
			}

			// catch end of the response
			response.on('end', () => {
				if (!error) {
					try {
						// try to decode the JSON
						body = JSON.parse(body);
					} catch (e) {
						// catch silently
					}
				}

				if (typeof onComplete !== 'undefined') {
					// resolve callback
					onComplete(error, response, body);
				}
			});
		});

		// catch system error (e.g. for missing connection)
		request.on('error', (error) => {
			if (typeof onComplete !== 'undefined') {
				// reject callback
				onComplete(error);
			}
		});
		
		// post payload and terminate connection
		request.write(payload);
		request.end();

		return this;
	}
}

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