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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.tools.excel.ExcelWriter;
import com.qmth.boot.tools.excel.enums.ExcelType;
import com.qmth.boot.tools.excel.model.DataMap;
import com.qmth.distributed.print.business.bean.dto.professional.MatrixDto;
import com.qmth.distributed.print.business.bean.dto.professional.MatrixRequirementDto;
import com.qmth.distributed.print.business.bean.dto.professional.MatrixSubRequirementDto;
import com.qmth.distributed.print.business.bean.dto.professional.ProfessionalExportHead;
import com.qmth.distributed.print.business.entity.*;
import com.qmth.distributed.print.business.mapper.TPMatrixMapper;
import com.qmth.distributed.print.business.service.*;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.SysPrivilege;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.RoleTypeEnum;
import com.qmth.teachcloud.common.service.SysPrivilegeService;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 专业认证毕业要求支撑矩阵 服务实现类
 * </p>
 *
 * @author xf
 * @since 2024-04-22
 */
@Service
public class TPMatrixServiceImpl extends ServiceImpl<TPMatrixMapper, TPMatrix> implements TPMatrixService {

    @Resource
    private TPProfessionalService tpProfessionalService;
    @Resource
    private TPCourseService tpCourseService;
    @Resource
    private TPRequirementService tpRequirementService;
    @Resource
    private SysPrivilegeService sysPrivilegeService;
    @Resource
    private TeachCourseService teachCourseService;

    @Override
    public List<MatrixDto> getMatrix(Long professionalId) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        List<TPMatrix> tpMatrixList = this.baseMapper.listMatrix(professionalId);
        if (CollectionUtils.isNotEmpty(tpMatrixList)) {
            List<SysPrivilege> sysPrivileges = sysPrivilegeService.getClientUrlByUserId(sysUser.getId());
            boolean canEditCourse = sysPrivileges.stream().filter(m -> m.getId() == 2056L && "canEditCourse".equals(m.getUrl())).count() > 0;
            List<String> codeList = new ArrayList<>();
            if (!canEditCourse) {
                List<TeachCourse> teachCourseList = teachCourseService.list(new QueryWrapper<TeachCourse>().lambda().eq(TeachCourse::getUserId, sysUser.getId()));
                if (CollectionUtils.isNotEmpty(teachCourseList)) {
                    codeList = teachCourseList.stream().map(TeachCourse::getCourseCode).distinct().collect(Collectors.toList());
                }
            }

            List<MatrixDto> matrixDtoList = new ArrayList<>();
            LinkedMultiValueMap<Long, TPMatrix> courseMap = new LinkedMultiValueMap<>();
            for (TPMatrix tpMatrix : tpMatrixList) {
                courseMap.add(tpMatrix.getCourseId(), tpMatrix);
            }
            List<String> finalCodeList = codeList;
            courseMap.entrySet().stream().forEach(entry -> {
                TPCourse tpCourse = tpCourseService.getById(entry.getKey());
                MatrixDto matrixDto = new MatrixDto();
                matrixDto.setCourseCode(tpCourse.getCourseCode());
                matrixDto.setCourseName(tpCourse.getCourseName());
                matrixDto.setCanEdit(canEditCourse || finalCodeList.contains(tpCourse.getCourseCode()));
                List<MatrixRequirementDto> matrixRequirementDtoList = new ArrayList<>();
                LinkedMultiValueMap<Long, TPMatrix> requirementMap = new LinkedMultiValueMap<>();
                for (TPMatrix tpMatrix : entry.getValue()) {
                    requirementMap.add(tpMatrix.getRequirementId(), tpMatrix);
                }
                requirementMap.entrySet().stream().forEach(requirementEntry -> {
                    TPRequirement tpRequirement = tpRequirementService.getById(requirementEntry.getKey());
                    MatrixRequirementDto matrixRequirementDto = new MatrixRequirementDto();
                    matrixRequirementDto.setName(tpRequirement.getName());
                    List<TPMatrix> requirementList = requirementEntry.getValue();
                    // 排序，解决字符串1和10排序问题
                    if (requirementList.size() > 1) {
                        requirementList.sort(Comparator.comparingInt(o -> Integer.parseInt(o.getSubName())));
                    }
                    List<MatrixSubRequirementDto> matrixSubRequirementDtoList = requirementList.stream().map(tpMatrix -> {
                        MatrixSubRequirementDto matrixSubRequirementDto = new MatrixSubRequirementDto();
                        matrixSubRequirementDto.setId(tpMatrix.getId());
                        matrixSubRequirementDto.setName(StringUtils.isNotBlank(tpMatrix.getSubName()) ? tpMatrix.getRequirementSortNum() + "-" + tpMatrix.getSubName() : tpMatrix.getSubName());
                        matrixSubRequirementDto.setContent(tpMatrix.getContent());
                        return matrixSubRequirementDto;
                    }).collect(Collectors.toList());
                    matrixRequirementDto.setSubRequirements(matrixSubRequirementDtoList);
                    matrixRequirementDtoList.add(matrixRequirementDto);
                });
                matrixDto.setRequirements(matrixRequirementDtoList);
                matrixDtoList.add(matrixDto);
            });
            return matrixDtoList;
        }
        return new ArrayList<>();
    }

    @Override
    public boolean saveMatrix(List<TPMatrix> tpMatrixList) {
        for (TPMatrix tpMatrix : tpMatrixList) {
            UpdateWrapper<TPMatrix> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(TPMatrix::getContent, tpMatrix.getContent())
                    .eq(TPMatrix::getId, tpMatrix.getId());
            this.update(updateWrapper);
        }
        return true;
    }

    @Override
    public void downloadMatrix(Long professionalId, HttpServletResponse response) {
        List<TPMatrix> tpMatrixList = this.baseMapper.listMatrix(professionalId);
        if (CollectionUtils.isNotEmpty(tpMatrixList)) {
            TPProfessional tpProfessional = tpProfessionalService.getById(professionalId);
            // 表头
            List<ProfessionalExportHead> professionalExportHeadList = new ArrayList<>();
            // 默认课程
            professionalExportHeadList.add(new ProfessionalExportHead("", 0, Arrays.asList("")));
            List<TPRequirement> tpRequirementList = tpRequirementService.listRequirement(professionalId);
            for (TPRequirement tpRequirement : tpRequirementList) {
                List<String> subNameList = new ArrayList<>();
                if (tpRequirement.getNodeCount() == null) {
                    subNameList.add("");
                } else {
                    for (Integer i = 1; i <= tpRequirement.getNodeCount(); i++) {
                        subNameList.add(tpRequirement.getSortNum() + "-" + i);
                    }
                }
                professionalExportHeadList.add(new ProfessionalExportHead(tpRequirement.getName(), tpRequirement.getSortNum(), subNameList));
            }

            // 内容
            List<List<String>> contentList = new ArrayList<>();
            LinkedMultiValueMap<Long, TPMatrix> courseMap = new LinkedMultiValueMap<>();
            for (TPMatrix tpMatrix : tpMatrixList) {
                courseMap.add(tpMatrix.getCourseId(), tpMatrix);
            }
            courseMap.entrySet().stream().forEach(entry -> {
                List<String> content = new ArrayList<>();
                TPCourse tpCourse = tpCourseService.getById(entry.getKey());
                content.add(tpCourse.getCourseName());
                LinkedMultiValueMap<Long, TPMatrix> requirementMap = new LinkedMultiValueMap<>();
                for (TPMatrix tpMatrix : entry.getValue()) {
                    requirementMap.add(tpMatrix.getRequirementId(), tpMatrix);
                }
                for (Map.Entry<Long, List<TPMatrix>> requirementEntry : requirementMap.entrySet()) {
                   List<TPMatrix> requirementList = requirementEntry.getValue();
                    // 排序，解决字符串1和10排序问题
                    if (requirementList.size() > 1) {
                        requirementList.sort(Comparator.comparingInt(o -> Integer.parseInt(o.getSubName())));
                    }
                    requirementList.stream().forEach(tpMatrix -> content.add(tpMatrix.getContent() == null ? "" : String.valueOf(tpMatrix.getContent())));
                }
                contentList.add(content);
            });

            // 生成excel
            exportExcel(response, professionalExportHeadList, contentList, tpProfessional.getName());
        }
    }

    private void exportExcel(HttpServletResponse response, List<ProfessionalExportHead> professionalExportHeadList, List<List<String>> contentList, String professionalName) {
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet("sheet1");

        // 表对样式
        XSSFCellStyle styleHead = wb.createCellStyle(); // 样式对象
        styleHead.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直
        styleHead.setAlignment(HorizontalAlignment.CENTER);// 水平
        Font font = wb.createFont();
        font.setBold(true);
        //设置字体样式
        font.setFontHeightInPoints((short) 12);//设置字体大小
        styleHead.setFont(font);

        //表格第一行（一级表头）
        XSSFRow row1 = sheet.createRow(0);
        // 计算一级表头合并单元格数量
        Integer requirementCount = professionalExportHeadList.stream().filter(m -> m.getSubName() != null).map(m -> m.getSubName().size()).collect(Collectors.summingInt(Integer::intValue));

        for (int i = 0; i < requirementCount; i++) {
            sheet.autoSizeColumn(i, true);
        }

        sheet.addMergedRegion(new CellRangeAddress(0, 0, 1, requirementCount));
        XSSFCell cell0 = row1.createCell(0);
        cell0.setCellStyle(styleHead);
        cell0.setCellValue("课程名称");
        XSSFCell cell1 = row1.createCell(1);
        cell1.setCellStyle(styleHead);
        cell1.setCellValue("毕业要求");

        //表格第二行（二级表头）
        XSSFRow row2 = sheet.createRow(1);
        XSSFCell cell2;
        int num = 0;
        for (int i = 0; i < professionalExportHeadList.size(); i++) {
            ProfessionalExportHead professionalExportHead = professionalExportHeadList.get(i);
            num = num + professionalExportHead.getSubName().size();
            int firstCol = num - professionalExportHead.getSubName().size();
            int lastCol = firstCol + professionalExportHead.getSubName().size() - 1;
            cell2 = row2.createCell(firstCol);
            cell2.setCellStyle(styleHead);
            cell2.setCellValue(professionalExportHead.getRequirementName());
            if (firstCol < lastCol) {
                sheet.addMergedRegion(new CellRangeAddress(1, 1, firstCol, lastCol));
            }
        }

        //表格第三行（三级表头）
        XSSFRow row3 = sheet.createRow(2);
        XSSFCell cell3;
        AtomicInteger atomicInteger = new AtomicInteger(0);
        for (ProfessionalExportHead professionalExportHead : professionalExportHeadList) {
            for (String s : professionalExportHead.getSubName()) {
                cell3 = row3.createCell(atomicInteger.getAndIncrement());
                cell3.setCellStyle(styleHead);
                cell3.setCellValue(s);
            }
        }
        // 合并课程名称
        sheet.addMergedRegion(new CellRangeAddress(0, 2, 0, 0));

        AtomicInteger i = new AtomicInteger(3);
        XSSFRow row;
        for (List<String> list : contentList) {
            row = sheet.createRow(i.getAndIncrement());
            XSSFCell cell;
            for (int j = 0; j < list.size(); j++) {
                cell = row.createCell(j);
                cell.setCellValue(list.get(j));
            }
        }

        ServletOutputStream outputStream = null;
        try {
            response.setHeader("Content-Disposition", "filename=" + URLEncoder.encode(professionalName + "毕业要求支撑矩阵", SystemConstant.CHARSET_NAME) + SystemConstant.EXCEL_PREFIX);
            response.setContentType("application/vnd.ms-excel");
            outputStream = response.getOutputStream();
            wb.write(outputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<TPMatrix> listMatrixByCondition(Long professionalId, Long courseId, Long requirementId) {
        QueryWrapper<TPMatrix> queryWrapper = new QueryWrapper<>();
        LambdaQueryWrapper<TPMatrix> lambda = queryWrapper.lambda();
        lambda.eq(TPMatrix::getProfessionalId, professionalId);
        if (courseId != null) {
            lambda.eq(TPMatrix::getCourseId, courseId);
        }
        if (requirementId != null) {
            lambda.eq(TPMatrix::getRequirementId, requirementId);
        }
        return this.list(queryWrapper);
    }

    @Override
    public void updateMatrixForNodeCount(TPCourse tpCourse, TPRequirement tpRequirement) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        List<TPMatrix> matrixList = this.listMatrixByCondition(tpRequirement.getProfessionalId(), tpCourse.getId(), tpRequirement.getId());
        List<TPMatrix> tpMatrixAddList = new ArrayList<>();
        List<Long> tpMatrixRemoveList = new ArrayList<>();
        // 新增
        if (CollectionUtils.isEmpty(matrixList)) {
            if (tpRequirement.getNodeCount() == null) {
                TPMatrix tpMatrix = new TPMatrix();
                tpMatrix.insertInfo(sysUser.getId());
                tpMatrix.setProfessionalId(tpRequirement.getProfessionalId());
                tpMatrix.setCourseId(tpCourse.getId());
                tpMatrix.setRequirementId(tpRequirement.getId());
                tpMatrixAddList.add(tpMatrix);
            } else {
                for (int i = 1; i <= tpRequirement.getNodeCount(); i++) {
                    TPMatrix tpMatrix = new TPMatrix();
                    tpMatrix.insertInfo(sysUser.getId());
                    tpMatrix.setProfessionalId(tpRequirement.getProfessionalId());
                    tpMatrix.setCourseId(tpCourse.getId());
                    tpMatrix.setRequirementId(tpRequirement.getId());
                    tpMatrix.setSubName(String.valueOf(i));
                    tpMatrixAddList.add(tpMatrix);
                }
            }
        }
        // 修改
        else {
            if (tpRequirement.getNodeCount() == null) {
                for (int i = 0; i < matrixList.size(); i++) {
                    tpMatrixRemoveList.add(matrixList.get(i).getId());
                }
                TPMatrix tpMatrix = new TPMatrix();
                tpMatrix.insertInfo(sysUser.getId());
                tpMatrix.setProfessionalId(tpRequirement.getProfessionalId());
                tpMatrix.setCourseId(tpCourse.getId());
                tpMatrix.setRequirementId(tpRequirement.getId());
                tpMatrixAddList.add(tpMatrix);
            } else {
                if (matrixList.size() == 1) {
                    if (matrixList.get(0).getSubName() == null) {
                        tpMatrixRemoveList.add(matrixList.get(0).getId());

                        for (int i = 1; i <= tpRequirement.getNodeCount(); i++) {
                            TPMatrix tpMatrix = new TPMatrix();
                            tpMatrix.insertInfo(sysUser.getId());
                            tpMatrix.setProfessionalId(tpRequirement.getProfessionalId());
                            tpMatrix.setCourseId(tpCourse.getId());
                            tpMatrix.setRequirementId(tpRequirement.getId());
                            tpMatrix.setSubName(String.valueOf(i));
                            tpMatrixAddList.add(tpMatrix);
                        }
                    } else {
                        if (tpRequirement.getNodeCount() > matrixList.size()) {
                            for (int i = matrixList.size() + 1; i <= tpRequirement.getNodeCount(); i++) {
                                TPMatrix tpMatrix = new TPMatrix();
                                tpMatrix.insertInfo(sysUser.getId());
                                tpMatrix.setProfessionalId(tpRequirement.getProfessionalId());
                                tpMatrix.setCourseId(tpCourse.getId());
                                tpMatrix.setRequirementId(tpRequirement.getId());
                                tpMatrix.setSubName(String.valueOf(i));
                                tpMatrixAddList.add(tpMatrix);
                            }
                        } else if (tpRequirement.getNodeCount() < matrixList.size()) {
                            for (int i = tpRequirement.getNodeCount(); i <= matrixList.size() - 1; i++) {
                                tpMatrixRemoveList.add(matrixList.get(i).getId());
                            }
                        }
                    }
                } else {
                    if (tpRequirement.getNodeCount() > matrixList.size()) {
                        for (int i = matrixList.size() + 1; i <= tpRequirement.getNodeCount(); i++) {
                            TPMatrix tpMatrix = new TPMatrix();
                            tpMatrix.insertInfo(sysUser.getId());
                            tpMatrix.setProfessionalId(tpRequirement.getProfessionalId());
                            tpMatrix.setCourseId(tpCourse.getId());
                            tpMatrix.setRequirementId(tpRequirement.getId());
                            tpMatrix.setSubName(String.valueOf(i));
                            tpMatrixAddList.add(tpMatrix);
                        }
                    } else if (tpRequirement.getNodeCount() < matrixList.size()) {
                        for (int i = tpRequirement.getNodeCount(); i <= matrixList.size() - 1; i++) {
                            tpMatrixRemoveList.add(matrixList.get(i).getId());
                        }
                    }
                }
            }
        }
        if (CollectionUtils.isNotEmpty(tpMatrixAddList)) {
            this.saveOrUpdateBatch(tpMatrixAddList);
        }
        if (CollectionUtils.isNotEmpty(tpMatrixRemoveList)) {
            this.removeByIds(tpMatrixRemoveList);
        }
    }

    @Override
    public void removeMatrixByCondition(Long professionalId, Long courseId, Long requirementId) {
        UpdateWrapper<TPMatrix> updateWrapper = new UpdateWrapper<>();
        LambdaUpdateWrapper<TPMatrix> lambda = updateWrapper.lambda();
        if (professionalId != null) {
            lambda.eq(TPMatrix::getProfessionalId, professionalId);
        }
        if (courseId != null) {
            lambda.eq(TPMatrix::getCourseId, courseId);
        }
        if (requirementId != null) {
            lambda.eq(TPMatrix::getRequirementId, requirementId);
        }
        this.remove(updateWrapper);
    }
}
