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

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.distributed.print.business.bean.dto.ExamStudentCourseDto;
import com.qmth.distributed.print.business.bean.dto.GradeBatchStudentDto;
import com.qmth.distributed.print.business.bean.params.analyze.GradeBatchParam;
import com.qmth.distributed.print.business.bean.result.analyze.GradeBatchResult;
import com.qmth.distributed.print.business.entity.ExamTask;
import com.qmth.distributed.print.business.entity.GradeBatch;
import com.qmth.distributed.print.business.entity.GradeBatchPaper;
import com.qmth.distributed.print.business.entity.GradeBatchStudent;
import com.qmth.distributed.print.business.mapper.ExamStudentMapper;
import com.qmth.distributed.print.business.mapper.GradeBatchMapper;
import com.qmth.distributed.print.business.service.ExamTaskService;
import com.qmth.distributed.print.business.service.GradeBatchPaperService;
import com.qmth.distributed.print.business.service.GradeBatchService;
import com.qmth.distributed.print.business.service.GradeBatchStudentService;
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.SysOrg;
import com.qmth.teachcloud.common.entity.SysRole;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.entity.SysUserRole;
import com.qmth.teachcloud.common.enums.*;
import com.qmth.teachcloud.common.service.SysRoleService;
import com.qmth.teachcloud.common.service.SysUserRoleService;
import com.qmth.teachcloud.common.service.SysUserService;
import com.qmth.teachcloud.common.service.TeachcloudCommonService;
import com.qmth.teachcloud.common.util.ExcelUtil;
import com.qmth.teachcloud.common.util.FileStoreUtil;
import com.qmth.teachcloud.common.util.ResultUtil;
import com.qmth.teachcloud.common.util.excel.ExcelError;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 分析-批次表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2022-05-20
 */
@Service
public class GradeBatchServiceImpl extends ServiceImpl<GradeBatchMapper, GradeBatch> implements GradeBatchService {
    @Resource
    GradeBatchPaperService gradeBatchPaperService;

    @Resource
    ExamStudentMapper examStudentMapper;

    @Resource
    GradeBatchStudentService gradeBatchStudentService;

    @Resource
    TeachcloudCommonService teachcloudCommonService;

    @Resource
    SysUserService sysUserService;

    @Resource
    SysRoleService sysRoleService;

    @Resource
    SysUserRoleService sysUserRoleService;

    @Resource
    ExamTaskService examTaskService;

    @Override
    public IPage<GradeBatchResult> gradeBatchPage(String batchName, int pageNumber, int pageSize, SysUser requestUser) {
        batchName = SystemConstant.translateSpecificSign(batchName);
        IPage<GradeBatchResult> page = this.baseMapper.findGradeBatchPage(new Page<>(pageNumber, pageSize), batchName, requestUser.getSchoolId());
        for (GradeBatchResult record : page.getRecords()) {
            String txtPath = record.getReportFilePath();
            if (SystemConstant.strNotNull(txtPath)) {
                record.setReportFilePath(teachcloudCommonService.filePreview(txtPath));
            }
        }
        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long editGradeBatch(GradeBatchParam gradeBatchParam, SysUser requestUser) {
        Long schoolId = requestUser.getSchoolId();
        Long semesterId = gradeBatchParam.getSemesterId();
        Long id = gradeBatchParam.getId();
        String batchName = gradeBatchParam.getBatchName();
        Long batchTime = gradeBatchParam.getBatchTime();
        if (!SystemConstant.longNotNull(batchTime)) {
            batchTime = System.currentTimeMillis();
        }

        // 教研分析批次名称学校唯一
        GradeBatch check = this.getOne(new QueryWrapper<GradeBatch>().lambda().eq(GradeBatch::getSchoolId, schoolId).eq(GradeBatch::getBatchName, batchName));
        if (Objects.nonNull(check) && !Objects.equals(check.getId(), id)) {
            throw ExceptionResultEnum.ERROR.exception("已存在名称为： " + batchName + "，的教研分析批次");
        }

        GradeBatch gradeBatch = new GradeBatch();
        gradeBatch.setBatchName(batchName);
        gradeBatch.setBatchTime(batchTime);
        gradeBatch.setBatchCode(DateFormatUtils.format(batchTime, "yyyyMMdd"));
        gradeBatch.setStatus(GradeAnalyzePaperStatusEnum.SETTING_GRADE_PAPER);
        gradeBatch.setSchoolId(schoolId);
        gradeBatch.setSemesterId(semesterId);
        if (!SystemConstant.longNotNull(id)) {
            id = SystemConstant.getDbUuid();
        }
        gradeBatch.setId(id);

        this.saveOrUpdate(gradeBatch);
        return id;
    }

    @Override
    public void deleteGradeBatch(List<Long> idList, SysUser requestUser) {
        Long schoolId = requestUser.getSchoolId();
        if (idList == null || idList.size() == 0) {
            throw ExceptionResultEnum.ERROR.exception("请选择要删除的批次对象");
        }
        // 要删除的批次试卷集合
        List<GradeBatchPaper> gradeBatchPaperList = gradeBatchPaperService.list(new QueryWrapper<GradeBatchPaper>()
                .lambda()
                .eq(GradeBatchPaper::getSchoolId, schoolId)
                .in(GradeBatchPaper::getBatchId, idList));

        if (gradeBatchPaperList != null && gradeBatchPaperList.size() > 0) {
            Set<Long> batchIdSet = gradeBatchPaperList.stream().map(GradeBatchPaper::getBatchId).collect(Collectors.toSet());
            List<String> cantDeleteBatchNameList = this.list(new QueryWrapper<GradeBatch>()
                            .lambda()
                            .in(GradeBatch::getId, batchIdSet))
                    .stream()
                    .map(GradeBatch::getBatchName)
                    .distinct().collect(Collectors.toList());

            throw ExceptionResultEnum.ERROR.exception("成绩分析批次【" + String.join(",", cantDeleteBatchNameList) + "】存在分析课程,不能删除");
        }
        this.removeByIds(idList);
    }

    @Override
    public void downloadFile(Long batchId, HttpServletResponse response) throws IOException {
        GradeBatch gradeBatch = this.getById(batchId);
        QueryWrapper<GradeBatchPaper> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(GradeBatchPaper::getBatchId, batchId);
        List<GradeBatchPaper> gradeBatchPaperList = gradeBatchPaperService.list(queryWrapper);
        if (gradeBatchPaperList.isEmpty()) {
            throw ExceptionResultEnum.ERROR.exception("请先添加分析课程");
        }

        List<ExamStudentCourseDto> finalList = new ArrayList<>();
        for (GradeBatchPaper gradeBatchPaper : gradeBatchPaperList) {
            List<ExamStudentCourseDto> examStudentList = examStudentMapper.listExamStudentByPaperNumberAndPaperType(gradeBatchPaper.getPaperNumber(), gradeBatchPaper.getPaperType());
            if (!examStudentList.isEmpty()) {
                finalList.addAll(examStudentList);
            }
        }

        if (finalList.isEmpty()) {
            throw ExceptionResultEnum.ERROR.exception("分析课程下没有考务数据");
        }

        String fileName = gradeBatch.getBatchName() + "考务数据.xlsx";
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet("考务数据");
        XSSFFont font = wb.createFont();
        font.setFontHeightInPoints((short) 11);
        font.setFontName("宋体"); //什么字体

        XSSFRow headRow = sheet.createRow(0);
        XSSFCellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setFont(font);

        // 表头
        String[] fieldsNameList = {"课程代码", "课程名称", "试卷编号", "试卷类型", "学号", "学生姓名", "准考证号", "任课老师", "任课老师工号"};
        for (int i = 0; i < fieldsNameList.length; i++) {
            XSSFCell cell = headRow.createCell(i);
            cell.setCellValue(fieldsNameList[i]);
            cell.setCellStyle(style);
        }
        for (int i = 1; i <= finalList.size(); i++) {
            ExamStudentCourseDto examStudentCourseDto = finalList.get(i - 1);
            XSSFRow row = sheet.createRow(i);
            XSSFCell cell0 = row.createCell(0);
            cell0.setCellValue(examStudentCourseDto.getCourseCode());

            XSSFCell cell1 = row.createCell(1);
            cell1.setCellValue(examStudentCourseDto.getCourseName());

            XSSFCell cell2 = row.createCell(2);
            cell2.setCellValue(examStudentCourseDto.getPaperNumber());

            XSSFCell cell3 = row.createCell(3);
            cell3.setCellValue(examStudentCourseDto.getPaperType());

            XSSFCell cell4 = row.createCell(4);
            cell4.setCellValue(examStudentCourseDto.getStudentCode());

            XSSFCell cell5 = row.createCell(5);
            cell5.setCellValue(examStudentCourseDto.getStudentName());

            XSSFCell cell6 = row.createCell(6);
            cell6.setCellValue(examStudentCourseDto.getTicketNumber());
        }

        OutputStream output = response.getOutputStream();
        response.reset();
        response.setCharacterEncoding("ISO-8859-1");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        wb.write(output);
        output.flush();
        output.close();
    }

    @Override
    public void uploadFile(Long batchId, MultipartFile file) throws IOException, NoSuchFieldException {
        List<GradeBatchStudentDto> list = new ArrayList<>();
        Map<String, String> teacherMap = new HashMap<>();
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(GradeBatchStudentDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            // 只允许导入一个sheet
            if (finalExcelList.size() > 1) {
                throw ExceptionResultEnum.ERROR.exception("excel中只允许有一个sheet");
            }

            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> gradeBatchStudentTempList = excelMap.get(i);
                for (int y = 0; y < Objects.requireNonNull(gradeBatchStudentTempList).size(); y++) {
                    GradeBatchStudentDto examStudentCourseDto = (GradeBatchStudentDto) gradeBatchStudentTempList.get(y);
                    if (StringUtils.isBlank(examStudentCourseDto.getCourseCode())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程代码]必填"));
                    }
                    if (StringUtils.isBlank(examStudentCourseDto.getCourseName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]必填"));
                    }
                    if (StringUtils.isBlank(examStudentCourseDto.getPaperNumber())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[试卷编号]必填"));
                    }
                    if (StringUtils.isBlank(examStudentCourseDto.getPaperType())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[试卷类型]必填"));
                    }
                    if (StringUtils.isBlank(examStudentCourseDto.getStudentCode())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[学号]必填"));
                    }
                    if (StringUtils.isBlank(examStudentCourseDto.getStudentName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[学生姓名]必填"));
                    }
                    if (StringUtils.isBlank(examStudentCourseDto.getTicketNumber())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[准考证号]必填"));
                    }
                    String teacherName = examStudentCourseDto.getTeacherName();
                    if (StringUtils.isBlank(teacherName)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[任课老师]必填"));
                    }
                    String teacherNumber = examStudentCourseDto.getTeacherNumber();
                    if (StringUtils.isBlank(teacherNumber)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[任课老师工号]必填"));
                    }
                    if (teacherMap.containsKey(teacherNumber) && !teacherName.equals(teacherMap.get(teacherNumber))) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel中同一个任课老师工号有多个姓名"));
                    }

                    teacherMap.put(examStudentCourseDto.getTeacherNumber(), examStudentCourseDto.getTeacherName());

                    list.add(examStudentCourseDto);
                }
            }

            if (excelErrorTemp.size() > 0) {
                List<String> errors = excelErrorTemp.stream().map(ExcelError::getExcelErrorType).collect(Collectors.toList());
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(errors));
            }
            return finalExcelList;
        });

        executeImportGradeBatchStudent(batchId, list);
    }

    @Override
    public void createTxt(GradeBatch gradeBatch, String exception) {
        final String TXT_PREFIX = ".txt";
        if (Objects.isNull(gradeBatch)) {
            throw ExceptionResultEnum.ERROR.exception("批次不存在");
        }
        FileStoreUtil fileStoreUtil = SpringContextHolder.getBean(FileStoreUtil.class);
        DictionaryConfig dictionaryConfig = SpringContextHolder.getBean(DictionaryConfig.class);
        ByteArrayOutputStream out = null;
        InputStream inputStream = null;
        try {
            // 有异常才创建txt文件
            if (SystemConstant.strNotNull(exception)) {
                boolean oss = dictionaryConfig.sysDomain().isOss();
                LocalDateTime nowTime = LocalDateTime.now();
                StringJoiner stringJoiner = new StringJoiner("");
                if (!oss) {
                    stringJoiner.add(SystemConstant.TEMP_FILES_DIR).add(File.separator);
                }
                stringJoiner.add(UploadFileEnum.FILE.getTitle()).add(File.separator);
                stringJoiner.add(String.valueOf(nowTime.getYear())).add(File.separator)
                        .add(String.format("%02d", nowTime.getMonthValue())).add(File.separator)
                        .add(String.format("%02d", nowTime.getDayOfMonth()));
                stringJoiner.add(File.separator).add(SystemConstant.getUuid()).add(TXT_PREFIX);

                JSONObject jsonObject = new JSONObject();
                out = new ByteArrayOutputStream();
                out.write(exception.getBytes(StandardCharsets.UTF_8));
                byte[] bookByteAry = out.toByteArray();
                inputStream = new ByteArrayInputStream(bookByteAry);

                if (oss) {//上传至oss\
                    String dirName = stringJoiner.toString().replaceAll("\\\\", "/");
                    fileStoreUtil.ossUpload(dirName, inputStream, DigestUtils.md5Hex(inputStream), UploadFileEnum.FILE.getFssType());
                    jsonObject.put(SystemConstant.TYPE, SystemConstant.OSS);
                    jsonObject.put(SystemConstant.PATH, dirName);
                } else {//上传至服务器
                    File finalFile = new File(stringJoiner.toString());
                    if (!finalFile.exists()) {
                        finalFile.getParentFile().mkdirs();
                        finalFile.createNewFile();
                    }
                    FileUtils.copyInputStreamToFile(inputStream, finalFile);
                    jsonObject.put(SystemConstant.TYPE, SystemConstant.LOCAL);
                    jsonObject.put(SystemConstant.PATH, stringJoiner.toString());
                }
                jsonObject.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.FILE);
                gradeBatch.setReportFilePath(jsonObject.toJSONString());
            }
        } catch (Exception e) {
            gradeBatch.setResult(TaskResultEnum.ERROR);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            try {
                if (Objects.nonNull(inputStream)) {
                    inputStream.close();
                }
                if (Objects.nonNull(out)) {
                    out.flush();
                    out.close();
                }
            } catch (IOException e) {
                log.error(SystemConstant.LOG_ERROR, e);
            }
            this.updateById(gradeBatch);
        }
    }

    @Transactional
    public void executeImportGradeBatchStudent(Long batchId, List<GradeBatchStudentDto> list) {
        UpdateWrapper<GradeBatchStudent> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(GradeBatchStudent::getBatchId, batchId);
        gradeBatchStudentService.remove(updateWrapper);

        List<GradeBatchStudent> gradeBatchStudents = list.stream().map(m -> {
            GradeBatchStudent gradeBatchStudent = new GradeBatchStudent();
            gradeBatchStudent.setBatchId(SystemConstant.getDbUuid());
            gradeBatchStudent.setBatchId(batchId);
            gradeBatchStudent.setCourseCode(m.getCourseCode());
            gradeBatchStudent.setCourseName(m.getCourseName());
            gradeBatchStudent.setPaperNumber(m.getPaperNumber());
            gradeBatchStudent.setPaperType(m.getPaperType());
            gradeBatchStudent.setStudentCode(m.getStudentCode());
            gradeBatchStudent.setStudentName(m.getStudentName());
            gradeBatchStudent.setTeacherName(m.getTeacherName());
            gradeBatchStudent.setTeacherNumber(m.getTeacherNumber());
            return gradeBatchStudent;
        }).collect(Collectors.toList());

        gradeBatchStudentService.saveBatch(gradeBatchStudents);

        GradeBatch gradeBatch = this.getById(batchId);

        if (gradeBatch == null) {
            throw ExceptionResultEnum.SUCCESS.exception("未找到批次信息");
        }
        Long schoolId = gradeBatch.getSchoolId();

        QueryWrapper<SysRole> sysRoleQueryWrapper = new QueryWrapper<>();
        sysRoleQueryWrapper.lambda().eq(SysRole::getType, RoleTypeEnum.TEACHER).eq(SysRole::getEnable, true);
        List<SysRole> sysRoleList = sysRoleService.list(sysRoleQueryWrapper);
        if (sysRoleList.isEmpty()) {
            throw ExceptionResultEnum.SUCCESS.exception("未找到任课老师角色，请联系管理员");
        }
        Map<String, List<GradeBatchStudentDto>> listMap = list.stream().collect(Collectors.groupingBy(m -> m.getTeacherNumber() + "##" + m.getTeacherName()));
        for (Map.Entry<String, List<GradeBatchStudentDto>> entry : listMap.entrySet()) {
            String[] key = entry.getKey().split("##");
            List<GradeBatchStudentDto> dtoList = entry.getValue();
            List<SysUser> sysUserList = sysUserService.findBySchoolIdAndCode(schoolId, key[0]);
            if (!sysUserList.isEmpty() && sysUserList.size() > 1) {
                throw ExceptionResultEnum.SUCCESS.exception(key[0] + "找到多条用户记录");
            }

            List<Long> orgIds = dtoList.stream().map(m -> m.getCourseCode() + "##" + m.getPaperNumber()).distinct().map(m -> {
                String[] strings = m.split("##");
                ExamTask examTask = examTaskService.getByCourseCodeAndPaperNumber(schoolId, strings[0], strings[1]);
                SysOrg sysOrg = gradeBatchPaperService.findTeachCollegeByOrgId(examTask.getTeachingRoomId());
                return sysOrg.getId();
            }).distinct().collect(Collectors.toList());
            if (orgIds.size() != 1) {
                throw ExceptionResultEnum.SUCCESS.exception("任课老师" + key[0] + "对应课程不能属于不同学院");
            }

            SysUser sysUser;
            if (sysUserList.isEmpty()) {
                //新增
                sysUser = new SysUser(schoolId, key[0], key[1], null, true);
                sysUser.setCode(key[0]);
                sysUser.setOrgId(orgIds.get(0));
                sysUserService.save(sysUser);
            } else {
                //修改
                sysUser = sysUserList.get(0);
                sysUser.setRealName(key[1]);
                sysUser.setEnable(true);
                sysUser.setOrgId(orgIds.get(0));
                sysUserService.updateById(sysUser);
            }

            // 绑定角色
            List<SysRole> sysUserRoles = sysUserRoleService.listRoleByUserId(sysUser.getId());
            long count = sysUserRoles.stream().filter(m -> m.getId().longValue() == sysRoleList.get(0).getId().longValue()).count();
            if (count == 0) {
                SysUserRole sysUserRole = new SysUserRole();
                sysUserRole.setId(SystemConstant.getDbUuid());
                sysUserRole.setUserId(sysUser.getId());
                sysUserRole.setRoleId(sysRoleList.get(0).getId());
                sysUserRole.setEnable(true);
                sysUserRoleService.save(sysUserRole);
            }
        }
    }
}
