package com.qmth.distributed.print.business.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.qmth.boot.tools.signature.SignatureType;
import com.qmth.distributed.print.business.bean.dto.SyncExamCardDto;
import com.qmth.distributed.print.business.bean.dto.SyncExamStudentDto;
import com.qmth.distributed.print.business.bean.params.SyncDataParam;
import com.qmth.distributed.print.business.entity.*;
import com.qmth.distributed.print.business.enums.PrintPlanStatusEnum;
import com.qmth.distributed.print.business.service.*;
import com.qmth.distributed.print.business.util.HttpKit;
import com.qmth.teachcloud.common.SignatureEntityTest;
import com.qmth.teachcloud.common.config.DictionaryConfig;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.BasicSchool;
import com.qmth.teachcloud.common.entity.SysConfig;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.TaskResultEnum;
import com.qmth.teachcloud.common.enums.TaskStatusEnum;
import com.qmth.teachcloud.common.service.CommonCacheService;
import com.qmth.teachcloud.common.service.SysConfigService;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Date: 2021/5/20.
 */
@Service
public class DataSyncServiceImpl implements DataSyncService {

    private final static Logger log = LoggerFactory.getLogger(DataSyncServiceImpl.class);

    @Autowired
    private ExamPrintPlanService examPrintPlanService;

    @Autowired
    private TBSyncTaskService tbSyncTaskService;

    @Autowired
    private DictionaryConfig dictionaryConfig;

    @Autowired
    private CommonCacheService commonCacheService;

    @Autowired
    private ExamStudentService examStudentService;

    @Autowired
    private ExamDetailService examDetailService;

    @Autowired
    private ExamCardService examCardService;

    @Autowired
    private SysConfigService sysConfigService;

    private ExecutorService executors = Executors.newFixedThreadPool(5);

    private static final String SAVE_EXAM_TYPE = "SCAN_IMAGE";
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final String POST_METHOD = "POST";

    /**
     * 定时任务批量同步
     */
    @Override
    public void syncToCloudReview() {
        // 查询可同步计划（同步状态为空：未同步，false：同步失败）
        QueryWrapper<ExamPrintPlan> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamPrintPlan::getStatus, PrintPlanStatusEnum.END)
                .and(q -> q.isNull(ExamPrintPlan::getSyncStatus).or().eq(ExamPrintPlan::getSyncStatus, false));
        List<ExamPrintPlan> examPrintPlans = examPrintPlanService.list(queryWrapper);
        if (!CollectionUtils.isEmpty(examPrintPlans)) {
            for (ExamPrintPlan examPrintPlan : examPrintPlans) {
                executors.execute(syncData(examPrintPlan, null, examPrintPlan.getName()));
            }
        }
    }

    /**
     * 单个同步
     *
     * @param printPlanId
     * @param thirdRelateId
     */
    @Override
    public void syncDataCloud(Long printPlanId, Long thirdRelateId) {
        ExamPrintPlan examPrintPlan = examPrintPlanService.getById(printPlanId);
        if (examPrintPlan == null) {
            throw ExceptionResultEnum.ERROR.exception("印刷计划数据异常");
        }
        if (!PrintPlanStatusEnum.END.equals(examPrintPlan.getStatus()) && !PrintPlanStatusEnum.PRINT_FINISH.equals(examPrintPlan.getStatus())) {
            throw ExceptionResultEnum.ERROR.exception("印刷计划未打印完成或者未结束，不能同步数据");
        }
        Long relateId = Objects.isNull(thirdRelateId) ? examPrintPlan.getThirdRelateId() : thirdRelateId;
        String thirdRelateName = Objects.isNull(examPrintPlan.getThirdRelateId()) ? examPrintPlan.getName() : examPrintPlan.getThirdRelateName();
        executors.execute(syncData(examPrintPlan, relateId, thirdRelateName));

    }

    @Override
    public void syncDataMerge(SyncDataParam syncDataParam) {
        List<Long> printPlans = syncDataParam.getList();
        if (CollectionUtils.isEmpty(printPlans)) {
            throw ExceptionResultEnum.ERROR.exception("请选择需要合并的印刷任务");
        }

        if(Objects.isNull(syncDataParam.getThirdRelateId()) || Objects.isNull(syncDataParam.getThirdRelateName())){
            throw ExceptionResultEnum.ERROR.exception("考试ID、考试名称至少填一个");
        }

        // todo 校验印刷计划是否完成
        if (syncDataParam.getThirdRelateId() == null) {
            Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
            String time = String.valueOf(System.currentTimeMillis());
            QueryWrapper<ExamPrintPlan> printPlanQueryWrapper = new QueryWrapper<>();
            printPlanQueryWrapper.lambda().in(ExamPrintPlan::getId, syncDataParam.getList());
            List<ExamPrintPlan> examPrintPlans = examPrintPlanService.list(printPlanQueryWrapper);
            ExamPrintPlan examPrintPlan = examPrintPlans.stream().max(Comparator.comparingLong(ExamPrintPlan::getExamEndTime)).get();
            String examTime = DateUtil.format(new Date(examPrintPlan.getExamEndTime()), DATE_FORMAT);
            syncDataParam.setThirdRelateId(getExamId(schoolId, time, syncDataParam.getThirdRelateName(), examTime));
        }

        for (Long printPlanId : printPlans) {
            ExamPrintPlan printPlan = examPrintPlanService.getById(printPlanId);
            executors.execute(syncData(printPlan, syncDataParam.getThirdRelateId(), syncDataParam.getThirdRelateName()));
        }
    }

    private TimerTask syncData(ExamPrintPlan examPrintPlan, Long thirdRelateId, String thirdRelateName) {
        return new TimerTask() {
            @Override
            public void run() {
                SysConfig sysConfig = sysConfigService.getByKey("sys.sync.enable");
                if (sysConfig != null && "true".equals(sysConfig.getConfigValue())) {
                    TBSyncTask syncTask = tbSyncTaskService.saveTask(examPrintPlan);
                    doSyncCore(examPrintPlan, thirdRelateId, thirdRelateName, syncTask);
                }
            }
        };
    }

    /**
     * 同步核心方法
     *
     * @param thirdRelateName
     * @param syncTask
     */
    @Transactional
    public void doSyncCore(ExamPrintPlan examPrintPlan, Long thirdRelateId, String thirdRelateName, TBSyncTask syncTask) {
        UpdateWrapper<ExamPrintPlan> updateWrapper = new UpdateWrapper<>();
        try {
            if (!PrintPlanStatusEnum.END.equals(examPrintPlan.getStatus()) && !PrintPlanStatusEnum.PRINT_FINISH.equals(examPrintPlan.getStatus())) {
                throw ExceptionResultEnum.ERROR.exception("印刷计划未打印完成或者未结束，不能同步数据");
            }
            // 校验同步url
            validatUrl();
            UpdateWrapper<TBSyncTask> tbSyncTaskUpdateWrapper = new UpdateWrapper<>();
            tbSyncTaskUpdateWrapper.lambda().set(TBSyncTask::getStatus, TaskStatusEnum.RUNNING).eq(TBSyncTask::getId, syncTask.getId());
            tbSyncTaskService.update(tbSyncTaskUpdateWrapper);

            // 同步计划 -> 对应云阅卷考试
            ExamPrintPlan printPlan = examSave(examPrintPlan, thirdRelateId, thirdRelateName);
            syncTask.setThirdRelateId(printPlan.getThirdRelateId());
            // 考试同步成功，才能同步考生和题卡
            if (Objects.nonNull(printPlan.getThirdRelateId())) {
                studentSave(printPlan);
                cardUpload(printPlan);
            } else {
                throw ExceptionResultEnum.ERROR.exception("无法获取云阅卷考试，同步数据失败");
            }

            // 更新计划状态
            updateWrapper.lambda().set(ExamPrintPlan::getSyncStatus, true).eq(ExamPrintPlan::getId, examPrintPlan.getId());

            // 更新日志表
            syncTask.setResult(TaskResultEnum.SUCCESS);
            syncTask.setErrorMessage(null);
        } catch (Exception e) {
            syncTask.setResult(TaskResultEnum.ERROR);
            syncTask.setErrorMessage(e.getMessage());
            updateWrapper.lambda().set(ExamPrintPlan::getSyncStatus, false).eq(ExamPrintPlan::getId, examPrintPlan.getId());
        } finally {
            syncTask.setStatus(TaskStatusEnum.FINISH);
            tbSyncTaskService.saveOrUpdate(syncTask);
            examPrintPlanService.update(updateWrapper);
        }

    }

    /**
     * 校验url是否非空
     */
    private void validatUrl() {
        String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
        String examSaveUrl = dictionaryConfig.syncDataDomain().getExamSaveUrl();
        String studentSaveUrl = dictionaryConfig.syncDataDomain().getStudentSaveUrl();
        String cardUploadUrl = dictionaryConfig.syncDataDomain().getCardUploadUrl();

        if (StringUtils.isAnyBlank(hostUrl, examSaveUrl, studentSaveUrl, cardUploadUrl)) {
            throw ExceptionResultEnum.ERROR.exception("云阅卷同步接口URL未正确配置");
        }
    }

    /**
     * 创建考试
     *
     * @param examPrintPlan
     * @param thirdRelateName
     * @return
     */
    public ExamPrintPlan examSave(ExamPrintPlan examPrintPlan, Long thirdRelateId, String thirdRelateName) {
        try {
            if (Objects.isNull(thirdRelateId)) {
                String code = String.valueOf(examPrintPlan.getId());
                String name = examPrintPlan.getName();
                String examTime = DateUtil.format(new Date(examPrintPlan.getExamEndTime()), DATE_FORMAT);
                thirdRelateId = getExamId(examPrintPlan.getSchoolId(), code, name, examTime);
            }
            UpdateWrapper<ExamPrintPlan> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(ExamPrintPlan::getThirdRelateId, thirdRelateId)
                    .set(ExamPrintPlan::getThirdRelateName, thirdRelateName)
                    .set(ExamPrintPlan::getSyncStatus, null)
                    .eq(ExamPrintPlan::getId, examPrintPlan.getId());
            examPrintPlanService.update(updateWrapper);
            return examPrintPlanService.getById(examPrintPlan.getId());
        } catch (Exception e) {
            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
        }
    }

    public Long getExamId(Long schoolId, String code, String name, String examTime) {
        String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
        String examSaveUrl = dictionaryConfig.syncDataDomain().getExamSaveUrl();
        String postUrl = hostUrl.concat(examSaveUrl);
        //参数
        Map<String, String> map = new HashMap<>();
        map.put("code", code);
        map.put("name", name);
        map.put("examTime", examTime);
        map.put("type", SAVE_EXAM_TYPE);

        String result = HttpKit.sendPost(postUrl, getHeaders(schoolId, examSaveUrl), map, null, null, null);
        JSONObject jsonObject = JSONObject.parseObject(result);
        if (jsonObject.containsKey("id")) {
            return Long.valueOf(jsonObject.get("id").toString());
        } else {
            throw ExceptionResultEnum.ERROR.exception("考试同步失败");
        }
    }

    /**
     * 新增考生
     *
     * @return
     */
    public void studentSave(ExamPrintPlan examPrintPlan) {
        String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
        String studentSaveUrl = dictionaryConfig.syncDataDomain().getStudentSaveUrl();
        String postUrl = hostUrl.concat(studentSaveUrl);

        List<SyncExamStudentDto> examStudents = examStudentService.listStudentByPrintPlanIdAndSyncStatus(examPrintPlan.getId());
        for (SyncExamStudentDto examStudent : examStudents) {
            try {
                //参数
                Map<String, String> map = new HashMap<>();
                map.put("examId", String.valueOf(examPrintPlan.getThirdRelateId()));
                map.put("examNumber", examStudent.getTicketNumber());
                map.put("studentCode", examStudent.getStudentCode());
                map.put("name", examStudent.getStudentName());
                map.put("college", "无");
                map.put("className", getClassName(examStudent.getExtendFields()));
                map.put("teacher", "无");
                map.put("subjectCode", examStudent.getPaperNumber()); // 取试卷编号
                map.put("subjectName", examStudent.getCourseName());

                String result = HttpKit.sendPost(postUrl, getHeaders(examPrintPlan.getSchoolId(), studentSaveUrl), map, null, null, null);
                JSONObject jsonObject = JSONObject.parseObject(result);
                if (jsonObject.containsKey("updateTime")) {
                    UpdateWrapper<ExamStudent> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.lambda().set(ExamStudent::getSyncStatus, true).eq(ExamStudent::getId, examStudent.getId());
                    examStudentService.update(updateWrapper);
                }
            } catch (Exception e) {
                throw ExceptionResultEnum.ERROR.exception(e.getMessage());
            }
        }
    }

    /**
     * 推送题卡
     *
     * @return
     */
    public void cardUpload(ExamPrintPlan examPrintPlan) {
        String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
        String cardUploadUrl = dictionaryConfig.syncDataDomain().getCardUploadUrl();
        String postUrl = hostUrl.concat(cardUploadUrl);
        List<ExamDetailCourse> examDetailCourses = examDetailService.listSyncPaperNumberByPrintPlanId(examPrintPlan.getId());
        if (CollectionUtils.isEmpty(examDetailCourses)) {
            return;
        }
        for (ExamDetailCourse examDetailCours : examDetailCourses) {
            List<SyncExamCardDto> syncExamCardDtos = examCardService.listSyncCardByCourseCodeAndPaperNumber(examDetailCours.getSchoolId(), examDetailCours.getCourseCode(), examDetailCours.getPaperNumber());
            if (CollectionUtils.isEmpty(syncExamCardDtos)) {
                return;
            }

            if (syncExamCardDtos.size() > 1) {
//                throw ExceptionResultEnum.ERROR.exception(String.format("数据异常，通过学校：%s,课程代码：%s,试卷编号：%s查出多个题卡数据", examDetailCours.getSchoolId(), examDetailCours.getCourseCode(), examDetailCours.getPaperNumber()));
                log.info("数据异常，通过学校：{},课程代码：{},试卷编号：{}查出{}个题卡数据", examDetailCours.getSchoolId(), examDetailCours.getCourseCode(), examDetailCours.getPaperNumber(), syncExamCardDtos.size());
                return;
            }

            SyncExamCardDto syncExamCardDto = syncExamCardDtos.get(0);
            if (StringUtils.isNotEmpty(syncExamCardDto.getContent())) {
                //生成json文件
                File file = null;
                try {
                    String filePath = SystemConstant.TEMP_FILES_DIR + File.separator + "card-upload" + File.separator + System.currentTimeMillis();
                    file = createJsonFile(filePath, syncExamCardDto.getContent());
                    Map<String, String> files = new HashMap<>();
                    if (file.exists()) {
                        files.put(syncExamCardDto.getTitle(), file.getPath());
                    }

                    //表单数据
                    Map<String, String> formText = new HashMap<>();
                    formText.put("examId", String.valueOf(examPrintPlan.getThirdRelateId()));
                    formText.put("subjectCode", syncExamCardDto.getPaperNumber());
                    formText.put("format", "json");
                    formText.put("md5", DigestUtils.md5Hex(new FileInputStream(file)));


                    String result = HttpKit.sendPost(postUrl, getHeaders(examDetailCours.getSchoolId(), cardUploadUrl), formText, files, null, null);
                    JSONObject jsonObject = JSONObject.parseObject(result);
                    if (jsonObject.containsKey("success")) {
                        String success = jsonObject.get("success").toString();
                        if (Boolean.valueOf(success)) {
                            UpdateWrapper<ExamCard> updateWrapper = new UpdateWrapper<>();
                            updateWrapper.lambda().set(ExamCard::getSyncStatus, true).eq(ExamCard::getId, syncExamCardDto.getId());
                            examCardService.update(updateWrapper);
                        }
                    }
                } catch (Exception e) {
                    throw ExceptionResultEnum.ERROR.exception(e.getMessage());
                } finally {
                    if (file != null && file.exists()) {
                        file.delete();
                    }
                }
            }

        }
    }

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

    /**
     * 签名
     *
     * @param schoolId
     * @param time
     * @param url
     * @return
     */
    private String createSign(Long schoolId, long time, String url) {
        BasicSchool basicSchool = commonCacheService.schoolCache(schoolId);
        if (basicSchool == null) {
            throw ExceptionResultEnum.ERROR.exception("学校不存在");
        } else {
            if (!basicSchool.getEnable()) {
                throw ExceptionResultEnum.ERROR.exception("学校已禁用");
            }
        }
        String signature = SignatureEntityTest.build(SignatureType.SECRET, POST_METHOD, url, time, basicSchool.getAccessKey(), basicSchool.getAccessSecret());
        return signature;
    }

    private String getClassName(String extendCloumn) {
        List<Map> mapList = JSONObject.parseArray(extendCloumn, Map.class);
        for (Map map : mapList) {
            if (Objects.equals("className", map.get("code").toString())) {
                return map.get("value").toString();
            }
        }
        return "无";
    }

    /*
     * 生成文件
     * @param file 文件路径+文件名称
     * @param conent 要生成的文件内容
     */
    public static File createJsonFile(String url, String conent) {

        File file = new File(url);
        if (!file.exists()) {
            file.mkdirs();
        }
        BufferedWriter out = null;
        file = new File(file, UUID.randomUUID().toString() + ".json");
        try {
            file.createNewFile();
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
            out.write(conent);
        } catch (Exception e) {
        } finally {
            try {
                out.close();
            } catch (IOException e) {
            }
        }
        return file;
    }
}
