package com.qmth.teachcloud.report.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.qmth.teachcloud.common.contant.SpringContextHolder;
import com.qmth.teachcloud.common.enums.GradeAnalyzePaperStatusEnum;
import com.qmth.teachcloud.report.business.bean.params.CalculateParams;
import com.qmth.teachcloud.report.business.entity.TBSyncTask;
import com.qmth.teachcloud.report.business.enums.DataCalculateSequenceEnum;
import com.qmth.teachcloud.report.business.enums.PublishStatusEnum;
import com.qmth.teachcloud.report.business.service.AnalyzeDataCalculateService;
import com.qmth.teachcloud.report.business.service.AnalyzeForReportService;
import com.qmth.teachcloud.report.business.service.TBExamCourseService;
import com.qmth.teachcloud.report.business.templete.strategy.CalculateTaskTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description: 分析计算及进度控制及断点续算服务实现类
 * @Author: CaoZixuan
 * @Date: 2022-06-10
 */
@Service
public class AnalyzeDataCalculateServiceImpl implements AnalyzeDataCalculateService {
    @Resource
    private TBExamCourseService tbExamCourseService;

    @Override
    public void dataCalculateStart(CalculateParams calculateParams) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException {
        AnalyzeForReportService analyzeForReportService = SpringContextHolder.getBean(AnalyzeForReportService.class);
        CalculateTaskTemplate calculateTaskTemplate = SpringContextHolder.getBean(CalculateTaskTemplate.class);

        TBSyncTask tbSyncTask = calculateParams.getTbSyncTask();
        Long examId = calculateParams.getExamId();
        Long schoolId = calculateParams.getSchoolId();
        List<String> courseCodeList = calculateParams.getCourseCode();
        List<String> needRepeatCourseCodeList = calculateParams.getRepeatCalculateCourseCodeList();
        if (needRepeatCourseCodeList == null) {
            needRepeatCourseCodeList = new ArrayList<>();
        }

        // 上次计算失败的课程和表
        String errorCourseCode = tbSyncTask.getObj();

        // 总任务数
        AtomicInteger totalTask = new AtomicInteger(courseCodeList.size() * DataCalculateSequenceEnum.getTotalWeight());
        AtomicInteger currentTask = new AtomicInteger(0);


        for (String courseCode : courseCodeList) {

            // 判断是否不需要计算
            if (!needRepeatCourseCodeList.contains(courseCode)) {
                // 如果不用重算，判断是否已经算过
                if (tbExamCourseService.checkFinishCalculate(schoolId, examId, courseCode)) {
                    // 如果不用重算且已经算过（记录进度）
                    currentTask.addAndGet(DataCalculateSequenceEnum.getTotalWeight());
                    this.submitTBSyncTask(tbSyncTask, totalTask, currentTask, courseCode, GradeAnalyzePaperStatusEnum.FINISH_CALCULATE);
                    calculateTaskTemplate.updateProgress(calculateParams);
                    continue;
                }
            }
            // 准备计算
            analyzeForReportService.realityForCalculate(examId, courseCode);
            // 上次计算异常且不需要重算的 - 断点续算
            try {
                if (courseCode.equals(errorCourseCode) && !needRepeatCourseCodeList.contains(courseCode)) {
                    // 上次计算异常的课程，且不需要重算
                    String errorMethod = tbSyncTask.getRemark();
                    DataCalculateSequenceEnum error = DataCalculateSequenceEnum.valueOf(errorMethod);
                    int errorIndex = error.getIndex();

                    // 表级计算
                    for (DataCalculateSequenceEnum dataCalculateSequenceEnum : DataCalculateSequenceEnum.values()) {
                        int currentIndex = dataCalculateSequenceEnum.getIndex();
                        if (currentIndex < errorIndex) {
                            // 已经计算过的方法，不用重算，但进度需要累加
                            currentTask.addAndGet(dataCalculateSequenceEnum.getWeight());
                            continue;
                        }
                        // 没有计算的方法和断点的方法都需要重新计算
                        tbSyncTask.setRemark(dataCalculateSequenceEnum.name());
                        String currentMethodName = dataCalculateSequenceEnum.getMethodName();

                        Method currenMethod = analyzeForReportService.getClass().getDeclaredMethod(currentMethodName, Long.class, String.class);
                        currenMethod.invoke(analyzeForReportService, examId, courseCode); // 实例化对象，参数
                        currentTask.addAndGet(dataCalculateSequenceEnum.getWeight());
                        this.submitTBSyncTask(tbSyncTask, totalTask, currentTask, courseCode, GradeAnalyzePaperStatusEnum.CALCULATING);
                        calculateTaskTemplate.updateProgress(calculateParams);
                    }
                } else {
                    // 正常计算和重算
                    // 表级计算
                    for (DataCalculateSequenceEnum dataCalculateSequenceEnum : DataCalculateSequenceEnum.values()) {
                        // 没有计算的方法和断点的方法都需要重新计算
                        tbSyncTask.setRemark(dataCalculateSequenceEnum.name());
                        String currentMethodName = dataCalculateSequenceEnum.getMethodName();
                        Method currenMethod = analyzeForReportService.getClass().getDeclaredMethod(currentMethodName, Long.class, String.class);
                        currenMethod.invoke(analyzeForReportService, examId, courseCode); // 实例化对象，参数
                        currentTask.addAndGet(dataCalculateSequenceEnum.getWeight());
                        this.submitTBSyncTask(tbSyncTask, totalTask, currentTask, courseCode, GradeAnalyzePaperStatusEnum.CALCULATING);
                        calculateTaskTemplate.updateProgress(calculateParams);
                    }
                }
                // 计算完成
                analyzeForReportService.updateCoursePublishStatus(examId, courseCode, PublishStatusEnum.UN_PUBLISH);
                this.submitTBSyncTask(tbSyncTask, totalTask, currentTask, courseCode, GradeAnalyzePaperStatusEnum.FINISH_CALCULATE);
                calculateTaskTemplate.updateProgress(calculateParams);
            } catch (Exception e) {
                // 教研分析计算部分出错时，需要回归t_b_exam_course表状态为未计算 再向外抛出异常
                analyzeForReportService.updateCoursePublishStatus(examId, courseCode, PublishStatusEnum.UN_COMPUTE);
                throw e;
            }
        } //所有科目计算完成后任务状态变成已完成并且清除obj和remark
    }

    /**
     * 提交task变化
     *
     * @param tbSyncTask                  tbSyncTask
     * @param totalTask                   总任务
     * @param currentTask                 当前执行的任务量
     * @param courseCode                  课程编号
     * @param gradeAnalyzePaperStatusEnum 状态
     */
    private void submitTBSyncTask(TBSyncTask tbSyncTask, AtomicInteger totalTask, AtomicInteger currentTask, String courseCode, GradeAnalyzePaperStatusEnum gradeAnalyzePaperStatusEnum) {
        tbSyncTask.setProgress(BigDecimal.valueOf(currentTask.get()).divide(BigDecimal.valueOf(totalTask.get()), 2, RoundingMode.HALF_UP));
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(courseCode, gradeAnalyzePaperStatusEnum);
        tbSyncTask.setObj(jsonObject.toJSONString());
    }
}
