const pkg = require('../package.json');
let beacon = null;

const getBeacon = (delay) => {
  if (!beacon) {
    // 涓嶆斁鍦ㄩ《灞傛槸閬垮厤棣栨寮曞叆灏辫鍔犺浇锛屼粠鑰岄伩鍏嶅湪鏌愪簺鐜姣斿webworker閲屽姞杞界伅濉攕dk鍐厀indow鐩稿叧瀵硅薄鎶ラ敊
    const BeaconAction = require('../lib/beacon.min');
    beacon = new BeaconAction({
      appkey: '0AND0VEVB24UBGDU',
      versionCode: pkg.version,
      channelID: 'js_sdk', //娓犻亾,閫夊~
      openid: 'openid', // 鐢ㄦ埛id, 閫夊~
      unionid: 'unid', //鐢ㄦ埛unionid , 绫讳技idfv,閫夊~
      strictMode: false, //涓ヨ嫑妯″紡寮€鍏�, 鎵撳紑涓ヨ嫑妯″紡浼氫富鍔ㄦ姏鍑哄紓甯�, 涓婄嚎璇峰姟蹇呭叧闂�!!!
      delay, // 鏅€氫簨浠跺欢杩熶笂鎶ユ椂闂�(鍗曚綅姣), 榛樿1000(1绉�),閫夊~
      sessionDuration: 60 * 1000, // session鍙樻洿鐨勬椂闂撮棿闅�, 涓€涓敤鎴锋寔缁�30鍒嗛挓(榛樿鍊�)娌℃湁浠讳綍涓婃姤鍒欑畻鍙︿竴娆� session,姣忓彉鏇翠竴娆ession涓婃姤涓€娆″惎鍔ㄤ簨浠�(rqd_applaunched),浣跨敤姣(ms),鏈€灏忓€�30绉�,閫夊~
    });
  }
  return beacon;
};

const utils = {
  // 鐢熸垚uid 姣忎釜閾捐矾瀵瑰簲鍞竴涓€鏉id
  getUid() {
    var S4 = function () {
      return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    };
    return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4();
  },
  // 鑾峰彇缃戠粶绫诲瀷
  getNetType() {
    if (typeof navigator === 'object') {
      const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
      return connection?.type || connection?.effectiveType || 'unknown';
    }
    return 'unknown';
  },
  // 鑾峰彇pc绔搷浣滅郴缁熺被鍨�
  getOsType() {
    if (typeof navigator !== 'object') {
      return 'unknown os';
    }
    var agent = navigator.userAgent.toLowerCase();
    var isMac = /macintosh|mac os x/i.test(navigator.userAgent);
    if (agent.indexOf('win32') >= 0 || agent.indexOf('wow32') >= 0) {
      return 'win32';
    }
    if (agent.indexOf('win64') >= 0 || agent.indexOf('wow64') >= 0) {
      return 'win64';
    }
    if (isMac) {
      return 'mac';
    }
    return 'unknown os';
  },
  isMobile() {
    const exp =
      /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i;
    if (typeof navigator === 'object' && navigator.userAgent.match(exp)) {
      return true; // 绉诲姩绔�
    }
    return false; // PC绔�
  },
  isAndroid() {
    const exp = /(Android|Adr|Linux)/i;
    if (typeof navigator === 'object' && navigator.userAgent.match(exp)) {
      return true;
    }
    return false;
  },
  isIOS() {
    const exp = /(iPhone|iPod|iPad|iOS)/i;
    if (typeof navigator === 'object' && navigator.userAgent.match(exp)) {
      return true;
    }
    return false;
  },
  isOtherMobile() {
    return isMobile && !isAndroid && !isIOS;
  },
  // 鑾峰彇娴忚鍣ㄧ被鍨�
  getDeviceName() {
    if (typeof navigator !== 'object') {
      return 'unknown device';
    }
    const explorer = navigator.userAgent.toLowerCase();
    // 鑵捐浼氳鍐呯疆娴忚鍣�
    if (explorer.includes('app/tencent_wemeet')) {
      return 'tencent_wemeet';
    }
    // 閬ㄦ父娴忚鍣�
    if (explorer.indexOf('maxthon') >= 0) {
      const match = explorer.match(/maxthon\/([\d.]+)/);
      const ver = (match && match[1]) || '';
      return `鍌叉父娴忚鍣� ${ver}`.trim();
    }
    // QQ娴忚鍣�
    if (explorer.indexOf('qqbrowser') >= 0) {
      const match = explorer.match(/qqbrowser\/([\d.]+)/);
      const ver = (match && match[1]) || '';
      return `QQ娴忚鍣� ${ver}`.trim();
    }
    // 鎼滅嫍娴忚鍣�
    if (explorer.indexOf('se 2.x') >= 0) {
      return '鎼滅嫍娴忚鍣�';
    }
    // 寰俊娴忚鍣�
    if (explorer.indexOf('wxwork') >= 0) {
      return '寰俊鍐呯疆娴忚鍣�';
    }
    // ie
    if (explorer.indexOf('msie') >= 0) {
      const match = explorer.match(/msie ([\d.]+)/);
      const ver = (match && match[1]) || '';
      return `IE ${ver}`.trim();
    }
    // firefox
    if (explorer.indexOf('firefox') >= 0) {
      const match = explorer.match(/firefox\/([\d.]+)/);
      const ver = (match && match[1]) || '';
      return `Firefox ${ver}`.trim();
    }
    // Chrome
    if (explorer.indexOf('chrome') >= 0) {
      const match = explorer.match(/chrome\/([\d.]+)/);
      const ver = (match && match[1]) || '';
      return `Chrome ${ver}`.trim();
    }
    // Opera
    if (explorer.indexOf('opera') >= 0) {
      const match = explorer.match(/opera.([\d.]+)/);
      const ver = (match && match[1]) || '';
      return `Opera ${ver}`.trim();
    }
    // Safari
    if (explorer.indexOf('safari') >= 0) {
      const match = explorer.match(/version\/([\d.]+)/);
      const ver = (match && match[1]) || '';
      return `Safari ${ver}`.trim();
    }
    if (explorer.indexOf('edge') >= 0) {
      const match = explorer.match(/edge\/([\d.]+)/);
      const ver = (match && match[1]) || '';
      return `edge ${ver}`.trim();
    }
    return explorer.substr(0, 200);
  },
};

const constant = {
  isMobile: utils.isMobile(),
  isBrowser: !utils.isMobile(),
  mobileOsType: utils.isAndroid() ? 'android' : utils.isIOS ? 'ios' : 'other_mobile',
  pcOsType: utils.getOsType(),
};

// 璁惧淇℃伅锛屽彧鍙栦竴娆″€�
const deviceInfo = {
  // 鈫撲笂鎶ラ」
  deviceType: constant.isMobile ? 'mobile' : constant.isBrowser ? 'browser' : 'unknown',
  devicePlatform: constant.isMobile ? constant.mobileOsType : constant.pcOsType,
  deviceName: utils.getDeviceName(), //娴忚鍣ㄥ悕绉�
};

// 鍒嗗潡涓婁紶鍘熷瓙鏂规硶
const sliceUploadMethods = [
  'multipartInit',
  'multipartUpload',
  'multipartComplete',
  'multipartList',
  'multipartListPart',
  'multipartAbort',
];

const uploadApi = ['putObject', 'postObject', 'appendObject', 'sliceUploadFile', 'uploadFile', 'uploadFiles'].concat(
  sliceUploadMethods
);
const downloadApi = ['getObject'];

function getEventCode(apiName) {
  if (uploadApi.includes(apiName)) {
    return 'cos_upload';
  }
  if (downloadApi.includes(apiName)) {
    return 'cos_download';
  }
  return 'base_service';
}

// 涓婃姤鍙傛暟椹煎嘲鏀逛笅鍒掔嚎
function camel2underline(key) {
  return key.replace(/([A-Z])/g, '_$1').toLowerCase();
}
function formatParams(params) {
  const formattedParams = {};
  const allReporterKeys = [
    'tracePlatform',
    'cossdkVersion',
    'region',
    'networkType',
    'host',
    'accelerate',
    'requestPath',
    'size',
    'httpMd5',
    'httpSign',
    'httpFull',
    'name',
    'result',
    'tookTime',
    'errorNode',
    'errorCode',
    'errorMessage',
    'errorRequestId',
    'errorStatusCode',
    'errorServiceName',
    'errorType',
    'traceId',
    'bucket',
    'appid',
    'partNumber',
    'retryTimes',
    'reqUrl',
    'customId',
    'fullError',
    'deviceType',
    'devicePlatform',
    'deviceName',
  ];
  const successKeys = [
    'tracePlatform',
    'cossdkVersion',
    'region',
    'bucket',
    'appid',
    'networkType',
    'host',
    'accelerate',
    'requestPath',
    'partNumber',
    'size',
    'name',
    'result',
    'tookTime',
    'errorRequestId',
    'retryTimes',
    'reqUrl',
    'customId',
    'deviceType',
    'devicePlatform',
    'deviceName',
  ];
  // 闇€瑕佷笂鎶ョ殑鍙傛暟瀛楁
  const reporterKeys = params.result === 'Success' ? successKeys : allReporterKeys;
  for (let key in params) {
    if (!reporterKeys.includes(key)) continue;
    const formattedKey = camel2underline(key);
    formattedParams[formattedKey] = params[key];
  }
  return formattedParams;
}

// 閾捐矾杩借釜鍣�
class Tracker {
  constructor(opt) {
    const { parent, traceId, bucket, region, apiName, fileKey, fileSize, accelerate, customId, delay, deepTracker } =
      opt;
    const appid = (bucket && bucket.substr(bucket.lastIndexOf('-') + 1)) || '';
    this.parent = parent;
    this.deepTracker = deepTracker;
    this.delay = delay;
    // 涓婃姤鐢ㄥ埌鐨勫瓧娈�
    this.params = {
      // 閫氱敤瀛楁
      cossdkVersion: pkg.version,
      region,
      networkType: '',
      host: '',
      accelerate: accelerate ? 'Y' : 'N',
      requestPath: fileKey || '',
      size: fileSize || -1,
      httpMd5: 0, // MD5鑰楁椂
      httpSign: 0, // 璁$畻绛惧悕鑰楁椂
      httpFull: 0, // http璇锋眰鑰楁椂
      name: apiName || '',
      result: '', // sdk api璋冪敤缁撴灉Success銆丗ail
      tookTime: 0, // 鎬昏€楁椂
      errorNode: '',
      errorCode: '',
      errorMessage: '',
      errorRequestId: '',
      errorStatusCode: 0,
      errorServiceName: '',

      // js琛ュ厖瀛楁
      tracePlatform: 'cos-js-sdk-v5', // 涓婃姤骞冲彴=js
      traceId: traceId || utils.getUid(), // 姣忔潯涓婃姤鍞竴鏍囪瘑
      bucket,
      appid,
      partNumber: 0, // 鍒嗗潡涓婁紶缂栧彿
      retryTimes: 0, // sdk鍐呴儴鍙戣捣鐨勮姹傞噸璇�
      reqUrl: '', // 璇锋眰url
      customId: customId || '', // 涓氬姟id
      deviceType: deviceInfo.deviceType, // 璁惧绫诲瀷 绉诲姩绔祻瑙堝櫒銆亀eb娴忚鍣�
      devicePlatform: deviceInfo.devicePlatform,
      deviceName: deviceInfo.deviceName,

      md5StartTime: 0, // md5璁$畻寮€濮嬫椂闂�
      md5EndTime: 0, // md5璁$畻缁撴潫鏃堕棿
      signStartTime: 0, // 璁$畻绛惧悕寮€濮嬫椂闂�
      signEndTime: 0, // 璁$畻绛惧悕缁撴潫鏃堕棿
      httpStartTime: 0, // 鍙戣捣缃戠粶璇锋眰寮€濮嬫椂闂�
      httpEndTime: 0, // 缃戣矾璇锋眰缁撴潫鏃堕棿
      startTime: new Date().getTime(), // sdk api璋冪敤璧峰鏃堕棿锛屼笉鏄函缃戠粶鑰楁椂
      endTime: 0, //  sdk api璋冪敤缁撴潫鏃堕棿锛屼笉鏄函缃戠粶鑰楁椂
    };
    this.beacon = getBeacon(delay);
  }

  // 鏍煎紡鍖杝dk鍥炶皟
  formatResult(err, data) {
    const now = new Date().getTime();
    const tookTime = now - this.params.startTime;
    const networkType = utils.getNetType();
    const errorCode = err ? err?.code || err?.error?.code || err?.error?.Code : '';
    const errorMessage = err ? err?.message || err?.error?.message || err?.error?.Message : '';
    const errorServiceName = err ? err?.resource || err?.error?.resource || err?.error?.Resource : '';
    const errorStatusCode = err ? err?.statusCode : data.statusCode;
    const requestId = err
      ? err?.headers && err?.headers['x-cos-request-id']
      : data?.headers && data?.headers['x-cos-request-id'];
    const errorType = err ? (requestId ? 'Server' : 'Client') : '';
    Object.assign(this.params, {
      tookTime,
      networkType,
      httpMd5: this.params.md5EndTime - this.params.md5StartTime,
      httpSign: this.params.signEndTime - this.params.signStartTime,
      httpFull: this.params.httpEndTime - this.params.httpStartTime,
      result: err ? 'Fail' : 'Success',
      errorType,
      errorCode,
      errorStatusCode,
      errorMessage,
      errorServiceName,
      errorRequestId: requestId,
    });
    if (err && (!errorCode || !errorMessage)) {
      // 鏆傚瓨鍏ㄩ噺err涓€娈垫椂闂� 瑙傚療鏄惁鎵€鏈塭rr鏍煎紡閮藉彲琚В鏋�
      this.params.fullError = err ? JSON.stringify(err) : '';
    }
    if (this.params.name === 'getObject') {
      this.params.size = data ? data.headers && data.headers['content-length'] : -1;
    }
    if (this.params.reqUrl) {
      try {
        const execRes = /^http(s)?:\/\/(.*?)\//.exec(this.params.reqUrl);
        this.params.host = execRes[2];
      } catch (e) {
        this.params.host = this.params.reqUrl;
      }
    }
    this.sendEvents();
  }

  // 璁剧疆褰撳墠閾捐矾鐨勫弬鏁�
  setParams(params) {
    Object.assign(this.params, params);
  }

  // 浣跨敤鐏寤舵椂涓婃姤
  sendEvents() {
    // DeepTracker妯″紡涓嬫墠浼氫笂鎶ュ垎鍧椾笂浼犲唴閮ㄧ粏鑺�
    if (sliceUploadMethods.includes(this.params.name) && !this.deepTracker) {
      return;
    }
    const eventCode = getEventCode(this.params.name);
    const formattedParams = formatParams(this.params);

    // 鍏滃簳澶勭悊
    if (!this.beacon) {
      this.beacon = getBeacon(this.delay || 5000);
    }

    if (this.delay === 0) {
      // 瀹炴椂涓婃姤
      this.beacon && this.beacon.onDirectUserAction(eventCode, formattedParams);
    } else {
      // 鍛ㄦ湡鎬т笂鎶�
      this.beacon && this.beacon.onUserAction(eventCode, formattedParams);
    }
  }

  // 鐢熸垚瀛愬疄渚嬶紝涓庣埗鎵€灞炰竴涓摼璺紝鍙敤浜庡垎鍧椾笂浼犲唴閮ㄦ祦绋嬩笂鎶ュ崟涓垎鍧楁搷浣�
  generateSubTracker(subParams) {
    Object.assign(subParams, {
      parent: this,
      deepTracker: this.deepTracker,
      traceId: this.params.traceId,
      bucket: this.params.bucket,
      region: this.params.region,
      fileKey: this.params.requestPath,
      customId: this.params.customId,
      delay: this.delay,
    });
    return new Tracker(subParams);
  }
}

module.exports = Tracker;