'use strict';

Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.Agent = undefined;

var _regenerator = require('babel-runtime/regenerator');

var _regenerator2 = _interopRequireDefault(_regenerator);

var _promise = require('babel-runtime/core-js/promise');

var _promise2 = _interopRequireDefault(_promise);

var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');

var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);

var _stringify = require('babel-runtime/core-js/json/stringify');

var _stringify2 = _interopRequireDefault(_stringify);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

require('source-map-support/register');

require('babel-polyfill');

var _AgentLogger = require('./AgentLogger');

var _AgentLogger2 = _interopRequireDefault(_AgentLogger);

var _express = require('express');

var _express2 = _interopRequireDefault(_express);

var _bodyParser = require('body-parser');

var _bodyParser2 = _interopRequireDefault(_bodyParser);

var _expressNunjucks = require('express-nunjucks');

var _expressNunjucks2 = _interopRequireDefault(_expressNunjucks);

var _sqsConsumer = require('sqs-consumer');

var _sqsConsumer2 = _interopRequireDefault(_sqsConsumer);

var _highlight = require('highlight.js');

var _highlight2 = _interopRequireDefault(_highlight);

var _EventHandler = require('./EventHandler');

var _AgentUtils = require('./AgentUtils');

var _Event = require('./Event');

var _formatJson = require('format-json');

var _formatJson2 = _interopRequireDefault(_formatJson);

var _gtmGithubHook = require('../serverless/gtmGithubHook/gtmGithubHook.js');

var _gtmGithubHook2 = _interopRequireDefault(_gtmGithubHook);

var _package = require('../../../package.json');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var log = _AgentLogger2.default.log();

// Setting up Instances
var app = (0, _express2.default)();
var AGENT_GROUP = process.env.GTM_AGENT_GROUP || 'default';

var pendingUrl = void 0;
var pendingQueueHandler = void 0;
var systemConfig = {};
var runmode = void 0;
systemConfig.gtmversion = _package.version;
systemConfig.agentGroup = AGENT_GROUP;
var isDev = void 0;

var Agent = exports.Agent = function () {
    function Agent() {
        (0, _classCallCheck3.default)(this, Agent);
    }

    (0, _createClass3.default)(Agent, [{
        key: 'start',


        /**
         * Start Agent
         */
        value: function start() {
            process.on('unhandledRejection', function (reason, p) {
                log.error('Unhandled Rejection at: Promise', p, 'reason:', reason);
                // application specific logging, throwing an error, or other logic here
            });

            if (!process.env.IAM_ENABLED) {
                if (!process.env.GTM_AGENT_AWS_ACCESS_KEY_ID || !process.env.GTM_AGENT_AWS_SECRET_ACCESS_KEY) {
                    log.error('### ERROR ### Environment Variables GTM_AGENT_AWS_ACCESS_KEY_ID, or GTM_AGENT_AWS_SECRET_ACCESS_KEY Missing!');
                    process.exit(1);
                }
            }

            if (!process.env.GTM_GITHUB_WEBHOOK_SECRET) {
                log.error('### ERROR ### Environment Variable GTM_GITHUB_WEBHOOK_SECRET missing!');
                process.exit(1);
            }

            this.setup();
            this.configureRoutes();
            this.consumeQueue();
        }

        /**
         * setup env
         */

    }, {
        key: 'setup',
        value: function setup() {
            try {
                runmode = process.env.NODE_ENV;
                if (runmode === undefined) runmode = 'production';
            } catch (error) {
                runmode = 'production';
                log.error(error);
            }
            systemConfig.event = {};

            isDev = runmode === 'development';
        }

        /**
         * setup express route mappings
         */

    }, {
        key: 'configureRoutes',
        value: function configureRoutes() {
            var _this = this;

            // Configure Templates
            app.set('views', __dirname + '/templates');

            // Configure Body Parser
            app.use(_bodyParser2.default.json());

            // Init Nunjucks
            (0, _expressNunjucks2.default)(app, {
                watch: isDev,
                noCache: isDev
            });

            app.get('/', function (req, res) {
                res.render('index.html', { globalProperties: systemConfig });
            });

            app.post('/hook', function (req, res) {
                var callback = function callback(err, details) {
                    if (err) {
                        log.error(details);
                        res.statusMessage = details.body;
                        res.status(details.statusCode).end();
                    } else {
                        res.json(details);
                    }
                };

                var virtualLambdaEvent = {
                    body: (0, _stringify2.default)(req.body),
                    headers: req.headers,
                    httpMethod: req.method
                };

                log.warn('Non-Lambda WebHook Received');

                _gtmGithubHook2.default.listener(virtualLambdaEvent, null, callback);
            });

            app.get('/event_test/', function (req, res) {
                var event = _AgentUtils.AgentUtils.samplePullRequestEvent();
                systemConfig.event.current = event;
                var result = _EventHandler.EventHandler.create('pull_request').handleEvent(event);
                if (result !== true) log.info('Event was not Handled');else log.info('Event Handled');
                res.redirect(302, '/process/');
            });

            app.get('/process/', function (req, res) {
                var updatedEventData = void 0;
                if (systemConfig.event.current) updatedEventData = _highlight2.default.highlight('json', (0, _stringify2.default)(systemConfig.event.current, null, 4)).value;else updatedEventData = null;
                res.render('event.html', {
                    globalProperties: systemConfig,
                    eventData: updatedEventData
                });
            });

            app.get('/stream/start/:group', function (req, res) {
                _this.startLogStream(req.params.group);
                if (req) res.end();
            });

            app.get('/stream/stop', function (req, res) {
                _this.stopAllStreams();
                if (req) res.end();
            });

            app.get('/stream/stop/:group', function (req, res) {
                _this.stopStream(req.params.group);
                if (req) res.end();
            });

            app.get('/stream/filter/:group/:stream', function (req, res) {
                _AgentUtils.AgentUtils.stream(req.params.group, req.params.stream);
                res.json({ group: req.params.group, stream: req.params.stream });
            });

            app.get('/stream/keepalive', function (req, res) {
                var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
                _AgentUtils.AgentUtils.registerActivity(ip);
                if (req) res.end();
            });

            app.get('/routes', function (req, res) {
                if (req) res.json(_this.showRoutes());
            });

            app.use('/static', _express2.default.static(__dirname + '/static'));

            app.get('/config', function (req, res) {
                res.json(systemConfig);
            });

            app.get('/config/pendingqueue', function (req, res) {
                res.json(systemConfig.pendingQueue);
            });

            app.get('/config/pendingqueue/:desiredState', function (req, res) {
                try {
                    var desiredState = req.params.desiredState;
                    if (!desiredState === 'enable' || !desiredState === 'disable') {
                        log.debug('Unknown State: ' + desiredState + '. Ignoring Request.');
                    } else {
                        if (desiredState === 'disable') {
                            pendingQueueHandler.stop();
                            log.info('Queue Processing Stopped');
                        } else {
                            pendingQueueHandler.start();
                            log.info('Queue Processing Started');
                        }
                    }
                } catch (error) {
                    log.error('Error Setting Queue State from Request');
                }
                systemConfig.pendingQueue.enabled = !pendingQueueHandler.stopped;
                systemConfig.pendingQueue.state = pendingQueueHandler.stopped ? 'Stopped' : 'Running';
                res.json({ state: systemConfig.pendingQueue.state });
            });
        }

        /**
         * connect to SQS pending queue and consume messages
         */

    }, {
        key: 'consumeQueue',
        value: function consumeQueue() {
            log.info('Connecting to inbound SQS queue..');

            var that = this;

            _AgentUtils.AgentUtils.getQueueUrl(process.env.GTM_SQS_PENDING_QUEUE).then(function (data) {
                var _this2 = this;

                pendingUrl = data;
                systemConfig.pendingQueue = {};
                systemConfig.pendingQueue.url = pendingUrl;

                pendingQueueHandler = _sqsConsumer2.default.create({
                    queueUrl: pendingUrl,
                    region: process.env.GTM_AWS_REGION,
                    messageAttributeNames: _Event.Event.requiredAttributes,

                    handleMessage: function () {
                        var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(message, done) {
                            var startTime, event, loopTimer;
                            return _regenerator2.default.wrap(function _callee$(_context) {
                                while (1) {
                                    switch (_context.prev = _context.next) {
                                        case 0:
                                            startTime = new Date().getTime();

                                            log.info('## == NEW EVENT ==================================');
                                            log.info('Received Message from Pending Queue');
                                            log.debug('message: ' + _formatJson2.default.plain(message));

                                            event = void 0;
                                            _context.prev = 5;

                                            event = new _Event.Event(message);
                                            _context.next = 14;
                                            break;

                                        case 9:
                                            _context.prev = 9;
                                            _context.t0 = _context['catch'](5);

                                            log.error(_context.t0);
                                            done(); //todo dead letter queue here rather than discard
                                            return _context.abrupt('return');

                                        case 14:
                                            if (!(event.attrs.ghAgentGroup !== AGENT_GROUP)) {
                                                _context.next = 19;
                                                break;
                                            }

                                            event.log.info('agentGroup mismatch - event: \'' + event.attrs.ghAgentGroup + '\' agent: \'' + AGENT_GROUP + '\' skipping..');
                                            _AgentUtils.AgentUtils.setSqsMessageTimeout(process.env.GTM_SQS_PENDING_QUEUE, message.ReceiptHandle, 5, log);
                                            done(new Error()); // Re-Queue Messages that don't match our Agent Group
                                            return _context.abrupt('return');

                                        case 19:
                                            if (_EventHandler.EventHandler.isRegistered(event.attrs.ghEventType)) {
                                                _context.next = 24;
                                                break;
                                            }

                                            log.error('No Event Handler for Type: \'' + event.attrs.ghEventType + '\' (Event ID: ' + event.attrs.ghEventId + ')');
                                            done();
                                            _context.next = 35;
                                            break;

                                        case 24:
                                            loopTimer = setInterval(function () {
                                                _AgentUtils.AgentUtils.setSqsMessageTimeout(process.env.GTM_SQS_PENDING_QUEUE, message.ReceiptHandle, 30, log);
                                            }, 5000);

                                            // handle the event and execute tasks

                                            _context.prev = 25;
                                            _context.next = 28;
                                            return _EventHandler.EventHandler.create(event.attrs.ghEventType, event.payload, event.log).handleEvent().then(function () {
                                                done();
                                                clearInterval(loopTimer);
                                                var endTime = new Date().getTime();
                                                var duration = endTime - startTime;
                                                return _promise2.default.resolve(event.log.info('### Event handled: type=' + event.attrs.ghEventType + ' id=' + event.attrs.ghEventId + ' duration=' + duration + 'ms'));
                                            });

                                        case 28:
                                            _context.next = 35;
                                            break;

                                        case 30:
                                            _context.prev = 30;
                                            _context.t1 = _context['catch'](25);

                                            event.log.error(_context.t1);
                                            clearInterval(loopTimer);
                                            done(_context.t1);

                                        case 35:
                                        case 'end':
                                            return _context.stop();
                                    }
                                }
                            }, _callee, _this2, [[5, 9], [25, 30]]);
                        }));

                        return function handleMessage(_x, _x2) {
                            return _ref.apply(this, arguments);
                        };
                    }()
                });

                that.listen(pendingQueueHandler);
            });
        }

        /**
         * start express listener, and handle queue errors
         * @param pendingQueueHandler
         */

    }, {
        key: 'listen',
        value: function listen(pendingQueueHandler) {
            pendingQueueHandler.on('error', function (err) {
                log.error('ERROR In SQS Queue Handler');
                log.error(err.message);
            });

            app.listen(process.env.GTM_AGENT_PORT, function () {
                _AgentUtils.AgentUtils.printBanner();
                log.info('GTM Version ' + _package.version);
                log.info('AGENT_ID: ' + _AgentUtils.AgentUtils.agentId());
                log.info('AGENT_GROUP: ' + AGENT_GROUP);
                log.info('GitHub Task Manager Agent Running on Port ' + process.env.GTM_AGENT_PORT);
                log.info('Runmode: ' + runmode);
                if (!process.env.IAM_ENABLED) {
                    log.info('AWS Access Key ID: ' + _AgentUtils.AgentUtils.maskString(process.env.GTM_AGENT_AWS_ACCESS_KEY_ID));
                    log.info('AWS Access Key: ' + _AgentUtils.AgentUtils.maskString(process.env.GTM_AGENT_AWS_SECRET_ACCESS_KEY));
                }
                log.info('Pending Queue URL: ' + pendingUrl);

                pendingQueueHandler.start();
                log.info('Queue Processing Started');
                systemConfig.pendingQueue.state = pendingQueueHandler.stopped ? 'Stopped' : 'Running';
                systemConfig.pendingQueue.enabled = !pendingQueueHandler.stopped;
                systemConfig.agentId = _AgentUtils.AgentUtils.agentId();
            });
        }

        /**
         * start cloudwatch log streaming via express SSE for given group
         * @param group
         */

    }, {
        key: 'startLogStream',
        value: function startLogStream(group) {
            // start cloudwatch streams
            _AgentUtils.AgentUtils.stream(group);

            // Server Sent Events stream hooked to cloudwatch
            app.get('/stream/' + group, _AgentUtils.AgentUtils.sse()[group].init);
        }

        /**
         * stop log stream for group
         * @param group
         */

    }, {
        key: 'stopStream',
        value: function stopStream(group) {
            _AgentUtils.AgentUtils.stopStream(group);
        }

        /**
         * stop all log streams
         */

    }, {
        key: 'stopAllStreams',
        value: function stopAllStreams() {
            _AgentUtils.AgentUtils.stopAllStreams();
        }

        /**
         * show all express routes configured
         */

    }, {
        key: 'showRoutes',
        value: function showRoutes() {
            return app._router.stack;
        }
    }], [{
        key: 'systemConfig',
        get: function get() {
            return systemConfig;
        }
    }]);
    return Agent;
}();