import EventEmitter from 'eventemitter3';
import OutgoingCall from './call/outgoing-call';
import IncomingCall from './call/incoming-call';
import Offer from './signalings/offer';

const DEFAULT_RTCCONF = {
  iceServers: [
    {
      urls: [
        'stun:stun.l.google.com:19302',
        'stun:stun1.l.google.com:19302',
        'stun:stun2.l.google.com:19302',
        'stun:stun3.l.google.com:19302',
        'stun:stun4.l.google.com:19302',
        'stun:stun.ekiga.net',
        'stun:stun.ideasip.com',
        'stun:stun.rixtelecom.se',
        'stun:stun.schlund.de',
        'stun:stun.stunprotocol.org:3478',
        'stun:stun.voiparound.com',
        'stun:stun.voipbuster.com',
        'stun:stun.voipstunt.com',
        'stun:stun.voxgratia.org',
      ],
    },
  ],
};

export default class WebRTCClient extends EventEmitter {
  /**
   * 无法直接实例化,请使用 {@link createWebRTCClient Realtime#createWebRTCClient} 创建新的 WebRTCClient
   */
  constructor(id, options) {
    if (typeof id !== 'string') {
      throw new TypeError('id is not a string');
    }
    super();
    /** @type {string} */
    this.id = id;
    this.options = Object.assign({
      RTCConfiguration: DEFAULT_RTCCONF,
    }, options);
  }

  _open(realtime, clientOptions) {
    return realtime.createIMClient(this.id, clientOptions, 'webrtc').then((imClient) => {
      this._imClient = imClient;
      this.id = imClient.id;
      imClient.on('message', (message, conversation) => {
        if (message instanceof Offer) {
          return this._handleOffer(message, conversation);
        }
        return false;
      });
      /**
       * 用户在其他客户端登录,当前客户端被服务端强行下线。详见文档「单点登录」章节。
       * @event WebRTCClient#conflict
       */
      imClient.on('conflict', (...payload) => this.emit('conflict', ...payload));
      return this;
    });
  }

  /**
   * 关闭客户端
   * @return {Promise}
   */
  close() {
    return this._imClient.close();
  }

  /**
   * 呼叫另一个用户
   * @param  {string} targetId 用户 ID
   * @param  {MediaStream} stream 本地流媒体,参见 {@link https://developer.mozilla.org/en-US/docs/Web/API/Media_Streams_API MediaStream}
   * @return {Promise.<OutgoingCall>} 呼出通话
   */
  call(targetId, stream) {
    if (typeof targetId !== 'string') {
      throw new TypeError('target id is not a string');
    }
    if (!stream) {
      throw new TypeError('a MediaStream instance is required to make a call');
    }
    return this._imClient.ping([targetId])
      .then((onlineClients) => {
        if (!onlineClients.length) {
          throw new Error(`Call failed as ${targetId} is not online`);
        }
        const outgoingCall = new OutgoingCall(targetId, null, this.options.RTCConfiguration);
        const promise = new Promise((resolve) => {
          outgoingCall._peerConnection.onnegotiationneeded = resolve;
        });
        outgoingCall._peerConnection.addStream(stream);
        return promise
          .then(() =>
            Promise.all([
              this._imClient.createConversation({
                members: [targetId],
                unique: true,
              }),
              outgoingCall._peerConnection.createOffer().then((localDescription) => {
                outgoingCall._peerConnection.setLocalDescription(localDescription);
              }),
            ]))
          .then(([conversation]) => {
            outgoingCall._setConversation(conversation);
            return conversation.send(new Offer(outgoingCall._peerConnection.localDescription));
          })
          .then(() => outgoingCall);
      });
  }

  _handleOffer(offer, conversation) {
    const incomingCall = new IncomingCall(offer, conversation, this.options.RTCConfiguration);
    /**
     * 收到其他用户的呼叫
     * @event WebRTCClient#call
     * @param {incomingCall} incomingCall 呼入通话
     */
    this.emit('call', incomingCall);
  }
}