package com.qmth.jkserver.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qmth.jkserver.constant.SystemConstant;
import com.qmth.jkserver.core.exception.JkServerException;
import com.qmth.jkserver.enums.YunFileUploadEnum;
import com.qmth.jkserver.model.SysConfig;
import com.qmth.jkserver.params.VerifyResultParams;
import com.qmth.jkserver.params.callApi.SaveExamParams;
import com.qmth.jkserver.params.callApi.SaveStudentParams;
import com.qmth.jkserver.params.callApi.SaveSubjectParams;
import com.qmth.jkserver.params.callApi.fileUpload.AnswerFileParams;
import com.qmth.jkserver.params.callApi.fileUpload.JsonFileParams;
import com.qmth.jkserver.params.callApi.fileUpload.PaperFileParams;
import com.qmth.jkserver.service.CallYunMarkApiService;
import com.qmth.jkserver.service.CommonCacheService;
import com.qmth.jkserver.signature.Constants;
import com.qmth.jkserver.signature.SignatureInfo;
import com.qmth.jkserver.signature.SignatureType;
import com.qmth.jkserver.util.HttpUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;

/**
 * @Description: 请求云阅卷接口处理接口实现类
 * @Author: CaoZixuan
 * @Date: 2021-05-08
 */
@Service
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
public class CallYunMarkApiServiceImpl implements CallYunMarkApiService {
    private static final Logger log = org.slf4j.LoggerFactory.getLogger(CallYunMarkApiServiceImpl.class);

    @Resource
    CommonCacheService commonCacheService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<Map> callStudentScoreApi(Long examId, String examCode, Map<String, String> secretMap, Long schoolId) throws IOException {
        SysConfig sysConfig = commonCacheService.addSysConfigCache(schoolId, SystemConstant.CLOUDMARK_HOST_URL);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new JkServerException("未配置云阅卷地址"));
        String url = sysConfig.getConfigValue() + SystemConstant.CLOUD_MARK_STUDENT_SCORE_API;
        Map<String, Object> params = new HashMap<>();
        if (Objects.nonNull(examId)) {
            params.put("examId", examId);
        } else if (Objects.nonNull(examCode)) {
            params.put("examCode", examCode);
        }
        int pageNumber = 1;
        int pageSize = 100;
        List<Map> students = null;
        while (true) {
            params.put("pageNumber", pageNumber);
            params.put("pageSize", pageSize);
            long timestamp = System.currentTimeMillis();
            String accessToken = SignatureInfo.build(SignatureType.SECRET, SystemConstant.METHOD, SystemConstant.CLOUD_MARK_STUDENT_SCORE_API, timestamp, secretMap.get(SystemConstant.ACCESS_KEY), secretMap.get(SystemConstant.ACCESS_SECRET));
            String result = HttpUtil.post(url, params, accessToken, timestamp);
            if (Objects.nonNull(result)) {
                log.info("callStudentScoreApi result:{}", result);
                if (result.contains("HTTP Status 401 – 未经授权的")) {
                    throw new JkServerException("考生成绩获取失败");
                }
                List<Map> tempList = JSONObject.parseArray(result, Map.class);
                if (Objects.nonNull(tempList) && tempList.size() > 0) {
                    if (Objects.isNull(students)) {
                        students = new ArrayList<>();
                    }
                    students.addAll(tempList);
                    pageNumber++;
                } else {
                    break;
                }
            } else {
                break;
            }
        }
        return students;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int callExamSaveApi(SaveExamParams saveExamParams, Map<String, String> secretMap, Long schoolId) throws IOException, IllegalAccessException {
        VerifyResultParams verifyResultParams = SystemConstant.verifyDBFields(saveExamParams, saveExamParams.getClass());
        if (!verifyResultParams.getStatus()) {
            throw new JkServerException(verifyResultParams.getMessage());
        }

        // 参数
        Map paramMap = JSON.parseObject(JSON.toJSONString(saveExamParams), Map.class);
        int id = Integer.parseInt(String.valueOf(paramMap.get("id")));
        String code = String.valueOf(paramMap.get("code"));
        if (id == 0 && (code == null || code.length() == 0 || code.equals("null"))) {
            throw new JkServerException("考试id和考试编号均为空");
        } else if (id > 0) {
            paramMap.remove("code");
        } else {
            paramMap.remove("id");
        }

        SysConfig sysConfig = commonCacheService.addSysConfigCache(schoolId, SystemConstant.CLOUDMARK_HOST_URL);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new JkServerException("未配置云阅卷地址"));
        String url = sysConfig.getConfigValue() + SystemConstant.CLOUD_MARK_EXAM_SAVE_API;

        Map<String, Object> resultMap;
        long timestamp = System.currentTimeMillis();
        String accessToken = SignatureInfo.build(SignatureType.SECRET, SystemConstant.METHOD, SystemConstant.CLOUD_MARK_EXAM_SAVE_API, timestamp, secretMap.get(SystemConstant.ACCESS_KEY), secretMap.get(SystemConstant.ACCESS_SECRET));
        String result = HttpUtil.post(url, paramMap, accessToken, timestamp);
        if (result.contains("HTTP")) {
            log.info("callExamSaveApi result:{}", result);
            throw new JkServerException("考试信息保存失败");
        }
        resultMap = (Map<String, Object>) JSON.parse(result);

        return Integer.parseInt(String.valueOf(resultMap.get("id")));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String callSubjectSaveApi(SaveSubjectParams saveSubjectParams, Map<String, String> secretMap, Long schoolId) throws IOException, IllegalAccessException {
        VerifyResultParams verifyResultParams = SystemConstant.verifyDBFields(saveSubjectParams, saveSubjectParams.getClass());
        if (!verifyResultParams.getStatus()) {
            throw new JkServerException(verifyResultParams.getMessage());
        }

        // 参数
        Map paramMap = JSON.parseObject(JSON.toJSONString(saveSubjectParams), Map.class);

        // 结果
        Map<String, Object> resultMap;

        // 请求参数
        long timestamp = System.currentTimeMillis();
        SysConfig sysConfig = commonCacheService.addSysConfigCache(schoolId, SystemConstant.CLOUDMARK_HOST_URL);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new JkServerException("未配置云阅卷地址"));
        String url = sysConfig.getConfigValue() + SystemConstant.CLOUD_MARK_SUBJECT_SAVE_API;
        String accessToken = SignatureInfo.build(SignatureType.SECRET, SystemConstant.METHOD, SystemConstant.CLOUD_MARK_SUBJECT_SAVE_API, timestamp, secretMap.get(SystemConstant.ACCESS_KEY), secretMap.get(SystemConstant.ACCESS_SECRET));
        String result = HttpUtil.post(url, paramMap, accessToken, timestamp);
        log.info("callSubjectSaveApi result:{}", result);
        if (result.contains("HTTP")) {
            throw new JkServerException("考试科目保存失败");
        }
        resultMap = (Map<String, Object>) JSON.parse(result);
        return String.valueOf(resultMap.get("updateTime"));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String callStudentSaveApi(SaveStudentParams saveStudentParams, Map<String, String> secretMap, Long schoolId) throws IOException, IllegalAccessException {
        VerifyResultParams verifyResultParams = SystemConstant.verifyDBFields(saveStudentParams, saveStudentParams.getClass());
        if (!verifyResultParams.getStatus()) {
            throw new JkServerException(verifyResultParams.getMessage());
        }
        // 参数
        Map paramMap = JSON.parseObject(JSON.toJSONString(saveStudentParams), Map.class);

        // 结果
        Map<String, Object> resultMap;

        // 请求参数
        long timestamp = System.currentTimeMillis();
        SysConfig sysConfig = commonCacheService.addSysConfigCache(schoolId, SystemConstant.CLOUDMARK_HOST_URL);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new JkServerException("未配置云阅卷地址"));
        String url = sysConfig.getConfigValue() + SystemConstant.CLOUD_MARK_STUDENT_SAVE_API;
        String accessToken = SignatureInfo.build(SignatureType.SECRET, SystemConstant.METHOD, SystemConstant.CLOUD_MARK_STUDENT_SAVE_API, timestamp, secretMap.get(SystemConstant.ACCESS_KEY), secretMap.get(SystemConstant.ACCESS_SECRET));
        String result = HttpUtil.post(url, paramMap, accessToken, timestamp);
        log.info("callStudentSaveApi result:{}", result);
        if (result.contains("HTTP Status 401 – authorization unexists")) {
            throw new JkServerException("考生信息保存失败");
        }
        resultMap = (Map<String, Object>) JSON.parse(result);
        return String.valueOf(resultMap.get("updateTime"));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean callFileUploadApi(Object obj, Map<String, String> secretMap, Long schoolId) throws IllegalAccessException, IOException {
        // 参数
        Map<String, Object> paramMap = JSON.parseObject(JSON.toJSONString(obj), Map.class);
        File file = new File(String.valueOf(paramMap.get("file")));

        paramMap.put("md5", DigestUtils.md5Hex(new FileInputStream(file)));

        // 类型
        String type = this.verifyAndGetParamType(obj);

        // api
        SysConfig sysConfig = commonCacheService.addSysConfigCache(schoolId, SystemConstant.CLOUDMARK_HOST_URL);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new JkServerException("未配置云阅卷地址"));
        String host = sysConfig.getConfigValue();
        String api = SystemConstant.CLOUD_MARK_FILE_UPLOAD_API;
        api = api.replaceAll(SystemConstant.FILE_UPLOAD_VARIABLE_URL, type);
        String url = host + api;

        // 结果
        Map<String, Object> resultMap;

        // 请求参数
        Map<String, String> headerMap = getHeaders(api, secretMap);
        String result = HttpUtil.postFile(url, paramMap, headerMap.get(Constants.HEADER_AUTHORIZATION), Long.parseLong(headerMap.get(Constants.HEADER_TIME)));
        log.info("callFileUploadApi result:{}", result);
        if (result.contains("HTTP")) {
            throw new JkServerException("调用云阅卷上传附件失败");
        }
        resultMap = (Map<String, Object>) JSON.parse(result);

        return (boolean) resultMap.get("success");
    }

    /**
     * 校验参数对象内属性准确性，并获取参数对象类型
     *
     * @param obj 参数对象
     * @return 类型
     * @throws IllegalAccessException 异常
     */
    private String verifyAndGetParamType(Object obj) throws IllegalAccessException {
        String type;
        if (obj instanceof JsonFileParams) {
            JsonFileParams jsonFileParams = (JsonFileParams) obj;
            VerifyResultParams verifyResultParams = SystemConstant.verifyDBFields(jsonFileParams, jsonFileParams.getClass());
            if (!verifyResultParams.getStatus()) {
                throw new JkServerException(verifyResultParams.getMessage());
            }
            type = YunFileUploadEnum.JSON.getType();
        } else if (obj instanceof PaperFileParams) {
            PaperFileParams paperFileParams = (PaperFileParams) obj;
            VerifyResultParams verifyResultParams = SystemConstant.verifyDBFields(paperFileParams, paperFileParams.getClass());
            if (!verifyResultParams.getStatus()) {
                throw new JkServerException(verifyResultParams.getMessage());
            }
            type = YunFileUploadEnum.PAPER.getType();
        } else if (obj instanceof AnswerFileParams) {
            AnswerFileParams answerFileParams = (AnswerFileParams) obj;
            VerifyResultParams verifyResultParams = SystemConstant.verifyDBFields(answerFileParams, answerFileParams.getClass());
            if (!verifyResultParams.getStatus()) {
                throw new JkServerException(verifyResultParams.getMessage());
            }
            type = YunFileUploadEnum.ANSWER.getType();
        } else {
            throw new JkServerException("调用云阅卷上传附件接口时，所传参数对象类型不符合任何云阅卷接口参数接受类型");
        }
        return type;
    }

    /**
     * http请求头
     *
     * @param url
     * @return
     */
    private Map<String, String> getHeaders(String url, Map<String, String> secretMap) {
        long time = System.currentTimeMillis();
        Map<String, String> header = new HashMap<>();
        header.put(Constants.HEADER_AUTHORIZATION, createSign(time, url, secretMap));
        header.put(Constants.HEADER_TIME, String.valueOf(time));
        return header;
    }

    /**
     * 签名
     *
     * @param time
     * @param url
     * @return
     */
    private String createSign(long time, String url, Map<String, String> secretMap) {
        String accessKey = secretMap.get(SystemConstant.ACCESS_KEY);
        String accessSecret = secretMap.get(SystemConstant.ACCESS_SECRET);
        String signature = SignatureInfo
                .build(SignatureType.SECRET, "post", url, time, accessKey,
                        accessSecret);
        return signature;
    }
}
