<script setup lang="ts">
import moment from "moment";
import VueQrcode from "@chenfengyuan/vue-qrcode";
import { onMounted, onUnmounted, watch } from "vue";
import { useTimers } from "@/setups/useTimers";
import { Close, Checkmark } from "@vicons/ionicons5";
import { closeMediaStream, getMediaStream } from "@/utils/camera";
import { useWXSocket } from "./Examing/setups/useWXSocket";
import { store } from "@/store/store";
import { httpApp } from "@/plugins/axiosApp";

const emit = defineEmits<{ (e: "on-close"): void }>();

const { addTimeout, addInterval } = useTimers();

const show = $ref(true);

let current = $ref(1);
// @ts-expect-error chrome支持，但还有浏览器不支持，所以没进类型定义
let downlink = navigator.connection.downlink;
// @ts-expect-error
let rtt = navigator.connection.rtt;
const network = $ref({
  downlink,
  downlinkStatus: downlink > 0.5,
  rrt: rtt,
  rrtStatus: rtt < 1000,
});

const time = $ref({
  currentTimeZone: moment().format("Z"),
  timeZoneStatus: new Date().getTimezoneOffset() / 60 === -8,
  clockRateDiff: null as unknown as number | null,
  clockRateStateResolved: false,
  clockRateStatus: false,
});

const camera = $ref({
  openCameraResolved: false,
  openCameraStatus: false,
  identityStatus: false,
  identityResolved: false,
});

const sound = $ref({
  downloadResolved: false,
  downloadStatus: false,
  playedStatusResolved: false,
  playedStatus: false,
});
const wechat = $ref({
  qrValue: " ",
  qrScannedResolved: false,
  qrScanned: false,
  uploadResolved: false,
  uploadStatus: false,
  studentAnswer: null as unknown as string | null,
  examRecordDataId: null as unknown as string | null,
});

const step1Status = $computed(() => {
  return network.downlinkStatus && network.rrtStatus;
});
const step2StatusResolved = $computed(() => {
  return time.clockRateStateResolved;
});
const step2Status = $computed(() => {
  return time.timeZoneStatus && time.clockRateStatus;
});
const step3StatusResolved = $computed(() => {
  return camera.identityResolved && camera.openCameraResolved;
});
const step3Status = $computed(() => {
  return camera.identityStatus && camera.openCameraStatus;
});
const step4StatusResolved = $computed(() => {
  return sound.downloadResolved && sound.playedStatusResolved;
});
const step4Status = $computed(() => {
  return sound.downloadStatus && sound.playedStatus;
});
const step5StatusResolved = $computed(() => {
  return wechat.qrScannedResolved && wechat.uploadResolved;
});
const step5Status = $computed(() => {
  return wechat.qrScanned && wechat.uploadStatus;
});

//#region 时钟处理
let nowDate: number = $ref(0);
addInterval(() => (nowDate = Date.now()), 1000);
console.log(nowDate);
const CLOCK_RATE_TIMEOUT = 10;
let start: moment.Moment, end: moment.Moment;
void fetch("/oe-web/", { method: "HEAD" }).then((e) => {
  start = moment(e.headers.get("date"));
});
addTimeout(() => {
  void fetch("/oe-web/", { method: "HEAD" }).then((e) => {
    // 可能已经离开这个页面了
    if (isUnmounted) return;
    end = moment(e.headers.get("date"));
    time.clockRateStateResolved = true;
    time.clockRateDiff = end.diff(start, "seconds") - CLOCK_RATE_TIMEOUT;
    time.clockRateStatus = end.diff(start, "seconds") < CLOCK_RATE_TIMEOUT + 2;
  });
}, CLOCK_RATE_TIMEOUT * 1000);

let isUnmounted = false;
onUnmounted(() => (isUnmounted = true));
//#endregion 时钟处理

//#region websocket
// 初始化wxSocket的前提
store.exam.WEIXIN_ANSWER_ENABLED = true;
useWXSocket();
onMounted(async () => {
  const examRecordDataId = store.user.id;
  const response = await httpApp.post<string>(
    "/api/ecs_oe_student/examControl/getQrCode",
    {
      examRecordDataId,
      order: 1,
      transferFileType: "AUDIO",
      testEnv: true,
    }
  );
  let origin = window.location.origin.replace(
    /.*(\.(ea100.com.cn|exam-cloud.cn))/,
    `${location.protocol}//www$1`
  );
  console.debug("测试验证:", origin);
  if (import.meta.env.DEV) {
    origin = import.meta.env.VITE_CONFIG_API_SERVER as string;
  }

  wechat.qrValue = response.data + encodeURIComponent("&apiServer=" + origin);
  const trueExamRecordDataId = decodeURIComponent(wechat.qrValue).match(
    /&examRecordDataId=(\d+)/
  )![1];
  wechat.examRecordDataId = trueExamRecordDataId;
});
// websocket 会在unmounted时自动关闭

watch(
  () => store.exam.questionQrCodeScanned,
  () => {
    wechat.qrScanned = true;
    wechat.qrScannedResolved = true;
  }
);
watch(
  () => store.exam.questionAnswerFileUrl,
  (value) => {
    const examRecordDataId = wechat.examRecordDataId;
    for (const q of value) {
      if (!q.saved) {
        let acknowledgeStatus = "CONFIRMED";

        httpApp
          .post(
            "/api/ecs_oe_student/examControl/saveUploadedFileAcknowledgeStatus",
            {
              examRecordDataId,
              filePath: q.fileUrl,
              order: q.order,
              acknowledgeStatus,
            }
          )
          .then(() => {
            wechat.studentAnswer = q.fileUrl;
            wechat.uploadResolved = true;
            wechat.uploadStatus = true;
            q.saved = true;
            if (acknowledgeStatus === "CONFIRMED")
              $message.info("小程序作答已更新");
          })
          .catch(() => {
            $message.error("更新小程序答案失败！");
          });
      }
    }
  }
);
//#endregion websocket

//#region 摄像头处理
onMounted(async () => {
  await openCamera();
});
const video: HTMLVideoElement = $ref();
async function openCamera() {
  const stream = await getMediaStream();
  video.srcObject = stream;
  try {
    await video.play();
    camera.openCameraStatus = true;
  } catch (error) {
    if (error instanceof Error) {
      if (error.name == "AbortError") {
        logger({
          cnl: ["server"],
          pgu: "AUTO",
          act: "video.paly",
          dtl: "AbortError and retry",
        });
        await video.play();
        logger({
          cnl: ["server"],
          pgu: "AUTO",
          act: "摄像头没有正常启用: AbortError 重试成功",
        });
      } else if (error.name == "NotSupportedError") {
        logger({
          cnl: ["server"],
          act: "摄像头没有正常启用",
          pgu: "AUTO",
          ejn: JSON.stringify(error),
          ext: {
            errorName: error.name,
            errorMessage: error.message,
            errorStack: error.stack,
          },
        });
        $message.error("摄像头没有正常启用: " + error);
      } else {
        throw error;
      }
    } else {
      logger({
        cnl: ["server"],
        pgu: "AUTO",
        act: "video.play",
        dtl: "not an Error",
        stk: error + "",
      });
    }
    throw error;
  } finally {
    camera.openCameraResolved = true;
  }
}
onUnmounted(() => {
  closeMediaStream();
});
//#endregion 摄像头处理

function previous() {
  if (current > 1) {
    current -= 1;
  }
}
function next() {
  if (current < 6) {
    current += 1;
  }
  if (current === 6) {
    logger({
      cnl: ["server"],
      act: "环境检测",
      ext: {
        network: step1Status,
        time: step2Status,
        camera: step3Status,
        sound: step4Status,
        wechat: step5Status,
      },
    });
  }
}
</script>

<template>
  <n-modal
    title="环境检测"
    :closable="false"
    :show="show"
    preset="card"
    style="width: 800px"
  >
    <div style="max-width: 800px; margin: 30px auto">
      <n-steps :current="current" size="small">
        <n-step title="网速"></n-step>
        <n-step title="时钟"></n-step>
        <n-step title="摄像头"></n-step>
        <n-step title="声音"></n-step>
        <n-step title="微信小程序"></n-step>
        <n-step title="检测结果"></n-step>
      </n-steps>

      <div v-if="current === 1" key="1" class="section">
        <div class="list">
          <table>
            <tbody class="list-row">
              <tr class="list-header qm-primary-strong-text">
                <td class="first-td">检查项</td>
                <td>值</td>
                <td>状态</td>
              </tr>

              <tr>
                <td>电脑当前下载速度</td>
                <td>{{ network.downlink }}Mb</td>
                <td>
                  <div v-if="network.downlinkStatus">
                    <n-icon class="pass-check" :component="Checkmark" />
                  </div>
                  <div v-else>
                    <n-icon
                      class="fail-cross"
                      title="下载速度不佳"
                      :component="Close"
                    />
                  </div>
                </td>
              </tr>
              <tr>
                <td>电脑当前网络延迟</td>
                <td>{{ network.rrt }}毫秒</td>
                <td>
                  <div v-if="network.rrtStatus">
                    <n-icon class="pass-check" :component="Checkmark" />
                  </div>
                  <div v-else>
                    <n-icon
                      class="fail-cross"
                      title="网络延迟较大"
                      :component="Close"
                    />
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>

      <div v-if="current === 2" key="2" class="section">
        <div class="list">
          <table>
            <tbody class="list-row">
              <tr class="list-header qm-primary-strong-text">
                <td class="first-td">检查项</td>
                <td>值</td>
                <td>状态</td>
              </tr>

              <tr>
                <td>电脑时区</td>
                <td>{{ time.currentTimeZone }}</td>
                <td>
                  <div v-if="time.timeZoneStatus">
                    <n-icon class="pass-check" :component="Checkmark" />
                  </div>
                  <div v-else>
                    <n-icon
                      class="fail-cross"
                      title="请将电脑设置为北京时区"
                      :component="Close"
                    />
                  </div>
                </td>
              </tr>
              <tr>
                <td>电脑时钟频率</td>
                <td>
                  <div v-if="time.clockRateStateResolved">
                    {{ (time.clockRateDiff ?? 0) > 3 ? "时钟过慢" : "正常" }}
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
                <td>
                  <div v-if="time.clockRateStateResolved">
                    <n-icon
                      v-if="time.clockRateStatus"
                      class="pass-check"
                      :component="Checkmark"
                    />
                    <n-icon
                      v-else
                      class="fail-cross"
                      title="请更换电脑"
                      :component="Close"
                    />
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>

      <div v-show="current === 3" key="3" class="section">
        <div>
          <div style="display: flex">
            <video id="video" ref="video" width="400" height="300" autoplay />

            <div
              v-if="camera.openCameraResolved && camera.openCameraStatus"
              style="margin-left: 50px; margin-top: 100px"
            >
              <n-button
                type="warning"
                @click="
                  camera.identityResolved = true;
                  camera.identityStatus = false;
                "
              >
                图像中不是电脑操作者本人
              </n-button>
              <div style="width: 30px; height: 30px"></div>
              <n-button
                type="primary"
                @click="
                  camera.identityResolved = true;
                  camera.identityStatus = true;
                "
              >
                图像中是电脑操作者本人&nbsp;&nbsp;&nbsp;&nbsp;
              </n-button>
            </div>
          </div>
        </div>

        <div class="list">
          <table>
            <tbody class="list-row">
              <tr class="list-header qm-primary-strong-text">
                <td class="first-td">检查项</td>
                <td>值</td>
                <td>状态</td>
              </tr>

              <tr>
                <td>摄像头正常启用</td>
                <td>
                  <div v-if="camera.openCameraResolved">
                    {{ camera.openCameraStatus ? "正常" : "请检查摄像头" }}
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
                <td>
                  <div v-if="camera.openCameraResolved">
                    <div v-if="camera.openCameraStatus">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon
                        class="fail-cross"
                        title="请检查摄像头"
                        :component="Close"
                      />
                    </div>
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
              </tr>
              <tr>
                <td>视频显示的是电脑操作者本人</td>
                <td>
                  <div
                    v-if="
                      (camera.openCameraResolved && !camera.openCameraStatus) ||
                      camera.identityResolved
                    "
                  >
                    {{ camera.identityStatus ? "正常" : "请检查摄像头" }}
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
                <td>
                  <div
                    v-if="
                      (camera.openCameraResolved && !camera.openCameraStatus) ||
                      camera.identityResolved
                    "
                  >
                    <div v-if="camera.identityStatus">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon
                        class="fail-cross"
                        title="请检查摄像头"
                        :component="Close"
                      />
                    </div>
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>

      <div
        v-show="current === 4"
        key="4"
        class="section"
        style="text-align: center"
      >
        <div>
          <div style="display: flex; margin-bottom: 30px">
            <audio
              src="https://ecs-static.qmth.com.cn/check-audio.mp3"
              controls
              nodownload
              @loadeddata="
                sound.downloadResolved = true;
                sound.downloadStatus = true;
              "
              @error="
                sound.downloadResolved = true;
                sound.downloadStatus = false;
              "
            />

            <div style="margin-left: 30px; display: flex">
              <n-button
                type="warning"
                title="或者听不到声音"
                @click="
                  sound.playedStatusResolved = true;
                  sound.playedStatus = false;
                "
              >
                不能播放声音
              </n-button>
              <div style="width: 30px; height: 30px"></div>
              <n-button
                type="primary"
                @click="
                  sound.playedStatusResolved = true;
                  sound.playedStatus = true;
                "
              >
                能够播放声音
              </n-button>
            </div>
          </div>
        </div>

        <div class="list">
          <table>
            <tbody class="list-row">
              <tr class="list-header qm-primary-strong-text">
                <td class="first-td">检查项</td>
                <td>值</td>
                <td>状态</td>
              </tr>

              <tr>
                <td>文件下载</td>
                <td>
                  <div v-if="sound.downloadResolved">
                    {{ sound.downloadStatus ? "正常" : "出错" }}
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
                <td>
                  <div v-if="sound.downloadResolved">
                    <div v-if="sound.downloadStatus">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon
                        class="fail-cross"
                        title="下载出错"
                        :component="Close"
                      />
                    </div>
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
              </tr>
              <tr>
                <td>声音播放</td>
                <td>
                  <div v-if="sound.playedStatusResolved">
                    {{ sound.playedStatus ? "正常" : "出错" }}
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
                <td>
                  <div v-if="sound.playedStatusResolved">
                    <div v-if="sound.playedStatus">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon
                        class="fail-cross"
                        title="不能播放声音"
                        :component="Close"
                      />
                    </div>
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>

      <div v-show="current === 5" key="5" class="section">
        <div>
          <div style="display: flex">
            <div>
              <div v-if="wechat.qrValue" style="display: flex">
                <VueQrcode
                  :value="wechat.qrValue"
                  :options="{ width: 200 }"
                  style="margin-left: -10px"
                ></VueQrcode>
                <div style="margin-top: 10px">
                  <div style="font-size: 30px">
                    请使用<span style="font-weight: 900; color: #1e90ff"
                      >微信</span
                    >扫描二维码后，在微信小程序上录音，并上传文件。
                  </div>
                  <div
                    v-if="wechat.qrScanned"
                    style="margin-top: 30px; font-size: 30px"
                  >
                    {{ wechat.studentAnswer ? "已上传" : "已扫描" }}
                    <n-icon :component="Checkmark" />
                  </div>
                </div>
              </div>
              <div v-else>正在获取二维码...</div>
            </div>
          </div>

          <div
            class="audio-answer audio-answer-line-height"
            style="margin-top: 20px; text-align: left"
          >
            <span class="audio-answer-line-height">上传文件：</span>
            <audio
              v-if="wechat.studentAnswer"
              class="audio-answer-line-height"
              controls
              controlsList="nodownload"
              :src="wechat.studentAnswer"
            />
            <span v-else class="audio-answer-line-height">未上传文件</span>
          </div>

          <div style="margin-top: 30px; display: flex; margin-bottom: 30px">
            <n-button
              type="warning"
              title="扫码不成功"
              @click="
                wechat.qrScannedResolved = true;
                wechat.qrScanned = false;
              "
            >
              不能正确扫描二维码
            </n-button>
            <div style="width: 30px; height: 30px"></div>
            <n-button
              type="warning"
              title="上传不成功"
              @click="
                wechat.uploadResolved = true;
                wechat.uploadStatus = false;
              "
            >
              上传不成功
            </n-button>
          </div>
        </div>

        <div class="list">
          <table>
            <tbody class="list-row">
              <tr class="list-header qm-primary-strong-text">
                <td class="first-td">检查项</td>
                <td>值</td>
                <td>状态</td>
              </tr>

              <tr>
                <td>扫描二维码</td>
                <td>
                  <div v-if="wechat.qrScannedResolved">
                    {{ wechat.qrScanned ? "正常" : "出错" }}
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
                <td>
                  <div v-if="wechat.qrScannedResolved">
                    <div v-if="wechat.qrScanned">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon
                        class="fail-cross"
                        title="扫描出错"
                        :component="Close"
                      />
                    </div>
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
              </tr>

              <tr>
                <td>上传录音</td>
                <td>
                  <div v-if="wechat.uploadResolved">
                    {{ wechat.uploadStatus ? "正常" : "出错" }}
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
                <td>
                  <div v-if="wechat.uploadResolved">
                    <div v-if="wechat.uploadStatus">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon class="fail-cross" :component="Close" />
                    </div>
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>

      <div v-show="current === 6" key="6" class="section">
        <div class="list">
          <table>
            <tbody class="list-row">
              <tr class="list-header qm-primary-strong-text">
                <td class="first-td">检查项</td>
                <td>结果</td>
              </tr>

              <tr>
                <td>网速</td>
                <td>
                  <div v-if="step1Status">
                    <n-icon class="pass-check" :component="Checkmark" />
                  </div>
                  <div v-else>
                    <n-icon class="fail-cross" :component="Close" />
                  </div>
                </td>
              </tr>
              <tr>
                <td>时钟</td>
                <td>
                  <div v-if="step2StatusResolved">
                    <div v-if="step2Status">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon class="fail-cross" :component="Close" />
                    </div>
                  </div>
                  <div v-else>
                    <n-spin size="small" />
                  </div>
                </td>
              </tr>
              <tr>
                <td>摄像头</td>
                <td>
                  <div v-if="step3StatusResolved">
                    <div v-if="step3Status">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon class="fail-cross" :component="Close" />
                    </div>
                  </div>
                  <div v-else class="fail-cross">
                    请在“摄像头”步骤进行人工确认！
                  </div>
                </td>
              </tr>
              <tr>
                <td>声音</td>
                <td>
                  <div v-if="step4StatusResolved">
                    <div v-if="step4Status">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon class="fail-cross" :component="Close" />
                    </div>
                  </div>
                  <div v-else class="fail-cross">
                    请在“声音”步骤进行人工确认！
                  </div>
                </td>
              </tr>
              <tr>
                <td>微信小程序</td>
                <td>
                  <div v-if="step5StatusResolved">
                    <div v-if="step5Status">
                      <n-icon class="pass-check" :component="Checkmark" />
                    </div>
                    <div v-else>
                      <n-icon class="fail-cross" :component="Close" />
                    </div>
                  </div>
                  <div v-else class="fail-cross">
                    请在“微信小程序”步骤进行人工确认！
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>

        <div style="color: red">
          <div v-if="!step1Status" key="a">
            检查网络是否连接，路由器是否正常工作。
          </div>
          <div v-if="step2StatusResolved && !step2Status" key="b">
            请调整电脑时间和社区与北京时间一致。
          </div>
          <div v-if="step3StatusResolved && !step3Status" key="c">
            请确认摄像头连接线正常，能正常工作，关闭杀毒软件、关闭摄像头滤镜软件；请确认您的电脑是否为双摄摄像头，启用的摄像头是否正确。
          </div>
          <div v-if="step4StatusResolved && !step4Status" key="d">
            请确认音箱连接正常，调整音量开关及大小。
          </div>
          <div v-if="step5StatusResolved && !step5Status" key="e">
            请确认微信已登录并连接网络。
          </div>

          <div
            v-if="
              !step1Status ||
              (step2StatusResolved && !step2Status) ||
              (step3StatusResolved && !step3Status) ||
              (step4StatusResolved && !step4Status) ||
              (step5StatusResolved && !step5Status)
            "
            key="f"
          >
            请按提示检查并调试，调试后可再次进行环境检测。
          </div>
        </div>
      </div>

      <div style="margin-top: 30px; text-align: center">
        <n-button type="primary" :disabled="current === 1" @click="previous">
          上一步
        </n-button>
        <div style="width: 30px; height: 1px; display: inline-block"></div>
        <n-button type="primary" :disabled="current === 6" @click="next">
          下一步
        </n-button>
        <div style="width: 30px; height: 1px; display: inline-block"></div>
        <n-button
          v-if="current === 6"
          key="xxx"
          type="primary"
          @click="() => emit('on-close')"
        >
          进入考试
        </n-button>
      </div>
    </div>
  </n-modal>
</template>
<style scoped>
.section {
  margin-top: 10px;
}

.list {
  border: 1px solid #eeeeee;
  border-radius: 6px;
  text-align: center;
}

.list table {
  width: 100%;
  border-collapse: collapse !important;
  border-spacing: 0;
}
.list td {
  border: 1px solid #eeeeee;
  border-radius: 6px;
  border-collapse: separate !important;
  padding: 10px;
}
.list .first-td {
  width: 50%;
}

.pass-check {
  font-size: 20px;
  color: green;
}

.fail-cross {
  font-size: 20px;
  color: red;
}
</style>
