package com.qmth.teachcloud.report.business.templete.strategy;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.teachcloud.common.config.DictionaryConfig;
import com.qmth.teachcloud.common.contant.SpringContextHolder;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.BasicSchool;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.GradeAnalyzePaperStatusEnum;
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.util.*;
import com.qmth.teachcloud.report.business.bean.params.CalculateParams;
import com.qmth.teachcloud.report.business.entity.TBSyncTask;
import com.qmth.teachcloud.report.business.service.TBSyncTaskService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Description: 分析数据模版
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2022/6/7
 */
public abstract class CalculateTaskTemplate {
    private final static Logger log = LoggerFactory.getLogger(CalculateTaskTemplate.class);

    /**
     * 创建异步任务
     *
     * @param calculateParams
     * @throws IOException
     */
    protected void createSyncTask(CalculateParams calculateParams) throws IOException {
        log.info("createSyncTask come in");
        TBSyncTaskService tbSyncTaskService = SpringContextHolder.getBean(TBSyncTaskService.class);
        Collections.sort(calculateParams.getCourseCode());

        String source = Base64Util.encode(ShaUtils.sha1(calculateParams.getExamId() + calculateParams.getCourseCode().toString()));
        QueryWrapper<TBSyncTask> tbSyncTaskQueryWrapper = new QueryWrapper<>();
        tbSyncTaskQueryWrapper.lambda().eq(TBSyncTask::getThirdRelateId, calculateParams.getExamId())
                .eq(TBSyncTask::getSource, source);
        TBSyncTask tbSyncTask = tbSyncTaskService.getOne(tbSyncTaskQueryWrapper);
        if (Objects.isNull(tbSyncTask)) {
            tbSyncTask = new TBSyncTask(calculateParams.getSchoolId(), calculateParams.getExamId(), source);
            tbSyncTaskService.save(tbSyncTask);
        }
        calculateParams.setTbSyncTask(tbSyncTask);
        Map<String, GradeAnalyzePaperStatusEnum> map = new LinkedHashMap<>();
        for (String s : calculateParams.getCourseCode()) {
            map.put(s, GradeAnalyzePaperStatusEnum.READY_TO_CALCULATE);
        }
        calculateParams.setCourseCodeMap(map);
    }

    /**
     * 数据校验，由具体类实现
     *
     * @param calculateParams
     * @throws IOException
     */
    public abstract void dataVaild(CalculateParams calculateParams) throws IOException;

    /**
     * 数据计算，由具体类实现
     *
     * @param calculateParams
     * @throws IOException
     */
    public abstract void calculate(CalculateParams calculateParams) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException;

    /**
     * 更新任务进度方法
     *
     * @param calculateParams
     * @throws IOException
     */
    public void updateProgress(CalculateParams calculateParams) throws IOException {
        log.info("updateProgress come in");
        TBSyncTaskService tbSyncTaskService = SpringContextHolder.getBean(TBSyncTaskService.class);
        TBSyncTask tbSyncTask = calculateParams.getTbSyncTask();
        tbSyncTaskService.updateById(tbSyncTask);

        if (Objects.nonNull(tbSyncTask.getObj())) {
            JSONObject jsonObject = JSONObject.parseObject(tbSyncTask.getObj());
            for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                calculateParams.getCourseCodeMap().put(entry.getKey(), GradeAnalyzePaperStatusEnum.valueOf((String) entry.getValue()));
            }
        }
        callback(calculateParams);
    }

    /**
     * 完成任务方法
     *
     * @param calculateParams
     * @throws IOException
     */
    protected void finished(CalculateParams calculateParams) throws IOException {
        log.info("finished come in");
        TBSyncTask tbSyncTask = calculateParams.getTbSyncTask();
        tbSyncTask.setStatus(TaskStatusEnum.FINISH);
        tbSyncTask.setResult(TaskResultEnum.SUCCESS);
        tbSyncTask.setRemark(null);
        tbSyncTask.setObj(null);
        updateProgress(calculateParams);
    }

    /**
     * 任务异常方法
     *
     * @param calculateParams
     * @throws IOException
     */
    protected void exception(CalculateParams calculateParams) throws IOException {
        log.info("exception come in");
        TBSyncTask tbSyncTask = calculateParams.getTbSyncTask();
        tbSyncTask.setStatus(TaskStatusEnum.FINISH);
        tbSyncTask.setResult(TaskResultEnum.ERROR);
        updateProgress(calculateParams);
    }

    /**
     * 回调通知方法
     *
     * @param calculateParams
     * @throws IOException
     */
    protected void callback(CalculateParams calculateParams) throws IOException {
        log.info("callback come in");
        DictionaryConfig dictionaryConfig = SpringContextHolder.getBean(DictionaryConfig.class);
        CommonCacheService commonCacheService = SpringContextHolder.getBean(CommonCacheService.class);
        TBSyncTask tbSyncTask = calculateParams.getTbSyncTask();

        Long time = System.currentTimeMillis();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("examId", tbSyncTask.getThirdRelateId());
        jsonObject.put("courseCode", calculateParams.getCourseCodeMap());
        jsonObject.put("progress", tbSyncTask.getProgress());
        jsonObject.put("status", tbSyncTask.getStatus());
        jsonObject.put("result", tbSyncTask.getResult());
        jsonObject.put("time", time);

        String sign = URLEncoder.encode(Base64Util.encode(ShaUtils.sha1(dictionaryConfig.printOpenDomain().getCallbackPwd() + jsonObject.toJSONString())), SystemConstant.CHARSET_NAME);
        String hostUrl = dictionaryConfig.printOpenDomain().getHostUrl();
        BasicSchool basicSchool = commonCacheService.schoolCache(calculateParams.getSchoolId());
        if (hostUrl.contains("*")) {
            hostUrl = hostUrl.replace("*", basicSchool.getCode());
        }

        String callbackResult = HttpUtil.postJson(hostUrl + dictionaryConfig.printOpenDomain().getCallbackUrlApi(), jsonObject.toJSONString(), sign, time);
        if (Objects.nonNull(callbackResult)) {
            log.info("callbackResult:{}", JacksonUtil.parseJson(callbackResult));
        }
    }

    /**
     * 开始计算
     *
     * @param calculateParams
     * @throws IOException
     */
    public void start(CalculateParams calculateParams) throws IOException {
        RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);
        String source = null;
        if (Objects.nonNull(calculateParams.getTbSyncTask())) {
            source = calculateParams.getTbSyncTask().getSource();
        } else {
            source = Base64Util.encode(ShaUtils.sha1(calculateParams.getExamId() + calculateParams.getCourseCode().toString()));
        }
        boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_CALCULATE_PREFIX + source, SystemConstant.REDIS_LOCK_CALCULATE_TIME_OUT);
        if (!lock) {
            throw ExceptionResultEnum.ERROR.exception("正在计算中，请稍候再试！");
        }
        try {
            createSyncTask(calculateParams);
            dataVaild(calculateParams);
            calculate(calculateParams);
            finished(calculateParams);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (Objects.nonNull(calculateParams.getTbSyncTask())) {
                calculateParams.getTbSyncTask().setSummary(e.getMessage());
                exception(calculateParams);
            }
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            redisUtil.releaseLock(SystemConstant.REDIS_LOCK_CALCULATE_PREFIX + source);
        }
    }
}

