"use strict";

//Check out API documentation for attribute names and other info when using SQS API
//http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Operations.html

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

module.exports = {

	init() {
		this.sqs = blackout.sqs;
	},

	//Create a new Queue
	// name: name of the new queue, must respect AWS rules: less than 80 characters long and contain
	//       only alphanumeric, underscore and hifen characters.
	// attributes: the queue attributes, check the API reference above for details
	createQueue(name, attributes = null, callback = null) {

		// Attributes can be omitted
		if (typeof attributes === 'function') {
			callback = attributes;
			attributes = null;
		}

		var params = {
			QueueName: name
		};

		if (attributes) {
			// Attribute values must be strings
			Object.keys(attributes).map(key => attributes[key] = String(attributes[key]));
			// Any attributes not provided will use AWS defaults
			params.Attributes = attributes;
		}

		this.sqs.createQueue(params, callback);
	},

	//Deletes a Queue
	// queueName: the name of the queue to be deleted, case-sensitive
	//
	// A queue can take up to 60 seconds to be deleted, you must wait that time to
	// create another queue with the same name
	deleteQueue(queueName, callback) {
		var _this = this;

		return _asyncToGenerator(function* () {

			try {

				// First, we need the queue URL
				let queueUrl = yield _this.getQueueUrlFromNameAsync(queueName);

				// Then delete the queue
				_this.sqs.deleteQueue({ QueueUrl: queueUrl }, callback);
			} catch (err) {
				callback(err);
			}
		})();
	},

	/**
  * Send a message to the queue
  * 
  * @param  {string}   queueName						Required. The name of the queue.
  * @param  {string} 	messageBody 				Required. The body of the message.
  * @param  {string} 	messageAttributes 	Optional. Attributes to accompany the message.
  * @param  {int} 			delaySeconds 				Optional. The number of seconds before the message will be receivable. Default 0.
  */
	sendMessage(options, callback) {
		var _this2 = this;

		return _asyncToGenerator(function* () {

			try {

				// First, we need the queue URL
				let queueUrl = yield _this2.getQueueUrlFromNameAsync(options.queueName);

				var params = {
					QueueUrl: queueUrl,
					MessageBody: options.messageBody ? options.messageBody : options.body
				};

				// Custom attribute names
				if ('messageAttributes' in options) {
					params.MessageAttributes = _this2.packSQS(options.messageAttributes);
				}

				// Delay
				if ('delaySeconds' in options) {
					params.DelaySeconds = parseInt(options.delaySeconds);
				}
				console.log('params', params);
				_this2.sqs.sendMessage(params, callback);
			} catch (err) {
				callback(err);
			}
		})();
	},

	/**
  * Receive one or more messages from a queue
  * 
  * @param  {string}		queueName								Required. The name of the queue.
  * @param  {int} 			maxMessages 						Optional. Max number of messages to get. Default = 1.
  * @param  {int} 			waitTimeSeconds 				Optional. Wait up to X seconds for messages to be delivered. Default = 0.
  * @param  {int} 			visibilityTimeout 			Optional. Hide message from other clients for a number of seconds. Defaults to queue settings.
  * @param  {array} 		attributeNames 					Optional. A list of native attributes to receive in the message. Default None.
  * @param  {array} 		messageAttributeNames 	Optional. A list of user attributes to receive in the message. Default None.
  */
	receiveMessages(options, callback) {
		var _this3 = this;

		return _asyncToGenerator(function* () {

			try {

				// First, we need the queue URL
				let queueUrl = yield _this3.getQueueUrlFromNameAsync(options.queueName);

				var params = {
					QueueUrl: queueUrl
				};

				// Max messages
				if ('maxMessages' in options) {
					params.MaxNumberOfMessages = parseInt(options.maxMessages);
				}

				// Wait time seconds
				if ('waitTimeSeconds' in options) {
					params.WaitTimeSeconds = parseInt(options.waitTimeSeconds);
				}

				// Custom visibility timeout
				if ('visibilityTimeout' in options) {
					params.VisibilityTimeout = parseInt(options.visibilityTimeout);
				}

				// Native attribute names
				if ('attributeNames' in options) {
					params.AttributeNames = options.attributeNames;
				}

				// Custom attribute names
				if ('messageAttributeNames' in options) {
					params.MessageAttributeNames = options.messageAttributeNames;
				}

				_this3.sqs.receiveMessage(params, function (err, data) {
					if (err) {
						callback(err);
					} else {
						if (data.Messages && data.Messages.length) {
							callback(null, _this3.unpackSQS(data.Messages));
						} else {
							callback(null, []);
						}
					}
				});
			} catch (err) {
				callback(err);
			}
		})();
	},

	/**
  * Deletes a message
  * 
  * @param  {string}   queueName						Required. The name of the queue.
  * @param  {string} 	receiptHandle 			Required. The receipt handle provided in receiveMessages
  */
	deleteMessage(options, callback) {
		var _this4 = this;

		return _asyncToGenerator(function* () {

			try {

				// First, we need the queue URL
				let queueUrl = yield _this4.getQueueUrlFromNameAsync(options.queueName);

				var params = {
					QueueUrl: queueUrl,
					ReceiptHandle: options.receiptHandle
				};

				_this4.sqs.deleteMessage(params, callback);
			} catch (err) {
				callback(err);
			}
		})();
	},

	/**
  * Change a message visibility timeout
  * 
  * @param  {string}   queueName						Required. The name of the queue.
  * @param  {string} 	receiptHandle 			Required. The receipt handle provided in receiveMessages
  * @param  {string} 	visibilityTimeout 	Required. The new visibilityTimeout
  */
	changeMessageVisibilityTimeout(options, callback) {
		var _this5 = this;

		return _asyncToGenerator(function* () {

			try {

				// First, we need the queue URL
				let queueUrl = yield _this5.getQueueUrlFromNameAsync(options.queueName);

				var params = {
					QueueUrl: queueUrl,
					ReceiptHandle: options.receiptHandle,
					VisibilityTimeout: options.visibilityTimeout
				};

				_this5.sqs.changeMessageVisibility(params, callback);
			} catch (err) {
				callback(err);
			}
		})();
	},

	// ----------------------------------------------------- Helper Functions

	// Local URL cache
	queueUrls: {},

	getQueueUrlFromName(queueName, callback) {
		var _this6 = this;

		return _asyncToGenerator(function* () {

			// Check local cache
			if (_this6.queueUrls[queueName]) {
				callback(null, _this6.queueUrls[queueName]);
			} else {

				try {

					// Query AWS
					let data = yield _this6.sqs.getQueueUrlAsync({ QueueName: queueName });

					// Save to local cache
					_this6.queueUrls[queueName] = data.QueueUrl;
					// Callback
					callback(null, data.QueueUrl);
				} catch (err) {
					callback(err);
				}
			}
		})();
	},

	/**
  * Converts our basic objects into SQS attribute structures
  * @param  {object} attrs     The list of attributes to send to SQS returned from dynamodb
  * @param  {bool}          raw      Internal flag for recursive calls
  * @return {dynamodb-item}
  */
	packSQS(attrs) {

		if (attrs) {

			let sqsItem = {};
			let err;

			for (let key in attrs) {

				let val = attrs[key];

				if (blackout.isNumeric(val)) {
					sqsItem[key] = { DataType: 'Number', StringValue: val.toString() };
				} else if (Array.isArray(val) || typeof val === 'object') {
					sqsItem[key] = { DataType: 'String', StringValue: JSON.stringify(val) };
				} else if (val === null || val === "") {
					// Don't add key
				} else if (typeof val === 'boolean') {
					console.log('Warning: Cannot send booleans as SQS attribute values.');
				} else {
					sqsItem[key] = { DataType: 'String', StringValue: val.toString() };
				}
			}

			if (err) {
				return err;
			} else {
				return sqsItem;
			}
		} else {
			return attrs;
		}
	},

	/**
  * Parses the SQS messageAttributes or a message or array of messages
  * @param  {SQS message}		message 		The message or set of messages returned from SQS
  * @param  {bool}						raw 				Internal flag for recursive calls
  */
	unpackSQS(message, raw = false) {

		if (blackout.isEmpty(message)) {
			return {};
		}

		if (!raw && blackout.isArray(message)) {

			for (let [i, m] of message.entries()) {
				message[i] = this.unpackSQS(m, true);
			}

			return message;
		} else {

			if (message.MessageAttributes) {

				let attrs = message.MessageAttributes;
				let parsedAttrs = {};
				let keys = Object.keys(attrs);

				for (let key of keys) {

					if (attrs[key].DataType === 'String') {
						if (blackout.isJson(attrs[key].StringValue)) {
							parsedAttrs[key] = JSON.parse(attrs[key].StringValue);
						} else {
							parsedAttrs[key] = attrs[key].StringValue;
						}
					} else if (attrs[key].DataType === 'Number') {
						parsedAttrs[key] = Number(attrs[key].StringValue);
					}
				}

				message.MessageAttributes = parsedAttrs;
			}

			return message;
		}
	}

};
//# sourceMappingURL=sqs.js.map