package com.qmth.themis.business.service.impl;

import com.qmth.themis.business.bean.exam.MobileAnswerSubmitReponseBean;
import com.qmth.themis.business.bean.exam.VersionBean;
import com.qmth.themis.business.bean.mobile.MobileAuthorizationBean;
import com.qmth.themis.business.bean.mobile.MobileAuthorizationMonitorBean;
import com.qmth.themis.business.bean.mobile.MobileAuthorizationParamBean;
import com.qmth.themis.business.bean.mobile.MobileAuthorizationUploadBean;
import com.qmth.themis.business.cache.ExamRecordCacheUtil;
import com.qmth.themis.business.cache.MobileAuthCacheUtil;
import com.qmth.themis.business.cache.RedisKeyHelper;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.cache.bean.ExamStudentAnswerCacheBean;
import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.AuthDto;
import com.qmth.themis.business.dto.ExpireTimeDTO;
import com.qmth.themis.business.dto.MqDto;
import com.qmth.themis.business.entity.SysConfig;
import com.qmth.themis.business.entity.TBAppVersion;
import com.qmth.themis.business.entity.TBSession;
import com.qmth.themis.business.enums.*;
import com.qmth.themis.business.service.*;
import com.qmth.themis.business.util.*;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.enums.Platform;
import com.qmth.themis.common.enums.Source;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.IpUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class TEMobileServiceImpl implements TEMobileService {

    @Resource
    RedisUtil redisUtil;

    @Resource
    TEExamService examService;

    @Resource
    TEExamStudentService examStudentService;

    @Resource
    TencentYunUtil tencentYunUtil;

    @Resource
    MqDtoService mqDtoService;

    @Resource
    MqUtil mqUtil;

    @Resource
    TBAppVersionService tbAppVersionService;

    @Resource
    ThemisCacheService themisCacheService;

    @Override
    public MobileAuthorizationBean authorization(MobileAuthorizationParamBean param) throws NoSuchAlgorithmException {
        MobileModeEnum mode = MobileModeEnum.valueOf(param.getMode().toUpperCase());
        String code = param.getCode();
        MobileAuthorizationBean ret = null;
        Source source = null;
        Platform platform = ServletUtil.getRequestPlatform();
        if (MobileModeEnum.MOBILE_MONITOR.equals(mode)) {
            MobileAuthorizationMonitorBean mb = new MobileAuthorizationMonitorBean();
            ret = mb;
            source = monitorAuthorization(mode, code, mb);
            TBAppVersion tbAppVersion = tbAppVersionService.getMaxAppVersion(platform.name());
            if (Objects.nonNull(tbAppVersion)) {
                VersionBean versionBean = new VersionBean(tbAppVersion.getName(), tbAppVersion.getValue(), tbAppVersion.getUrl());
                ret.setVersion(versionBean);
            }
        } else if (MobileModeEnum.PHOTO_UPLOAD.equals(mode) || MobileModeEnum.AUDIO_UPLOAD.equals(mode)) {
            MobileAuthorizationUploadBean ub = new MobileAuthorizationUploadBean();
            ret = ub;
            source = answerUploadAuthorization(mode, code, ub);
        }
        if (ExamRecordCacheUtil.getId(ret.getRecordId()) == null) {
            throw new BusinessException(ExceptionResultEnum.NOT_FOUND_EXAM_RECORD);
        }
        SystemConstant.checkExamStatus(ret.getRecordId());
        ExamStudentCacheBean es = examStudentService
                .getExamStudentCacheBean(ExamRecordCacheUtil.getExamStudentId(ret.getRecordId()));
        if (es == null) {
            throw new BusinessException(ExceptionResultEnum.NOT_FOUND_EXAM_STUDENT);
        }
        ExamRecordStatusEnum sta = ExamRecordCacheUtil.getStatus(ret.getRecordId());
        if (ExamRecordStatusEnum.FINISHED.equals(sta) || ExamRecordStatusEnum.PERSISTED.equals(sta) || sta == ExamRecordStatusEnum.JUNK) {
            throw new BusinessException(ExceptionResultEnum.EXAM_FINISH);
        }
        String deviceId = ServletUtil.getRequestDeviceId();
        AuthDto authDto = themisCacheService.addStudentAuthCache(es.getStudentId());
        // 生成token
        String token = SystemConstant.getNanoId();
        String sessionId = ret.getSessionId();
        ExpireTimeDTO expireBean = SystemConstant.getExpireTime(source, platform);
        TBSession tbSession = new TBSession(Objects.nonNull(authDto.getTbOrg()) ? authDto.getTbOrg().getId() : null, sessionId, String.valueOf(es.getStudentId()),
                authDto.getRoleCodes().toString(), source.name(), platform.name(), deviceId,
                ServletUtil.getRequest().getLocalAddr(), token, expireBean.getDate().getTime());
        redisUtil.setUserSession(sessionId, tbSession, expireBean.getExpireSeconds());
        ret.setAccessToken(token);
        ret.setTime(System.currentTimeMillis());
        ret.setMode(mode);
        //mq发送消息start
        MqDto mqDto = new MqDto(mqUtil.getTopic(), platform.name(), SystemOperationEnum.SESSION, sessionId);
        mqDtoService.assembleSendAsyncMsg(mqDto);
        MqDto mqDtoLog = new MqDto(mqUtil.getTopic(), MqTagEnum.STUDENT.name(),
                SystemOperationEnum.LOGIN, MqTagEnum.STUDENT, String.valueOf(es.getStudentId()), IpUtil.getRemoteIp(ServletUtil.getRequest()));
        mqDtoService.assembleSendAsyncMsg(mqDtoLog);
        //mq发送消息end
        //测试
        //        String test = SignatureInfo.build(SignatureType.TOKEN, sessionId, token);
        //        ret.setAccessToken(test);
        return ret;
    }

    private Source monitorAuthorization(MobileModeEnum mode, String code, MobileAuthorizationMonitorBean ret)
            throws NoSuchAlgorithmException {
        Long recordId = MobileAuthCacheUtil.getRecordId(mode, code);
        MonitorVideoSourceEnum monitorVideoSource = MobileAuthCacheUtil.getMonitorVideoSource(mode, code);
        String monitorRecord = ExamRecordCacheUtil.getMonitorRecord(recordId);
        if (MobileAuthCacheUtil.getMode(mode, code) == null) {
            throw new BusinessException(ExceptionResultEnum.QR_CODE_EXPIRE);
        }
        ret.setRecordId(recordId);
        ret.setMonitorAppId(tencentYunUtil.getAppId());
        ret.setMonitorKey(ExamRecordCacheUtil.getMonitorKey(recordId));
        ret.setMonitorVideoSource(monitorVideoSource);
        ret = getMonitorAudioEnable(recordId, monitorVideoSource, ret);
        SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.MONITOR_CONFIG_PREFIX);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置监控前缀"));
        ret.setMonitorStreamId(SystemConstant.setStreamId(sysConfig.getConfigValue(), recordId, monitorVideoSource));
        Map<String, String> monitorRecordMap = null;
        if (Objects.nonNull(monitorRecord) && !Objects.equals(monitorRecord.trim().replaceAll(" ", ""), "")) {
            List<String> monitorRecordList = Arrays.asList(monitorRecord.trim().toUpperCase().split(","));
            monitorRecordMap = monitorRecordList.stream()
                    .collect(Collectors.toMap(Function.identity(), s -> s));
            String monitorRecordStr = monitorRecordMap.get(monitorVideoSource.name());
            ret.setMonitorRecordId(Objects.nonNull(monitorRecordStr) ? ret.getMonitorStreamId() : null);
        }

        Source sourceEnum = monitorVideoSource.getSessionSource();
        ExamStudentCacheBean es = examStudentService
                .getExamStudentCacheBean(ExamRecordCacheUtil.getExamStudentId(ret.getRecordId()));
        String userType = MobileAuthCacheUtil.getUserType(mode, code);
        AuthDto authDto = themisCacheService.addStudentAuthCache(es.getStudentId());
        String sessionId = SessionUtil
                .digest(es.getStudentId(), Math.abs(authDto.getRoleCodes().toString().hashCode()), sourceEnum);
        if (Objects.nonNull(userType) && userType.contains(RoleEnum.STUDENT.name())) {
            ret.setMonitorUserId("s_" + sessionId);
        } else {
            ret.setMonitorUserId("m_" + sessionId);
        }
        ret.setSessionId(sessionId);
        ret.setMonitorUserSig(tencentYunUtil.getSign(ret.getMonitorUserId(), SystemConstant.TENCENT_EXPIRE_TIME));
        return sourceEnum;
    }

    private Source answerUploadAuthorization(MobileModeEnum mode, String code, MobileAuthorizationUploadBean ret)
            throws NoSuchAlgorithmException {
        Long recordId = MobileAuthCacheUtil.getRecordId(mode, code);
        Integer mainNumber = MobileAuthCacheUtil.getMainNumber(mode, code);
        Integer subNumber = MobileAuthCacheUtil.getSubNumber(mode, code);
        Integer subIndex = MobileAuthCacheUtil.getSubIndex(mode, code);
        String questionNumber = MobileAuthCacheUtil.getQuestionNumber(mode, code);
        if (MobileAuthCacheUtil.getMode(mode, code) == null) {
            throw new BusinessException(ExceptionResultEnum.QR_CODE_EXPIRE);
        }
        ExamStudentCacheBean es = examStudentService
                .getExamStudentCacheBean(ExamRecordCacheUtil.getExamStudentId(recordId));
        Source sourceEnum = Source.OE_ANSWER;
        AuthDto authDto = themisCacheService.addStudentAuthCache(es.getStudentId());
        String sessionId = SessionUtil
                .digest(es.getStudentId(), Math.abs(authDto.getRoleCodes().toString().hashCode()), sourceEnum);
        ret.setRecordId(recordId);
        ret.setMainNumber(mainNumber);
        ret.setSubNumber(subNumber);
        ret.setSubIndex(subIndex);
        ret.setQuestionNumber(Objects.isNull(questionNumber) ? Objects.nonNull(subIndex) ? "第" + mainNumber + "大题第" + subNumber + "题第" + questionNumber + "小题" : "第" + mainNumber + "大题第" + questionNumber + "小题" : questionNumber);
        ret.setCourseName(es.getCourseName());
        ret.setSessionId(sessionId);
        return sourceEnum;
    }

    @Override
    public MobileAnswerSubmitReponseBean answerSubmit(Long studentId, Long recordId, Integer mainNumber,
                                                      Integer subNumber, Integer subIndex, String answer) {
        // 校验当前登录用户和参数一致性
        if (ExamRecordCacheUtil.getId(recordId) == null) {
            throw new BusinessException(ExceptionResultEnum.NOT_FOUND_EXAM_RECORD);
        }
        Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
        ExamStudentCacheBean es = (ExamStudentCacheBean) redisUtil
                .get(RedisKeyHelper.examStudentCacheKey(examStudentId));
        if (es == null) {
            throw new BusinessException(ExceptionResultEnum.NOT_FOUND_EXAM_STUDENT);
        }
        if (!studentId.equals(es.getStudentId())) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ID_NOT_EQUALY);
        }
        ExamRecordStatusEnum sta = ExamRecordCacheUtil.getStatus(recordId);
        if (ExamRecordStatusEnum.FINISHED.equals(sta) || ExamRecordStatusEnum.PERSISTED.equals(sta) || sta == ExamRecordStatusEnum.JUNK) {
            throw new BusinessException(ExceptionResultEnum.EXAM_FINISH);
        }
        ExamStudentAnswerCacheBean answerCache = (ExamStudentAnswerCacheBean) redisUtil
                .get(RedisKeyHelper.examAnswerKey(recordId),
                        RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex));
        if (answerCache == null) {
            answerCache = new ExamStudentAnswerCacheBean();
            answerCache.setMainNumber(mainNumber);
            answerCache.setSubNumber(subNumber);
            answerCache.setSubIndex(subIndex);
            answerCache.setAnswer(answer);
        }
        // 更新考生作答
        redisUtil.set(RedisKeyHelper.examAnswerKey(recordId),
                RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex), answerCache);
        MobileAnswerSubmitReponseBean ret = new MobileAnswerSubmitReponseBean();
        ret.setUpdateTime(System.currentTimeMillis());
        return ret;
    }

    /**
     * 设置是否允许考生端开启语音
     *
     * @param recordId
     * @param videoSource
     * @param ret
     * @return
     */
    private MobileAuthorizationMonitorBean getMonitorAudioEnable(Long recordId, MonitorVideoSourceEnum videoSource, MobileAuthorizationMonitorBean ret) {
        Long examId = ExamRecordCacheUtil.getExamId(recordId);
        ExamCacheBean exam = examService.getExamCacheBean(examId);
        ret.setExamStudentCallEnable(exam.getExamStudentCallEnable());
        String monitorVideoSource = exam.getMonitorVideoSource();
        if (!monitorVideoSource.toUpperCase().contains(MonitorVideoSourceEnum.CLIENT_SCREEN.name())
                && !monitorVideoSource.toUpperCase().contains(MonitorVideoSourceEnum.CLIENT_CAMERA.name())
                && MonitorVideoSourceEnum.MOBILE_FIRST.equals(videoSource)) {
            ret.setMonitorAudioEnable(true);
        } else {
            ret.setMonitorAudioEnable(false);
        }
        return ret;
    }

}