'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var _regeneratorRuntime = require('@babel/runtime/regenerator');
var tslib_es6 = require('../tslib.es6-0d92ef81.js');
var git_git = require('../git/git.js');
var node_nodeCommand = require('../node/node-command.js');
var tgit_branch = require('../tgit/branch.js');
var tgit_project = require('../tgit/project.js');
var wecomRobot_batchSend = require('../wecom-robot/batch-send.js');
var dailyMerge_helper = require('./helper.js');
require('../validate/validate.js');
require('@babel/runtime/helpers/typeof');
require('../nodejs/child_process.js');
require('../tgit/helper.js');
require('axios');
require('../wecom-robot/base.js');
require('../wecom-robot/helper.js');
require('../wecom-robot/send-img.js');
require('../nodejs/path.js');
require('../node-img/img.js');
require('../nodejs/crypto.js');
require('../nodejs/fs.js');
require('../nodejs/request.js');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var _regeneratorRuntime__default = /*#__PURE__*/_interopDefaultLegacy(_regeneratorRuntime);

var CHAT_ID = ['ALL'];
var DOC_URL = 'https://docs.qq.com/doc/DSGN2Uk1hTllydkRh';
function getStartTime() {
  var date = new Date();
  date.setHours(0, 0, 0, 0);
  var startTime = parseInt("".concat(date.getTime()), 10) - 24 * 60 * 60 * 1000;
  return startTime;
}
function getBranches(_ref) {
  var baseUrl = _ref.baseUrl,
    repoName = _ref.repoName,
    privateToken = _ref.privateToken,
    whiteBranchReg = _ref.whiteBranchReg;
  return tslib_es6.__awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime__default["default"].mark(function _callee() {
    var res, list, branches;
    return _regeneratorRuntime__default["default"].wrap(function (_context) {
      while (1) switch (_context.prev = _context.next) {
        case 0:
          _context.next = 1;
          return tgit_branch.getBranchesByProjectName({
            projectName: repoName,
            privateToken: privateToken,
            baseUrl: baseUrl
          })["catch"](function (err) {
            console.log('[getBranches] err', err);
          });
        case 1:
          res = _context.sent;
          list = (res || []).filter(function (item) {
            var commit = item.commit;
            var date = commit === null || commit === void 0 ? void 0 : commit.committed_date;
            var time = new Date(date).getTime();
            if (time > getStartTime()) return true;
            return false;
          });
          console.log('[Origin Branches]', list.map(function (item) {
            return item === null || item === void 0 ? void 0 : item.name;
          }));
          branches = list.filter(function (branch) {
            return dailyMerge_helper.shouldInclude(branch.name, whiteBranchReg);
          });
          console.log('[Filter  Branches]', branches.map(function (item) {
            return item === null || item === void 0 ? void 0 : item.name;
          }));
          return _context.abrupt("return", branches);
        case 2:
        case "end":
          return _context.stop();
      }
    }, _callee);
  }));
}
function mergeMainBranch(_ref2) {
  var _ref2$branch = _ref2.branch,
    branch = _ref2$branch === void 0 ? {} : _ref2$branch,
    _ref2$results = _ref2.results,
    results = _ref2$results === void 0 ? [] : _ref2$results,
    _ref2$devRoot = _ref2.devRoot,
    devRoot = _ref2$devRoot === void 0 ? '' : _ref2$devRoot,
    _ref2$mainBranch = _ref2.mainBranch,
    mainBranch = _ref2$mainBranch === void 0 ? 'develop' : _ref2$mainBranch,
    _ref2$isDryRun = _ref2.isDryRun,
    isDryRun = _ref2$isDryRun === void 0 ? false : _ref2$isDryRun;
  var _a, _b;
  var branchName = branch.name;
  var author = ((_a = branch === null || branch === void 0 ? void 0 : branch.commit) === null || _a === void 0 ? void 0 : _a.author_name) || ((_b = branch === null || branch === void 0 ? void 0 : branch.author) === null || _b === void 0 ? void 0 : _b.username) || '';
  if (!devRoot) {
    results.push({
      name: branchName,
      author: author,
      status: 'skipped',
      reason: '未提供 devRoot'
    });
    return;
  }
  function tExecCommand(command) {
    var stdio = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'inherit';
    console.log('[Exec] ', command);
    node_nodeCommand.execCommand(command, devRoot, stdio);
  }
  console.log('[Merging] ', branchName);
  tExecCommand('git clean -df');
  tExecCommand('git reset --hard HEAD');
  tExecCommand("git checkout ".concat(mainBranch, " && git pull && git submodule update --init"), 'inherit');
  tExecCommand("git checkout \"".concat(branchName, "\" && git reset --hard \"origin/").concat(branchName, "\" && git pull"));
  var originCommitInfo = git_git.getGitCommitInfo(devRoot, true, false, false);
  console.log('[originCommitInfo]', originCommitInfo);
  try {
    var out = tExecCommand("git merge ".concat(mainBranch, " -m \"Merge branch '").concat(mainBranch, "' into '").concat(branchName, "' by robot\""), 'inherit');
    console.log('[out]', out);
  } catch (err) {
    console.log('[Merge Error]', err);
    results.push({
      name: branchName,
      author: author,
      status: 'conflict',
      reason: (err === null || err === void 0 ? void 0 : err.message) ? String(err.message).slice(0, 500) : 'merge 冲突',
      originCommit: originCommitInfo === null || originCommitInfo === void 0 ? void 0 : originCommitInfo.hash
    });
    return;
  }
  var nowCommitInfo = git_git.getGitCommitInfo(devRoot, true, false, false);
  console.log('[nowCommitInfo]', nowCommitInfo);
  if (nowCommitInfo.hash !== originCommitInfo.hash) {
    try {
      if (isDryRun) {
        results.push({
          name: branchName,
          author: author,
          status: 'success',
          reason: 'dryRun 不实际推送',
          originCommit: originCommitInfo === null || originCommitInfo === void 0 ? void 0 : originCommitInfo.hash,
          nowCommit: nowCommitInfo === null || nowCommitInfo === void 0 ? void 0 : nowCommitInfo.hash
        });
      } else {
        tExecCommand('git push');
        results.push({
          name: branchName,
          author: author,
          status: 'success',
          originCommit: originCommitInfo === null || originCommitInfo === void 0 ? void 0 : originCommitInfo.hash,
          nowCommit: nowCommitInfo === null || nowCommitInfo === void 0 ? void 0 : nowCommitInfo.hash
        });
      }
    } catch (err) {
      console.log('[Push Error]', err);
      results.push({
        name: branchName,
        author: author,
        status: 'conflict',
        reason: "push \u5931\u8D25\uFF1A".concat((err === null || err === void 0 ? void 0 : err.message) ? String(err.message).slice(0, 500) : err),
        originCommit: originCommitInfo === null || originCommitInfo === void 0 ? void 0 : originCommitInfo.hash,
        nowCommit: nowCommitInfo === null || nowCommitInfo === void 0 ? void 0 : nowCommitInfo.hash
      });
    }
  } else {
    results.push({
      name: branchName,
      author: author,
      status: 'noMerge',
      reason: 'merge 后无新提交',
      originCommit: originCommitInfo === null || originCommitInfo === void 0 ? void 0 : originCommitInfo.hash
    });
  }
}
function getMessageContent(_ref3) {
  var appName = _ref3.appName,
    projectId = _ref3.projectId,
    baseUrl = _ref3.baseUrl,
    quickLink = _ref3.quickLink,
    _ref3$successBranches = _ref3.successBranches,
    successBranches = _ref3$successBranches === void 0 ? [] : _ref3$successBranches,
    _ref3$conflictBranche = _ref3.conflictBranches,
    conflictBranches = _ref3$conflictBranche === void 0 ? [] : _ref3$conflictBranche,
    _ref3$noMergeBranches = _ref3.noMergeBranches,
    noMergeBranches = _ref3$noMergeBranches === void 0 ? [] : _ref3$noMergeBranches,
    _ref3$toConfirmBranch = _ref3.toConfirmBranches,
    toConfirmBranches = _ref3$toConfirmBranch === void 0 ? [] : _ref3$toConfirmBranch;
  var link = quickLink ? quickLink : "".concat(baseUrl, "/search?tab=11&search=by%20robot&order=time&es_sort=desc&project_id=").concat(projectId || '');
  var list = [">\u3010".concat(appName || '', "\u81EA\u52A8\u5408\u5E76\u3011[\u67E5\u770B](").concat(link, ")"), "- \u6210\u529F\u5408\u5E76\uFF1A".concat(successBranches.join('、')), "- \u5B58\u5728\u51B2\u7A81\uFF1A".concat(conflictBranches.join('、')), "- \u65E0\u9700\u5408\u5E76\uFF1A".concat(noMergeBranches.join('、')), "- [\u8BF4\u660E\u6587\u6863](".concat(DOC_URL, ")<@novlan1>")];
  if (toConfirmBranches.length) {
    list.push("- \u9700\u8981\u786E\u8BA4\uFF1A".concat(toConfirmBranches.join('、')));
  }
  var content = list.join('\n');
  return content;
}
function sendSendMsg(content, webhookUrl) {
  return tslib_es6.__awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime__default["default"].mark(function _callee2() {
    return _regeneratorRuntime__default["default"].wrap(function (_context2) {
      while (1) switch (_context2.prev = _context2.next) {
        case 0:
          _context2.next = 1;
          return wecomRobot_batchSend.batchSendWxRobotMarkdown({
            content: content,
            chatId: CHAT_ID,
            webhookUrl: webhookUrl
          });
        case 1:
        case "end":
          return _context2.stop();
      }
    }, _callee2);
  }));
}
/**
 * 每日合并
 * 1. 获取昨天有活跃的分支
 * 2. 对于每个分支，进行合并并推送
 *     - 清理 Git 环境
 *     - 切到主分支，并拉最新代码
 *     - 切到当前分支，拉最新代码
 *     - 尝试执行 git merge
 *     - 对比 merge 前后的 commit 信息是否相同，作为判断 merge 是否成功的依据
 * 3. 发送机器人消息
 *
 *
 * @export
 * @async
 * @param {object} param0 参数
 * @param {string} param0.webhookUrl 机器人地址
 * @param {string} param0.appName 项目名称
 * @param {string} param0.devRoot 项目根路径
 * @param {string} param0.baseUrl 基础请求 url
 * @param {string} param0.repoName 仓库名称
 * @param {string} param0.privateToken 密钥
 * @param {boolean} [param0.isDryRun=false] 是否演练
 * @param {string} [param0.mainBranch='develop'] 主分支
 * @param {Regexp} [param0.whiteBranchReg=/^release\|develop\|hotfix\\/.+$/] 不处理的分支正则
 * @param {function} [param0.onReport] 执行完成后的结构化结果回调（可用于上报到自建后台）
 * @returns {Promise<DailyMergeReportInfo>}
 * @example
 *
 * ```ts
 * dailyMerge({
 *   webhookUrl: 'xx',
 *   appName: 'xx',
 *   devRoot: 'xx',
 *
 *   baseUrl: 'xx',
 *   repoName: 'xx',
 *   privateToken: 'xx',
 *
 *   isDryRun: false,
 *   onReport: async (info) => { await axios.post('/api/daily-merge', info); },
 * })
 * ```
 */
function dailyMerge(_ref4) {
  var webhookUrl = _ref4.webhookUrl,
    appName = _ref4.appName,
    devRoot = _ref4.devRoot,
    baseUrl = _ref4.baseUrl,
    quickLink = _ref4.quickLink,
    repoName = _ref4.repoName,
    privateToken = _ref4.privateToken,
    _ref4$isDryRun = _ref4.isDryRun,
    isDryRun = _ref4$isDryRun === void 0 ? false : _ref4$isDryRun,
    _ref4$mainBranch = _ref4.mainBranch,
    mainBranch = _ref4$mainBranch === void 0 ? 'develop' : _ref4$mainBranch,
    _ref4$whiteBranchReg = _ref4.whiteBranchReg,
    whiteBranchReg = _ref4$whiteBranchReg === void 0 ? dailyMerge_helper.DEFAULT_WHITE_REG : _ref4$whiteBranchReg,
    onReport = _ref4.onReport;
  return tslib_es6.__awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime__default["default"].mark(function _callee4() {
    var _this = this;
    var startTime, branchResults, buildInfo, safeReport, _info, _info2, branches, _info3, emptyContent, _robotNotified, _info4, successBranches, conflictBranches, noMergeBranches, toConfirmBranches, projectId, repoUrl, projectDetail, content, robotNotified, info, _t2, _t3, _t4, _t5;
    return _regeneratorRuntime__default["default"].wrap(function (_context4) {
      while (1) switch (_context4.prev = _context4.next) {
        case 0:
          console.log('\nSTART Daily Merge =====>');
          startTime = Date.now(); // 用于收集本次执行各分支结果的容器（即使中途异常也能上报）
          branchResults = [];
          /** 构造最终上报的 info 对象 */
          buildInfo = function buildInfo() {
            var extra = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
            var endTime = Date.now();
            var success = branchResults.filter(function (b) {
              return b.status === 'success';
            }).length;
            var conflict = branchResults.filter(function (b) {
              return b.status === 'conflict';
            }).length;
            var noMerge = branchResults.filter(function (b) {
              return b.status === 'noMerge';
            }).length;
            var skipped = branchResults.filter(function (b) {
              return b.status === 'skipped';
            }).length;
            var total = branchResults.length;
            var status = 'success';
            if (total === 0) status = 'empty';else if (conflict === 0) status = 'success';else if (success === 0 && noMerge === 0) status = 'failed';else status = 'partial';
            return Object.assign({
              repoName: repoName,
              appName: appName,
              mainBranch: mainBranch,
              isDryRun: isDryRun,
              startTime: startTime,
              endTime: endTime,
              duration: endTime - startTime,
              branches: branchResults,
              summary: {
                total: total,
                success: success,
                conflict: conflict,
                noMerge: noMerge,
                skipped: skipped
              },
              status: status
            }, extra);
          };
          /** 调用 onReport，吞掉错误避免影响主流程 */
          safeReport = function safeReport(info, err) {
            return tslib_es6.__awaiter(_this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime__default["default"].mark(function _callee3() {
              var _t;
              return _regeneratorRuntime__default["default"].wrap(function (_context3) {
                while (1) switch (_context3.prev = _context3.next) {
                  case 0:
                    if (!(typeof onReport !== 'function')) {
                      _context3.next = 1;
                      break;
                    }
                    return _context3.abrupt("return");
                  case 1:
                    _context3.prev = 1;
                    _context3.next = 2;
                    return onReport(info, err);
                  case 2:
                    _context3.next = 4;
                    break;
                  case 3:
                    _context3.prev = 3;
                    _t = _context3["catch"](1);
                    console.log('[onReport Error]', _t);
                  case 4:
                  case "end":
                    return _context3.stop();
                }
              }, _callee3, null, [[1, 3]]);
            }));
          };
          if (baseUrl) {
            _context4.next = 2;
            break;
          }
          console.error('Please Input Base Url');
          _info = buildInfo({
            status: 'failed'
          });
          _context4.next = 1;
          return safeReport(_info, {
            message: 'baseUrl 未提供'
          });
        case 1:
          return _context4.abrupt("return", _info);
        case 2:
          if (repoName) {
            _context4.next = 4;
            break;
          }
          console.error('Please Input Repo Name');
          _info2 = buildInfo({
            status: 'failed'
          });
          _context4.next = 3;
          return safeReport(_info2, {
            message: 'repoName 未提供'
          });
        case 3:
          return _context4.abrupt("return", _info2);
        case 4:
          branches = [];
          _context4.prev = 5;
          _context4.next = 6;
          return getBranches({
            baseUrl: baseUrl,
            repoName: repoName,
            privateToken: privateToken,
            whiteBranchReg: whiteBranchReg
          });
        case 6:
          branches = _context4.sent;
          _context4.next = 9;
          break;
        case 7:
          _context4.prev = 7;
          _t2 = _context4["catch"](5);
          _info3 = buildInfo({
            status: 'failed'
          });
          _context4.next = 8;
          return safeReport(_info3, {
            message: "\u83B7\u53D6\u5206\u652F\u5217\u8868\u5931\u8D25\uFF1A".concat((_t2 === null || _t2 === void 0 ? void 0 : _t2.message) || _t2),
            stack: _t2 === null || _t2 === void 0 ? void 0 : _t2.stack
          });
        case 8:
          throw _t2;
        case 9:
          if (branches === null || branches === void 0 ? void 0 : branches.length) {
            _context4.next = 15;
            break;
          }
          console.log('[no branch to merge]');
          emptyContent = ">\u3010".concat(appName || '', "\u81EA\u52A8\u5408\u5E76\u3011\u672A\u627E\u5230\u9700\u8981\u5408\u5E76\u7684\u5206\u652F");
          _robotNotified = false;
          if (!webhookUrl) {
            _context4.next = 13;
            break;
          }
          _context4.prev = 10;
          _context4.next = 11;
          return sendSendMsg(emptyContent, webhookUrl);
        case 11:
          _robotNotified = true;
          _context4.next = 13;
          break;
        case 12:
          _context4.prev = 12;
          _t3 = _context4["catch"](10);
          console.log('[sendSendMsg Error]', _t3);
        case 13:
          _info4 = buildInfo({
            status: 'empty',
            robotMessage: emptyContent,
            robotNotified: _robotNotified
          });
          _context4.next = 14;
          return safeReport(_info4);
        case 14:
          return _context4.abrupt("return", _info4);
        case 15:
          branches.forEach(function (branch) {
            mergeMainBranch({
              branch: branch,
              results: branchResults,
              mainBranch: mainBranch,
              devRoot: devRoot,
              isDryRun: isDryRun
            });
          });
          // 处理可能的"未被处理"分支（防御兜底）
          branches.forEach(function (branch) {
            if (!branchResults.find(function (r) {
              return r.name === branch.name;
            })) {
              branchResults.push({
                name: branch.name,
                status: 'pending',
                reason: '未被处理'
              });
            }
          });
          // 拼装机器人消息所需的字段
          successBranches = branchResults.filter(function (b) {
            return b.status === 'success';
          }).map(function (b) {
            return b.author ? "".concat(b.name, "<@").concat(b.author, ">") : b.name;
          });
          conflictBranches = branchResults.filter(function (b) {
            return b.status === 'conflict';
          }).map(function (b) {
            return b.author ? "".concat(b.name, "<@").concat(b.author, ">") : b.name;
          });
          noMergeBranches = branchResults.filter(function (b) {
            return b.status === 'noMerge';
          }).map(function (b) {
            return b.name;
          });
          toConfirmBranches = branchResults.filter(function (b) {
            return b.status === 'pending' || b.status === 'skipped';
          }).map(function (b) {
            return b.name;
          });
          projectId = '';
          _context4.prev = 16;
          _context4.next = 17;
          return tgit_project.getOneProjectDetail({
            baseUrl: baseUrl,
            projectName: repoName,
            privateToken: privateToken
          });
        case 17:
          projectDetail = _context4.sent;
          projectId = (projectDetail === null || projectDetail === void 0 ? void 0 : projectDetail.id) || '';
          repoUrl = (projectDetail === null || projectDetail === void 0 ? void 0 : projectDetail.web_url) || (projectDetail === null || projectDetail === void 0 ? void 0 : projectDetail.http_url_to_repo);
          console.log('[projectDetail]', projectDetail);
          _context4.next = 19;
          break;
        case 18:
          _context4.prev = 18;
          _t4 = _context4["catch"](16);
          console.log('[getOneProjectDetail Error]', _t4);
        case 19:
          content = getMessageContent({
            appName: appName,
            projectId: projectId,
            baseUrl: baseUrl,
            quickLink: quickLink,
            successBranches: successBranches,
            conflictBranches: conflictBranches,
            noMergeBranches: noMergeBranches,
            toConfirmBranches: toConfirmBranches
          });
          console.log('[successBranches]', JSON.stringify(successBranches));
          console.log('[conflictBranches]', JSON.stringify(conflictBranches));
          console.log('[noMergeBranches]', JSON.stringify(noMergeBranches));
          console.log('[robot message]', content);
          robotNotified = false;
          if (webhookUrl) {
            _context4.next = 20;
            break;
          }
          console.log('[No webhookUrl]');
          _context4.next = 23;
          break;
        case 20:
          _context4.prev = 20;
          _context4.next = 21;
          return sendSendMsg(content, webhookUrl);
        case 21:
          robotNotified = true;
          _context4.next = 23;
          break;
        case 22:
          _context4.prev = 22;
          _t5 = _context4["catch"](20);
          console.log('[sendSendMsg Error]', _t5);
        case 23:
          console.log('<===== END ');
          info = buildInfo({
            repoUrl: repoUrl,
            projectId: projectId,
            robotMessage: content,
            robotNotified: robotNotified
          });
          _context4.next = 24;
          return safeReport(info);
        case 24:
          return _context4.abrupt("return", info);
        case 25:
        case "end":
          return _context4.stop();
      }
    }, _callee4, null, [[5, 7], [10, 12], [16, 18], [20, 22]]);
  }));
}

exports.dailyMerge = dailyMerge;
