package com.qmth.distributed.print.api;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.TCFinalScoreDto;
import com.qmth.distributed.print.business.bean.excel.ExcelField;
import com.qmth.distributed.print.business.bean.excel.PaperStructDto;
import com.qmth.distributed.print.business.bean.result.EditResult;
import com.qmth.distributed.print.business.entity.TCFinalScore;
import com.qmth.distributed.print.business.entity.TCPaperStruct;
import com.qmth.distributed.print.business.service.PrintCommonService;
import com.qmth.distributed.print.business.service.TCFinalScoreService;
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.MarkPaperService;
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 javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import java.io.IOException;
import java.util.*;

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

    @Resource
    PrintCommonService printCommonService;

    @Resource
    TCFinalScoreService tcFinalScoreService;

    @Resource
    MarkQuestionService markQuestionService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    TRBasicInfoService trBasicInfoService;

    @Resource
    TCPaperStructService tcPaperStructService;

    @Resource
    MarkPaperService markPaperService;

    @ApiOperation(value = "导入期末成绩-模板下载")
    @RequestMapping(value = "/final_score/template_download", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "下载成功", response = Object.class)})
    public void finalScoreTemplateDownload(@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);
        TCPaperStruct tcPaperStruct = tcPaperStructService.queryPaperStruct(examId, courseCode, paperNumber, teachCourseId);
        List<ExcelField> excelFieldList = new ArrayList<>();
        excelFieldList.addAll(new ArrayList<>(Arrays.asList(new ExcelField("studentCode", "学号", true), new ExcelField("name", "姓名", true), new ExcelField("score", "成绩", true))));
        if (Objects.isNull(tcPaperStruct) || Objects.isNull(tcPaperStruct.getPaperStruct())) {
            List<MarkQuestion> markQuestionList = markQuestionService.listQuestionByExamIdAndPaperNumberAndPaperType(examId, paperNumber, null);
            if (CollectionUtils.isEmpty(markQuestionList)) {
                throw ExceptionResultEnum.ERROR.exception("未找到试卷结构");
            }
            for (int i = 0; i < markQuestionList.size(); i++) {
                MarkQuestion markQuestion = markQuestionList.get(i);
                excelFieldList.add(new ExcelField("key" + (i + 1), markQuestion.getMainNumber() + "-" + markQuestion.getSubNumber(), true));
            }
        } else {
            List<PaperStructDto> paperStructDtoList = GsonUtil.fromJson(tcPaperStruct.getPaperStruct(), new TypeToken<List<PaperStructDto>>() {
            }.getType());
            for (int i = 0; i < paperStructDtoList.size(); i++) {
                PaperStructDto paperStructDto = paperStructDtoList.get(i);
                excelFieldList.add(new ExcelField("key" + (i + 1), paperStructDto.getMainNumber() + "-" + paperStructDto.getSubNumber(), true));
            }
        }
        printCommonService.scoreDownLoadExaminationTemplate("1、所有字段均为必填字段；\n" +
                "2、期末成绩按总分，和小题分录入，题号按大题号-小题号方式录入，如下，1-1表示第一大题第一小题，2-1表示第二大题第一小题；\n" +
                "3、请不要删除此行，也不要删除模板中的任何列；\n" +
                "4、使用前请先删除样例数据。", "sheet1", "期末成绩导入模版", excelFieldList);
    }

    @ApiOperation(value = "导入期末成绩")
    @RequestMapping(value = "/final_score/import", method = RequestMethod.POST)
    @OperationLogDetail(operationType = OperationTypeEnum.IMPORT)
    @ApiResponses({@ApiResponse(code = 200, message = "导入成功", response = EditResult.class)})
    public Result finalScoreImport(@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_FINAL_SCORE_DATA_FLOW_PREFIX + SystemConstant.IMPORT + examId + "_" + courseCode + "_" + paperNumber + "_" + teachCourseId;
        boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_LOCK_IMPORT_EXAM_DATA_TIME_OUT);
        if (!lock) {
            throw ExceptionResultEnum.ERROR.exception("正在导入数据，请稍候再试！");
        }
        Map<String, String> map = null;
        try {
            map = tcFinalScoreService.finalScoreExcelImport(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/choose_paper", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "同步成功", response = EditResult.class)})
    @OperationLogDetail(operationType = OperationTypeEnum.SYNC)
    public Result finalScoreSyncChoosePaper(@ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                                            @ApiParam(value = "科目编码", required = true) @RequestParam String courseCode,
                                            @ApiParam(value = "教学课程id", required = true) @RequestParam Long teachCourseId) {
        return ResultUtil.ok(markPaperService.list(new QueryWrapper<MarkPaper>().lambda().eq(MarkPaper::getExamId, examId).eq(MarkPaper::getCourseCode, courseCode)));
    }

    @ApiOperation(value = "同步期末成绩")
    @RequestMapping(value = "/final_score/sync", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "同步成功", response = EditResult.class)})
    @OperationLogDetail(operationType = OperationTypeEnum.SYNC)
    @Aac(rateLimit = @RateLimit(count = 1, period = 1000L))
    public Result finalScoreSync(@ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                                 @ApiParam(value = "科目编码", required = true) @RequestParam String courseCode,
                                 @ApiParam(value = "试卷编号", required = true) @RequestParam String paperNumber,
                                 @ApiParam(value = "教学课程id", required = true) @RequestParam Long teachCourseId) throws IOException {
        printCommonService.getLock(examId, courseCode, paperNumber, teachCourseId);
        String lockKey = SystemConstant.REDIS_FINAL_SCORE_DATA_FLOW_PREFIX + SystemConstant.SYNC + examId + "_" + courseCode + "_" + paperNumber + "_" + teachCourseId;
        boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_LOCK_IMPORT_EXAM_DATA_TIME_OUT);
        if (!lock) {
            throw ExceptionResultEnum.ERROR.exception("正在同步数据，请稍候再试！");
        }
        Map<String, String> map = null;
        try {
            map = tcFinalScoreService.finalScoreSync(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/list", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "分页查询", response = TCFinalScore.class)})
    public Result finalScoreList(@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,
                                 @ApiParam(value = "分页页码", required = true) @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
                                 @ApiParam(value = "分页数", required = true) @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) throws IOException {
        return ResultUtil.ok(tcFinalScoreService.finalScoreList(new Page<>(pageNumber, pageSize), examId, courseCode, paperNumber, teachCourseId));
    }

    @ApiOperation(value = "期末成绩编辑")
    @RequestMapping(value = "/final_score/edit", method = RequestMethod.POST)
    @OperationLogDetail(operationType = OperationTypeEnum.SAVE)
    @ApiResponses({@ApiResponse(code = 200, message = "编辑", response = TCFinalScore.class)})
    public Result finalScoreEdit(@ApiParam(value = "主键", required = true) @RequestParam Long id) throws IOException {
        TCFinalScore tcFinalScore = tcFinalScoreService.getById(id);
        Objects.requireNonNull(tcFinalScore, "未找到期末成绩信息");
        return ResultUtil.ok(tcFinalScoreService.getById(id));
    }

    @ApiOperation(value = "期末成绩保存")
    @RequestMapping(value = "/final_score/save", method = RequestMethod.POST)
    @OperationLogDetail(operationType = OperationTypeEnum.UPDATE)
    @ApiResponses({@ApiResponse(code = 200, message = "保存", response = EditResult.class)})
    @Transactional
    public Result finalScoreSave(@ApiParam(value = "期末考试成绩信息", required = true) @Valid @RequestBody TCFinalScore tcFinalScore, BindingResult bindingResult) throws IOException {
        if (bindingResult.hasErrors()) {
            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
        }
        TCFinalScore tcFinalScoreDb = tcFinalScoreService.getById(tcFinalScore.getId());
        Objects.requireNonNull(tcFinalScoreDb, "未找到期末成绩信息");

        printCommonService.getLock(tcFinalScoreDb.getExamId(), tcFinalScoreDb.getCourseCode(), tcFinalScoreDb.getPaperNumber(), tcFinalScore.getTeachCourseId());

        List<TCFinalScoreDto> tcFinalScoreDtoList = GsonUtil.fromJson(tcFinalScore.getScoreDetail(), new TypeToken<List<TCFinalScoreDto>>() {
        }.getType());
        Double score = tcFinalScoreDtoList.stream().mapToDouble(e -> Double.valueOf(e.getScore())).sum();
        if (score.doubleValue() > 100d) {
            throw ExceptionResultEnum.ERROR.exception("分数总分不能大于试卷总分");
        }

        TCFinalScore tcFinalScoreSource = new TCFinalScore();
        BeanUtils.copyProperties(tcFinalScoreDb, tcFinalScoreSource);
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        tcFinalScoreDb.updateInfo(tcFinalScore, sysUser.getId());

        if (!tcFinalScoreSource.equals(tcFinalScoreDb)) {
            trBasicInfoService.clearReportData(tcFinalScoreDb.getExamId(), tcFinalScoreDb.getCourseCode(), tcFinalScoreDb.getPaperNumber(), tcFinalScoreDb.getTeachCourseId(), false);
            tcFinalScoreService.updateById(tcFinalScoreDb);
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "期末成绩启用/禁用")
    @RequestMapping(value = "/final_score/enable", method = RequestMethod.POST)
    @OperationLogDetail(operationType = OperationTypeEnum.UPDATE)
    @ApiResponses({@ApiResponse(code = 200, message = "编辑", response = EditResult.class)})
    @Transactional
    public Result finalScoreEnable(@ApiParam(value = "主键", required = true) @RequestParam Long id,
                                   @ApiParam(value = "启用/禁用", required = true) @RequestParam Boolean enable,
                                   @ApiParam(value = "教学课程id", required = true) @RequestParam Long teachCourseId) throws IOException {
        TCFinalScore tcFinalScoreDb = tcFinalScoreService.getById(id);
        Objects.requireNonNull(tcFinalScoreDb, "未找到期末成绩信息");

        printCommonService.getLock(tcFinalScoreDb.getExamId(), tcFinalScoreDb.getCourseCode(), tcFinalScoreDb.getPaperNumber(), teachCourseId);

        TCFinalScore tcFinalScoreSource = new TCFinalScore();
        BeanUtils.copyProperties(tcFinalScoreDb, tcFinalScoreSource);
        tcFinalScoreDb.setEnable(enable);
        if (!tcFinalScoreSource.equals(tcFinalScoreDb)) {
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            tcFinalScoreDb.updateInfo(sysUser.getId());
            trBasicInfoService.clearReportData(tcFinalScoreDb.getExamId(), tcFinalScoreDb.getCourseCode(), tcFinalScoreDb.getPaperNumber(), teachCourseId, false);
            tcFinalScoreService.updateById(tcFinalScoreDb);
        }
        return ResultUtil.ok(true);
    }
}
