"use strict";

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 = {

	initCompleter() {
		this.tasksToWaitFor = [];
		this.hasCompleted = false;
		delete this.callMeWhenDone;
	},

	/**
  * Call this once we're all done and we want to send our response
  * 
  * response should only be used in engines, not in API mode
  */
	complete(internalErr, response) {
		var _this = this;

		return _asyncToGenerator(function* () {

			if (!_this.hasCompleted) {

				_this.hasCompleted = true;

				try {

					yield _this.waitForTasksAsync();

					if (internalErr) {

						if (typeof internalErr === 'string') {
							internalErr = new Error(internalErr);
						}

						// Protect against accidentally sending a non error to this function
						if (!internalErr.message || typeof internalErr.message !== 'string') {
							_this.complete(new Error('A non error item has been passed to blackout.complete as the first argument'));
							return;
						}

						let isRedisError = internalErr.message.indexOf('Could not connect to redis') === 0;

						// Send obfuscated response
						let body = {
							message: "Internal Error in Blackout API"
						};

						let slackSuccess = false;

						if (!isRedisError && !blackoutLocalMode) {

							const redis = _this.redis;
							let secondsBetweenDeliveries = 24 * 60 * 60; // 24 hours

							// Only send errors once every 24 hours
							// Use message as identifier, which allows us to force new errors by simply changing the message if we need to
							let errorId = _this.md5(internalErr.message);
							let errorCacheKey = 'Error-' + errorId;
							body.id = errorId;

							// Check redis for errorId
							let errorExists = yield redis.getAsync(errorCacheKey);

							// Continue if error does not exist
							if (!errorExists) {

								// Generate random string to identify error
								let token = _this.randtoken.generate(7);
								body.token = token;

								// Pretty print event
								let prettyEvent = JSON.stringify(_this.event, null, 4);

								// Text
								let el = "  \n";
								let errorText = `${internalErr.message}\n${el}Error ID ${errorId}\n${el}STACK\n---------\n${internalErr.stack}\n${el}EVENT\n---------\n${prettyEvent}`;

								// Send internal errors to Slack
								let slackBody = {
									attachments: [{
										pretext: 'Internal error from API',
										title: `Error Token ${token}`,
										text: errorText,
										fallback: errorText,
										color: 'danger' }]
								};

								let opts = {
									url: 'https://hooks.slack.com/services/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',
									json: slackBody
								};

								let res = yield _this.http.postAsync(opts);

								if (res.statusCode === 200 && res.body === 'ok') {

									// Expire this key after error delay
									yield redis.setexAsync(errorCacheKey, secondsBetweenDeliveries, errorText);
									slackSuccess = true;
								} else {
									// Could not contact slack to send error there, internet maybe down?
									console.log('COULD NOT SEND ERROR TO SLACK. ERROR DETAILS FOLLOW:');
								}
							} else {
								// Error has already sent to Slack recently, so continue as successful
								slackSuccess = true;
							}
						} else {}

						// We can't connect to redis, so we can't use it to manage how many instances of a certain error have been sent
						// We should not send this error to slack to avoid massive amounts of messages hitting us.
						// If redis is down, we should find out through some other uptime monitoring service.

						// Close redis
						yield _this.completeCleanupAsync();

						// Always log the error, just in case and for when working locally
						console.log(internalErr);

						if (!slackSuccess && !blackoutLocalMode) {
							// Silent if we were unable to get a message to Slack
							body.silent = true;
						}

						let lambdaBody = _this.isAPI ? {
							statusCode: 500,
							headers: {},
							body: JSON.stringify(body)
						} : body;

						// We don't rely on response library in case we didn't get far enough for it to be setup
						_this.callback(null, lambdaBody);

						// If there were previous exception handlers (added by AWS), run them now
						// NOPE. If we run this, lambda will log the error and exit the process.
						/*if(internalErr.originalExHandlers){
      	setTimeout(()=>{
      		
      		for( let handler of internalErr.originalExHandlers ){
      			handler(internalErr);
      		}
      	},1);
      }*/
					} else {

						if (_this.isAPI && _this.session) {

							// Save session
							yield _this.session.saveAsync();
						}

						yield _this.completeCleanupAsync();

						// Respond
						response = _this.isAPI && _this.response ? _this.response.getResponse() : response ? response : null;
						_this.callback(internalErr, response); // error, response
					}
				} catch (err) {
					// Prevent infinite looping
					if (err !== internalErr) {
						_this.complete(err);
					} else {
						console.log('Error occured internally in primary error handler');
						throw err;
					}
				}
			}
		})();
	},

	completeCleanup(callback) {
		var _this2 = this;

		return _asyncToGenerator(function* () {

			// Cleanup any remaining client locks
			yield _this2.cleanupLocksAsync();

			// Disconnect redis
			if (_this2.redis) {
				_this2.redis.quit();
				delete _this2.redis;
			}
			callback();
		})();
	},

	autoComplete() {

		// Process seems to stick between lambda runs so we can use it to track this
		if (process.hasBlackoutAutoComplete) {
			return;
		}

		let myExit = () => {
			// blackoutcomplete <- Don't change this comment string!
			this.complete();
		};

		this.addListenerFirst(process, 'beforeExit', myExit);

		process.hasBlackoutAutoComplete = true;
	},

	// ----------------------------- Wait for things

	/**
  * This mini-system allows other parts of the API to make asynconous updates without making anything
  * else wait - apart from the completion of the request of course – which is what this is for, so that
  * we can delay completion until these other tasks complete.
  */

	waitForMe(taskKey) {

		this.tasksToWaitFor.push(taskKey);
	},

	okImDone(taskKey) {

		for (let [i, waitingTaskKey] of this.tasksToWaitFor.entries()) {
			if (taskKey === waitingTaskKey) {
				this.tasksToWaitFor.splice(i, 1);
				break;
			}
		}

		if (!this.tasksToWaitFor.length && this.callMeWhenDone) {
			this.callMeWhenDone();
		}
	},

	waitForTasks(callback) {

		if (this.tasksToWaitFor.length) {

			// Wait
			this.callMeWhenDone = callback;
		} else {
			callback();
		}
	}

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