<template>
  <div class="realtime-monitoring-full">
    <div :style="viewStyle" class="monitor-view">
      <RealtimeMonitoringFullHeader
        class="monitor-view-header"
        @back="onBack"
        @filterChange="onFilterChange"
      />
      <div class="monitor-view-content">
        <div class="monitor-view-content-left">
          <EarlyWarningList @warnClick="toDetail" :list="warningList" />
          <RequestCallList @backCall="onBackCall" :list="communicationList" />
        </div>
        <div class="monitor-view-content-right">
          <div class="card-box monitor-view-content-right-header">
            <SummaryLineFull data-type="trouble" :examId="filter.examId" />
            <el-button
              v-if="IS_ADMIN"
              class="finish-invigilation"
              :type="curExamIsOver ? 'success' : 'danger'"
              icon="icon icon-over"
              :disabled="!filter.examId"
              @click="finishInvigilationExam"
              >{{ curExamIsOver ? "恢复监考" : "结束监考" }}</el-button
            >
          </div>
          <div class="monitor-view-content-video-table">
            <div
              class="invigilation-student-item"
              :class="{ mobile: isMobile }"
              v-for="item in dataList"
              :key="item.examRecordId + item.liveUrl"
            >
              <invigilation-student-full
                ref="InvigilationStudent"
                :data="item"
                :talking="talking"
                :enableVideoSource="!!filter.monitorVideoSource"
                :warning="isWarning(item)"
                :beCalled="isBeCalled(item)"
                :isMobile="isMobile"
                @operation="onOperation"
                @muted-change="videoAllMuted"
                @click.native="toDetail(item)"
                @video-view-change="videoViewChange"
              ></invigilation-student-full>
            </div>
          </div>
          <div class="monitor-view-content-video-pagination">
            <el-pagination
              background
              layout="prev, pager, next"
              :current-page="filter.pageNumber"
              :total="total"
              hide-on-single-page
              :page-size="filter.pageSize"
              @current-change="(pageNumber) => onFilterChange({ pageNumber })"
            >
              <template #prev>
                <span>1</span>
              </template>
            </el-pagination>
          </div>
        </div>
      </div>
    </div>
    <!-- warning-text-message-dialog -->
    <warning-text-message-dialog
      :record-id="operationStudent['message'].examRecordId"
      ref="WarningTextMessageDialog"
    ></warning-text-message-dialog>
    <!-- audio-record-dialog -->
    <audio-record-dialog
      :record-id="operationStudent['voice'].examRecordId"
      ref="AudioRecordDialog"
    ></audio-record-dialog>
    <InvigilationStudentTalk
      ref="talkDialog"
      :record-id="operationStudent['talk'].examRecordId"
      :data="operationStudent['talk']"
      @talking="onTalking"
      @muted-change="videoAllMuted"
    />
  </div>
</template>

<script>
import {
  invigilateVideoList,
  invigilateExamFinish,
  invigilationWarningMessage,
  communicationList,
  communicationCalling,
  communicationOver,
} from "@/api/invigilation";

import { mapState, mapMutations, mapActions } from "vuex";
import RealtimeMonitoringFullHeader from "./components/RealtimeMonitoringFullHeader";
import EarlyWarningList from "./components/EarlyWarningList";
import RequestCallList from "./components/RequestCallList";
import SummaryLineFull from "./components/SummaryLineFull";
import InvigilationStudentFull from "./components/InvigilationStudentFull";
import InvigilationStudentTalk from "./components/InvigilationStudentTalk";
import WarningTextMessageDialog from "./WarningTextMessageDialog";
import AudioRecordDialog from "./audioRecord/AudioRecordDialog";
import { debounce } from "lodash-es";
import IntervalMixin from "./mixins/IntervalMixin";
import axios from "axios";

const DESIGN_WIDTH = 1920;
const DESIGN_HEIGHT = 1080;

export default {
  name: "RealtimeMonitoringFull",
  mixins: [IntervalMixin],
  components: {
    RealtimeMonitoringFullHeader,
    EarlyWarningList,
    RequestCallList,
    SummaryLineFull,
    InvigilationStudentFull,
    WarningTextMessageDialog,
    AudioRecordDialog,
    InvigilationStudentTalk,
  },

  data() {
    return {
      filter: {
        examId: "",
        roomCode: "",
        monitorVideoSource: "",
        pageSize: 24,
        pageNumber: 1,
      },
      curExamMonitorStatus: "",
      /** 监考视频列表 */
      dataList: [],
      /** 监考视频总数 */
      total: 0,
      /** 当前操作的学生 */
      operationStudent: {
        message: {},
        voice: {},
        talk: {},
        "back-call": {},
      },
      /** 通话中 */
      talking: {
        examRecordId: "",
        isVideo: false,
      },
      /** 预警列表 */
      warningList: [],
      /** 通话列表 */
      communicationList: [],
      /** 屏幕适配缩放 */
      viewStyle: {
        "transform-origin": "0 0",
        transform: "scale(1) translate(-50%)",
      },
      videoIsLargeView: false,
      IS_ADMIN: this.$store.state.user.roleCodes.includes("ADMIN"),
    };
  },
  mounted() {
    this.observerResize();
  },
  computed: {
    ...mapState("invigilation", ["liveDomains"]),
    isFullScreen() {
      return this.$store.state.isFullScreen;
    },
    isMobile() {
      return this.filter.monitorVideoSource.indexOf("MOBILE") === 0;
    },
    curExamIsOver() {
      return this.curExamMonitorStatus === "END";
    },
  },
  watch: {
    isFullScreen: {
      immediate: true,
      handler(val, oldVal) {
        if (val !== oldVal && !val) {
          this.$router.replace({ name: "RealtimeMonitoring" });
        }
      },
    },
    filter: {
      handler() {
        this.refreshList();
      },
      deep: true,
    },
  },
  methods: {
    ...mapActions("invigilation", ["updateDetailIds"]),
    ...mapMutations("invigilation", ["setDetailIds"]),
    /** esc 返回 */
    onBack() {
      document.exitFullscreen?.();
    },

    /** filter change */
    onFilterChange(filter) {
      this.filter = Object.assign(this.filter, filter);
      this.curExamMonitorStatus = filter.monitorStatus;
      this.getList();
    },

    /** 查询视频列表 */
    async getList() {
      if (this.videoIsLargeView) return;

      if (this.videoListSource) {
        this.videoListSource.cancel("cancel prev");
      }
      this.videoListSource = axios.CancelToken.source();
      const res = await invigilateVideoList(this.filter, {
        cancelToken: this.videoListSource.token,
      });

      const domainLen = this.liveDomains.length;
      this.dataList = res.data.data.records.map((item, index) => {
        const domain = domainLen ? this.liveDomains[index % domainLen] : "";
        item.label = `${item.identity} ${item.courseName}(${item.courseCode}) ${item.name}`;
        item.liveUrl = item.monitorLiveUrl
          ? `${domain}/live/${item.monitorLiveUrl.toLowerCase()}.flv`
          : "";
        item.progress = item.progress ? Math.round(item.progress * 100) : 0;
        return item;
      });
      this.total = res.data.data.total;
      if (this.total > this.filter.pageSize) {
        this.updateDetailIds({
          filterData: this.filter,
          fetchFunc: invigilateVideoList,
        });
      } else {
        const ids = this.dataList.map((item) => item.examRecordId);
        this.setDetailIds([...new Set(ids)]);
      }
    },

    /** 结束监考 */
    async finishInvigilationExam() {
      const actionName = this.curExamIsOver ? "恢复监考" : "结束监考";
      const result = await this.$confirm(`确定要${actionName}吗?`, "确认提醒", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        iconClass: "el-icon-warning",
        customClass: "el-message-box__error",
      }).catch(() => {});

      if (!result) return;

      await invigilateExamFinish(this.filter.examId);
      this.onBack();
    },

    onOperation({ type, data }) {
      const newData = { ...data, examStudentName: data.name };
      if (["video"].includes(type)) {
        this.operationStudent["talk"] = newData;
      } else if (["answer"].includes(type)) {
        const backSource = this.communicationList.find(
          (talk) => talk.examRecordId == data.examRecordId
        );
        return backSource && this.onBackCall(backSource);
      } else {
        this.operationStudent[type] = newData;
      }
      const operationMap = {
        message: this.toSendTextMsg,
        voice: this.toSendAudioMsg,
        talk: () => this.toTalk(),
        video: () => this.toTalk(true),
      };
      this.$nextTick(() => {
        operationMap[type]();
      });
    },

    /** 全部静音 */
    videoAllMuted() {
      this.$refs.InvigilationStudent.forEach((refInst) => {
        refInst.mutedPlayer(true);
      });
    },
    videoViewChange(isLarge) {
      this.videoIsLargeView = isLarge;
    },

    /** 跳转详情 */
    toDetail(item) {
      this.$router
        .push({
          name: "WarningDetail",
          params: { examRecordId: item.examRecordId },
        })
        .then(() => {
          document.exitFullscreen?.();
        });
    },

    /** 发送文字消息 */
    toSendTextMsg() {
      this.$refs.WarningTextMessageDialog.open();
    },

    /** 发送语音消息 */
    toSendAudioMsg() {
      this.$refs.AudioRecordDialog.open();
    },

    /** 发起视频/语音通话 */
    toTalk(isVideo = false) {
      this.talking.isVideo = isVideo;
      this.$refs.talkDialog.open(isVideo);
    },

    /** 回拨 */
    onBackCall(data) {
      if (this.talking.examRecordId) {
        return;
      }
      this.operationStudent["back-call"] = data;
      this.operationStudent["talk"] = data;
      this.toTalk();
    },

    /** 通话中事件... */
    async onTalking({ examRecordId, talking }) {
      if (this.operationStudent["back-call"]?.examRecordId) {
        /** 回拨类型 */
        try {
          if (talking) {
            await communicationCalling({
              recordId: this.operationStudent["back-call"].examRecordId,
              source: this.operationStudent["back-call"].source,
            });
          } else {
            await communicationOver({
              recordId: this.operationStudent["back-call"].examRecordId,
              source: this.operationStudent["back-call"].source,
            });
            this.operationStudent["back-call"] = {};
          }
        } catch (error) {
          console.error(error);
        }
        this.refreshList();
      }
      Object.assign(this.talking, {
        examRecordId: talking ? examRecordId : "",
        isVideo: talking ? this.talking.isVideo : false,
      });
    },

    /** 是否预警状态 */
    isWarning(item) {
      return !!this.warningList.find(
        (warn) => warn.examRecordId === item.examRecordId
      );
    },
    /** 是否申请通话中 */
    isBeCalled(item) {
      return !!this.communicationList.find(
        (talk) =>
          talk.callStatus !== "CANCEL" &&
          talk.examRecordId === item.examRecordId
      );
    },
    /** 获取通话代办列表 */
    async getCommunicationList() {
      if (!this.filter.examId) return;
      const res = await communicationList({
        examId: this.filter.examId,
        roomCode: this.filter.roomCode,
        callStatus: "START,CANCEL",
        pageNumber: 1,
        pageSize: 100,
      });
      this.communicationList = res.data.data.records;
    },
    /** 获取预警列表 */
    async fetchWarningNotice() {
      if (!this.filter.examId) return;
      const res = await invigilationWarningMessage(this.filter.examId);
      let warningListMap = {};
      res.data.data.forEach((item, index) => {
        item.index = index;
        if (!warningListMap[item.examRecordId])
          warningListMap[item.examRecordId] = item;
      });
      this.warningList = Object.values(warningListMap).sort(
        (a, b) => a.index - b.index
      );
    },
    /** 定时刷新预警/通话待办 */
    refreshList() {
      if (this.stopInterval) {
        this.stopInterval();
      }
      Promise.all([
        this.getList(),
        this.getCommunicationList(),
        this.fetchWarningNotice(),
      ]);
      this.stopInterval = this.interval(() => {
        Promise.all([
          this.getList(),
          this.getCommunicationList(),
          this.fetchWarningNotice(),
        ]);
      }, 10 * 1000);
    },

    observerResize() {
      const resizeHandle = debounce(() => {
        const scale = this.getScale();
        this.viewStyle.transform = `scale(${scale}) translate(-50%)`;
      }, 60);
      resizeHandle();
      window.addEventListener("resize", resizeHandle);
      this.$once("hook:beforeDestroy", () => {
        window.removeEventListener("resize", resizeHandle);
      });
    },
    getScale() {
      const wScale = window.innerWidth / DESIGN_WIDTH;
      const hScale = window.innerHeight / DESIGN_HEIGHT;
      return wScale < hScale ? wScale : hScale;
    },
  },
  beforeDestroy() {
    try {
      this.$refs.talkDialog.hangup();
    } catch (error) {
      console.log(error);
    }
  },
};
</script>

<style lang="scss" scoped>
.realtime-monitoring-full {
  position: fixed;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  background-color: #1a1d1f;
  z-index: 2;
  .card-box {
    width: 280px;
    background: #2e3238;
    border-radius: 10px;
    & ~ .card-box {
      margin-top: 10px;
    }
  }
  .ellipsis {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  .monitor-view {
    width: 1920px;
    height: 1080px;
    position: absolute;
    left: 50%;
    top: 0;
    transform-origin: 0 0;
    transform: translate(-50%);
    .monitor-view-header {
      height: 64px;
      background: #2e3238;
    }
    .monitor-view-content {
      padding: 20px;
      display: flex;
      .monitor-view-content-left {
        width: 280px;
        margin-right: 20px;
      }
      .monitor-view-content-right {
        flex: 1;
        .monitor-view-content-right-header {
          width: 100%;
          padding: 10px;
          display: flex;
          align-items: center;
          justify-content: space-between;
          .finish-invigilation {
            width: 136px;
            height: 48px;
          }
        }
        .monitor-view-content-video-table {
          display: flex;
          flex-wrap: wrap;
          margin-top: 15px;
          .invigilation-student-item {
            margin-right: 10px;
            &:nth-child(6n) {
              margin-right: 0;
            }
            &:nth-child(n + 7) {
              margin-top: 10px;
            }
            &.mobile {
              &:nth-child(6n) {
                margin-right: 10px;
              }
              &:nth-child(n + 7) {
                margin-top: 0;
              }
              &:nth-child(8n) {
                margin-right: 0;
              }
              &:nth-child(n + 9) {
                margin-top: 10px;
              }
            }
          }
        }
        .monitor-view-content-video-pagination {
          margin-top: 15px;
          ::v-deep {
            .el-pagination {
              text-align: right;
              .btn-prev,
              .btn-next,
              .number,
              .more {
                width: 32px;
                height: 32px;
                border: none;
                background-color: #2e3238;
                border-radius: 10px;
              }
              .btn-prev,
              .btn-next {
                &[disabled="disabled"] {
                  background-color: #6a6e74;
                }
                &:not([disabled="disabled"]):hover {
                  background-color: #1886fe;
                  color: #fff;
                }
              }
              .number.active {
                background-color: #1886fe;
              }
            }
          }
        }
      }
    }
  }
}
</style>
