'use strict';

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

var _promise2 = _interopRequireDefault(_promise);

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

var _regenerator2 = _interopRequireDefault(_regenerator);

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

var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);

var connect = function () {
    var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(context) {
        var githubOptions, github, token, meta;
        return _regenerator2.default.wrap(function _callee$(_context) {
            while (1) {
                switch (_context.prev = _context.next) {
                    case 0:
                        console.log('Connecting to GitHub');
                        console.log('GitHub SSL Validation: ' + String(ghEnforceValidSsl));
                        githubOptions = {
                            baseUrl: 'https://' + (process.env.GTM_GITHUB_HOST || 'api.github.com') + (process.env.GTM_GITHUB_PATH_PREFIX || ''),
                            timeout: parseInt(process.env.GTM_GITHUB_TIMEOUT) || 5000
                        };

                        if (process.env.GTM_GITHUB_PROXY) {
                            githubOptions.agent = new https.Agent({
                                proxy: process.env.GTM_GITHUB_PROXY,
                                rejectUnauthorized: false
                            });
                        }

                        console.log('Creating GitHub API Connection');
                        github = new GitHubApi(githubOptions);
                        _context.next = 8;
                        return _KmsUtils2.default.getDecrypted(process.env.GTM_CRYPT_GITHUB_TOKEN);

                    case 8:
                        token = _context.sent;

                        if (!context) {
                            _context.next = 18;
                            break;
                        }

                        _context.next = 12;
                        return _KmsUtils2.default.getDecrypted(process.env['GTM_CRYPT_GITHUB_TOKEN_' + context.toUpperCase().replace('-', '_')]);

                    case 12:
                        _context.t0 = _context.sent;

                        if (_context.t0) {
                            _context.next = 17;
                            break;
                        }

                        _context.next = 16;
                        return _KmsUtils2.default.getDecrypted(process.env.GTM_CRYPT_GITHUB_TOKEN);

                    case 16:
                        _context.t0 = _context.sent;

                    case 17:
                        token = _context.t0;

                    case 18:

                        console.log('Authenticating with GitHub');
                        github.authenticate({
                            type: 'oauth',
                            token: token
                        });

                        // test connection
                        _context.prev = 20;
                        _context.next = 23;
                        return github.misc.getMeta({});

                    case 23:
                        meta = _context.sent;

                        console.log('Connected to GitHub at ' + githubOptions.baseUrl + '. metadata: ' + json.plain(meta));
                        _context.next = 31;
                        break;

                    case 27:
                        _context.prev = 27;
                        _context.t1 = _context['catch'](20);

                        console.log(_context.t1);
                        throw _context.t1;

                    case 31:
                        return _context.abrupt('return', github);

                    case 32:
                    case 'end':
                        return _context.stop();
                }
            }
        }, _callee, this, [[20, 27]]);
    }));

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

var invalidHook = function () {
    var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(event) {
        var err, errMsg, token, headers, sig, githubEvent, id, calculatedSig, validators;
        return _regenerator2.default.wrap(function _callee2$(_context2) {
            while (1) {
                switch (_context2.prev = _context2.next) {
                    case 0:
                        err = null;
                        errMsg = null;
                        _context2.next = 4;
                        return _KmsUtils2.default.getDecrypted(process.env.GTM_CRYPT_GITHUB_WEBHOOK_SECRET);

                    case 4:
                        token = _context2.sent;
                        headers = event.headers;
                        sig = headers['X-Hub-Signature'] || headers['x-hub-signature'];
                        githubEvent = headers['X-GitHub-Event'] || headers['x-github-event'];
                        id = headers['X-GitHub-Delivery'] || headers['x-github-delivery'];
                        calculatedSig = signRequestBody(token, event.body);
                        validators = [{
                            name: 'github secret',
                            check: typeof token !== 'string',
                            msg: 'Must provide a \'GTM_CRYPT_GITHUB_WEBHOOK_SECRET\' env variable'
                        }, {
                            name: 'X-Hub-Signature',
                            check: !sig,
                            msg: 'No X-Hub-Signature found on request'
                        }, {
                            name: 'X-Github-Event',
                            check: !githubEvent,
                            msg: 'No X-Github-Event found on request'
                        }, {
                            name: 'X-Github-Delivery',
                            check: !id,
                            msg: 'No X-Github-Delivery found on request'
                        }, {
                            name: 'X-Hub-Signature signing',
                            check: sig !== calculatedSig,
                            msg: 'X-Hub-Signature incorrect. Github webhook token doesn\'t match'
                        }];


                        try {
                            validators.forEach(function (v) {
                                if (v.check) {
                                    errMsg = v.msg;
                                    throw new Error(errMsg);
                                }
                                console.log(v.name + ' is ok!');
                            });
                        } catch (e) {
                            console.log(e.message);
                            err = e;
                        }

                        return _context2.abrupt('return', err);

                    case 13:
                    case 'end':
                        return _context2.stop();
                }
            }
        }, _callee2, this);
    }));

    return function invalidHook(_x2) {
        return _ref2.apply(this, arguments);
    };
}();

var getFile = function () {
    var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(params) {
        var github;
        return _regenerator2.default.wrap(function _callee3$(_context3) {
            while (1) {
                switch (_context3.prev = _context3.next) {
                    case 0:
                        _context3.next = 2;
                        return connect();

                    case 2:
                        github = _context3.sent;
                        _context3.next = 5;
                        return github.repos.getContent(params);

                    case 5:
                        return _context3.abrupt('return', _context3.sent);

                    case 6:
                    case 'end':
                        return _context3.stop();
                }
            }
        }, _callee3, this);
    }));

    return function getFile(_x3) {
        return _ref3.apply(this, arguments);
    };
}();

var updateGitHubPullRequest = function () {
    var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(status, done) {
        return _regenerator2.default.wrap(function _callee4$(_context4) {
            while (1) {
                switch (_context4.prev = _context4.next) {
                    case 0:
                        if (!(status.context === 'COMMENT_ONLY')) {
                            _context4.next = 6;
                            break;
                        }

                        _context4.next = 3;
                        return addGitHubPullRequestComment(status, done);

                    case 3:
                        return _context4.abrupt('return', _context4.sent);

                    case 6:
                        _context4.next = 8;
                        return updateGitHubPullRequestStatus(status, done);

                    case 8:
                        return _context4.abrupt('return', _context4.sent);

                    case 9:
                    case 'end':
                        return _context4.stop();
                }
            }
        }, _callee4, this);
    }));

    return function updateGitHubPullRequest(_x4, _x5) {
        return _ref4.apply(this, arguments);
    };
}();

var updateGitHubPush = function () {
    var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5(status, done) {
        return _regenerator2.default.wrap(function _callee5$(_context5) {
            while (1) {
                switch (_context5.prev = _context5.next) {
                    case 0:
                        _context5.next = 2;
                        return addGitHubPushComments(status, done);

                    case 2:
                        return _context5.abrupt('return', _context5.sent);

                    case 3:
                    case 'end':
                        return _context5.stop();
                }
            }
        }, _callee5, this);
    }));

    return function updateGitHubPush(_x6, _x7) {
        return _ref5.apply(this, arguments);
    };
}();

/**
 * Create a Status Object to Send to GitHub
 * @param {object} eventData - Data from GitHub Event
 * @param {string} state - Current Task State (pending, passed, failed)
 * @param {string} context - Content Name to Display in GitHub
 * @param {string} description - Short Description to Display in GitHub
 * @param {string} url - Link to more detail
 *
 */


/**
 * export type ReposCreateStatusParams =
 & {
      owner: string;
      repo: string;
      sha: string;
      state: "pending"|"success"|"error"|"failure";
      target_url?: string;
      description?: string;
      context?: string;
    };
 */
var updateGitHubPullRequestStatus = function () {
    var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6(status, done) {
        var github;
        return _regenerator2.default.wrap(function _callee6$(_context6) {
            while (1) {
                switch (_context6.prev = _context6.next) {
                    case 0:
                        console.log('updating github for pull_request event ' + status.eventData.ghEventId);
                        //console.log(status);

                        _context6.prev = 1;
                        _context6.next = 4;
                        return connect(status.context);

                    case 4:
                        github = _context6.sent;
                        _context6.next = 7;
                        return github.repos.createStatus(status).then(function () {
                            done();
                        });

                    case 7:
                        return _context6.abrupt('return', _context6.sent);

                    case 10:
                        _context6.prev = 10;
                        _context6.t0 = _context6['catch'](1);

                        if (!(_context6.t0.message === 'OAuth2 authentication requires a token or key & secret to be set')) {
                            _context6.next = 14;
                            break;
                        }

                        throw _context6.t0;

                    case 14:
                        console.log('----- ERROR COMMUNICATING WITH GITHUB -----');
                        console.log(_context6.t0);
                        done();

                    case 17:
                    case 'end':
                        return _context6.stop();
                }
            }
        }, _callee6, this, [[1, 10]]);
    }));

    return function updateGitHubPullRequestStatus(_x8, _x9) {
        return _ref6.apply(this, arguments);
    };
}();

var addGitHubPullRequestComment = function () {
    var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee7(status, done) {
        var github;
        return _regenerator2.default.wrap(function _callee7$(_context7) {
            while (1) {
                switch (_context7.prev = _context7.next) {
                    case 0:
                        console.log('add comment on pull_request completion ' + status.eventData.ghEventId);
                        /**
                         * declare type PullRequestsCreateReviewParams =
                         & {
                          owner: string;
                          repo: string;
                          number: number;
                          commit_id?: string;
                          body?: string;
                          event?: "APPROVE"|"REQUEST_CHANGES"|"COMMENT"|"PENDING";
                          comments?: string[];
                        };
                         */
                        _context7.prev = 1;
                        _context7.next = 4;
                        return connect();

                    case 4:
                        github = _context7.sent;
                        _context7.next = 7;
                        return github.pullRequests.createReview({
                            owner: status.owner,
                            repo: status.repo,
                            number: parseInt(status.number),
                            body: status.description,
                            event: 'COMMENT'
                        }).then(function () {
                            done();
                        });

                    case 7:
                        return _context7.abrupt('return', _context7.sent);

                    case 10:
                        _context7.prev = 10;
                        _context7.t0 = _context7['catch'](1);

                        console.log('----- ERROR COMMUNICATING WITH GITHUB -----');
                        console.log(_context7.t0);
                        done();

                    case 15:
                    case 'end':
                        return _context7.stop();
                }
            }
        }, _callee7, this, [[1, 10]]);
    }));

    return function addGitHubPullRequestComment(_x10, _x11) {
        return _ref7.apply(this, arguments);
    };
}();

var addGitHubPushComments = function () {
    var _ref8 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee8(status, done) {
        var github, promises;
        return _regenerator2.default.wrap(function _callee8$(_context8) {
            while (1) {
                switch (_context8.prev = _context8.next) {
                    case 0:
                        console.log('add comment on push completion ' + status.eventData.ghEventId);
                        /**
                         * declare type ReposCreateCommitCommentParams =
                         & {
                          owner: string;
                          repo: string;
                          sha: string;
                          body: string;
                          path?: string;
                          position?: number;
                        };
                         */
                        _context8.prev = 1;
                        _context8.next = 4;
                        return connect();

                    case 4:
                        github = _context8.sent;
                        promises = [];
                        // adding the same comment to each commit in the push for now..

                        status.eventData.commits.forEach(function (commit) {
                            promises.push(github.repos.createCommitComment({
                                owner: status.owner,
                                repo: status.repo,
                                sha: commit.id,
                                body: status.description
                            }));
                        });
                        return _context8.abrupt('return', _promise2.default.all(promises).then(function () {
                            done();
                        }));

                    case 10:
                        _context8.prev = 10;
                        _context8.t0 = _context8['catch'](1);

                        console.log('----- ERROR COMMUNICATING WITH GITHUB -----');
                        console.log(_context8.t0);
                        done();

                    case 15:
                    case 'end':
                        return _context8.stop();
                }
            }
        }, _callee8, this, [[1, 10]]);
    }));

    return function addGitHubPushComments(_x12, _x13) {
        return _ref8.apply(this, arguments);
    };
}();

var updateGitHubComment = function () {
    var _ref9 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee9(status, done) {
        return _regenerator2.default.wrap(function _callee9$(_context9) {
            while (1) {
                switch (_context9.prev = _context9.next) {
                    case 0:
                        console.log('updating github for comment event ' + status.eventData.ghEventId);

                        //let github = connect(status.context);
                        //return await github.repos.createStatus(status).then(() => {
                        done();
                        //});

                    case 2:
                    case 'end':
                        return _context9.stop();
                }
            }
        }, _callee9, this);
    }));

    return function updateGitHubComment(_x14, _x15) {
        return _ref9.apply(this, arguments);
    };
}();

var handleEventTaskResult = function () {
    var _ref10 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee10(message, done) {
        var status, updaterFunction;
        return _regenerator2.default.wrap(function _callee10$(_context10) {
            while (1) {
                switch (_context10.prev = _context10.next) {
                    case 0:
                        status = JSON.parse(message.Body);

                        console.log(status);

                        updaterFunction = githubUpdaters[status.eventData.ghEventType];

                        if (!updaterFunction) {
                            _context10.next = 9;
                            break;
                        }

                        _context10.next = 6;
                        return updaterFunction(status, done);

                    case 6:
                        return _context10.abrupt('return', _context10.sent);

                    case 9:
                        console.error('gitub updates for event type \'' + status.eventData.ghEventType + '\' are not supported yet.');
                        done();

                    case 11:
                    case 'end':
                        return _context10.stop();
                }
            }
        }, _callee10, this);
    }));

    return function handleEventTaskResult(_x16, _x17) {
        return _ref10.apply(this, arguments);
    };
}();

var isCommitForPullRequest = function () {
    var _ref11 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee11(commitSha) {
        var github, query, prResult;
        return _regenerator2.default.wrap(function _callee11$(_context11) {
            while (1) {
                switch (_context11.prev = _context11.next) {
                    case 0:
                        _context11.prev = 0;
                        _context11.next = 3;
                        return connect();

                    case 3:
                        github = _context11.sent;
                        query = commitSha + '+is:pr+state:open';
                        _context11.next = 7;
                        return github.search.issues({ q: query });

                    case 7:
                        prResult = _context11.sent;

                        console.log('isCommitForPullRequest result: ' + json.plain(prResult));
                        return _context11.abrupt('return', prResult.data.items && prResult.data.items.length > 0);

                    case 12:
                        _context11.prev = 12;
                        _context11.t0 = _context11['catch'](0);

                        console.log('----- ERROR COMMUNICATING WITH GITHUB -----');
                        console.log(_context11.t0);

                    case 16:
                    case 'end':
                        return _context11.stop();
                }
            }
        }, _callee11, this, [[0, 12]]);
    }));

    return function isCommitForPullRequest(_x18) {
        return _ref11.apply(this, arguments);
    };
}();

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

var _KmsUtils = require('./../KmsUtils');

var _KmsUtils2 = _interopRequireDefault(_KmsUtils);

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

var json = require('format-json');
if (process.env.GTM_GITHUB_DEBUG) process.env.DEBUG = 'octokit:rest*';
var GitHubApi = require('@octokit/rest');
var crypto = require('crypto');
var https = require('https');
var githubUpdaters = {
    pull_request: updateGitHubPullRequest,
    comment: updateGitHubComment,
    push: updateGitHubPush
};


var ghEnforceValidSsl = process.env.NODE_TLS_REJECT_UNAUTHORIZED === 0;

function signRequestBody(key, body) {
    return 'sha1=' + crypto.createHmac('sha1', key).update(body, 'utf-8').digest('hex');
}

function decodeFileResponse(fileResponse) {
    console.log(json.plain(fileResponse));
    var buff = new Buffer(fileResponse.data.content, 'base64');
    var content = JSON.parse(buff.toString('ascii'));

    return content;
}

function createPullRequestStatus(eventData, state, context, description, url) {
    return {
        owner: eventData.repository.owner.login || 'Default_Owner',
        repo: eventData.repository.name || 'Default_Repository',
        sha: eventData.pull_request.head.sha || 'Missing SHA',
        number: eventData.pull_request.number,
        state: state,
        target_url: url ? url : 'https://github.com/zotoio/github-task-manager',
        description: description,
        context: context,
        eventData: eventData
    };
}

module.exports = {
    connect: connect,
    signRequestBody: signRequestBody,
    invalidHook: invalidHook,
    decodeFileResponse: decodeFileResponse,
    getFile: getFile,
    handleEventTaskResult: handleEventTaskResult,
    updateGitHubPullRequestStatus: updateGitHubPullRequestStatus,
    createPullRequestStatus: createPullRequestStatus,
    isCommitForPullRequest: isCommitForPullRequest
};