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.ExamStudentCourseClassDto;
import com.qmth.distributed.print.business.bean.dto.GradeBatchStudentClazzDto;
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.GradeBatchStudentClazz;
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.GradeBatchStudentClazzService;
import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
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.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.GradeAnalyzePaperStatusEnum;
import com.qmth.teachcloud.common.enums.RoleTypeEnum;
import com.qmth.teachcloud.common.enums.TaskResultEnum;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.ExcelUtil;
import com.qmth.teachcloud.common.util.ResultUtil;
import com.qmth.teachcloud.common.util.excel.ExcelError;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.poi.ss.usermodel.*;
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.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
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
    GradeBatchStudentClazzService gradeBatchStudentClazzService;

    @Resource
    TeachcloudCommonService teachcloudCommonService;

    @Resource
    SysUserService sysUserService;

    @Resource
    SysRoleService sysRoleService;

    @Resource
    SysUserRoleService sysUserRoleService;

    @Resource
    ExamTaskService examTaskService;

    @Resource
    SysOrgService sysOrgService;

    @Resource
    BasicRoleDataPermissionService basicRoleDataPermissionService;

    @Override
    public IPage<GradeBatchResult> gradeBatchPage(Long semesterId, Long examId, String batchName, int pageNumber, int pageSize, SysUser requestUser) {
        batchName = SystemConstant.translateSpecificSign(batchName);
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission();
        IPage<GradeBatchResult> page = this.baseMapper.findGradeBatchPage(new Page<>(pageNumber, pageSize), semesterId, examId, batchName, requestUser.getSchoolId(), dpr);
        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 examId = gradeBatchParam.getExamId();
        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);
        gradeBatch.setExamId(examId);
        if (!SystemConstant.longNotNull(id)) {
            // 新增
            gradeBatch.insertInfo(requestUser.getId());
            gradeBatch.setOrgId(requestUser.getOrgId());
        } else {
            gradeBatch.updateInfo(requestUser.getId());
        }
        this.saveOrUpdate(gradeBatch);
        return gradeBatch.getId();
    }

    @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).eq(GradeBatchPaper::getEnable, true);
        List<GradeBatchPaper> gradeBatchPaperList = gradeBatchPaperService.list(queryWrapper);
        if (gradeBatchPaperList.isEmpty()) {
            throw ExceptionResultEnum.ERROR.exception("请先添加分析课程");
        }

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

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

        String fileName = gradeBatch.getBatchName() + "任课老师填写模板.xlsx";
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet("数据");

        // 表头
        CellStyle headerStyle = wb.createCellStyle();
        headerStyle.setAlignment(HorizontalAlignment.CENTER);
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        headerStyle.setBorderRight(BorderStyle.THIN);
        headerStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
        headerStyle.setBorderLeft(BorderStyle.THIN);
        headerStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
        headerStyle.setBorderTop(BorderStyle.THIN);
        headerStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
        headerStyle.setBorderBottom(BorderStyle.THIN);
        headerStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
        // 背景颜色
        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headerStyle.setFillForegroundColor(IndexedColors.GREY_80_PERCENT.getIndex());
        Font font = wb.createFont();
        font.setFontHeightInPoints((short) 12);
        font.setFontName("宋体");
        font.setColor(IndexedColors.WHITE.getIndex());
        headerStyle.setFont(font);

        // 数据
        XSSFCellStyle dataStyle = wb.createCellStyle();
        dataStyle.setAlignment(HorizontalAlignment.CENTER);
        dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        dataStyle.setBorderRight(BorderStyle.THIN);
        dataStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
        dataStyle.setBorderLeft(BorderStyle.THIN);
        dataStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
        dataStyle.setBorderTop(BorderStyle.THIN);
        dataStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
        dataStyle.setBorderBottom(BorderStyle.THIN);
        dataStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
        dataStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        // 背景颜色
        dataStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        font = wb.createFont();
        font.setFontHeightInPoints((short) 10);
        font.setFontName("宋体");
        dataStyle.setFont(font);

        // 锁定样式
        XSSFCellStyle lockStyle = wb.createCellStyle();
        lockStyle.setLocked(true);//设置锁定

        // 未锁定样式
        XSSFCellStyle unlockStyle = wb.createCellStyle();
        unlockStyle.setLocked(false);//设置未锁定

        XSSFRow headRow = sheet.createRow(0);
        // 表头
        String[] fieldsNameList = {"课程代码", "课程名称", "试卷编号", "试卷类型", "班级名称", "任课老师", "任课老师工号"};
        for (int i = 0; i < fieldsNameList.length; i++) {
            XSSFCell cell = headRow.createCell(i);
            cell.setCellValue(fieldsNameList[i]);
            cell.setCellStyle(headerStyle);
        }

        for (int i = 1; i <= finalList.size(); i++) {
            ExamStudentCourseClassDto examStudentCourseDto = finalList.get(i - 1);
            XSSFRow row = sheet.createRow(i);

            XSSFCell cell0 = row.createCell(0);
            cell0.setCellStyle(lockStyle);
            cell0.setCellStyle(dataStyle);
            cell0.setCellValue(examStudentCourseDto.getCourseCode());

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

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

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

            XSSFCell cell4 = row.createCell(4);
            cell4.setCellStyle(lockStyle);
            cell4.setCellStyle(dataStyle);
            cell4.setCellValue(examStudentCourseDto.getClazzName());

            XSSFCell cell5 = row.createCell(5);
            cell5.setCellStyle(unlockStyle);
            cell5.setCellType(CellType.STRING);
            if (StringUtils.isNotBlank(examStudentCourseDto.getTeacherName())) {
                cell5.setCellValue(examStudentCourseDto.getTeacherName());
            }

            XSSFCell cell6 = row.createCell(6);
            cell6.setCellStyle(unlockStyle);
            cell6.setCellType(CellType.STRING);
            if (StringUtils.isNotBlank(examStudentCourseDto.getTeacherNumber())) {
                cell6.setCellValue(examStudentCourseDto.getTeacherNumber());
            }
        }

        for (int i = 0; i < fieldsNameList.length; i++) {
            sheet.autoSizeColumn(i);
            sheet.setColumnWidth(i, sheet.getColumnWidth(i) * 17 / 10);
        }

        // sheet添加保护，这个一定要否则光锁定还是可以编辑的
        sheet.protectSheet(SystemConstant.EXCEL_PROTECT_KEY);

        OutputStream output = response.getOutputStream();
        response.reset();
        response.setCharacterEncoding("ISO-8859-1");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, SystemConstant.CHARSET_NAME));
        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<GradeBatchStudentClazzDto> list = new ArrayList<>();
        Map<String, String> teacherMap = new HashMap<>();
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(GradeBatchStudentClazzDto.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++) {
                    GradeBatchStudentClazzDto gradeBatchStudentClazzDto = (GradeBatchStudentClazzDto) gradeBatchStudentTempList.get(y);
                    if (StringUtils.isBlank(gradeBatchStudentClazzDto.getCourseCode())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程代码]必填"));
                    }
                    if (StringUtils.isBlank(gradeBatchStudentClazzDto.getCourseName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]必填"));
                    }
                    if (StringUtils.isBlank(gradeBatchStudentClazzDto.getPaperNumber())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[试卷编号]必填"));
                    }
                    if (StringUtils.isBlank(gradeBatchStudentClazzDto.getPaperType())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[试卷类型]必填"));
                    }
                    if (StringUtils.isBlank(gradeBatchStudentClazzDto.getClazzName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[班级名称]必填"));
                    }
                    String teacherName = gradeBatchStudentClazzDto.getTeacherName();
                    if (StringUtils.isBlank(teacherName)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[任课老师]必填"));
                    }
                    String teacherNumber = gradeBatchStudentClazzDto.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(gradeBatchStudentClazzDto.getTeacherNumber(), gradeBatchStudentClazzDto.getTeacherName());

                    list.add(gradeBatchStudentClazzDto);
                }
            }

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

        executeImportGradeBatchStudent(batchId, list);
    }

    @Override
    public void createTxt(GradeBatch gradeBatch, String exception) {
        Optional.ofNullable(gradeBatch).orElseThrow(() -> ExceptionResultEnum.PARAMS_ERROR.exception("批次不存在"));
        gradeBatch = this.getById(gradeBatch.getId());
        try {
            // 有异常才创建txt文件
            if (SystemConstant.strNotNull(exception)) {
                JSONObject json = SystemConstant.createTxt(exception);
                gradeBatch.setReportFilePath(json.toJSONString());
            }
        } catch (Exception e) {
            gradeBatch.setResult(TaskResultEnum.ERROR);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            this.updateById(gradeBatch);
        }
    }

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

        List<GradeBatchStudentClazz> gradeBatchStudentClazzes = list.stream().map(m -> {
            GradeBatchStudentClazz gradeBatchStudentClazz = new GradeBatchStudentClazz();
            gradeBatchStudentClazz.setId(SystemConstant.getDbUuid());
            gradeBatchStudentClazz.setBatchId(batchId);
            gradeBatchStudentClazz.setCourseCode(m.getCourseCode());
            gradeBatchStudentClazz.setCourseName(m.getCourseName());
            gradeBatchStudentClazz.setPaperNumber(m.getPaperNumber());
            gradeBatchStudentClazz.setPaperType(m.getPaperType());
            gradeBatchStudentClazz.setClazzName(m.getClazzName());
            gradeBatchStudentClazz.setTeacherName(m.getTeacherName());
            gradeBatchStudentClazz.setTeacherNumber(m.getTeacherNumber());
            return gradeBatchStudentClazz;
        }).collect(Collectors.toList());

        gradeBatchStudentClazzService.saveBatch(gradeBatchStudentClazzes);

        GradeBatch gradeBatch = this.getById(batchId);

        if (gradeBatch == null) {
            throw ExceptionResultEnum.ERROR.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.ERROR.exception("未找到任课老师角色，请联系管理员");
        }
        Map<String, List<GradeBatchStudentClazzDto>> listMap = list.stream().collect(Collectors.groupingBy(m -> m.getTeacherNumber() + "##" + m.getTeacherName()));
        for (Map.Entry<String, List<GradeBatchStudentClazzDto>> entry : listMap.entrySet()) {
            String[] key = entry.getKey().split("##");
            List<GradeBatchStudentClazzDto> dtoList = entry.getValue();
            List<SysUser> sysUserList = sysUserService.findBySchoolIdAndCode(schoolId, key[0]);
            if (!sysUserList.isEmpty() && sysUserList.size() > 1) {
                throw ExceptionResultEnum.ERROR.exception(key[0] + "找到多条用户记录");
            }

            List<Long> orgIds = dtoList.stream().map(m -> m.getCourseCode() + "##" + m.getPaperNumber()).distinct().map(m -> {
                String[] strings = m.split("##");
                // todo xf 20230413
                ExamTask examTask = examTaskService.getByExamIdAndCourseCodeAndPaperNumber(gradeBatch.getExamId(), strings[0], strings[1]);
                SysOrg sysOrg = sysOrgService.findCollegeLevelOrgByOrgId(examTask.getTeachingRoomId());
                return sysOrg.getId();
            }).distinct().collect(Collectors.toList());
            if (orgIds.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception("任课老师" + key[0] + "对应课程不能属于不同学院");
            }
            Long courseSecondaryOrgId = orgIds.get(0);

            SysUser sysUser;
            if (sysUserList.isEmpty()) {
                //新增
                sysUser = new SysUser(schoolId, key[0], key[1], null, true);
                sysUser.setCode(key[0]);
                sysUser.setOrgId(courseSecondaryOrgId);
                sysUserService.save(sysUser);
            } else {
                //修改
                sysUser = sysUserList.get(0);
                Long orgId = sysUser.getOrgId();
                SysOrg userSecondaryOrg = sysOrgService.findCollegeLevelOrgByOrgId(orgId);
                Long userSecondaryOrgId = userSecondaryOrg.getId();
                if (!courseSecondaryOrgId.equals(userSecondaryOrgId)) {
                    throw ExceptionResultEnum.ERROR.exception("导入的excel中，工号为【" + key[0] + "】的用户所在学院为【" + userSecondaryOrg.getName() +
                            "】，和课程所在学院【" + sysOrgService.getById(courseSecondaryOrgId).getName() + "】不一致");
                }
                String dbRealName = sysUser.getRealName();
                if (!dbRealName.equals(key[1])) {
                    throw ExceptionResultEnum.ERROR.exception("导入的excel中，工号为【" + key[0] + "】的用户姓名【" + key[1] +
                            "】，与系统用户姓名【" + dbRealName + "】不一致 请检查excel数据");
                }
                sysUser.setRealName(key[1]);
                sysUser.setEnable(true);
                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);
            }
        }
    }
}
