<template>
  <!-- 通话弹出层 -->
  <div
    v-if="dialogVisible"
    class="communication-dialog"
    v-move-ele.prevent.stop
  >
    <div class="communication-box" v-show="!isWaiting">
      <div class="communication-host" id="communication-host"></div>
      <div class="communication-guest" id="communication-guest"></div>
      <div class="communication-action" @mousedown.stop>
        <el-button round type="danger" @click.stop="hangup">结束通话</el-button>
      </div>
      <div class="communication-info">
        <span>持续时长：<second-timer ref="SecondTimer"></second-timer></span>
      </div>
    </div>
    <div class="communication-wait" v-show="isWaiting">
      <p class="communication-wait-tips">等待接听…</p>
      <div class="communication-wait-avatar">
        <img :src="data.basePhotoPath" :alt="data.examStudentName" />
      </div>
      <p class="communication-wait-username">
        {{ data.examStudentName }}
      </p>
      <div class="communication-wait-action" @mousedown.stop>
        <el-button round type="danger" @click="hangup">取消通话</el-button>
      </div>
    </div>
  </div>
</template>

<script>
import { getUserMonitorKey } from "@/api/invigilation";
import {
  checkSystemRequirements,
  createClient,
  createStream,
} from "@/plugins/trtc";
import MoveEle from "@/plugins/move-ele";
import SecondTimer from "../../common/SecondTimer";

const domEmpty = (dom) => {
  dom.childNodes.forEach((childNode) => {
    dom.removeChild(childNode);
  });
};

export default {
  name: "InvigilationStudentTalk",
  components: {
    SecondTimer,
  },
  directives: { MoveEle },
  props: {
    recordId: {
      type: String,
    },
    data: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      // detailInfo: {},
      dialogVisible: false,
      isWaiting: false,
      isHandUp: false,
      subscribeSetTs: [],
      userMonitor: {},
      client: null,
      localStream: null,
    };
  },
  watch: {
    dialogVisible(v) {
      v && this.initData();
      this.$emit("talking", { examRecordId: this.recordId, talking: v });
    },
  },
  methods: {
    async initData() {
      // await this.getInvigilateDetail().catch(() => {});
      this.holding = false;
    },
    // async getInvigilateDetail() {
    //   const res = await invigilateDetail(this.recordId);
    //   this.detailInfo = res.data.data;
    // },
    async initClient(recordId) {
      const res = await getUserMonitorKey(recordId);
      this.userMonitor = res.data.data;
      this.client = createClient({
        mode: "live",
        sdkAppId: this.userMonitor.appId * 1,
        userId: this.userMonitor.monitorUserId,
        userSig: this.userMonitor.monitorUserSig,
        useStringRoomId: true,
      });
    },
    async answer(isVideo) {
      const result = await checkSystemRequirements().catch(() => {
        this.$message.error(
          `您的浏览器不支持当前音视频通讯版本。建议使用最新版的chrome浏览器！`
        );
      });
      if (!result) return;

      // 客户端两路视频公用一个userId：
      // main：有音频，有视频
      // auxiliary：无音频，有视频
      // 手机端userId各不同
      if (this.holding) return;
      this.holding = true;
      this.$emit("muted-change");

      await this.initClient(this.recordId).catch(() => {});
      if (!this.client) {
        this.holding = false;
        return;
      }
      this.localStream = await this.getLocalMedia(isVideo);
      if (!this.localStream) {
        this.holding = false;
        return;
      }

      this.dialogVisible = true;
      this.holding = false;
      // 添加远程用户视频发布监听
      this.client.on("stream-added", (event) => {
        console.log(event);
        console.log(event.stream.getUserId(), this.userMonitor.sourceUserId);
        const remoteStream = event.stream;
        if (remoteStream.getUserId() !== this.userMonitor.sourceUserId) return;
        if (remoteStream.getType() !== "main") return;
        console.log(`有效视频${remoteStream.getUserId()}，准备订阅`);

        this.subscribeSetTs.push(
          setTimeout(() => {
            this.client
              .subscribe(remoteStream, { audio: true, video: true })
              .catch((error) => {
                console.log(`${remoteStream.getUserId()}视频订阅失败！`, error);
                this.notifyError("学生视频获取失败！");
              });
          }, 5000)
        );
      });
      this.client.on("stream-subscribed", (event) => {
        const remoteStream = event.stream;
        console.log(event);
        console.log(`${remoteStream.getUserId()}视频已订阅！`);
        this.isWaiting = false;
        this.$nextTick(() => {
          if (!this.$refs.SecondTimer.recoding) this.$refs.SecondTimer.start();
          domEmpty(document.getElementById("communication-host"));
          remoteStream.play("communication-host", { objectFit: "contain" });
        });
      });
      this.client.on("stream-removed", (event) => {
        const remoteStream = event.stream;
        if (
          remoteStream.getUserId() !== this.userMonitor.sourceUserId ||
          remoteStream.getType() !== "main" ||
          this.isHandUp
        )
          return;

        console.log(event);
        console.log(`${remoteStream.getUserId()}已退出房间！`);
        this.notifyError("对方已挂断！");
        this.hangup();
      });

      // 加入房间
      let roomJoinResult = true;
      await this.client
        .join({
          roomId: this.userMonitor.monitorKey,
          role: "audience",
        })
        .catch((error) => {
          roomJoinResult = false;
          console.log("加入房间失败！", error);
          this.notifyError("发起通信失败！");
        });
      if (!roomJoinResult) return;
      console.log("加入房间成功！");

      // 切换角色，连麦互动
      let switchResult = true;
      await this.client.switchRole("anchor").catch((error) => {
        console.log("切换角色失败！", error);
        this.notifyError("角色错误！");
        switchResult = false;
      });
      if (!switchResult) return;

      // 发布本地视频
      let publishStreamResult = true;
      this.client.publish(this.localStream).catch((error) => {
        console.log("发布本地视频失败！", error);
        this.notifyError("本地音视频推送失败！");
        publishStreamResult = false;
      });
      if (!publishStreamResult) return;
      console.log("发布本地音视频成功！");

      // 播放本地视频
      this.localStream.play("communication-guest", { muted: true });
      this.isHandUp = false;
    },
    async getLocalMedia(isVideo) {
      const localStream = createStream({
        userId: this.userMonitor.monitorUserId,
        audio: true,
        video: !!isVideo,
      });
      const errorTips = {
        NotFoundError: "找不到硬件设备，请确保硬件设备正常。",
        NotAllowedError: "不授权摄像头/麦克风访问无法进行音视频通话。",
        NotReadableError:
          "暂时无法访问摄像头/麦克风，请确保当前没有其他应用请求访问摄像头/麦克风，并重试。",
        OverConstrainedError: "设备异常",
        AbortError: "设备异常",
      };

      let initLocalStreamResult = true;
      await localStream.initialize().catch((error) => {
        console.log(errorTips[error.name]);
        this.notifyError(errorTips[error.name] || "未知错误");
        initLocalStreamResult = false;
        localStream.close();
      });
      return initLocalStreamResult && localStream;
    },
    async hangup() {
      if (this.isHandUp) return;
      this.isHandUp = true;
      this.clearSubscribeSetTs();
      this.$refs.SecondTimer.end();

      // 取消发布本地视频
      await this.client.unpublish(this.localStream).catch((error) => {
        console.log("取消发布本地视频失败！", error);
      });

      this.localStream.close();
      this.localStream = null;

      // 离开房间
      let result = true;
      await this.client.leave().catch((error) => {
        console.log("离开房间失败！", error);
        this.notifyError("操作异常，请重新尝试！");
        result = false;
      });
      if (!result) return;

      this.client.off("*");
      this.client = null;
      this.userMonitor = {};

      this.dialogVisible = false;
      this.isWaiting = true;
    },
    notifyError(content) {
      this.$notify({
        type: "error",
        message: content,
      });
    },
    clearSubscribeSetTs() {
      if (!this.subscribeSetTs.length) return;
      this.subscribeSetTs.forEach((sett) => {
        clearTimeout(sett);
      });
      this.subscribeSetTs = [];
    },
    open(isVideo = false) {
      this.dialogVisible = true;
      this.answer(isVideo);
    },
  },
};
</script>
