<script setup lang="tsx">
import { httpApp } from "@/plugins/axiosApp";
import { useTimers } from "@/setups/useTimers";
import { store } from "@/store/store";
import { showLogout } from "@/utils/utils";
import moment from "moment";
import { onMounted, onUnmounted, watch } from "vue";
import { useRoute } from "vue-router";

const emit = defineEmits<{
  (e: "on-endtime"): void;
  (e: "on-start-answer", data: { usedExamSeconds: number }): void;
}>();

const route = useRoute();
let remainTime: number | null = $ref(null);
let enhancedRemainTimeStyle = $ref(true);

const remainTimeFormatted = $computed(() =>
  typeof remainTime === "number"
    ? moment.utc(remainTime).format("HH:mm:ss")
    : "计算中"
);

const { addTimeout, addInterval } = useTimers();

const updateTimeFromServerId = addInterval(
  () => getRemainTimeFromServer(),
  60 * 1000
);

const updateRemainTimeId = addInterval(() => {
  if (typeof remainTime === "number") {
    if (remainTime > 0) {
      remainTime = remainTime - 1000;
    }
    // 剩余时间永远不应该为负数，否则会显示 "23:59:59"
    if (remainTime <= 0) {
      remainTime = 0;
      emit("on-endtime");
    }
    store.exam.remainTime = remainTime;
  }
}, 1000);

addTimeout(() => (enhancedRemainTimeStyle = false), 10 * 1000);

onMounted(async () => {
  try {
    const result = await httpApp.post(
      "/api/ecs_oe_student/examControl/startAnswer?examRecordDataId=" +
        route.params.examRecordDataId,
      { "axios-retry": { retries: 4 }, noErrorMessage: true }
    );

    emit("on-start-answer", {
      usedExamSeconds: result?.data?.usedExamSeconds || 0,
    });
  } catch (error) {
    const msg = "/startAnswer 出错";
    logger({ cnl: ["server"], pgu: "AUTO", act: msg, possibleError: error });
    showLogout("网络异常，请重新登录!");
    return;
  }

  await getRemainTimeFromServer();
});

// 离开此页面时，可能还有心跳请求未返回
onUnmounted(() => cancelHeartBeat && cancelHeartBeat.abort());

let handledNetworkException = false;
let cancelHeartBeat: AbortController;
async function getRemainTimeFromServer() {
  if (store.exam.remainTime <= 0 && store.exam.isSubmittingPaper) {
    logger({
      cnl: ["server"],
      act: "stop heartbeat",
      ext: {
        remainTime: store.exam.remainTime,
        isSubmittingPaper: store.exam.isSubmittingPaper,
      },
    });
    return;
  }
  try {
    logger({ cnl: ["server"], act: "发出心跳" });
    cancelHeartBeat = new AbortController();
    const res = await httpApp.get(
      "/api/ecs_oe_student/examControl/examHeartbeat",
      {
        "axios-retry": {
          retries: 10,
          retryDelay: () => 10 * 1000,
        },
        noErrorMessage: true,
        signal: cancelHeartBeat.signal,
      }
    );
    const rt: number = res.data;

    if (typeof rt !== "number") {
      logger({
        cnl: ["server"],
        key: "不可能的事情发生了",
        dtl: "服务器返回的心跳结果不是数字",
        ext: { remainTime: rt },
      });
      return;
    }

    logger({
      pgu: "AUTO",
      cnl: ["server"],
      act: "重置剩余时间：",
      ext: { remainTime: res.data, diff: remainTime && remainTime - res.data },
    });
    remainTime = rt;
  } catch (error) {
    if (handledNetworkException) return;
    // 如果不处理，由于心跳请求有多次，延迟下来，可能有多次心跳请求的错误处理会进下面
    handledNetworkException = true;
    const descMaybe: string | undefined = (error as any).response?.data?.desc;
    if (descMaybe) {
      $message.error(descMaybe);
      logger({ cnl: ["server"], pgu: "AUTO", act: "心跳失败", dtl: descMaybe });
    }
    logger({
      cnl: ["server"],
      pgu: "AUTO",
      act: "心跳失败",
      dtl: descMaybe,
      possibleError: error,
    });

    // 当多次重试后，可能时过境迁，心跳已经没有意义了
    if (store.exam.remainTime <= 0 && store.exam.isSubmittingPaper) {
      logger({
        cnl: ["server"],
        act: "心跳错误处理停止",
        ext: {
          remainTime: store.exam.remainTime,
          isSubmittingPaper: store.exam.isSubmittingPaper,
        },
      });
      return;
    }
    $dialog.error({
      title: "网络连接异常",
      content: "退出考试",
      maskClosable: false,
      closable: false,
      positiveText: "确定",
      onPositiveClick: () => {
        logger({
          cnl: ["server", "local"],
          pgu: "AUTO",
          act: "用户点击对话框退出",
        });
        showLogout("网络连接异常，退出考试");
      },
    });

    clearInterval(updateTimeFromServerId);
    clearInterval(updateRemainTimeId);

    addTimeout(() => {
      logger({ cnl: ["server", "local"], pgu: "AUTO", act: "90秒后自动退出" });
      $dialog.destroyAll();
      showLogout("网络连接异常，退出考试");
    }, 90 * 1000);
  }
}

let reaminModalCreated = false;
let reaminModalClosed = false;
watch(
  () => store.exam.remainTime,
  (val) => {
    if (val === 5 * 60 * 1000) {
      reaminModalCreated = true;
      $dialog.info({
        title: "温馨提醒",
        content: () => (
          <div>
            <div style="margin-top: 20px; margin-left: 20px; flex: 1">
              <div style="margin-bottom: 1.5em">
                还有<span style="font-weight: bold; color: red;"> 五 </span>
                分钟即将结束本场考试，请合理分配时间！
              </div>
            </div>
          </div>
        ),
        positiveText: "确定",
        onPositiveClick: () => (reaminModalClosed = true),
      });
    } else if (val === 5 * 60 * 1000 - 10 * 1000) {
      if (reaminModalCreated && !reaminModalClosed) {
        $dialog.destroyAll();
      }
    }
  }
);
</script>

<template>
  <div class="remain-time">
    <span>剩余时间</span>
    <span
      class="enhanced-remain-time"
      :class="[enhancedRemainTimeStyle && 'animated infinite pulse']"
    >
      {{ remainTimeFormatted }}
    </span>
  </div>
</template>

<style scoped>
.remain-time {
  width: 100%;
  margin-left: 10px;

  display: flex;
  align-items: center;
  flex-wrap: wrap;
}

.enhanced-remain-time {
  font-size: 25px;
  color: red;
  margin-left: 4px;
}

.animated {
  animation-duration: 1s;
  animation-fill-mode: both;
  display: inline-block;
}

.animated.infinite {
  animation-iteration-count: infinite;
}

@keyframes pulse {
  from {
    transform: scale3d(1, 1, 1);
  }

  50% {
    transform: scale3d(1.05, 1.05, 1.05);
  }

  to {
    transform: scale3d(1, 1, 1);
  }
}

.pulse {
  animation-name: pulse;
}
</style>
