/*
 * @Description: 音视频通话集成
 */
import TRTC from "trtc-js-sdk";
import { isUndefined } from "@/utils";

export default {
  data() {
    return {
      client: null,
      localStream: null,
      remoteStreamList: [],
      isJoining: false,
      isJoined: false,
      isPublishing: false,
      isPublished: false,
      isMutedVideo: false,
      isMutedAudio: false,
      isPlayingLocalStream: false,
    };
  },

  methods: {
    // 初始化客户端
    async initClient() {
      this.client = TRTC.createClient({
        mode: "rtc",
        sdkAppId: Number(process.env.VUE_APP_SDK_TRTC_ID),
        userId: this.userID?.toString(),
        userSig: this.userSig,
      });
      // https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/TRTC.Logger.html
      TRTC.Logger.setLogLevel(process.env.NODE_ENV === "production" ? TRTC.Logger.LogLevel.NONE : TRTC.Logger.LogLevel.DEBUG);
      // this.addSuccessLog(`Client [${this.userID}] created.`);
      this.addSuccessLog(`客户端已创建`);
      this.handleClientEvents();
    },

    async initLocalStream({ audio = true, video = true }) {
      this.localStream = TRTC.createStream({
        audio,
        video,
        userId: this.userId,
        cameraId: this.cameraId,
        microphoneId: this.microphoneId,
      });
      try {
        await this.localStream.initialize();
        // this.addSuccessLog(`LocalStream [${this.userId}] initialized.`);
        this.addSuccessLog(`本地音视频流已初始化`);
      } catch (error) {
        this.localStream = null;
        switch (error.name) {
          case "NotFoundError":
            this.addFailedLog(`未找到摄像头或麦克风，请检查您的摄像头或麦克风`);
            break;
          case "NotReadableError":
            // 提示用户：暂时无法访问摄像头/麦克风，请确保当前没有其他应用请求访问摄像头/麦克风，并重试。
            this.addFailedLog(`暂时无法访问摄像头/麦克风，请确保当前没有其他应用请求访问摄像头/麦克风，并重试`);

            break;
          case "RtcError":
            // DEVICE_NOT_FOUND
            console.log("error", error);
            if (error.getCode() === 4099) {
              // 当前设备没有麦克风或没有摄像头，但尝试采集麦克风、摄像头。
              // 处理建议：引导用户检查设备的摄像头及麦克风是否正常，业务侧应在进房前的进行设备检测。
              // 未检测到摄像头，请检查您的摄像头
              this.addFailedLog(`未检测到摄像头或麦克风，请检查您的摄像头或麦克风`);
            }
            break;
          case "NotAllowedError":
            this.addFailedLog(
              `检查是否拒绝了当前的浏览器实例的访问音频、视频、屏幕分享请求。不授权摄像头/麦克风访问将无法进行音视频通话。`
            );
            break;
          default:
            console.error(error);
            break;
        }

        throw error;
      }
    },

    playLocalStream() {
      this.localStream
        .play("localStream", {
          objectFit: "fill",
        })
        .then(() => {
          this.isPlayingLocalStream = true;
          // this.addSuccessLog(`LocalStream [${this.userId}] playing.`);
          this.addSuccessLog(`正在播放本地音视频流`);
        })
        .catch((error) => {
          this.addFailedLog(`LocalStream [${this.userId}] failed to play. Error: ${error.message}`);
        });
    },

    destroyLocalStream() {
      this.localStream && this.localStream.stop();
      this.localStream && this.localStream.close();
      this.localStream = null;
      this.isPlayingLocalStream = false;
    },

    playRemoteStream(remoteStream, element) {
      if (remoteStream.getType() === "main" && remoteStream.getUserId().indexOf("share") >= 0) {
        remoteStream.play(element, { objectFit: "fill" }).catch();
      } else {
        remoteStream.play(element, { objectFit: "fill" }).catch();
      }
    },

    resumeStream(stream) {
      stream.resume();
    },

    async join() {
      if (this.isJoining || this.isJoined) {
        return;
      }
      this.isJoining = true;
      !this.client && (await this.initClient());
      try {
        await this.client.join({ roomId: this.roomId });
        this.isJoining = false;
        this.isJoined = true;

        // this.addSuccessLog(`Join room [${this.roomId}] success.`);
        this.addSuccessLog(`成功加入房间 [${this.roomId}] `);
        // this.reportSuccessEvent("joinRoom");

        this.startGetAudioLevel();
      } catch (error) {
        this.isJoining = false;
        console.error("join room failed", error);
        this.addFailedLog(`Join room ${this.roomId} failed, please check your params. Error: ${error.message}`);
        // this.reportFailedEvent("joinRoom", error);
        throw error;
      }
    },

    async publish() {
      if (!this.isJoined || this.isPublishing || this.isPublished) {
        return;
      }
      this.isPublishing = true;
      try {
        await this.client.publish(this.localStream);
        this.isPublishing = false;
        this.isPublished = true;

        // this.addSuccessLog("LocalStream is published successfully.");
        this.addSuccessLog("本地音视频流发布成功");
        // this.reportSuccessEvent("publish");
      } catch (error) {
        this.isPublishing = false;
        console.error("publish localStream failed", error);
        this.addFailedLog(`LocalStream is failed to publish. Error: ${error.message}`);
        // this.reportFailedEvent("publish");
        throw error;
      }
    },

    async unPublish() {
      if (!this.isPublished || this.isUnPublishing) {
        return;
      }
      this.isUnPublishing = true;
      try {
        await this.client.unpublish(this.localStream);
        this.isUnPublishing = false;
        this.isPublished = false;

        // this.addSuccessLog("localStream unpublish successfully.");
        this.addSuccessLog("本地音视频流取消发布成功");
        // this.reportSuccessEvent("unpublish");
      } catch (error) {
        this.isUnPublishing = false;
        console.error("unpublish localStream failed", error);
        this.addFailedLog(`LocalStream is failed to unpublish. Error: ${error.message}`);
        // this.reportFailedEvent("unpublish", error);
        throw error;
      }
    },

    async subscribe(remoteStream, config = { audio: true, video: true }) {
      try {
        await this.client.subscribe(remoteStream, {
          audio: isUndefined(config.audio) ? true : config.audio,
          video: isUndefined(config.video) ? true : config.video,
        });
        // this.userLog(remoteStream.getUserId(), "订阅成功");
      } catch (error) {
        console.error(`subscribe ${remoteStream.getUserId()} with audio: ${config.audio} video: ${config.video} error`, error);
        // this.userLog(remoteStream.getUserId(), "订阅失败", true);
      }
    },

    async unSubscribe(remoteStream) {
      try {
        await this.client.unsubscribe(remoteStream);
        // this.userLog(remoteStream.getUserId(), "取消订阅成功");
      } catch (error) {
        console.error(`unsubscribe ${remoteStream.getUserId()} error`, error);
        // this.userLog(remoteStream.getUserId(), "取消订阅失败", true);
      }
    },

    async leave() {
      if (!this.isJoined || this.isLeaving) {
        return;
      }
      this.isLeaving = true;
      this.stopGetAudioLevel();
      this.isPublished && (await this.unPublish());
      this.localStream && this.destroyLocalStream();

      try {
        await this.client.leave();
        this.isLeaving = false;
        this.isJoined = false;

        // this.addSuccessLog("Leave room success.");
        this.addSuccessLog("呼叫已结束");
        // this.reportSuccessEvent("leaveRoom");
      } catch (error) {
        this.isLeaving = false;
        console.error("leave room error", error);
        this.addFailedLog(`Leave room failed. Error: ${error.message}`);
        // this.reportFailedEvent("leaveRoom", error);
        throw error;
      }
    },

    muteVideo() {
      if (this.localStream) {
        this.localStream.muteVideo();
        this.isMutedVideo = true;
        // this.addSuccessLog("LocalStream muted video.");
        this.addSuccessLog("已禁用视频轨道");
      }
    },

    muteAudio() {
      if (this.localStream) {
        this.localStream.muteAudio();
        this.isMutedAudio = true;
        // this.addSuccessLog("LocalStream muted audio.");
        this.addSuccessLog("已禁用音频轨道");
      }
    },

    unmuteVideo() {
      if (this.localStream) {
        this.localStream.unmuteVideo();
        this.isMutedVideo = false;
        // this.addSuccessLog("LocalStream unmuted video.");
        this.addSuccessLog("已启用视频轨道");
      }
    },

    unmuteAudio() {
      if (this.localStream) {
        this.localStream.unmuteAudio();
        this.isMutedAudio = false;
        // this.addSuccessLog("LocalStream unmuted audio.");
        this.addSuccessLog("已启用音频轨道");
      }
    },

    switchDevice(type, deviceId) {
      try {
        if (this.localStream) {
          this.localStream.switchDevice(type, deviceId);
          this.addSuccessLog(`Switch ${type} device success.`);
        }
      } catch (error) {
        console.error("switchDevice failed", error);
        this.addFailedLog(`Switch ${type} device failed.`);
      }
    },

    startGetAudioLevel() {
      // 文档：https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/module-ClientEvent.html#.AUDIO_VOLUME
      this.client.on("audio-volume", (event) => {
        event.result.forEach(({ userId, audioVolume }) => {
          if (audioVolume > 2) {
            console.log(`user: ${userId} is speaking, audioVolume: ${audioVolume}`);
            this.memberSpeaking(userId);
          }
        });
      });
      this.client.enableAudioVolumeEvaluation(200);
    },

    stopGetAudioLevel() {
      this.client && this.client.enableAudioVolumeEvaluation(-1);
    },

    handleClientEvents() {
      this.client.on("error", (error) => {
        console.error(error);
        alert(error);
      });
      this.client.on("client-banned", async (event) => {
        console.warn(`client has been banned for ${event.reason}`);

        this.isPublished = false;
        this.localStream = null;
        await this.leave();
      });
      // fired when a remote peer is joining the room
      this.client.on("peer-join", (event) => {
        // 结束拨号计时
        this.endDialingTimeCounting();
        const { userId } = event;
        console.log(`peer-join ${userId}`, event);
        // this.handlePeerJoin(userId);
        const list = [];
        this.memberList.forEach((member) => {
          if (member.tencentUserId === userId) {
            member.state = "CONNECTED";
          }
          list.push(member);
        });
        this.$emit("update:member-list", list);
        // this.addSuccessLog(`${userId}已进入房间`);
        this.userLog(userId, "已进入房间");
      });
      // fired when a remote peer is leaving the room
      this.client.on("peer-leave", (event) => {
        const { userId } = event;
        console.log(`peer-leave ${userId}`, event);
        // this.handlePeerLeave(userId);
        const list = [];
        this.memberList.forEach((member) => {
          if (member.tencentUserId === userId) {
            member.state = "DISCONNECTED";
            member.hangup = true;
          }
          list.push(member);
        });
        this.$emit("update:member-list", list);
        // this.addSuccessLog(`${userId}已离开房间`);
        this.userLog(userId, "已离开房间");
      });

      // fired when a remote stream is added
      this.client.on("stream-added", (event) => {
        const { stream: remoteStream } = event;
        const remoteUserId = remoteStream.getUserId();
        if (remoteUserId === `share_${this.userId}`) {
          // don't need screen shared by us
          this.unSubscribe(remoteStream);
        } else {
          console.log(`remote stream added: [${remoteUserId}] type: ${remoteStream.getType()}`);
          // subscribe to this remote stream
          remoteStream.on("connection-state-changed", (event) => {
            /**
             * Stream 连接状态变更事件
             *
             * 'DISCONNECTED'：连接断开
             * 'CONNECTING'：正在连接中
             * 'CONNECTED'：已连接
             * 'RECONNECTING'：自动重连中
             * 不同状态变更的含义：
             *
             * DISCONNECTED -> CONNECTING: 正在尝试建立连接，调用推流或者订阅接口但尚未成功时触发。
             * CONNECTING -> CONNECTED: 连接建立成功，推流成功或订阅成功触发。
             * CONNECTED -> DISCONNECTED: 连接中断，当网络异常导致连接断开时触发。
             * DISCONNECTED -> RECONNECTING: 正在重连，当连接异常断开时，SDK 尝试自动重连时触发。
             * RECONNECTING -> CONNECTED: SDK 自动重连成功。
             * RECONNECTING -> DISCONNECTED: SDK 自动重连失败。
             */
            console.log(`[${remoteUserId}] connection-state-changed prevState: ${event.prevState}, state: ${event.state}`);
            // const list = []
            // this.memberList.forEach(member => {
            //   if (member.tencentUserId === remoteUserId) {
            //     member.state = event.state;
            //   }
            //   list.push(member);
            // })
            // this.$emit("update:member-list",list)
          });
          this.subscribe(remoteStream);
          // this.addSuccessLog(`RemoteStream added: [${remoteUserId}].`);
          // this.addSuccessLog(`[${remoteUserId}]已加入`);
          this.userLog(remoteUserId, "已加入");
        }
      });
      // fired when a remote stream has been subscribed
      this.client.on("stream-subscribed", (event) => {
        const { stream: remoteStream } = event;
        const remoteUserId = remoteStream.getUserId();
        console.log("stream-subscribed userId: ", remoteUserId);
        // this.addSuccessLog(`RemoteStream subscribed: [${remoteUserId}].`);
        // this.addSuccessLog(`[${remoteUserId}]已加入`);
        this.userLog(remoteUserId, "已加入");
        this.remoteStreamList.push(remoteStream);
        this.$nextTick(() => {
          this.playRemoteStream(remoteStream, remoteUserId);
        });
      });
      // fired when the remote stream is removed, e.g. the remote user called Client.unpublish()
      this.client.on("stream-removed", (event) => {
        const { stream: remoteStream } = event;
        remoteStream.stop();
        // 解除所有事件绑定
        remoteStream.off("*");
        const index = this.remoteStreamList.indexOf(remoteStream);
        if (index >= 0) {
          this.remoteStreamList.splice(index, 1);
        }
        console.log(`stream-removed userId: ${remoteStream.getUserId()} type: ${remoteStream.getType()}`);
      });

      this.client.on("stream-updated", (event) => {
        const { stream: remoteStream } = event;
        console.log(
          `type: ${remoteStream.getType()} stream-updated hasAudio: ${remoteStream.hasAudio()} hasVideo: ${remoteStream.hasVideo()}`
        );
        this.addSuccessLog(
          `RemoteStream updated: [${remoteStream.getUserId()}] audio:${remoteStream.hasAudio()}, video:${remoteStream.hasVideo()}.`
        );
      });

      this.client.on("mute-audio", (event) => {
        const { userId } = event;
        console.log(`${userId} mute audio`);
        this.userLog(event.userId, "麦克风已关闭"); // [xxx]麦克风/摄像头已关闭 09:45
      });
      this.client.on("unmute-audio", (event) => {
        const { userId } = event;
        console.log(`${userId} unmute audio`);
        this.userLog(event.userId, "麦克风已开启");
      });
      this.client.on("mute-video", (event) => {
        const { userId } = event;
        console.log(`${userId} mute video`);
        this.userLog(event.userId, "摄像头已关闭");
      });
      this.client.on("unmute-video", (event) => {
        const { userId } = event;
        console.log(`${userId} unmute video`);
        this.userLog(event.userId, "摄像头已开启");
      });

      this.client.on("connection-state-changed", (event) => {
        console.log(`RtcClient state changed to ${event.state} from ${event.prevState}`);
      });

      this.client.on("network-quality", (event) => {
        const { uplinkNetworkQuality, downlinkNetworkQuality } = event;
        console.log(
          `network-quality uplinkNetworkQuality: ${uplinkNetworkQuality}, downlinkNetworkQuality: ${downlinkNetworkQuality}`
        );
      });
    },
  },
};
