export type Store = {
  /** 当前用户 */
  user: {
    id: number;
    /** 身份证号 */
    identityNumber: string;
    /** 学号 */
    studentCodeList: string[];
    /** 显示的姓名 */
    displayName: string;
    /** @deprecated 姓名，但可能为空 */
    name: string;
    /** 学生手机号 */
    phoneNumber: string;
    /** 属于那个学校 */
    schoolDomain: SchoolDomain;
    /** 顶级机构id */
    rootOrgId: number;
    /** 底照URL */
    photoPath: string;
    /** 机构名称 */
    orgName: string;
    /** 顶级机构名称 */
    rootOrgName: string;
    /** 用户的另一个id */
    key: string;
    /** 登录认证信息 */
    token: string | null;
    /** 专业 */
    specialty: string;
    /** 加密方式ABCDEFG */
    salt: string;
  };
  /** 学生端配置。 Q代表qmth */
  QECSConfig: {
    /** 学号登录别名 */
    STUDENT_CODE_LOGIN_ALIAS: string;
    /** 身份证号登录别名 */
    IDENTITY_NUMBER_LOGIN_ALIAS: string;
    /** 防作弊配置 !!!需对json进行转换!!! */
    PREVENT_CHEATING_CONFIG: Partial<
      [
        "FULL_SCREEN_TOP",
        "DISABLE_REMOTE_ASSISTANCE",
        "DISABLE_VIRTUAL_CAMERA",
        "DISABLE_MULTISCREEN",
        "RECORD_SWITCH_SCREEN"
      ]
    >;
    /** 学校是否有定制logo !!!需对json进行转换!!! */
    IS_CUSTOM_MENU_LOGO: boolean;
    /** 定制logo的URL */
    CUS_MENU_LOGO_FILE_URL: string;
    /** 定制登录页背景图的URL */
    STUDENT_CLIENT_BG_PICTURE_URL: string;
    /** 学生端控制台设置 */
    STUDENT_CLIENT_CONSOLE_CONFIG: string;
    /** 首页背景图 */
    LOGO_FILE_URL: string;
    /** 产品名称，登录页显示 */
    OE_STUDENT_SYS_NAME: string;
    /** 登录页可选的登录类型 !!!需对json进行转换!!! */
    LOGIN_TYPE: Array<"STUDENT_CODE" | "IDENTITY_NUMBER">;
    ROOT_ORG_ID: number;
    /** @deprecated 登录支持的客户端类型。新版只支持Electron包。 */
    LOGIN_SUPPORT: Partial<["NATIVE", "BROWSER"]>;
  };
  /** 电脑时间管理 */
  sysTime: {
    /** 与服务器差异，服务器时间大于学生端时间，则返回正数，否则返回负数 */
    difference: number;
    /** 网络延迟 通过网络请求来判断 */
    rtt: number;
    /** 上次的Date.now() 有的电脑上存在Date.now()不更新 */
    lastTime: number;
    /** 判断时钟是否不走动了 */
    isTimerFrozen: boolean;
  };
  /** 网络状况 */
  network: {
    /** 网络状况是即时的，如何获取？ */
    /** computed */
    isOK: boolean;
    /** 百度不通，则网络不通 */
    pingBaidu: boolean;
    /** 云平台api是否通畅 */
    pingQmth: boolean;
    /** 阿里云文件存储 阿里云日志 分开？ */
    pingAli: boolean;
    /** websocket */
    pingWebSocket: boolean;
  };
  /** 菜单项 */
  menus: Array<{
    /** 菜单名称 */
    name: string;
    /** 菜单指向链接，是path */
    link: string;
    /** 此处要细化为 type = 'STU_NOTICE' | 'STU_MODIFY_PWD'，在router中也要用 */
    routeCode: string;
  }>;
  /** app下载 */
  appDownload: {
    /** 是否开放下载 */
    enabled: boolean;
    /** 下载链接 */
    url: string;
  };
  /** 站内消息 */
  siteMessage: {
    messages: SiteMessage[];
    /** 第一个未读消息 popup用 computed */
    firstUnreadMessage: SiteMessage | null;
    /** 忽略的站内消息，不弹popup */
    ignoreMessageIds: number[];
    /** 未读消息总数 */
    unreadCount: number;
  };
  siteMessagesTimeStamp: number;
  /** 在线考试待考列表 */
  examList: OnlineExam[];
  /** 在线考试已结束的考试列表 */
  endedExamList: OnlineExam[];
  /** 在线作业 */
  homeworkList: OnlineExam[];
  /** 在线练习 */
  practiceList: PracticeExam[];
  /** 当前开考的exam，非断点续考才会需要发出这么网络请求，正常开考是从前页面带过来的 */
  exam: {
    /** 考生的考试记录id */
    examRecordDataId: number;
    /** 考试批次id */
    examId: number;
    /** 当前试题序号，起始为1，初始为1. 和route里面的order不好算优先级和时机，暂时不用这个字段了 */
    // order: number;
    /** 考试类型 */
    examType: ExamType;
    /** 课程名称 */
    courseName: string;
    /** 是否开启微信作答 */
    weixinAnswerEnabled: boolean;
    /** 是否开启人脸比对 */
    faceCheckEnabled: boolean;
    /** 是否开启活体检测 */
    faceLivenessEnabled: boolean;
    /** 活体检测选项 */
    faceLivenessOption: {
      /** 进入考试后多久开启活体检测(分钟) */
      faceVerifyMinute: number;
      /** 活体检测类型 S1: FaceID S2: 自研活体 */
      identificationOfLivingBodyScheme: "S1" | "S2";
    };
    /** 是否在进行活体检测 */
    isDoingFaceLiveness: boolean;
    /** 抓拍间隔(秒) */
    SNAPSHOT_INTERVAL: number;
    /** 人脸定时比对的结果集合 */
    compareResultMap: Map<string, boolean>;
    /** 考试冻结(秒) */
    freezeTime: number;
    /** 考试时长(秒) */
    duration: number;
    /** 考试剩余时间(毫秒ms) */
    remainTime: number;
    /** 是否开启微信作答方式 */
    WEIXIN_ANSWER_ENABLED: boolean;
    /** 练习显示答案的类型 IN_PRACTICE：在练习过程中显示答案  NO_ANSWER：练习过程中不显示答案 */
    practiceType: "IN_PRACTICE" | "NO_ANSWER";
    /** 试卷结构 */
    paperStruct: PaperStruct;
    /** 试题结构 */
    examQuestionList: ExamQuestion[];
    /** 当前题目 */
    currentQuestion: ExamQuestion;
    /** 试题过滤类型 */
    questionFilterType: "ALL" | "ANSWERED" | "SIGNED" | "UNANSWERED";
    /** 为方便访问将[0].audioPlayTimes提出来，并parse */
    allAudioPlayTimes: AudioPlayTime[];
    /** 某试题的二维码被扫码识别了 */
    questionQrCodeScanned: { order: number };
    /** 试题的文件作答地址 */
    questionAnswerFileUrl: {
      order: number;
      fileUrl: string;
      transferFileType: string;
      saved?: boolean;
    }[];
    /** 是否超过了切屏次数 */
    isExceededSwitchCount: boolean;
    /** 是否正在交卷 */
    isSubmittingPaper: boolean;
  };
  // /** 考试中的状态 */
  // examing: {};
  // /** 获取到的camera source */
  camera: {
    /** 获取摄像头，且在多页面共享，减少打开摄像头失败的次数 */
    stream: MediaStream | null;
  };
  globalMaskCount: 0;
  spinMessage?: string;
};

type SchoolDomain = `${string}.ecs.qmth.com.cn`;
export type SiteMessage = {
  id: number;
  hasRead: boolean;
  hasRecalled: boolean;
  title: string;
  content: string;
  publishTime: string;
};

export type ExamType = "ONLINE" | "ONLINE_HOMEWORK" | "PRACTICE";

type BaseExam = {
  /** 考试批次id */
  examId: number;
  /** 考生id，用户的每场考试都有一个考生id */
  examStudentId: number;
  /** 考生的考试记录id */
  examRecordDataId: number;
  /** 考试类型 */
  examType: ExamType;
  /** 课程名称 */
  courseName: string;
  /** 课程层次 */
  courseLevel: string;
  /** 专业名称 */
  specialtyName: string;
  /** 考试开始时间。日期时间字符串，以前叫后台改为数字，但后台不改，遗留的设计错误。 */
  startTime: string;
  /** 考试结束时间 */
  endTime: string;
};

type ExamCycle = {
  /** 考试周期是否开启循环 */
  examCycleEnabled: boolean;
  /** 周循环中周几开启考试 1-7 */
  examCycleWeek: number[];
  /** 周期循环中开启的时间段 HHmm [['08:00', '10:30'], ['15:00', '17:00']] */
  examCycleTimeRange: { timeRange: [string, string] }[];
};

export type OnlineExam = BaseExam &
  ExamCycle & {
    /** 课程id */
    courseId: number;
    /** 是否显示考生承诺书 */
    showUndertaking: boolean;
    /** 考生承诺书内容 */
    undertaking: string;
    /** 是否启用人脸比对 */
    faceEnable: boolean;
    /** 是否强制启用人脸比对 */
    faceCheck: boolean;
    /** 剩余考试次数 */
    allowExamCount: number;
    /** 是否允许查看客观分 */
    isObjScoreView: boolean;
  };

export type PracticeExam = ExamCycle & {
  /** 考试批次id */
  examId: number;
  /** 考试批次名称 */
  examName: string;
  /** 考试类型 */
  examType: ExamType;
  /** 考生id，用户的每场考试都有一个考生id */
  examStudentId: number;
  /** 课程id */
  courseId: number;
  /** 课程名称 */
  courseName: string;
  /** 课程code */
  courseCode: string;
  /** 考试开始时间。 */
  startTime: string;
  /** 考试结束时间 */
  endTime: string;
  /** 练习次数 */
  practiceCount: number;
  /** 剩余练习次数 */
  allowExamCount: number;
  /** 最近正确率(后端已乘100) */
  recentObjectiveAccuracy: number;
  /** 平均正确率(后端已乘100) */
  aveObjectiveAccuracy: number;
  /** 最高正确率(后端已乘100) */
  maxObjectiveAccuracy: number;
};

export type OfflineExam = BaseExam & {
  /** 机构名称 */
  orgName: string;
  /** 考试周期是否开启循环 */
  offlineFiles: Array<{ offlineFileUrl: string; originalFileName: string }>;
  /** 试卷id */
  paperId: string;
};

export type OnlinePracticeRecord = {
  id: number;
  /** 考试开始时间。日期时间字符串，以前叫后台改为数字，但后台不改，遗留的设计错误。 */
  startTime: string;
  /** 考试结束时间 */
  endTime: string;
  /** 练习时长 类型待确认？？？ */
  usedExamTime: number;
  /** 总题量 */
  totalQuestionCount: number;
  /** 正确的数量  */
  succQuestionNum: number;
  /** 错误的数量 */
  failQuestionNum: number;
  /** 未答的数量 */
  notAnsweredCount: number;
  /** 客观题正确率(后端已乘100) */
  objectiveAccuracy: number;
};

export type OnlinePracticeRecordResult = {
  courseName: string;
  courseCode: string;
  /** 客观题正确率(后端已乘100) */
  objectiveAccuracy: string;
  paperStructInfos: Array<{
    index: number;
    /** 题目分类 */
    title: string;
    /** 题量 */
    questionCount: number;
    /** 正确的数量  */
    succQuestionNum: number;
    /** 错误的数量 */
    failQuestionNum: number;
    /** 未答的数量 */
    notAnsweredCount: number;
  }>;
};

export type QuestionUnitItem = {
  body: string;
  questionOptionList: Array<{ body: string }>;
  questionType: string;
  rightAnswer: string[];
};

export type QuestionWrapperItem = {
  questionId: string;
  /** 试题先导内容，一般出现在套题中 */
  body: string;
  /** 试题小题的信息列表 */
  examQuestionList: ExamQuestion[];
  /** 试题小题的内容列表，一般应该是用不到的 */
  questionUnitList: QuestionUnitItem[];
  /** 小题的列表，学生端用不着，只用到它的length */
  questionUnitWrapperList: Array<{ id: string }>;
  limitedPlayTimes: number;
};

export type PaperStruct = {
  defaultPaper: {
    /** index = mainNumber - 1 ？？待确定 */
    questionGroupList: Array<{
      groupName: string;
      groupScore: number;
      /** 试题列表 */
      questionWrapperList: QuestionWrapperItem[];
    }>;
  };
};

export type AudioPlayTime = { audioName: string; times: number };
export type ExamQuestion = {
  /** 试题id */
  questionId: string;
  /** 题目序号 */
  order: number;
  /** 题目在答题中的序号。 old groupOrder */
  inGroupOrder: number;
  /** 限制音频播放次数。从paperStruct[][]['limitedPlayTimes']得到。前端添加。 */
  limitedPlayTimes: number;
  questionScore: number;
  /** 学生填写的答案，和C端、App端结构一致 */
  studentAnswer: string | null;
  /** 试题类型 */
  questionType:
    | "SINGLE_CHOICE"
    | "MULTIPLE_CHOICE"
    | "TRUE_OR_FALSE"
    | "FILL_UP"
    | "ESSAY";
  /** 小题乱序。细化？？？ */
  optionPermutation: number[];
  /** 大题名称 */
  groupName: string;
  /** 什么的总分？？？ */
  groupTotal: number;
  /** 是否被标上星号。 */
  isStarred: boolean;
  /** 大题号 */
  mainNumber: number;
  /** 小题号 */
  subNumber: number;
  /** 套题的body */
  parentBody: string | null;
  /** 试题内容。重点要重构音频、填空题###的处理逻辑 */
  body: string;
  /** 是否为套题。此处要梳理数据结构，重新计算！！！ */
  isNestedQuestion: boolean;
  /** 文本作答的题目，是否开启音频作答。和考试的weixinAnswerEnabled有关。 */
  answerType: "SINGLE_AUDIO" | null;
  /** 练习时有正确答案 */
  rightAnswer?: string[];
  /** 选择题的选项 */
  questionOptionList: { body: string }[];
  /** 题目中是否有音频 */
  hasAudios: boolean;
  /** 试题内容。通过网络获取。 */
  questionContent: string;
  /** 试题内容是否已通过网络获取到。 */
  gotQuestionContent: boolean;
  /** 答案是否已经被用户更新过了 */
  dirty: boolean;
  /** 只有第一题有此数据，用来像服务器保存音频播放次数 */
  audioPlayTimes: AudioPlayTime[];
};

export type ExamInProgress = {
  /**  "S-101000" 请重试 */
  code: "000000" | "S-101000";
  /** 是否超过断点次数限制 */
  isExceed: boolean;
  /** 允许的最大断点次数 */
  maxInterruptNum: number;
  /** 允许的最大切屏次数 */
  maxSwitchScreenCount: number;
  examId: number;
  examRecordDataId: number;
};

// define process.env

// clientVersion.ts 管理可用客户端版本。
// api: getCurrentClientVersion isSupportedClientVersion

/** 得到当前客户端版本 1.9.* */
type GetCurrentClientVersion = () => string;
type IsSupportedClientVersion = () => boolean;

// updateManager.ts 应用(非客户端)是否应该更新了
// api: isNewWebAppAvailable isNewBackendAvailable getNewBackendDesc

// native.ts 管理原生及系统命令，详细说明path和相对path
// api: isElectron execCmd readFile getRemoteApps getVirtualCams

// runtimeMonitor.ts 检测当前运行的环境是否合法
// 检测是否打开了控制台；检测是否使用了高版本的chrome(feature检测)
// 检测onResize；截屏

// cache design
// service worker ; image cache; audio cache/prefetch

// router design 严格受控的页面跳转，既考虑刷新，也考虑缓存

// localStorage design
// loginType accountValue

// sessionStorage design
// store 防破解？仅保存 user, QECSConfig 信息

// 开考流程
// 1. 确保每一次请求的结果都得到充分利用，即在页面中保存好信息，不做多余的重试，不在中途刷新页面，除敏感信息外要从上个页面带到下个页面
// 2. 开考和考试中的信息获取要隔离，方便从不同的页面中进入考试
// 3. 进入页面后，不轻易退出，要做足够的重试。退出的条件要设置充分，比如网络断了。

// 不加下面这句的话，整个.d.ts定义的类型就会默认为export，都可以被import
export {};
