/**
 * 监控流程：监控错误 -> 搜集错误 -> 存储错误 -> 分析错误 -> 错误报警-> 定位错误 -> 解决错误
 * 前端报错：
 *  1. js编译时异常（开发阶段就能排） - 开发直接查询
 *  2. js运行时异常 - onerror、
 *  3. 加载静态资源异常（路径写错、资源服务器异常、CDN异常、跨域）
 *  4. 接口请求异常
 */

/**
 * 页面JS错误监控
 *  1. 重写window.onerror 方法
 *  2. 重写console.error方法，如果App首次向浏览器注入的Js代码报错了，window.onerror是无法监控到的，所以只能重写console.error的方式来进行捕获，也许会有更好的办法
 *  3. 重写window.onunhandledrejection方法，当你用到Promise的时候，而你又忘记写reject的捕获方法的时候，系统总是会抛出一个叫 Unhandled Promise rejection. 没有堆栈，没有其他信息，特别是在写fetch请求的时候很容易发生。
 *
 * 设置回调，回调中上传错误信息
 */
var jsMonitorStarted = false
function recordJavaScriptError (callback) {
  // 重写console.error, 可以捕获更全面的报错信息
  var oldError = console.error
  console.error = function () {
    // arguments的长度为2时，才是error上报的时机
    // if (arguments.length < 2) return;
    var errorMsg = arguments[0] && arguments[0].message
    var url = window.location.href
    var lineNumber = 0
    var columnNumber = 0
    var errorObj = arguments[0] && arguments[0].stack
    if (!errorObj) errorObj = arguments[0]
    // 如果onerror重写成功，就无需在这里进行上报了
    !jsMonitorStarted && siftAndMakeUpMessage(errorMsg, url, lineNumber, columnNumber, errorObj)
    return oldError.apply(console, arguments)
  }
  // 重写 onerror 进行jsError的监听
  window.onerror = function (errorMsg, url, lineNumber, columnNumber, errorObj) {
    jsMonitorStarted = true
    var errorStack = errorObj ? errorObj.stack : null
    siftAndMakeUpMessage(errorMsg, url, lineNumber, columnNumber, errorStack)
  }

  function siftAndMakeUpMessage (originErrorMsg, originUrl, originLineNumber, originColumnNumber, originErrorObj) {
    var errorMsg = originErrorMsg || ''
    var errorObj = originErrorObj || ''
    var errorType = ''
    if (errorMsg) {
      var errorStackStr = JSON.stringify(errorObj)
      errorType = errorStackStr.split(': ')[0].replace(/"/, '')
    }
    var _obj = {
      errorType: errorType,
      errorMsg: errorMsg,
      errorObj: errorObj
    }
    callback(_obj)
  }
}

window.recordJavaScriptError = recordJavaScriptError
