package com.qmth.distributed.print.api;

import com.google.gson.reflect.TypeToken;
import com.qmth.boot.api.annotation.Aac;
import com.qmth.boot.api.constant.ApiConstant;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.boot.core.rateLimit.annotation.RateLimit;
import com.qmth.distributed.print.business.bean.dto.CourseWeightDto;
import com.qmth.distributed.print.business.bean.excel.PaperStructDto;
import com.qmth.distributed.print.business.bean.params.report.PaperStructParams;
import com.qmth.distributed.print.business.bean.result.CourseWeightResult;
import com.qmth.distributed.print.business.bean.result.EditResult;
import com.qmth.distributed.print.business.bean.result.report.PaperStructDimensionResult;
import com.qmth.distributed.print.business.entity.TCPaperStruct;
import com.qmth.distributed.print.business.service.PrintCommonService;
import com.qmth.distributed.print.business.service.TCPaperStructService;
import com.qmth.distributed.print.business.service.TRBasicInfoService;
import com.qmth.teachcloud.common.annotation.OperationLogDetail;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.MarkQuestion;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.log.OperationTypeEnum;
import com.qmth.teachcloud.common.util.*;
import com.qmth.teachcloud.mark.entity.MarkPaper;
import com.qmth.teachcloud.mark.service.MarkQuestionService;
import io.swagger.annotations.*;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 试卷结构 前端控制器
 * </p>
 *
 * @author wangliang
 * @since 2024-02-18
 */
@Api(tags = "课程目标达成度-成绩管理-试卷结构Controller")
@RestController
@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + SystemConstant.PREFIX_URL_COURSE_DEGREE)
public class TCPaperStructController {
    private final static Logger log = LoggerFactory.getLogger(TCPaperStructController.class);

    @Resource
    MarkQuestionService markQuestionService;

    @Resource
    TCPaperStructService tcPaperStructService;

    @Resource
    TRBasicInfoService trBasicInfoService;

    @Resource
    PrintCommonService printCommonService;

    @Resource
    RedisUtil redisUtil;

    @ApiOperation(value = "导入试卷结构")
    @RequestMapping(value = "/final_score/paper_struct/import", method = RequestMethod.POST)
    @OperationLogDetail(operationType = OperationTypeEnum.IMPORT)
    @ApiResponses({@ApiResponse(code = 200, message = "导入成功", response = EditResult.class)})
    public Result finalScorePaperStructImport(@ApiParam(value = "上传文件", required = true) @RequestParam MultipartFile file,
                                              @ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                                              @ApiParam(value = "科目编码", required = true) @RequestParam String courseCode,
                                              @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
                                              @ApiParam(value = "教学课程id", required = true) @RequestParam Long teachCourseId) throws IOException {
        printCommonService.getLock(examId, courseCode, paperNumber, teachCourseId);
        String lockKey = SystemConstant.REDIS_PAPER_STRUCT_FLOW_PREFIX + SystemConstant.IMPORT + examId + "_" + courseCode + "_" + paperNumber + "_" + teachCourseId;
        boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_PAPER_STRUCT_TIME_OUT);
        if (!lock) {
            throw ExceptionResultEnum.ERROR.exception("正在导入数据，请稍候再试！");
        }
        Map<String, String> map = null;
        try {
            map = tcPaperStructService.paperStructExcelImport(file, examId, courseCode, paperNumber, teachCourseId);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            redisUtil.releaseLock(lockKey);
        }
        return ResultUtil.ok(map);
    }

    @ApiOperation(value = "同步试卷蓝图结构")
    @RequestMapping(value = "/final_score/paper_struct_dimension/sync", method = RequestMethod.POST)
    @OperationLogDetail(operationType = OperationTypeEnum.SYNC)
    @ApiResponses({@ApiResponse(code = 200, message = "同步成功", response = EditResult.class)})
    @Aac(rateLimit = @RateLimit(count = 1, period = 1000L))
    public Result finalScorePaperStructDimensionSync(@ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                                                     @ApiParam(value = "科目编码", required = true) @RequestParam String courseCode,
                                                     @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
                                                     @ApiParam(value = "教学课程id", required = true) @RequestParam Long teachCourseId) throws IOException {
        Objects.requireNonNull(paperNumber, "未查询到题和知识点对应关系请手动关联");
        printCommonService.getLock(examId, courseCode, paperNumber, teachCourseId);
        String lockKey = SystemConstant.REDIS_PAPER_STRUCT_FLOW_PREFIX + SystemConstant.SYNC + examId + "_" + courseCode + "_" + paperNumber + "_" + teachCourseId;
        boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_PAPER_STRUCT_TIME_OUT);
        if (!lock) {
            throw ExceptionResultEnum.ERROR.exception("正在同步数据，请稍候再试！");
        }
        Map<String, String> map = null;
        try {
            map = tcPaperStructService.paperStructSync(examId, courseCode, paperNumber, teachCourseId);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            redisUtil.releaseLock(lockKey);
        }
        return ResultUtil.ok(map);
    }

    @ApiOperation(value = "期末成绩试卷蓝图保存")
    @RequestMapping(value = "/final_score/paper_struct/save", method = RequestMethod.POST)
    @OperationLogDetail(operationType = OperationTypeEnum.UPDATE)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷蓝图保存", response = Object.class)})
    @Transactional
    public Result finalScorePaperStructSave(@ApiParam(value = "试卷蓝图结构", required = true) @Valid @RequestBody PaperStructParams paperStructParams, BindingResult bindingResult) throws IOException {
        if (bindingResult.hasErrors()) {
            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
        }
        printCommonService.getLock(paperStructParams.getExamId(), paperStructParams.getCourseCode(), paperStructParams.getPaperNumber(), paperStructParams.getTeachCourseId());
        CourseWeightResult courseWeightResult = trBasicInfoService.findCourseWeightResultRmi(paperStructParams.getTeachCourseId());
        for (CourseWeightDto c : courseWeightResult.getSubmitForm()) {
            for (PaperStructDimensionResult paperStructDimensionResult : paperStructParams.getPaperStruct()) {
                Objects.requireNonNull(paperStructDimensionResult.getMainNumber(), "大题号为空");
                Objects.requireNonNull(paperStructDimensionResult.getSubNumber(), "小题号为空");
                if (!CollectionUtils.isEmpty(paperStructDimensionResult.getTargetList()) && paperStructDimensionResult.getTargetList().size() > 1) {
                    throw ExceptionResultEnum.ERROR.exception("一个题只能属于一个目标");
                }
            }
            List<PaperStructDimensionResult> paperStructDimensionResultList = paperStructParams.getPaperStruct();
            Double score = paperStructDimensionResultList.stream().filter(s -> Objects.equals(s.getCourseTargetName(), c.getCourseTargetName())).mapToDouble(PaperStructDimensionResult::getScore).sum();
            Objects.requireNonNull(c.getTotalWeight(), "[" + c.getCourseTargetName() + "]未设置权重");
            Objects.requireNonNull(c.getTotalScore(), "[" + c.getCourseTargetName() + "]未设置目标分值");
            if (new BigDecimal(score).compareTo(c.getTotalWeight()) == 1) {
                throw ExceptionResultEnum.ERROR.exception("[" + c.getCourseTargetName() + "]包含的小题总分与权重设置不一致，请到期末成绩--试卷蓝图设置里调整");
            }
            paperStructDimensionResultList.stream().filter(s -> {
                if (Objects.equals(s.getCourseTargetName(), c.getCourseTargetName())) {
                    s.getTargetList().stream().peek(e -> e.setFinalScoreQuestionScoreSum(score)).collect(Collectors.toList());
                    return true;
                }
                return false;
            }).collect(Collectors.toList());
        }
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        TCPaperStruct tcPaperStructDb = tcPaperStructService.queryPaperStruct(paperStructParams.getExamId(), paperStructParams.getCourseCode(), paperStructParams.getPaperNumber(), paperStructParams.getTeachCourseId());
        if (Objects.isNull(tcPaperStructDb)) {
            MarkPaper markPaper = printCommonService.getMarkPaper(paperStructParams.getExamId(), paperStructParams.getCourseCode(), paperStructParams.getPaperNumber());
            tcPaperStructDb = new TCPaperStruct(paperStructParams.getExamId(), paperStructParams.getCourseCode(), markPaper.getCourseName(), paperStructParams.getPaperNumber(), paperStructParams.getTeachCourseId(), JacksonUtil.parseJson(paperStructParams.getPaperStruct()), sysUser.getId(), courseWeightResult.getDimensionSign());
            tcPaperStructService.save(tcPaperStructDb);
        } else {
            TCPaperStruct tcPaperStructSource = new TCPaperStruct();
            BeanUtils.copyProperties(tcPaperStructDb, tcPaperStructSource);
            tcPaperStructDb.updateInfo(JacksonUtil.parseJson(paperStructParams.getPaperStruct()), sysUser.getId());
            if (!tcPaperStructDb.equals(tcPaperStructSource)) {
                trBasicInfoService.clearReportData(tcPaperStructDb.getExamId(), tcPaperStructDb.getCourseCode(), tcPaperStructDb.getPaperNumber(), paperStructParams.getTeachCourseId(), false);
                tcPaperStructDb.setDimensionSign(courseWeightResult.getDimensionSign());
                tcPaperStructService.updateById(tcPaperStructDb);
            }
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "期末成绩试卷蓝图查询")
    @RequestMapping(value = "/final_score/paper_struct/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷蓝图查询", response = PaperStructDimensionResult.class)})
    public Result finalScorePaperStructQuery(@ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                                             @ApiParam(value = "科目编码", required = true) @RequestParam String courseCode,
                                             @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
                                             @ApiParam(value = "教学课程id", required = true) @RequestParam Long teachCourseId) throws IOException {
        List<PaperStructDimensionResult> paperStructDimensionResultList = null;
        TCPaperStruct tcPaperStruct = tcPaperStructService.queryPaperStruct(examId, courseCode, paperNumber, teachCourseId);
        if (Objects.isNull(tcPaperStruct) || (Objects.isNull(tcPaperStruct.getPaperStruct()) && Objects.isNull(tcPaperStruct.getPaperStructDimension()))) {
            List<MarkQuestion> markQuestionList = markQuestionService.listQuestionByExamIdAndPaperNumberAndPaperType(examId, paperNumber, null);
            if (CollectionUtils.isEmpty(markQuestionList)) {
                throw ExceptionResultEnum.ERROR.exception("未找到试卷结构");
            }
            paperStructDimensionResultList = new ArrayList<>(markQuestionList.size());
            for (MarkQuestion markQuestion : markQuestionList) {
                paperStructDimensionResultList.add(new PaperStructDimensionResult(markQuestion.getMainNumber(), markQuestion.getSubNumber(), markQuestion.getPaperNumber(), markQuestion.getTotalScore()));
            }
        } else {
            CourseWeightResult courseWeightResult = trBasicInfoService.findCourseWeightResultRmi(teachCourseId);
            if (Objects.nonNull(tcPaperStruct.getDimensionSign()) && tcPaperStruct.getDimensionSign().longValue() != courseWeightResult.getDimensionSign().longValue()) {
                trBasicInfoService.clearReportData(examId, courseCode, paperNumber, teachCourseId, false);
                paperStructDimensionResultList = this.getPaperStructDimensionResult(tcPaperStruct, paperStructDimensionResultList);
            } else {
                paperStructDimensionResultList = this.getPaperStructDimensionResult(tcPaperStruct, paperStructDimensionResultList);
            }
            paperStructDimensionResultList.stream().peek(s -> s.setPaperNumber(tcPaperStruct.getPaperNumber())).collect(Collectors.toList());
        }
        return ResultUtil.ok(paperStructDimensionResultList);
    }

    /**
     * 获取试卷结构蓝图数据
     *
     * @param tcPaperStruct
     * @param paperStructDimensionResultList
     * @return
     */
    protected List<PaperStructDimensionResult> getPaperStructDimensionResult(TCPaperStruct tcPaperStruct,
                                                                             List<PaperStructDimensionResult> paperStructDimensionResultList) {
        if (Objects.nonNull(tcPaperStruct.getPaperStructDimension())) {
            paperStructDimensionResultList = GsonUtil.fromJson(tcPaperStruct.getPaperStructDimension(), new TypeToken<List<PaperStructDimensionResult>>() {
            }.getType());
        } else if (Objects.nonNull(tcPaperStruct.getPaperStruct())) {
            List<PaperStructDto> paperStructDtoList = GsonUtil.fromJson(tcPaperStruct.getPaperStruct(), new TypeToken<List<PaperStructDto>>() {
            }.getType());
            paperStructDimensionResultList = new ArrayList<>(paperStructDtoList.size());
            for (PaperStructDto paperStructDto : paperStructDtoList) {
                paperStructDimensionResultList.add(new PaperStructDimensionResult(paperStructDto.getMainNumber(), paperStructDto.getSubNumber(), tcPaperStruct.getPaperNumber(), paperStructDto.getScore()));
            }
        }
        return paperStructDimensionResultList;
    }
}
