<template>
  <div class="home">
    <header class="header">
      <div class="school-logo">
        <img
          v-show="logoPath"
          class="logo-size"
          :src="logoPath"
          alt="school logo"
          style="background: linear-gradient(to bottom, #38f6f5 0%, #8efdf4 100%);"
          @load="e => (e.target.style = '')"
        />
      </div>
      <a
        class="close"
        style="border-bottom-left-radius: 6px;"
        @click="closeApp"
      >
        关闭
      </a>
    </header>

    <div class="center">
      <div class="content">
        <div style="display:flex;">
          <a
            v-if="allowLoginType.includes('STUDENT_CODE')"
            :class="[
              'qm-big-text',
              'login-type',
              loginType === 'STUDENT_CODE' && 'active-type',
              allowLoginType.length === 1 && 'single-login-type',
            ]"
            style="border-top-left-radius: 6px"
            @click="loginType = 'STUDENT_CODE'"
          >
            {{ QECSConfig.STUDENT_CODE_LOGIN_ALIAS }}
          </a>
          <a
            v-if="allowLoginType.includes('IDENTITY_NUMBER')"
            :class="[
              'qm-big-text',
              'login-type',
              loginType !== 'STUDENT_CODE' && 'active-type',
              allowLoginType.length === 1 && 'single-login-type',
            ]"
            style="border-top-right-radius: 6px"
            @click="loginType = 'STUDENT_IDENTITY_NUMBER'"
          >
            {{ QECSConfig.IDENTITY_NUMBER_LOGIN_ALIAS }}
          </a>
          <a
            v-if="allowLoginType.length === 0"
            :class="['qm-big-text', 'login-type']"
            >loading...</a
          >
        </div>

        <div class="qm-title-text" style="margin: 40px 0 20px 0">
          {{ productName }}
        </div>

        <div style="margin: 0 40px 40px 40px">
          <i-form ref="loginForm" :model="loginForm" :rules="loginFormRule">
            <i-form-item
              prop="accountValue"
              style="margin-bottom:35px;height:42px"
            >
              <i-input
                v-model="loginForm.accountValue"
                type="text"
                size="large"
              >
                <i-icon slot="prepend" type="ios-person"></i-icon>
              </i-input>
            </i-form-item>
            <i-form-item prop="password" style="margin-bottom:35px;height:42px">
              <i-input
                v-model="loginForm.password"
                type="password"
                size="large"
                @on-enter="login"
              >
                <i-icon slot="prepend" type="ios-lock"></i-icon>
              </i-input>
            </i-form-item>
            <i-form-item style="position: relative">
              <div
                v-if="errorInfo !== ''"
                style="position: absolute; top: -37px; width: 100%"
              >
                <i-alert type="error" show-icon>{{ errorInfo }}</i-alert>
              </div>
              <i-button
                size="large"
                class="qm-primary-button"
                long
                :disabled="disableLoginBtn"
                :loading="loginBtnLoading"
                @click="login"
              >
                {{ newVersionAvailable ? "点击更新版本" : "登录" }}
              </i-button>
            </i-form-item>
          </i-form>
        </div>
      </div>
    </div>

    <footer class="footer">
      <div style="position: absolute; right: 20px; bottom: 20px;">
        版本: {{ VUE_APP_GIT_REPO_VERSION }}
      </div>
      <DevTools />
    </footer>
  </div>
</template>

<script>
import moment from "moment";
import { mapMutations } from "vuex";
import { FACE_API_MODEL_PATH } from "@/constants/constants";
import DevTools from "./DevTools.vue";
import nativeExe from "@/utils/nativeExe";
import { EPCC_DOMAIN } from "@/constants/constants";

// 检测devtools.  仅在chrome 72+ 有效。
let element = new Image();
Object.defineProperty(element, "id", {
  get: function() {
    // console.log("trigger devtools log");
    window._hmt.push([
      "_trackEvent",
      "登录页面",
      `控制台打开`,
      window.location.href +
        "|||" +
        localStorage.getItem("domain") +
        "|||" +
        localStorage.getItem("key"),
    ]);
    return null;
  },
});
console.log(".", element);

/**
 * 在任何组件需要强制退出，做以下步骤
 * 1. this.$Message.info()
 * 2. this.$router.push("/login"+domain);
 * 因为在/login里会删除localStorage的token，而在router.beforeEach会检查是否有token，达到退出的目的。
 */

export default {
  name: "Login",
  components: {
    DevTools,
  },
  data() {
    return {
      domainInUrl: this.$route.params.domain,
      QECSConfig: {},
      loginType: "STUDENT_CODE",
      errorInfo: "",
      loginForm: {
        accountValue: "",
        password: "",
      },
      loginFormRule: {
        accountValue: [
          {
            required: true,
            message: "请填写登录账号",
            trigger: "blur",
          },
        ],
        password: [
          {
            required: true,
            message: "请填写密码",
            trigger: "blur",
          },
        ],
      },
      loginBtnLoading: false,
      isElectron: true,
      disableLoginBtnBecauseRemoteApp: true,
      disableLoginBtnBecauseVCam: true,
      disableLoginBtnBecauseNoRemoteAppChecker: false,
      disableLoginBtnBecauseRefreshServiceWorker: false,
      newVersionAvailable: false,
      VUE_APP_GIT_REPO_VERSION: process.env.VUE_APP_GIT_REPO_VERSION,
    };
  },
  computed: {
    logoPath() {
      return this.QECSConfig.LOGO_FILE_URL
        ? this.QECSConfig.LOGO_FILE_URL + "!/progressive/true/format/webp"
        : "";
    },
    productName() {
      return this.QECSConfig.OE_STUDENT_SYS_NAME || "远程教育网络考试";
    },
    allowLoginType() {
      return (
        (this.QECSConfig.LOGIN_TYPE && this.QECSConfig.LOGIN_TYPE.split(",")) ||
        []
      );
    },
    schoolDomain() {
      const domain = this.domainInUrl;
      if (!domain || !domain.includes("qmth.com.cn")) {
        this.$Message.error({
          content: "机构地址出错，请关闭程序后再登录。",
          duration: 15,
          closable: true,
        });
      }
      return domain;
    },
    isEPCC() {
      return this.schoolDomain === EPCC_DOMAIN;
    },
    usernameInputPlaceholder() {
      if (this.loginType === "STUDENT_CODE") {
        return "请输入学号";
      } else {
        return "请输入身份证号";
      }
    },
    passwordInputPlaceholder() {
      if (this.loginType === "STUDENT_CODE") {
        return "初始密码为身份证号后6位";
      } else {
        return "初始密码为身份证号后6位";
      }
    },
    disableLoginBtn() {
      // console.log(
      //   this.isElectron,
      //   this.disableLoginBtnBecauseRemoteApp,
      //   this.disableLoginBtnBecauseVCam,
      //   this.disableLoginBtnBecauseNoRemoteAppChecker
      // );
      return (
        (this.isElectron &&
          (this.disableLoginBtnBecauseRemoteApp ||
            this.disableLoginBtnBecauseVCam ||
            this.disableLoginBtnBecauseNoRemoteAppChecker)) ||
        this.disableLoginBtnBecauseRefreshServiceWorker
      );
    },
  },
  async mounted() {
    // this.testServiceWorker();

    this.examShellStats();
    // await this.checkNewVersion();
    if (localStorage.getItem("__swReload")) {
      localStorage.removeItem("__swReload");
      this.$Message.info({
        content: "正在更新版本...",
      });
      this.disableLoginBtnBecauseRefreshServiceWorker = true;
      await new Promise(resolve => {
        setTimeout(() => {
          resolve();
        }, 2000);
      });
      location.reload(true);
    }
    // manual precache for models
    fetch(
      FACE_API_MODEL_PATH + "tiny_face_detector_model-weights_manifest.json"
    );
    fetch(FACE_API_MODEL_PATH + "face_landmark_68_model-weights_manifest.json");
    // alread precached
    // fetch("/models/tiny_face_detector_model-shard1");
    // fetch("/models/face_landmark_68_model-shard1");

    // this.checkNewVersionInterval = setInterval(() => {
    //   if (window.__newSWAvailable) {
    //     this.newVersionAvailable = true;
    //   }
    // }, 1000);
    this.checkNewVersionListener = document.addEventListener(
      "__newSWAvailable",
      () => {
        this.newVersionAvailable = true;
      },
      { once: true }
    );
    if (this.isEPCC) {
      const redirectUrl = new URLSearchParams(location.search).get(
        "redirectUrl"
      );
      if (redirectUrl) {
        sessionStorage.setItem("redirectUrl", redirectUrl);
      }
      this.login();
    }
  },
  async created() {
    if (navigator.connection) {
      window._hmt.push([
        "_trackEvent",
        "登录页面",
        `当前网速: ${Number(navigator.connection.downlink).toPrecision(
          1
        )}Mib; 网络延迟: ${Number(navigator.connection.rrt).toPrecision(1)}ms`,
      ]);
    }
    if (this.isEPCC) {
      this.$Spin.show({
        render: () => {
          return <div style="font-size: 44px">正在登录...</div>;
        },
      });
    }

    this.isElectron = typeof nodeRequire != "undefined";

    if (
      navigator.userAgent.indexOf("WOW64") != -1 ||
      navigator.userAgent.indexOf("Win64") != -1
    ) {
      window._hmt.push(["_trackEvent", "登录页面", "64Bit OS"]);
    } else {
      window._hmt.push(["_trackEvent", "登录页面", "非64Bit OS"]);
    }

    this.$Message.config({
      duration: 15,
      size: "large",
      closable: true, // 没有影响到所有的组件。。。  https://github.com/iview/iview/issues/2962
    });

    window.sessionStorage.removeItem("token");
    window.sessionStorage.clear();
    window.localStorage.removeItem("key");
    if (localStorage.getItem("user-for-reload")) {
      const lsUser = JSON.parse(localStorage.getItem("user-for-reload"));
      this.loginForm.accountValue =
        lsUser.studentCode ||
        (lsUser.studentCodeList && lsUser.studentCodeList[0]);
      this.loginForm.password =
        process.env.NODE_ENV === "production" ? "" : "180613";
    }

    if (
      [
        "xjtu.ecs.qmth.com.cn",
        "ccnu.ecs.qmth.com.cn",
        "snnu.ecs.qmth.com.cn",
        "cup.ecs.qmth.com.cn",
        "swjtu.ecs.qmth.com.cn",
      ].includes(this.$route.params.domain)
    ) {
      if (
        typeof nodeRequire == "undefined" ||
        !window.nodeRequire("fs").existsSync("multiCamera.exe")
      ) {
        this.disableLoginBtnBecauseNoRemoteAppChecker = true;
        this.$Message.error({
          content: "请与学校申请最新的客户端，进行考试！",
          duration: 2 * 24 * 60 * 60,
        });
      }
    }

    await this.checkElectronConfig();
    await this.checkVCam();

    if (
      this.allowLoginType.length === 1 &&
      this.allowLoginType[0] === "IDENTITY_NUMBER"
    ) {
      this.loginType = "STUDENT_IDENTITY_NUMBER";
    }
  },
  beforeDestroy() {
    clearTimeout(this.loginTimeout);
    // clearInterval(this.checkNewVersionInterval);
    document.removeEventListener(
      "__newSWAvailable",
      this.checkNewVersionListener
    );
  },
  methods: {
    ...mapMutations(["updateUser", "updateTimeDifference", "updateQECSConfig"]),
    testServiceWorker() {
      if ("serviceWorker" in navigator) {
        // Register service worker
        navigator.serviceWorker
          .register("/service-worker-test.js")
          .then(function(reg) {
            console.log("Registration OK!. Scope is " + reg.scope);
          })
          .catch(function(err) {
            console.error("Registration FAILED! " + err);
          });
      }
    },
    async login() {
      // epcc立即登录
      if (!this.isEPCC && (this.disableLoginBtn || this.loginBtnLoading)) {
        return;
      }
      this.loginBtnLoading = true;

      try {
        const hasNewVersion = await this.checkNewVersion();
        if (hasNewVersion) return;
      } catch (error) {
        console.log("检测新版本出错");
      }

      this.loginTimeout = setTimeout(() => {
        this.loginBtnLoading = false;
      }, 10 * 1000);
      let loginResponse;
      if (!this.isEPCC) {
        // https://www.cnblogs.com/weiqinl/p/6708993.html
        const valid = await this.$refs.loginForm.validate();
        if (!valid) {
          return;
        }

        let repPara = this.loginForm;
        // 以下网络请求失败，直接报网络异常错误
        loginResponse = await this.$http.post("/api/ecs_core/auth/login", {
          ...repPara,
          accountType: this.loginType,
          domain: this.schoolDomain,
          alwaysOK: true,
        });
      } else {
        loginResponse = await this.epccLogin();
        if (!loginResponse) {
          return;
        } else {
          loginResponse.data = { content: loginResponse.data, code: "200" }; // 和老接口的always ok保持一致
        }
      }
      let data = loginResponse.data;
      // if (
      //   Math.abs() >
      //   5 * 60 * 1000
      // ) {
      //   window._hmt.push(["_trackEvent", "登录页面", "本机时间误差过大"]);
      //   this.$Message.error({
      //     content: "与服务器时间差异超过5分钟，请校准本机时间之后再重试！",
      //     duration: 30
      //   });
      //   throw "与服务器时间差异超过5分钟，请校准本机时间之后再重试！";
      // }

      this.updateTimeDifference(
        moment(loginResponse.headers.date).diff(moment())
      );
      if (data.code == "200") {
        data = data.content;
        this.errorInfo = "";
        //缓存用户信息
        window.sessionStorage.setItem("token", data.token);
        window.localStorage.setItem("key", data.key);
        window.localStorage.setItem("domain", this.schoolDomain);

        try {
          const student = (await this.$http.get(
            "/api/ecs_core/student/getStudentInfoBySession"
          )).data;
          const specialty = (await this.$http.get(
            "/api/ecs_exam_work/exam_student/specialtyNameList/"
          )).data;
          const user = {
            ...data,
            ...student,
            specialty: specialty.join(),
            schoolDomain: this.schoolDomain,
          };
          this.updateUser(user);
          window.localStorage.setItem("user-for-reload", JSON.stringify(user));

          window._hmt.push([
            "_trackEvent",
            "登录页面",
            "登录",
            this.$route.query.LogoutReason,
          ]);
          const alreadyInExam = await this.checkExamInProgress();
          if (alreadyInExam) {
            window._hmt.push([
              "_trackEvent",
              "登录页面",
              "断点续考",
              "重新登录",
            ]);
            return;
          }
          this.$router.push("/online-exam");
          window._hmt.push(["_trackEvent", "登录页面", "登录成功"]);
          this.$Spin.hide();
        } catch (error) {
          window._hmt.push([
            "_trackEvent",
            "登录页面",
            "登录失败",
            "getStudentInfoBySession失败",
          ]);
          this.$Message.error({
            content: "获取学生信息失败，请重试！",
            duration: 15,
            closable: true,
          });
          if (this.isEPCC) {
            this.$Spin.hide();
            this.logout();
          }
        }
      } else {
        window._hmt.push(["_trackEvent", "登录页面", "登录失败", data.desc]);
        this.errorInfo = data.desc;
      }
    },
    async checkNewVersion() {
      let myHeaders = new Headers();
      myHeaders.append("Content-Type", "application/javascript");
      myHeaders.append("Cache-Control", "no-cache");
      const response = await fetch(
        document.scripts[document.scripts.length - 1].src + "?x" + Date.now(),
        {
          method: process.env.NODE_ENV === "development" ? "GET" : "HEAD",
          headers: myHeaders,
        }
      );
      if (!response.ok || this.newVersionAvailable) {
        if (
          response.ok &&
          this.newVersionAvailable &&
          localStorage.getItem("__swReload")
        ) {
          window._hmt.push([
            "_trackEvent",
            "登录页面",
            "service worker刷新失败",
          ]);
          this.$Message.destroy();
          this.$Message.info({
            content: "请重新打开程序。",
            duration: 2 * 24 * 60 * 60,
          });
          return true;
        }
        window._hmt.push([
          "_trackEvent",
          "登录页面",
          "新版本发布后，客户端自动刷新",
        ]);
        this.$Message.info({
          content: "正在获取新版本...",
        });
        localStorage.setItem("__swReload", "anything");
        this.disableLoginBtnBecauseRefreshServiceWorker = true;
        await new Promise(resolve => {
          setTimeout(() => {
            resolve();
          }, 1000);
        });
        location.reload(true);

        return true;
      }
    },
    async checkElectronConfig() {
      try {
        const fileSever =
          location.hostname === "ecs.qmth.com.cn"
            ? "https://ecs-static.qmth.com.cn"
            : "https://ecs-test-static.qmth.com.cn";

        const res = await fetch(
          fileSever +
            "/org_properties/byOrgDomain/" +
            this.domainInUrl +
            "/studentClientConfig.json" +
            "?" +
            Date.now() +
            Math.random()
        );
        if (res.ok) {
          const json = await res.json();
          this.QECSConfig = json;
          this.updateQECSConfig(json);
        } else {
          this.$Message.error({
            content: "获取远程配置文件出错，请重新打开程序！",
            duration: 15,
            closable: true,
          });
          return;
        }
      } catch (e) {
        this.$Message.error({
          content: "获取远程配置文件出错，请重新打开程序！",
          duration: 15,
          closable: true,
        });
        return;
      }

      await this.checkRemoteApp();
    },
    async checkRemoteApp() {
      if (typeof nodeRequire == "undefined") {
        return;
      }

      function checkRemoteAppTxt() {
        let applicationNames;
        try {
          const fs = window.nodeRequire("fs");
          applicationNames = fs.readFileSync("remoteApplication.txt", "utf-8");
        } catch (error) {
          window._hmt.push([
            "_trackEvent",
            "登录页面",
            "读取remoteApplication.txt出错",
          ]);
          this.$Message.error({
            content: "系统检测出错，请退出程序后重试！",
            duration: 2 * 24 * 60 * 60,
          });
          return;
        }
        if (applicationNames && applicationNames.trim()) {
          this.disableLoginBtnBecauseRemoteApp = true;
          this.$Message.info({
            content:
              "在考试期间，请关掉" +
              applicationNames.trim() +
              "软件，诚信考试。",
            duration: 2 * 24 * 60 * 60,
          });
        } else {
          this.disableLoginBtnBecauseRemoteApp = false;
        }
      }
      //如果配置中配置了 DISABLE_REMOTE_ASSISTANCE
      if (
        this.QECSConfig.PREVENT_CHEATING_CONFIG.includes(
          "DISABLE_REMOTE_ASSISTANCE"
        )
      ) {
        await nativeExe("Project1.exe", checkRemoteAppTxt.bind(this));
      } else {
        this.disableLoginBtnBecauseRemoteApp = false;
      }
    },
    async checkVCam() {
      if (typeof nodeRequire == "undefined") {
        return;
      }

      const vcamList = [
        "17GuaGua Cam",
        "91KBOX",
        "ASUS Virtual Camera",
        "e2eSoft iVCam",
        "e2eSoft VCam",
        "FaceRig Virtual Camera",
        "Lenovo Virtual Camera",
        "MagicCamera Capture",
        "MeiSe",
        "Virtual Cam",
        "YY伴侣",
        "WebcamMax Capture",
      ];

      function checkVCamTxt() {
        let applicationNames;
        try {
          const fs = window.nodeRequire("fs");
          applicationNames = fs.readFileSync("CameraInfo.txt", "utf-8");
        } catch (error) {
          window._hmt.push([
            "_trackEvent",
            "登录页面",
            "读取CameraInfo.txt出错",
          ]);
          this.$Message.error({
            content: "系统检测出错，请退出程序后重试！",
            duration: 2 * 24 * 60 * 60,
          });
          return;
        }
        this.disableLoginBtnBecauseVCam = false;
        if (applicationNames && applicationNames.trim()) {
          for (const vc of vcamList) {
            if (applicationNames.includes(vc)) {
              this.disableLoginBtnBecauseVCam = true;
              this.$Message.info({
                content:
                  "在考试期间，请关掉" + "虚拟摄像头" + "软件，诚信考试。",
                duration: 2 * 24 * 60 * 60,
              });
              console.log(applicationNames);
            }
          }
        }
      }
      //如果配置中配置了 DISABLE_VIRTUAL_CAMERA
      if (
        this.QECSConfig.PREVENT_CHEATING_CONFIG.includes(
          "DISABLE_VIRTUAL_CAMERA"
        )
      ) {
        await nativeExe("multiCamera.exe", checkVCamTxt.bind(this));
      } else {
        this.disableLoginBtnBecauseVCam = false;
      }
    },
    closeApp() {
      window.close();
    },
    examShellStats() {
      const shellVersion = window.navigator.userAgent
        .split(" ")
        .find(v => v.startsWith("electron-exam-shell/"));
      if (shellVersion) {
        window._hmt.push([
          "_trackEvent",
          "登录页面",
          "学生端版本",
          shellVersion,
        ]);
      } else {
        const chromeVersion = window.navigator.userAgent
          .split(" ")
          .find(v => v.startsWith("Chrome/"));
        if (chromeVersion) {
          window._hmt.push([
            "_trackEvent",
            "登录页面",
            "学生端版本/chrome",
            shellVersion,
          ]);
        } else {
          window._hmt.push([
            "_trackEvent",
            "登录页面",
            "学生端版本/其他浏览器",
            window.navigator.userAgent,
          ]);
        }
      }
    },
    async epccLogin() {
      const {
        accountType,
        accountValue,
        rootOrgId,
        appId,
        timestamp,
        token,
        redirectUrl,
      } = this.$route.query;

      if (
        accountType &&
        accountValue &&
        rootOrgId &&
        appId &&
        timestamp &&
        token &&
        redirectUrl
      ) {
        try {
          const response = await this.$http.post(
            "/api/ecs_core/auth/thirdPartyStudentAccess" + location.search
          );
          return response;
        } catch (error) {
          window._hmt.push(["_trackEvent", "第三方登录页面", "接口出错", ""]);
          this.$Spin.hide();
          this.logout();
        }
      } else {
        this.$Spin.hide();
        this.logout();
      }
    },
  },
};
</script>

<style scoped>
.home {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.school-logo {
  justify-self: flex-start;
  margin-left: 100px;
}

.logo-size {
  height: 100px;
  width: 400px;
  object-fit: cover;
}

.header {
  min-height: 120px;
  display: grid;
  align-items: center;
  justify-items: center;
}

.center {
  background-image: url("https://cdn.qmth.com.cn/ui/ecs-client-bg.jpg!/progressive/true");
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  width: 100vw;
  min-height: 600px;
}

.content {
  margin-top: 100px;
  margin-left: 65%;
  width: 300px;
  border-radius: 6px;
  background-color: white;
  display: grid;
  grid-template-areas: "";
}

.login-type {
  flex: 1;
  line-height: 40px;
  background-color: #eeeeee;
}

.active-type {
  background-color: #ffffff;
}
.single-login-type {
  border-top-left-radius: 6px;
  border-top-right-radius: 6px;
}

.close {
  position: absolute;
  top: 0;
  right: 0;
  background-color: #eeeeee;
  color: #999999;
  width: 80px;
  height: 40px;
  line-height: 40px;
}

.close:hover {
  color: #444444;
}

.footer {
  position: relative;
}
</style>

<style>
.ivu-message-notice-content-text {
  font-size: 32px;
}
.ivu-message-notice-content-text i.ivu-icon {
  font-size: 32px;
}
</style>
