const JsSIP = require('jssip');
const dns = require('dns');
let ktLog;
// 电话实例
let inviterEntity = null;
// userAgent实例
let userAgent = null;
// 注册状态定时监测
let registererInteror = null;
// 软通话检测状态信息
let mesStatus = '';
/* 上次拨打时间 */
let upCallTime = 0;
// 是否创建了音频流
// let serverUrl = null, //软通话服务器根地址(不包含端口)
//   serverPort = null, //软通话服务器端口
//   webSocketPort = null, //软通话服务器WebSocket通讯端口
//   localNumber = null, //软通话本机账号
//   password = null, //软通话本机账号密码
//   sipState = null, // 回调函数，注册状态
//   isResetAutoRegisterTimer = true; //是否自动重置掉线自动重新注册的定时器
let sipOptions = {
  registerIp: '', //注册服务器地址
  registerPort: '', //注册端口号
  registerWsOrWss: 1, //注册协议，ws或wss  1为 ws 2为wss 默认1
  signallingIp: '', //信令服务器地址
  signallingPort: '', //信令服务器端口号
  sipLocalNumber: '', //本机号码
  sipLocalPassword: '', //本机密码
  sipStateCall: () => {}, //软通话注册状态回调
  AutoRegisterTimer: true, // 断线后是否自动重新注册
  isOpenDns: true, //是否开启Dns解析
};
// 通过DNS解析到的公网ip地址
let ipAddress = '';
let ipInterval = null;
//是否在释放旧实例，如果在释放的话无法初始化软通话，防止频繁初始化软通话导致状态错误
let freedOld = false;
let statusErrorNum = 0; //检测到软通话状态异常次数
let statusConUpdateTime = 0; //软通话链接状态态更新时间
//  sip 协议状态码说明地址 https://en.wikipedia.org/wiki/List_of_SIP_response_codes
let rejectMessage = {
  400: '错误请求',//由于语法错误，无法理解请求
  401: '未经授权',//该请求需要用户身份验证。此响应由 UAS 和注册商发出
  402: '保留为后续使用',
  403: '禁止',//服务器理解了该请求，但是拒绝响应 ,有时候（非所有时候）这意味着该呼叫被接收方拒接
  404: '未找到',//服务器有明确信息表明用户不存在于请求URI中指定的域中。如果请求 URI 中的域与请求接收方处理的任何域都不匹配，也会返回此状态
  405: '方法不允许',//请求行中指定的方法可以被理解，但是不允许用于请求 URI 标识的地址。
  406: '不可接受',//该请求所标识的资源仅能够生成具有内容特征的响应主体，但是根据请求中发送的Accept标头字段不被接受。
  407: '需要代理身份验证',//该请求需要用户身份验证。该响应由代理发出
  408: '请求超时',//无法找到实时用户，服务器无法在适当的时间内产生响应，例如，如果它无法及时确定用户的位置。客户端可能在以后的任何时间重复该请求而无需做任何修改。
  409: "冲突",//用户已注册
  410: "已消失",//该用户曾经存在，但不再可用。
  411: "长度要求",//如果没有有效的 Content-Length，服务器将不会接受该请求。
  412: "条件请求失败",//给定的先决条件尚未满足。
  413: "请求实体太大",//请求实体太大。
  414: "请求URI太长",//服务器拒绝提供该请求，因为请求 URI 的长度超出了服务器可解析的长度。
  415: "媒体类型无法支持",//请求体格式无法被支持。
  416: "URI方案无法被支持",//服务器无法识别请求 URI。
  417: "资源优先级未知",//有一个资源优先级选项标签，但没有资源优先级标头。
  420: "错误的扩展",//使用的 SIP 协议扩展不正确，服务器无法理解
  421: "需要扩展",//服务器需要支持的标头中未列出的特定扩展。[1]: §21.4.16 
  422: "会话间隔太短",//收到的请求包含一个 Session-Expires 标头字段，其持续时间低于最短计时器。
  423: "间隔太简短",//资源的过期时间太短。
  424: "错误的位置信息",//请求的位置内容格式不正确或不令人满意。
  425: "错误的告警信息",//服务器拒绝非交互式紧急呼叫，表明请求格式不正确，无法确定对警报的合理紧急响应。
  428: "使用身份标头",//服务器策略需要一个身份标头，但尚未提供。
  429: "提供推荐人身份",//服务器未收到请求中的有效 Referred-By 令牌。
  430: "流程失败",//发往用户代理的特定流程失败，但其他流程可能会成功。此响应旨在供代理设备之间使用，不应被端点看到（如果被端点看到，则应被视为 400 错误请求响应）
  433: "不允许匿名",//该请求因匿名被拒绝
  436: "错误的身份信息",//该请求具有 Identity-Info 标头，并且该标头中的 URI 方案无法取消引用。
  437: "证书不受支持",//服务器无法验证签署请求域的证书。
  438: "无效的身份标头",//服务器获得了请求中声称用于签署请求的有效证书，但无法验证该签名。
  439: "第一跳缺乏出站支持",//用户尝试注册的第一个出站代理不支持 RFC 5626 的“出站”功能，尽管注册商支持该功能。
  440: "超出最大宽度",//如果 SIP 代理确定响应上下文的传入最大宽度不足以执行所需的并行分叉，并且代理不愿意/无法通过连续分叉或发送重定向进行补偿，则该代理必须返回 440 响应。收到 440 响应的客户端可以推断其请求未到达所有可能的目的地。
  469: "不良信息包",//如果 SIP UA 收到与信息包相关的 INFO 请求，而 UA 尚未表示愿意接收该信息包，则 UA 必须发送 469 响应，该响应包含一个 Recv-Info 标头字段，其中包含 UA 愿意接收其 INFO 请求的信息包
  470: "需要同意",//请求的来源没有获得接收者的许可来提出此类请求。
  480: '暂时不可用',//被叫方当前无法接通。
  481: "呼叫 / 交易不存在",//服务器收到一个请求，该请求与任何对话或事务均不匹配
  482: "检测到环路",//服务器检测到环路。
  483: "跳数过多",//Max-Forwards 标头已达到值“0”。
  484: '地址不完整',//请求的URI 不完整。
  485: '模糊',//请求的URI模糊。
  486: '忙线',//被呼叫方忙线中
  487: '请求已终止',//请求已通过再见或取消而终止。
  488: '此处不可接受', //需要内网穿透 。会话描述或请求 URI 的某些方面不可接受。
  489: "不良事件",//服务器无法理解事件标头字段中指定的事件包
  491: '请求待处理',//服务器有来自同一对话框的几条待处理请求
  493: "无法解读",//请求包含加密的 MIME 主体，收件人无法解密。
  494: "需要安全协议",//服务器收到了需要协商安全机制的请求，并且响应包含适合请求者选择的安全机制列表[21]：§§2.3.1–2.3.2 或摘要认证挑战。
  500: '内部服务器错误',//由于某些意外情况，服务器无法响应。
  501: '未实施',//服务器没有能力响应请求，例如因为它无法识别请求方法。（与 405 方法不允许相比，服务器识别该方法但不允许或无法支持它。
  502: '网关故障',//该服务器充当网关或代理，并在尝试满足请求时从下游服务器收到无效响应。
  503: '暂停服务',//服务器正在进行维护或暂时过载，因此无法处理请求。“Retry-After”标头字段可以指定客户端何时可以重新尝试其请求。
  504: '服务器超时',//服务器尝试访问另一台服务器来处理请求，但未收到及时响应。
  505: '版本不被支持',//请求中的SIP协议版本不受服务器支持。
  513: '消息过大',//请求消息长度超出服务器可处理的长度。
  555 :"不支持推送通知服务",//服务器不支持“pn-provider”SIP URI 参数中标识的推送通知服务。
  580 :"先决条件失败",//服务器无法或不愿意满足提供的某些限制
  600: '到处忙碌',//所有可能的目的地都忙。与 486 响应不同，此响应表示目的地知道没有其他目的地（例如语音邮件服务器）可以接受呼叫。
  603: '拒绝',//目的地不希望参与呼叫，或者无法参与，此外目的地知道没有其他目的地（例如语音邮件服务器）愿意接受呼叫
  604: '不存在 ',//服务器有权威信息表明请求的用户在任何地方都不存在。
  606: '不可接受',//已成功联系用户代理，但会话描述的某些方面（例如请求的媒体、带宽或寻址样式）不可接受
  607: "不需要的",//被叫方不想接听主叫方的来电。主叫方将来的尝试也可能会被同样拒绝。
  608 :"已拒绝",//中间机器或进程拒绝了呼叫尝试。[25] 这与 607（不需要）SIP 响应代码形成对比，在该代码中，人（被叫方）拒绝了呼叫。拒绝呼叫的中间方应包含一个 Call-Info 标头，其“目的”值为“jwscard”，其中包含 jCard[26] 的联系方式。如果呼叫方想对拒绝提出异议，可以使用此 jCard。
  未初始化软通话: '未初始化软通话',
  软通话网络连接断开: '软通话网络连接断开',
  软通话未注册: '软通话未注册',
  正在通话中: '正在通话中',
  '5秒后重新按键呼叫': '软通话异常，请5秒后重新按键呼叫',
  请您稍等一下: '请您稍等一下',
  请检查声卡: '请检查声卡',
  通话异常: '通话异常',
};

/**
 * setRejectMessage 设置自定义错误码
 */
function setRejectMessage(options) {
  rejectMessage = {...rejectMessage,...options} ;
}
/**
 * 注册软通话
 * @param {*} options  {
 * registerIp：注册服务器地址
 * registerPort：注册端口号
 * registerWsOrWss:注册协议，ws或wss  1为 ws 2为wss 默认1
 * signallingIp：信令服务器地址
 * signallingPort：信令端口号
 * sipLocalNumber:本机号码
 * sipLocalPassword:本机密码
 * sipStateCall:软通话注册状态回调
 * AutoRegisterTimer: 断线后是否自动重新注册
 * isOpenDns:是否开启Dns解析
 * }
 *  @param {*} log  @keytop/ktlog实例
 * @returns
 */
function register(options, log) {
  if (freedOld) {
    return;
  }
  if (log) {
    ktLog = log;
  } else {
    ktLog = {
      i(msg) {
        console.log(msg)
      }
    }
  }

  sipOptions = Object.assign(sipOptions, options);
  // sipUrl, sipPort, sipWSPort, sipLocalNumber, sipLocalPassword, sipStateCall, AutoRegisterTimer = true
  initSip();
}
/**
 * 初始化软通话
 */
async function initSip() {
  if (userAgent) {
    freedOld = true;
    userAgent.terminateSessions();
    userAgent.unregister(true);
    userAgent.stop();
    userAgent = null;
    await sleepTime(3);
    freedOld = false;
  }
  if (registererInteror) {
    clearInterval(registererInteror);
    registererInteror = null;
  }
  if (ipInterval) {
    clearInterval(ipInterval);
    ipInterval = null;
  }
  // 开启日志
  JsSIP.debug.enable('JsSIP:*');
  if (sipOptions.isOpenDns) {
    // 定时解析DNS获取服务器公网ip
    setIpAddress(sipOptions.registerIp);
    if (!ipAddress) {
      ktLog.i('未解析到公网ip，一秒后重新尝试初始化');
      setTimeout(() => {
        initSip();
      }, 1000);
      return;
    }
    ipInterval = setInterval(() => {
      setIpAddress(sipOptions.registerIp);
    }, 1000 * 60 * 5);
  }

  // 重新初始化先尝试挂断软通话防止状态出错
  disconnectSip();
  // WebSocket 服务器连接
  let server = (sipOptions.registerWsOrWss == 2 ? 'wss://' : 'ws://') + sipOptions.registerIp + ':' + sipOptions.registerPort; //5066
  // 注册端口用5066  信令端口用10060，注册uri，sip：本地用户号码@服务器地址
  let uriPath = 'sip:' + sipOptions.sipLocalNumber + '@' + sipOptions.signallingIp + ':' + sipOptions.signallingPort;
  if (sipOptions.isOpenDns) {
    server = (sipOptions.registerWsOrWss == 2 ? 'wss://' : 'ws://') + ipAddress + ':' + sipOptions.registerPort;
    if (sipOptions.registerIp == sipOptions.signallingIp) {
      uriPath = 'sip:' + sipOptions.sipLocalNumber + '@' + ipAddress + ':' + sipOptions.signallingPort;
    }
  }
  let socket = new JsSIP.WebSocketInterface(server);
  let configuration = {
    // 实例集。该参数可以用多种方式表示：
    sockets: [socket],
    // 与用户代理 ( ) 关联的SIP URIString。这是您的提供商提供给您的SIP地址。
    uri: uriPath,
    // SIP验证密码
    password: sipOptions.sipLocalPassword,
    // WebSocket 重新连接尝试之间的最大间隔 ( ) 以秒为单位。默认值为30。
    connection_recovery_max_interval: 5,
    session_timers: false, // 启用会话计时器（根据RFC 4028）
  };
  userAgent = new JsSIP.UA(configuration);
  //连接成功
  userAgent.on('connected', function (e) {
    sipOptions.sipStateCall('connected');
  });
  // 连接断开
  userAgent.on('disconnected', function (e) {
    console.log('软通话网络连接断开', e);
    sipOptions.sipStateCall('unregistered', rejectMessage['软通话网络连接断开']);
    updateConnTime();
  });
  // 注册成功
  userAgent.on('registered', function (e) {
    sipOptions.sipStateCall('registered');
  });
  userAgent.on('unregistered', function (e) {
    console.log('unregistered', e);
    // 注册失败
    sipOptions.sipStateCall('registrationFailed');
    updateConnTime();
  });
  userAgent.on('registrationFailed', function (e) {
    console.log('registrationFailed', e);
    // 注册失败
    sipOptions.sipStateCall('registrationFailed');
    updateConnTime();
  });
  //   新的来电或去电事件
  userAgent.on('newRTCSession', function (e) {
    console.log('newRTCSession', e);
    let session = e.session;
    // remote来电，local去电
    if (e.originator == 'remote') {
      ktLog.i('收到回拨来电');
      if (sipIsCallingState()) {
        //  拒绝电话
        session.terminate();
        ktLog.i('有通话正在进行中,拒绝电话');
        return;
      }

      session.on('progress', () => {
        // 正在接通
        console.log('call is in progress');
        sipOptions.sipStateCall('progress');
      });
      session.on('confirmed', (e) => {
        // 会话已建立。
        console.log('call confirmed', e);
        sipOptions.sipStateCall('confirmed');
      });
      session.on('failed', (e) => {
        // 通话错误
        sipOptions.sipStateCall('failed');
        console.log('call failed with cause: ', e);
      });
      session.on('ended', (e) => {
        ktLog.i('软通话： 结束');
        // 结束
        sipOptions.sipStateCall('ended');
        console.log('call ended with cause: ', e);
      });
      inviterEntity = session;
      sipOptions.sipStateCall('sipAnswer');
      return;
    }
  });
  userAgent.start();
  if (sipOptions.AutoRegisterTimer) {
    registererInterorFunc();
  }
}
/**
 * 拨打软通话
 * @param {*} sipcustom
 * @param {*} callState
 * @returns
 */
function sipCall(sipcustom, callState) {
  if (!userAgent) {
    callState('failed', rejectMessage['未初始化软通话'], true);
    return { code: false, status: 'failed', msg: '未初始化软通话' };
  }
  if (!userAgent.isConnected()) {
    callState('failed', rejectMessage['软通话网络连接断开'], true);
    return { code: false, status: 'failed', msg: '软通话网络连接断开' };
  }
  if (!userAgent.isRegistered()) {
    callState('failed', rejectMessage['软通话未注册'], true);
    return { code: false, status: 'failed', msg: '软通话未注册' };
  }
  let nowTime = new Date().getTime();
  if (sipIsCallingState()) {
    if (nowTime - upCallTime < 1000 * 20) {
      callState('isCalling', rejectMessage['正在通话中']);
      ktLog.i('正在通话中,20秒内重复拨打忽略本次拨打');
      return { code: false, status: 'isCalling' };
    }
    // 挂掉上次的通话
    disconnectSip();
    upCallTime = nowTime;
    callState('busy', rejectMessage['5秒后重新按键呼叫']);
    return { code: false, status: 'busy' };
  }
  if (nowTime - upCallTime < 1000 * 3) {
    callState('wait', rejectMessage['请您稍等一下']);
    return { code: false, status: 'wait' };
  }
  upCallTime = nowTime;
  let eventHandlers = {
    // 正在接通
    progress: function (e) {
      console.log('call is in progress');
      callState('progress');
    },
    // 通话错误
    failed: function (e) {
      console.log('call failed with cause: ', e);
      callState('rejected', `${failedMessage(e)}`);
    },
    // 结束
    ended: function (e) {
      ktLog.i('软通话： 结束');
      callState('ended');
      console.log('call ended with cause: ', e);
    },
    // 会话已建立。
    confirmed: function (e) {
      console.log('call confirmed', e);
      callState('confirmed');
    },
  };
  let options = {
    eventHandlers,
    mediaConstraints: { audio: true, video: false },
  };
  // 邀请会话uri，sip：远程用户号（被呼叫的用户号码）@服务器地址
  let uriPath = 'sip:' + sipcustom + '@' + sipOptions.signallingIp + ':' + sipOptions.signallingPort;
  if (sipOptions.isOpenDns && sipOptions.registerIp == sipOptions.signallingIp) {
    uriPath = `sip:${sipcustom}@${ipAddress}:${sipOptions.signallingPort}`;
  }
  console.log('==uriPath==', uriPath);
  inviterEntity = userAgent.call(uriPath, options);
  return { code: true };
}
/**
 * 回拨接通通话
 * isVideo 是否是视频, 默认false
 * remoteVideo 远程视频音频
 * answerCallback 回调函数，通话状态
 * vol 通话音量大小
 */
function sipAnswerCall(isvideo, remoteVideo) {
  console.log('sipAnswerCall', inviterEntity);
  inviterEntity.answer();
}
// 结束通话
// function terminateCall() {
//   //   if (inviterEntity) {
//   //     // inviterEntity.terminate();
//   //   }
// }
// 主动挂断
function disconnectSip() {
  if (userAgent) {
    userAgent.terminateSessions();
  }
}

/**
 * 将音频流绑定到audio标签上
 * @param {*} mediaElement  audio标签元素
 */
function createAudioStream(mediaElement) {
  const remoteStream = new MediaStream();
  inviterEntity.connection.getReceivers().forEach((receiver) => {
    if (receiver.track) {
      ktLog.i('receiver.track :');
      remoteStream.addTrack(receiver.track);
    }
  });
  mediaElement.srcObject = remoteStream;
  mediaElement.play();
  ktLog.i('软通话已接通音量为：' + mediaElement.volume);
  sipOptions.sipStateCall('accepted');
}
/** 是否正在呼叫 */
function sipIsCallingState() {
  // 是否在通话
  if (inviterEntity) {
    // inviterEntity.isEnded() 通话结束返回true
    return !inviterEntity.isEnded();
  }

  return false;
}

/**
 * 定时检测注册状态
 */
function registererInterorFunc() {
  if (registererInteror) {
    clearInterval(registererInteror);
    registererInteror = null;
    statusErrorNum = 0;
  }

  registererInteror = setInterval(() => {
    mesStatus = '';
    if (!userAgent) {
      mesStatus = rejectMessage['未初始化软通话'];
    } else if (!userAgent.isConnected()) {
      mesStatus = rejectMessage['软通话网络连接断开'];
    } else if (!userAgent.isRegistered()) {
      mesStatus = rejectMessage['软通话未注册'];
    }
    if (mesStatus && mesStatus.length > 0) {
      ktLog.i('检测软通话状态错误:' + mesStatus);
      statusErrorNum++;
      if (statusErrorNum > 5) {
        ktLog.i('检测软通话状态错误并且检测到错误状态次数大于5次重新初始化软通话');
        sipOptions.sipStateCall('registrationFailed', mesStatus);
        initSip();
        return;
      }
      if (new Date().getTime() - statusConUpdateTime > 1000 * 20) {
        ktLog.i('检测软通话状态错误并且连接状态更新时间大于20秒主动重连');
        sipOptions.sipStateCall('registrationFailed', mesStatus);
        userAgent.start();
        return;
      }
    } else {
      statusErrorNum = 0;
      ktLog.i('检测软通话状态正常');
    }
  }, 1000 * 30);
}
// DNS解析
function setIpAddress(address) {
  if (!address) {
    return;
  }
  let tsStr = /[a-zA-Z]/;
  if (tsStr.test(address)) {
    // 包含字母，进行域名解析
    dns.resolve(address, (err, addressIp) => {
      if (err) {
        ktLog.i(`DNS:${address},解析错误`);
      } else {
        ktLog.i(`DNS: ${address},解析成功.IP地址为：${addressIp[0]}`);
        if (ipAddress && ipAddress != addressIp[0]) {
          ktLog.i(`DNS: 解析地址新旧地址不一样重新初始化软通话，新地址${addressIp[0]},旧地址${ipAddress}`);
          ipAddress = addressIp[0];
          initSip();
          return;
        }
        ipAddress = addressIp[0];
      }
    });
  } else {
    // 不包含字母就当成是公网ip直接赋值
    ipAddress = address;
  }
}
function sleepTime(num) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, num * 1000);
  });
}

function failedMessage(e) {
  if (e.message) {
    return e.message.status_code + ',' + rejectMessage[e.message.status_code];
  }
  if (e.cause) {
    if (e.cause == 'User Denied Media Access') {
      return rejectMessage['请检查声卡'];
    }
    return e.cause;
  }
  return rejectMessage['通话异常'];
}
function updateConnTime() {
  statusConUpdateTime = new Date().getTime();
}
module.exports = {
  setRejectMessage,
  register,
  sipCall,
  disconnectSip,
  sipAnswerCall,
  createAudioStream,
  sipIsCallingState,
  // sipSetVumevalue,
};
