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

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.distributed.print.business.bean.dto.CourseDimensionDto;
import com.qmth.distributed.print.business.bean.dto.CourseWeightDetailDto;
import com.qmth.distributed.print.business.bean.dto.CourseWeightDto;
import com.qmth.distributed.print.business.bean.dto.CourseWeightFormDto;
import com.qmth.distributed.print.business.bean.params.CourseWeightParam;
import com.qmth.distributed.print.business.bean.result.CourseEvaluationResult;
import com.qmth.distributed.print.business.bean.result.CourseTargetResult;
import com.qmth.distributed.print.business.bean.result.CourseWeightResult;
import com.qmth.distributed.print.business.entity.CourseEvaluation;
import com.qmth.distributed.print.business.entity.CourseTarget;
import com.qmth.distributed.print.business.entity.CourseWeight;
import com.qmth.distributed.print.business.entity.TeachCourse;
import com.qmth.distributed.print.business.enums.CourseEvaluationTypeEnum;
import com.qmth.distributed.print.business.enums.CourseSettingTypeEnum;
import com.qmth.distributed.print.business.mapper.CourseWeightMapper;
import com.qmth.distributed.print.business.service.CourseEvaluationService;
import com.qmth.distributed.print.business.service.CourseTargetService;
import com.qmth.distributed.print.business.service.CourseWeightService;
import com.qmth.distributed.print.business.service.TeachCourseService;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 课程权重设置 服务实现类
 * </p>
 *
 * @author CaoZixuan
 * @since 2024-02-22
 */
@Service
public class CourseWeightServiceImpl extends ServiceImpl<CourseWeightMapper, CourseWeight> implements CourseWeightService {

    @Resource
    private TeachCourseService teachCourseService;

    @Resource
    private CourseTargetService courseTargetService;

    @Resource
    private CourseEvaluationService courseEvaluationService;

    @Override
    public CourseWeightResult findCourseWeight(Long teachCourseId) {
        TeachCourse teachCourse = teachCourseService.getById(teachCourseId);
        if (Objects.isNull(teachCourse)) {
            throw ExceptionResultEnum.ERROR.exception("教学班不存在");
        }

        BigDecimal defaultRate = teachCourse.getDefaultRate();
        BigDecimal customRate = teachCourse.getCustomRate();

        // 现有目标
        List<CourseTargetResult> courseTargetList = courseTargetService.findCourseTargetList(teachCourseId);
        // 现有评价方式
        List<CourseEvaluationResult> courseEvaluationResultList = courseEvaluationService.findCourseEvaluationList(
                teachCourseId).stream().sorted((o1, o2) -> {
            if (o1.getType().getIndex() == o2.getType().getIndex()) {
                return o1.getEvaluationId() < o2.getEvaluationId() ? -1 : 0;
            } else {
                return o2.getType().getIndex() - o1.getType().getIndex();
            }
        }).collect(Collectors.toList());
        // 现有权重
        List<CourseWeight> courseWeightList = this.list(
                new QueryWrapper<CourseWeight>().lambda().eq(CourseWeight::getTeachCourseId, teachCourseId));
        // 权重map
        Map<String, CourseWeight> courseWeightMap = courseWeightList.stream()
                .collect(Collectors.toMap(k -> k.getTargetId() + SystemConstant.HYPHEN + k.getEvaluationId(), v -> v));

        List<CourseWeightDto> submitForm = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(courseTargetList)) {
            for (CourseTargetResult courseTarget : courseTargetList) {
                Long targetId = courseTarget.getId();
                String targetName = courseTarget.getTargetName();
                List<CourseDimensionDto> dimensionList = courseTarget.getDimensionList();
                String degreeRequirement = courseTarget.getDegreeRequirement();
                BigDecimal totalWeight = courseTarget.getTotalWeight();
                BigDecimal totalScore = new BigDecimal(0);

                List<CourseWeightDetailDto> courseWeightDetailDtoList = new ArrayList<>();
                for (CourseEvaluationResult courseEvaluationResult : courseEvaluationResultList) {
                    Long evaluationId = courseEvaluationResult.getEvaluationId();
                    String evaluationName = courseEvaluationResult.getEvaluation();
                    String key = targetId + SystemConstant.HYPHEN + evaluationId;

                    Boolean enable = false;
                    BigDecimal weight = BigDecimal.ZERO;
                    BigDecimal targetScore = BigDecimal.ZERO;
                    if (courseWeightMap.containsKey(key)) {
                        // 权重表中包含该目标评价的设置 - 获取权重表的数据
                        CourseWeight courseWeight = courseWeightMap.get(key);
                        enable = courseWeight.getEnable();
                        weight = courseWeight.getWeight();
                        targetScore = courseWeight.getTargetScore();
                    } else if (CourseEvaluationTypeEnum.DEFAULT.equals(courseEvaluationResult.getType())) {
                        enable = true;
                        weight = new BigDecimal(100);
                    }
                    CourseWeightDetailDto detail = new CourseWeightDetailDto();
                    detail.setEvaluationId(evaluationId);
                    detail.setEvaluationName(evaluationName);
                    detail.setEvaluationDesc(courseEvaluationResult.getEvaluationDesc());
                    detail.setEnable(enable);
                    detail.setWeight(weight);
                    detail.setTargetScore(targetScore);
                    detail.setEvaluationType(courseEvaluationResult.getType());
                    if (enable) {
                        totalScore = totalScore.add(targetScore);
                    }
                    courseWeightDetailDtoList.add(detail);
                }

                CourseWeightDto courseWeightDto = new CourseWeightDto();
                courseWeightDto.setCourseTargetId(targetId);
                courseWeightDto.setCourseTargetName(targetName);
                courseWeightDto.setDimensionList(dimensionList);
                courseWeightDto.analyzeDimensionStr();
                courseWeightDto.setDegreeRequirement(degreeRequirement);
                courseWeightDto.setTotalWeight(totalWeight);
                courseWeightDto.setTotalScore(totalScore);
                courseWeightDto.setEvaluationList(courseWeightDetailDtoList);
                courseWeightDto.analyzeDimensionStr();
                submitForm.add(courseWeightDto);
            }
        }
        CourseWeightResult result = new CourseWeightResult();
        result.setWeightSettingSign(teachCourse.getWeightSettingSign());
        result.setDimensionSign(teachCourse.getDimensionSign());
        result.setDefaultRate(defaultRate);
        result.setCustomRate(customRate);
        result.setSubmitForm(submitForm);
        return result;
    }

    @Override
    public CourseWeightResult findCourseWeightReport(Long teachCourseId) {
        TeachCourse teachCourse = teachCourseService.getById(teachCourseId);
        if (Objects.isNull(teachCourse)) {
            throw ExceptionResultEnum.ERROR.exception("教学班不存在");
        }

        BigDecimal defaultRate = teachCourse.getDefaultRate();
        BigDecimal customRate = teachCourse.getCustomRate();

        // 现有目标
        List<CourseTargetResult> courseTargetList = courseTargetService.findCourseTargetList(teachCourseId);
        // 现有评价方式
        List<CourseEvaluationResult> courseEvaluationResultList = courseEvaluationService.findCourseEvaluationList(
                teachCourseId).stream().sorted((o1, o2) -> {
            if (o1.getType().getIndex() == o2.getType().getIndex()) {
                return o1.getEvaluationId() < o2.getEvaluationId() ? -1 : 0;
            } else {
                return o2.getType().getIndex() - o1.getType().getIndex();
            }
        }).collect(Collectors.toList());
        // 现有权重
        List<CourseWeight> courseWeightList = this.list(
                new QueryWrapper<CourseWeight>().lambda().eq(CourseWeight::getTeachCourseId, teachCourseId));
        // 权重map
        Map<String, CourseWeight> courseWeightMap = courseWeightList.stream()
                .collect(Collectors.toMap(k -> k.getTargetId() + SystemConstant.HYPHEN + k.getEvaluationId(), v -> v));

        List<CourseWeightDto> submitForm = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(courseTargetList)) {
            for (CourseTargetResult courseTarget : courseTargetList) {
                Long targetId = courseTarget.getId();
                String targetName = courseTarget.getTargetName();
                List<CourseDimensionDto> dimensionList = courseTarget.getDimensionList();
                String degreeRequirement = courseTarget.getDegreeRequirement();
                BigDecimal totalWeight = courseTarget.getTotalWeight();
                BigDecimal totalScore = new BigDecimal(0);

                List<CourseWeightDetailDto> courseWeightDetailDtoList = new ArrayList<>();
                for (CourseEvaluationResult courseEvaluationResult : courseEvaluationResultList) {
                    Long evaluationId = courseEvaluationResult.getEvaluationId();
                    String evaluationName = courseEvaluationResult.getEvaluation();
                    String key = targetId + SystemConstant.HYPHEN + evaluationId;
                    Boolean enable;
                    BigDecimal weight;
                    BigDecimal targetScore;
                    if (courseWeightMap.containsKey(key)) {
                        // 权重表中包含该目标评价的设置 - 获取权重表的数据
                        CourseWeight courseWeight = courseWeightMap.get(key);
                        enable = courseWeight.getEnable();
                        if (enable) {
                            weight = courseWeight.getWeight();
                            targetScore = courseWeight.getTargetScore();
                            CourseWeightDetailDto detail = new CourseWeightDetailDto();
                            detail.setEvaluationId(evaluationId);
                            detail.setEvaluationName(evaluationName);
                            detail.setEvaluationDesc(courseEvaluationResult.getEvaluationDesc());
                            detail.setEnable(true);
                            detail.setWeight(weight);
                            detail.setTargetScore(targetScore);
                            detail.setEvaluationType(courseEvaluationResult.getType());
                            totalScore = totalScore.add(targetScore);
                            courseWeightDetailDtoList.add(detail);
                        }
                    }
                }
                CourseWeightDto courseWeightDto = new CourseWeightDto();
                courseWeightDto.setCourseTargetId(targetId);
                courseWeightDto.setCourseTargetName(targetName);
                courseWeightDto.setDimensionList(dimensionList);
                courseWeightDto.analyzeDimensionStr();
                courseWeightDto.setDegreeRequirement(degreeRequirement);
                courseWeightDto.setTotalWeight(totalWeight);
                courseWeightDto.setTotalScore(totalScore);
                courseWeightDto.setEvaluationList(courseWeightDetailDtoList);
                courseWeightDto.analyzeDimensionStr();
                submitForm.add(courseWeightDto);
            }
        }
        CourseWeightResult result = new CourseWeightResult();
        result.setWeightSettingSign(teachCourse.getWeightSettingSign());
        result.setDimensionSign(teachCourse.getDimensionSign());
        result.setDefaultRate(defaultRate);
        result.setCustomRate(customRate);
        result.setSubmitForm(submitForm);
        return result;
    }

    @Transactional
    @Override
    public void saveCourseWeight(CourseWeightParam courseWeightParam, SysUser requestUser) {
        List<String> errorMsgList = new ArrayList<>();
        Long requestUserId = requestUser.getId();
        Long teachCourseId = courseWeightParam.getTeachCourseId();
        BigDecimal defaultRate = courseWeightParam.getDefaultRate();
        BigDecimal customRate = courseWeightParam.getCustomRate();

        List<CourseWeightDto> submitForm = courseWeightParam.getSubmitForm();

        TeachCourse teachCourse = teachCourseService.getById(teachCourseId);
        if (Objects.isNull(teachCourse)) {
            throw ExceptionResultEnum.ERROR.exception("教学课程不存在");
        }
        Long examId = teachCourse.getExamId();
        String courseCode = teachCourse.getCourseCode();

        teachCourseService.clearCourseSetting(teachCourseId, CourseSettingTypeEnum.COURSE_WEIGHT);

        List<CourseTarget> courseTargetList = new ArrayList<>();
        List<CourseWeight> courseWeightList = new ArrayList<>();

        // 课程目标整体权重检测
        BigDecimal checkTargetTotalWeight = BigDecimal.ZERO;

        // 一个评价方式必须在一个目标下
        Map<Long, CourseEvaluationTypeEnum> evaluationMap = new HashMap<>();

        for (CourseWeightDto courseWeightDto : submitForm) {
            Long courseTargetId = courseWeightDto.getCourseTargetId();
            CourseTarget courseTarget = courseTargetService.getById(courseTargetId);
            if (Objects.isNull(courseTarget)) {
                throw ExceptionResultEnum.ERROR.exception("课程目标不存在");
            }

            BigDecimal totalWeight = courseWeightDto.getTotalWeight();
            courseTarget.setTotalWeight(totalWeight);
            courseTarget.updateInfo(requestUserId);
            courseTargetList.add(courseTarget);
            checkTargetTotalWeight = checkTargetTotalWeight.add(totalWeight);

            // 评价方式整体权重检测(自定义)
            BigDecimal checkEvaluationCustomTotalWeight = BigDecimal.ZERO;
            List<CourseWeightDetailDto> detailDtoList = courseWeightDto.getEvaluationList();
            for (CourseWeightDetailDto detail : detailDtoList) {
                Long evaluationId = detail.getEvaluationId();
                CourseEvaluation courseEvaluation = courseEvaluationService.getById(evaluationId);
                BigDecimal rate = new BigDecimal(0);
                CourseEvaluationTypeEnum evaluationType = courseEvaluation.getType();
                if (CourseEvaluationTypeEnum.DEFAULT.equals(evaluationType)) {
                    rate = defaultRate;
                } else if (CourseEvaluationTypeEnum.CUSTOM.equals(evaluationType)) {
                    rate = customRate;
                }

                Boolean enable = detail.getEnable();
                BigDecimal weight = detail.getWeight();
                if (enable && CourseEvaluationTypeEnum.CUSTOM.equals(evaluationType)) {
                    if (evaluationMap.containsKey(evaluationId)) {
                        throw ExceptionResultEnum.ERROR.exception("评价方式只能被一个课程目标使用");
                    } else {
                        evaluationMap.put(evaluationId, evaluationType);
                    }
                    checkEvaluationCustomTotalWeight = checkEvaluationCustomTotalWeight.add(weight);

                }

                BigDecimal denominator = new BigDecimal(100);
                // 目标分值计算: 100 * 类型占比 * 目标占比 * 评价方式占比
                BigDecimal targetScore = new BigDecimal(0);
                if (enable) {
                    targetScore = new BigDecimal(100).multiply(rate).divide(denominator, 2, RoundingMode.HALF_UP)
                            .multiply(totalWeight).divide(denominator, 2, RoundingMode.HALF_UP).multiply(weight)
                            .divide(denominator, 2, RoundingMode.HALF_UP);
                }

                CourseWeight courseWeight = new CourseWeight();
                courseWeight.setTeachCourseId(teachCourseId);
                courseWeight.setSchoolId(requestUser.getSchoolId());
                courseWeight.setExamId(examId);
                courseWeight.setCourseCode(courseCode);
                courseWeight.setUserId(requestUserId);
                courseWeight.setTargetId(courseTargetId);
                courseWeight.setEvaluationId(evaluationId);
                courseWeight.setEnable(enable);
                courseWeight.setWeight(weight);
                courseWeight.setTargetScore(targetScore);
                courseWeight.insertInfo(requestUserId);
                courseWeightList.add(courseWeight);
            }
            // 数据校验2:各课程目标下自定义评价方式的总权重应等于100%；
            int checkEvaluationTotalWeightCompareResult = checkEvaluationCustomTotalWeight.compareTo(new BigDecimal(100));
            if (checkEvaluationTotalWeightCompareResult > 0) {
                errorMsgList.add(String.format("课程目标[%s]下评价方式的总权重为[%s],超过[%s]", courseTarget.getTargetName(),
                        String.valueOf(checkEvaluationCustomTotalWeight) + '%', "100%"));
            } else if (checkEvaluationTotalWeightCompareResult < 0) {
                errorMsgList.add(String.format("课程目标[%s]下评价方式的总权重为[%s],低于[%s]", courseTarget.getTargetName(),
                        String.valueOf(checkEvaluationCustomTotalWeight) + '%', "100%"));
            }
        }

        // 数据校验1:默认和自定义的权重合为100；
        if (defaultRate.add(customRate).compareTo(new BigDecimal(100)) != 0) {
            errorMsgList.add("平时成绩权重和期末成绩权重之和不为100%");
        }

        // 数据校验3:目标整体权重应等于100%，用于计算课程整体达成度。
        int checkTargetTotalWeightCompareResult = checkTargetTotalWeight.compareTo(new BigDecimal(100));
        if (checkTargetTotalWeightCompareResult > 0) {
            errorMsgList.add(
                    String.format("所有课程目标整体权重为[%s],超过[%s]", String.valueOf(checkTargetTotalWeight) + '%', "100%"));
        } else if (checkTargetTotalWeightCompareResult < 0) {
            errorMsgList.add(
                    String.format("所有课程目标整体权重为[%s],低于[%s]", String.valueOf(checkTargetTotalWeight) + '%', "100%"));
        }
        if (CollectionUtils.isNotEmpty(errorMsgList)) {
            throw ExceptionResultEnum.ERROR.exception(String.join(";", errorMsgList));
        }

        // 更新目标整体权重
        courseTargetService.updateBatchById(courseTargetList);
        // 保存课程权重设置
        this.saveBatch(courseWeightList);
        // 更新课程权重设置状态
        teachCourse.setWeightSetting(true);
        teachCourse.setWeightSettingSign(SystemConstant.getDbUuid());
        teachCourse.setDefaultRate(defaultRate);
        teachCourse.setCustomRate(customRate);
        teachCourseService.updateById(teachCourse);
    }

    @Transactional
    @Override
    public void deleteCourseWeight(Long teachCourseId) {
        this.remove(new QueryWrapper<CourseWeight>().lambda().eq(CourseWeight::getTeachCourseId, teachCourseId));
        UpdateWrapper<CourseTarget> courseTargetUpdateWrapper = new UpdateWrapper<>();
        courseTargetUpdateWrapper.lambda().eq(CourseTarget::getTeachCourseId, teachCourseId)
                .set(CourseTarget::getTotalWeight, null);
        courseTargetService.update(courseTargetUpdateWrapper);

        UpdateWrapper<TeachCourse> teachCourseUpdateWrapper = new UpdateWrapper<>();
        teachCourseUpdateWrapper.lambda().eq(TeachCourse::getId, teachCourseId)
                .set(TeachCourse::getWeightSettingSign, null).set(TeachCourse::getDefaultRate, null).set(TeachCourse::getCustomRate, null);
        teachCourseService.update(teachCourseUpdateWrapper);
    }

    @Override
    public List<CourseWeightFormDto> findCourseWeightForm(Long teachCourseId) {
        TeachCourse teachCourse = teachCourseService.getById(teachCourseId);
        if (Objects.isNull(teachCourse)) {
            throw ExceptionResultEnum.ERROR.exception("教学课程不存在");
        }
        return this.baseMapper.findCourseWeightForm(teachCourseId);
    }
}