import { oncePromiseResolve } from '../../../utils/call'
import sendManager from './sendManager'
import errUtil from '../../../utils/error';
import ERRORS from '../const/error';
import { silenceLogger } from '../../../common/logger';
import event, { EVENTS } from '../../../common/event';

const { ERR_BROKEN } = ERRORS;

class PingManager {
  constructor() {
    this._pass = null // 是否畅通
    // 最多查询_max次，每次_timeout秒
    this._max = 15 // 循环查询次数上限
    this._timeout = 3000 // 查询超时
    
    // 对外方法，只返回一次成功
    this.ping = oncePromiseResolve(this.check.bind(this))
  }

  /**
   * 检查
   */
  async check() {
    silenceLogger.debug('loop ping')
    const pass = await this._loop()
    if (!pass) {
      throw errUtil.pkgErr(ERR_BROKEN, `链路不通，请稍后重试`)
    }
    return pass
  }

  _pingwarn(cont, msg) {
    // 查询超过一次 且 首次有结果 上报告警
    if (cont > 1 && this._pass === null) { 
      event.emit(EVENTS.WARN_INFO, { api: 'ping__', msg })
    }
  }

  /**
   * 循环检查
   * @param {number} [cont] 循环次数
   */
  async _loop(cont = 1) {
    // 返回缓存结果
    if (this._pass !== null) return this._pass

    // ping
    try {
      const pass = await this._ping()
      if (pass) {
        this._pingwarn(cont, `ping ${cont} succ`)
        return (this._pass = true)
      }
    } catch(err) {
      // 
    }

    // 不通过，重试 max 次后返回结果
    if (cont >= this._max) {
      this._pingwarn(cont, `ping ${cont} fail`)
      return (this._pass = false)
    }
    
    // 链路不通，重试
    return await this._loop(++cont)
  }

  /**
   * 链路畅通校验，不通则直接返回 false
   * 理论上请求未定义方法会立即返回
   */
  _ping() {
    return new Promise((resolve, reject) => {
      const funcName = `ping__`
      const callbackName = `${funcName}Callback`;

      // 超时计时
      const timer = setTimeout(() => {
        terminate() 
        resolve(false)
      }, this._timeout)

      // 回调
      window[callbackName] = (res) => {
        terminate() 
        resolve(true)
      }

      // 结束
      const terminate = () => {
        try {
          clearTimeout(timer);
          delete window[callbackName];
        } catch(err) {
          // 
        }
      }
      
      try {
        // 发送请求
        sendManager.post({ funcName, callbackName }, true)
      } catch(err) {
        terminate() 
        resolve(false)
      }
    })
  }
}

export default new PingManager()
