package com.xhkjedu.smarking.service.exam;

import com.xhkjedu.exception.ServiceException;
import com.xhkjedu.smarking.mapper.exam.*;
import com.xhkjedu.smarking.mapper.paper.*;
import com.xhkjedu.smarking.mapper.stupaper.*;
import com.xhkjedu.smarking.mapper.system.SchoolMapper;
import com.xhkjedu.smarking.mapper.system.UserMapper;
import com.xhkjedu.smarking.model.exam.*;
import com.xhkjedu.smarking.model.paper.MsPaper;
import com.xhkjedu.smarking.model.paper.MsPaperQtypeQuestion;
import com.xhkjedu.smarking.model.stupaper.*;
import com.xhkjedu.smarking.utils.MarkingUtil;
import com.xhkjedu.smarking.vo.exam.EBaseVo;
import com.xhkjedu.utils.N_Utils;
import com.xhkjedu.vo.ResultVo;
import com.xhkjedu.vo.system.UserVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description 阅卷考试表 服务实现类
 * @Author auto
 * @Date 2024-10-15
 */
@Service
@Slf4j
public class MsExamService {
    @Resource
    private MsExamMapper msExamMapper;
    @Resource
    private SchoolMapper schoolMapper;
    @Resource
    private MsSubjectMapper msSubjectMapper;
    @Resource
    private MsPaperMapper msPaperMapper;
    @Resource
    private MsClassMapper msClassMapper;
    @Resource
    private MsClassStudentMapper msClassStudentMapper;
    @Resource
    private MsPaperStudentMapper msPaperStudentMapper;
    @Resource
    private MsPaperStudentQuestionMapper msPaperStudentQuestionMapper;
    @Resource
    private MsPaperQtypeQuestionMapper msPaperQtypeQuestionMapper;
    @Resource
    private UserMapper userMapper;

    /**
     * @Description 新增
     * @Param [msExam]
     * @Author auto
     * @Date 2024-10-15
     **/
    @Transactional(rollbackFor = Exception.class)
    public void add(MsExam msExam) {
        msExam.setExamstate(0);
        msExam.setTeashowrank(0);
        msExam.setStushowrank(0);
        msExam.setStuzero(1);
        msExam.setFixedscore(0);
        msExam.setDeleted(1);
        msExam.setHashb(0);
        msExam.setYear(schoolMapper.getYearById(msExam.getSchoolid()));
        msExam.setCreatetime(N_Utils.getSecondTimestamp());
        msExamMapper.insertUseGeneratedKeys(msExam);
        Integer examid = msExam.getId();

        saveExam(msExam, examid);//保存考试基本信息
    }

    //保存考试基本信息
    private void saveExam(MsExam model, Integer examid) {
        List<MsSubject> subjects = model.getSubjects();
        List<String> subjectids = subjects.stream().map(MsSubject::getSubjectid).collect(Collectors.toList());
        saveClassAndStudent(model, examid, subjectids);//保存考试班级和学生

        //考试科目
        Integer schoolid = model.getSchoolid();
        List<MsPaper> papers = new ArrayList<>();//考试试卷
        for (MsSubject s : subjects) {
            s.setSchoolid(schoolid);
            s.setExamid(examid);
            s.setMsstate(0);
            s.setPstate(0);
            s.setPtstate(0);
            s.setPbstate(0);
            s.setCheckstate(0);
            s.setCheckset(0);
            s.setInvigilatestate(0);

            MsPaper p = new MsPaper();
            p.setExamid(examid);
            p.setSubjectid(s.getSubjectid());
            p.setPtype(0);
            p.setPnum(0);
            p.setMergepnum(0);
            p.setPscore(0.0);
            p.setAnswered(0);
            p.setHasfile(0);
            p.setHearnum(0);
            p.setCorrecttype(0);
            p.setYjstate(0);
            papers.add(p);
        }
        msSubjectMapper.insertList(subjects);//考试科目

        Integer createid = model.getCreateid();
        Integer createtime = model.getCreatetime();
        for (int i = 0; i < papers.size(); i++) {
            MsPaper ePaper = papers.get(i);
            ePaper.setMsid(subjects.get(i).getId());
            ePaper.setCreateid(createid);
            ePaper.setCreatetime(createtime);
        }
        msPaperMapper.insertList(papers);//考试试卷
    }

    //保存考试班级和学生
    private void saveClassAndStudent(MsExam model, Integer examid, List<String> subjectids) {
        List<Integer> classids = model.getClassids();
        if (N_Utils.isListNotEmpty(classids)) {
            if (!model.getExammode().equals(3)) {//线下考试必须有考号
                Integer num = msClassStudentMapper.getNoExamNoNumByClassIds(classids);//根据班级ids获取没有考号学生数量
                if (num != 0) throw new ServiceException("有学生未设置考号，请先设置");
                //验证选择学校的准考证号长度是否一致；获取参加考试的学校所有学生考号长度
                List<Integer> schoolExamNo = msClassStudentMapper.listSchoolExamNoLength(classids);
                if (N_Utils.isListNotEmpty(schoolExamNo) && schoolExamNo.size() > 1) {
                    throw new ServiceException("选择考试班级学生考号长度不一致");
                }
            }

            //考试班级
            List<MsClass> classList = msClassMapper.listByClassIds(classids);
            List<MsClassStudent> studentList = msClassStudentMapper.listByClassIds(classids);
            List<MsClass> classes = new ArrayList<>();
            List<MsClassStudent> students = new ArrayList<>();
            for (String subjectid : subjectids) {
                for (MsClass c : classList) {
                    MsClass c1 = new MsClass();
                    c1.setExamid(examid);
                    c1.setSubjectid(subjectid);
                    c1.setClassid(c.getClassid());
                    c1.setClassname(c.getClassname());
                    c1.setSchoolid(c.getSchoolid());
                    c1.setClasstype(c.getClasstype());
                    classes.add(c1);
                }
                for (MsClassStudent cs : studentList) {
                    MsClassStudent cs1 = new MsClassStudent();
                    cs1.setExamid(examid);
                    cs1.setSubjectid(subjectid);
                    cs1.setClassid(cs.getClassid());
                    cs1.setStudentid(cs.getStudentid());
                    students.add(cs1);
                }
            }
            msClassMapper.insertList(classes);

            //考试学生
            if (N_Utils.isListNotEmpty(students)) msClassStudentMapper.insertList(students);
        }
    }

    /**
     * @Description 列表
     * @Date 2024/10/17 10:00
     * @Author YWX
     * @Param [exam]
     * @Return java.util.List<java.util.Map>
     **/
    public List<Map> queryList(MsExam exam) {
        return msExamMapper.listAll(exam);
    }

    /**
     * @Description 根据考试id获取考试状态
     * @Date 2024/10/17 17:00
     * @Author YWX
     * @Param [examid]
     * @Return com.xhkjedu.smarking.vo.exam.EBaseVo
     **/
    public EBaseVo getExamStateById(Integer examid) {
        return msExamMapper.getExamStateById(examid);
    }

    /**
     * @Description 发布考试
     * @Date 2024/10/17 17:24
     * @Author YWX
     * @Param [examid, examcomm, students]
     * @Return java.lang.Integer
     **/
    public Integer updateExamStateStart(Integer examid, String examcomm, List<UserVo> students) {
        try {
            //发布考试，获取考试试卷题量和分值
            List<MsPaper> papers = msPaperMapper.listNumAndScoreByExamId(examid);
            for (MsPaper paper : papers) {
                //如果考试已经发布，更改学生提交指定试题为需要批改状态
                new Thread(() -> {
                    try {
                        savePaperStudent(paper, students);
                    } catch (Exception e) {
                        log.error("保存学生试题重试：{}", e.getMessage());
                        try {
                            savePaperStudent(paper, students);
                        } catch (Exception se) {
                            log.error("保存学生试题重试出错：{}", se.getMessage());
                        }
                    }
                }).start();
            }
            //更改考试状态
            msExamMapper.postExam(examid, examcomm);
        } catch (Exception e) {
            log.error("发布考试失败：{}", e.getMessage());
            return 1;
        }
        return 0;
    }

    //保存学生试题
    @Transactional(rollbackFor = Exception.class)
    public void savePaperStudent(MsPaper paper, List<UserVo> students) {
        int timestamp = N_Utils.getSecondTimestamp();
        try {
            List<MsPaperStudent> studentList = new ArrayList<>();
            Integer examid = paper.getExamid();
            Integer msid = paper.getMsid();
            Integer mpid = paper.getMpid();
            Integer pnum = paper.getPnum();
            Double pscore = paper.getPscore();
            String subjectid = paper.getSubjectid();
            List<Integer> studentIds = students.stream().filter(s -> s.getSubjectid().equals(subjectid)).map(UserVo::getUserid).distinct().collect(Collectors.toList());
            for (Integer studentId : studentIds) {
                MsPaperStudent es = new MsPaperStudent();
                es.setExamid(examid);
                es.setMsid(msid);
                es.setMpid(mpid);
                es.setSubjectid(subjectid);
                es.setStudentid(studentId);
                es.setSstate(0);
                es.setPnum(pnum);
                es.setPscore(pscore);
                es.setStuscore(0.0);
                es.setCosttime(0);
                es.setCreatetime(timestamp);
                es.setHasbad(0);
                es.setStuscoretype(0);
                studentList.add(es);
            }
            msPaperStudentMapper.insertList(studentList);//保存考试学生信息

            //获取试卷中所有试题
            List<MsPaperQtypeQuestion> queslist = msPaperQtypeQuestionMapper.listQuestionsByMpid(mpid);
            //获取该还是卷下试题学生信息
            for (MsPaperQtypeQuestion q : queslist) {
                Integer eptqid = q.getMptqid();
                Integer ctype = q.getCtype();
                String questionid = q.getQuestionid();
                String qn = q.getQn();
                Integer qorder = q.getQorder();
                Double score = q.getQscore();
                List<MsPaperStudentQuestion> stuqlist = new ArrayList<>();
                for (MsPaperStudent ps : studentList) {
                    MsPaperStudentQuestion epsq = new MsPaperStudentQuestion();
                    epsq.setStudentid(ps.getStudentid());
                    epsq.setExamid(ps.getExamid());
                    epsq.setSubjectid(ps.getSubjectid());
                    epsq.setMpid(mpid);
                    epsq.setMpsid(ps.getId());
                    epsq.setMptqid(eptqid);
                    epsq.setCtype(ctype);
                    epsq.setQuestionid(questionid);
                    epsq.setQn(qn);
                    epsq.setQorder(qorder);
                    epsq.setAnswered(0);
                    epsq.setAnswertype(0);
                    epsq.setConverted(0);
                    epsq.setCosttime(0);
                    epsq.setQscore(score);
                    epsq.setChecked(0);
                    stuqlist.add(epsq);
                }
                msPaperStudentQuestionMapper.insertList(stuqlist);
            }
        } catch (Exception e) {
            log.error("保存学生试题失败：{}", e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new RuntimeException("保存学生试题失败");
        }
    }

    /**
     * @Description 班级选择列表（创建考试用）
     * @Date 2024/11/6 17:14
     * @Author YWX
     * @Param [createid]
     * @Return java.util.List<java.util.Map>
     **/
    public List<Map> listClass(Integer createid) {
        UserVo vo = userMapper.getUserInfoForHandleLog(createid);
        if (vo == null) throw new ServiceException("用户不存在");
        Integer schoolid = vo.getSchoolid();
        Integer year = schoolMapper.getYearById(schoolid);
        Integer teacherid = null;
        if (!vo.getUsertype().equals(0)) {
            teacherid = createid;
        }
        return msClassMapper.listSchoolClass(schoolid, year, teacherid);
    }

    /**
     * @Description 删除
     * @Date 2024/11/25 16:46
     * @Author YWX
     * @Param [examid]
     * @Return void
     **/
    public void deleteById(Integer examid) {
        msExamMapper.deleteById(examid);
    }

    /**
     * @Description 取消发布
     * @Date 2024/11/25 17:33
     * @Author YWX
     * @Param [examid]
     * @Return void
     **/
    @Transactional(rollbackFor = Exception.class)
    public void qxFb(Integer examid) {
        EBaseVo exam = msExamMapper.getExamStateById(examid);
        Long timestamp = Long.valueOf(N_Utils.getSecondTimestamp() + 30 * 60);
        Long begintime = MarkingUtil.strToTimestamp(exam.getStrtime());
        if (begintime != 0 && begintime.compareTo(timestamp) != 1) throw new ServiceException("开考前30分钟禁止取消发布");
        msPaperStudentMapper.deleteByExamId(examid);//删除考试学生信息
        msExamMapper.updateExamState(examid, 0);//更新考试状态
    }

    /**
     * @Description 修改
     * @Date 2024/11/25 17:13
     * @Author YWX
     * @Param [model]
     * @Return com.xhkjedu.vo.ResultVo
     **/
    @Transactional(rollbackFor = Exception.class)
    public ResultVo update(MsExam model) {
        EBaseVo exam = msExamMapper.getExamStateById(model.getExamid());
        if (0 != exam.getExamstate()) return new ResultVo(1, "考试已发布禁止操作");
        Long timestamp = Long.valueOf(N_Utils.getSecondTimestamp() + 30 * 60);
        Long begintime = MarkingUtil.strToTimestamp(exam.getStrtime());
        if (begintime != 0 && begintime.compareTo(timestamp) != 1) return new ResultVo(1, "开考前30分钟禁止编辑");
        //禁止修改年级、科目、考试模式
        model.setGradeid(null);
        model.setExammode(null);
        msExamMapper.updateByPrimaryKeySelective(model);
        model.setExammode(exam.getExammode());
        Integer examid = model.getExamid();
        List<Integer> classids = model.getClassids();
        //班级集合为空不处理班级
        if (N_Utils.isListEmpty(classids)) return new ResultVo(0, "修改成功");
        msExamMapper.deleteClassById(examid);//删除关联的班级
        List<String> subjectids = msSubjectMapper.listSubjectIdsByExamId(examid);
        saveClassAndStudent(model, examid, subjectids);//保存考试班级和学生

        return new ResultVo(0, "修改成功");
    }

    /**
     * @Description 详情
     * @Date 2024/11/26 8:50
     * @Author YWX
     * @Param [examid]
     * @Return java.util.Map
     **/
    public Map findById(Integer examid) {
        Map map = msExamMapper.findById(examid);
        map.put("subjects", msSubjectMapper.listSubjectNameByExamId(examid));
        map.put("classes",  msClassMapper.listNameAndNumByExamId(examid));
        return map;
    }


    /**
     * @Description 获取考试关联学校
     * @Date 2024/11/26 11:49
     * @Author YWX
     * @Param [examid]
     * @Return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
     **/
    public List<Map<String, Object>> listSchool(Integer examid) {
        Integer schoolid = msExamMapper.getSchoolIdByExamId(examid);
        return msClassMapper.listSchoolByExamId(schoolid, examid);
    }

    /**
     * @Description 考试科目列表
     * @Date 2024/11/26 15:17
     * @Author YWX
     * @Param [examid]
     * @Return java.util.Map
     **/
    public Map  listSubject(Integer examid) {
        Map map = msExamMapper.findById(examid);
        map.put("subjects", msSubjectMapper.listByExamId(examid));
        return map;
    }

    /*
     * @Description 获取考试状态
     * @Date 2025/1/7 17:18:40
     * @Author WN
     * @Param [examid]
     * @Return java.lang.Integer
     **/
    public Integer getExamstate(Integer examid){
        return msExamMapper.getExamstate(examid);
    }

    //改状态-一键统分
    public void updateExamStateForYjtf(Integer examid){
        Integer examstateOld = msExamMapper.getExamstate(examid);
        if(examstateOld < 3){
            msExamMapper.updateExamState(examid,3);
        }else{
            throw new ServiceException("已统分，禁止操作");
        }
    }

    //改状态-取消一键统分
    public void updateExamStateForUnYjtf(Integer examid){
        Integer examstateOld = msExamMapper.getExamstate(examid);
        if(examstateOld > 3){
            msExamMapper.updateExamState(examid,2);
        }
    }

    //该状态-发布成绩
    public void updateExamStateForfbcj(Integer examid){
        Integer examstateOld = msExamMapper.getExamstate(examid);
        if(examstateOld == 4){
            msExamMapper.updateExamState(examid,5);
        }else if(examstateOld < 4){
            throw new ServiceException("未生成报告，无法发布成绩");
        }
    }
    //该状态-取消发布成绩
    public void updateExamStateForUnfbcj(Integer examid){
        Integer examstateOld = msExamMapper.getExamstate(examid);
        if(examstateOld == 5){
            msExamMapper.updateExamState(examid,4);
        }
    }
}
