package com.yida.data.school.classSchedule.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yida.data.common.core.entity.classschedule.EduClassSchedule;
import com.yida.data.common.core.entity.classschedule.EduClassScheduleTime;
import com.yida.data.common.core.entity.classschedule.EduClassScheduleTimeClass;
import com.yida.data.common.core.entity.user.EduCourse;
import com.yida.data.common.core.entity.user.EduStaff;
import com.yida.data.common.core.entity.user.EduUserDept;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.Asserts;
import com.yida.data.common.core.utils.ExcelUtil;
import com.yida.data.common.core.utils.FebsUtil;
import com.yida.data.school.classSchedule.mapper.EduClassScheduleMapper;
import com.yida.data.school.classSchedule.mapper.EduClassScheduleTimeClassMapper;
import com.yida.data.school.classSchedule.service.EduClassScheduleService;
import com.yida.data.school.classSchedule.service.EduClassScheduleTimeClassService;
import com.yida.data.school.classSchedule.service.EduClassScheduleTimeService;
import com.yida.data.school.dto.classSchedule.ClassSchedulePageDTO;
import com.yida.data.school.dto.classSchedule.DownloadTemplateDTO;
import com.yida.data.school.vo.classSchedule.ClassScheduleImportVO;
import com.yida.data.school.vo.classSchedule.ClassSchedulePageInfoVO;
import com.yida.data.school.vo.classSchedule.ClassScheduleTimeVO;
import com.yida.data.system.feign.RemoteDeptService;
import com.yida.data.user.feign.RemoteCourseService;
import com.yida.data.user.feign.RemoteTeacherService;
import com.yida.data.user.feign.RemoteUserDeptService;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

/**
 * 课程表管理 Service实现
 *
 * @author wjm
 * @date 2021-08-13 16:53:48
 */
@Service
@RequiredArgsConstructor
@Transactional
public class EduClassScheduleServiceImpl extends ServiceImpl
    <EduClassScheduleMapper, EduClassSchedule> implements EduClassScheduleService {

    private final EduClassScheduleTimeService eduClassScheduleTimeService;
    private final RemoteDeptService remoteDeptService;
    private final EduClassScheduleTimeClassService eduClassScheduleTimeClassService;
    private final EduClassScheduleTimeClassMapper eduClassScheduleTimeClassMapper;
    private final RemoteCourseService remoteCourseService;
    private final RemoteTeacherService remoteTeacherService;
    private final RemoteUserDeptService remoteUserDeptService;

    @Override
    public IPage<ClassSchedulePageInfoVO> listClassSchedulePage(ClassSchedulePageDTO dto) {
        return baseMapper.listClassSchedulePage(dto.toPage(), dto);
    }

    @Override
    public void saveClassSchedule(EduClassSchedule classSchedule) {
        Asserts.isNotNull(classSchedule.getName(), "课程表名称不能为空");
        // 保存课程表主信息
        saveOrUpdate(classSchedule);
        // 保存课程时间
        saveClassTime(classSchedule.getId(), classSchedule.getTimeList());
    }

    @Override
    public void saveClassTime(Long classScheduleId, List<EduClassScheduleTime> timeList) {
        EduClassSchedule classSchedule = getById(classScheduleId);
        List<Long> existId = timeList.stream().filter(x -> x.getId() != null).map(x -> x.getId())
            .collect(Collectors.toList());
        LambdaQueryWrapper<EduClassScheduleTime> delCondition = Wrappers.<EduClassScheduleTime>lambdaQuery()
            .eq(EduClassScheduleTime::getScheduleId, classScheduleId);
        if (CollUtil.isNotEmpty(existId)) {
            delCondition.notIn(EduClassScheduleTime::getId, existId);
        }
        // 删除要删除的时间
        List<EduClassScheduleTime> delTime = eduClassScheduleTimeService.list(delCondition);
        if (CollUtil.isNotEmpty(delTime)) {
            List<Long> delTimeId = delTime.stream().map(x -> x.getId()).collect(Collectors.toList());
            eduClassScheduleTimeService.removeByIds(delTimeId);
            eduClassScheduleTimeClassService.remove(Wrappers.<EduClassScheduleTimeClass>lambdaQuery()
                .in(EduClassScheduleTimeClass::getScheduleTimeId, delTimeId));
        }
        // 保存新的时间
        if (CollUtil.isNotEmpty(timeList)) {
            for (EduClassScheduleTime time : timeList) {
                time.setScheduleId(classScheduleId);
            }
            eduClassScheduleTimeService.saveOrUpdateBatch(timeList);
        }
        // 保存或修改班级关联信息
        List<EduUserDept> classList = remoteUserDeptService.findChildByParent(classSchedule.getGradeId()).getData();
        // 新增的时间列表
        List<EduClassScheduleTime> saveTimeList = CollUtil.isNotEmpty(existId)
            ? timeList.stream().filter(x -> !existId.contains(x.getId())).collect(Collectors.toList())
            : timeList;
        if (CollUtil.isNotEmpty(classList)) {
            List<EduClassScheduleTimeClass> timeClassList = new ArrayList<>();
            // 构造并保存课程表-时间-班级关系
            for (EduUserDept clazz : classList) {
                for (EduClassScheduleTime time : saveTimeList) {
                    EduClassScheduleTimeClass timeClass = new EduClassScheduleTimeClass();
                    timeClass.setClassId(clazz.getDeptId());
                    timeClass.setClassName(clazz.getDeptName());
                    timeClass.setScheduleId(classSchedule.getId());
                    timeClass.setScheduleTimeId(time.getId());
                    timeClass.setPlace("教室");
                    timeClassList.add(timeClass);
                }
            }
            eduClassScheduleTimeClassService.saveBatch(timeClassList);
        }
    }

    @Override
    public void updateClassScheduleStatus(Long classScheduleId) {
        EduClassSchedule classSchedule = getById(classScheduleId);
        Asserts.isNotNull(classSchedule, "课程表不存在");
        if (classSchedule.getStatus() == 0) {
            classSchedule.setStatus(1);
        } else {
            classSchedule.setStatus(0);
        }
        updateById(classSchedule);
    }

    @Override
    public void delClassScheduleBatch(List<Long> ids) {
        removeByIds(ids);
        eduClassScheduleTimeService.remove(Wrappers.lambdaQuery(new EduClassScheduleTime())
            .in(EduClassScheduleTime::getScheduleId, ids));
        eduClassScheduleTimeClassService.remove(Wrappers.lambdaQuery(new EduClassScheduleTimeClass())
            .in(EduClassScheduleTimeClass::getScheduleId, ids));
    }

    @Override
    public ClassScheduleTimeVO listClassScheduleTime(Long classScheduleId) {
        ClassScheduleTimeVO timeVO = new ClassScheduleTimeVO();
        List<EduClassScheduleTime> times = eduClassScheduleTimeService
            .list(Wrappers.lambdaQuery(new EduClassScheduleTime())
                .eq(EduClassScheduleTime::getScheduleId, classScheduleId));
        if (CollUtil.isNotEmpty(times)) {
            Map<Integer, List<EduClassScheduleTime>> weekTime = times.stream()
                .collect(Collectors.groupingBy(EduClassScheduleTime::getWeekId));
            for (Map.Entry<Integer, List<EduClassScheduleTime>> entry : weekTime.entrySet()) {
                List<EduClassScheduleTime> list = entry.getValue();
                list.stream().sorted((x, y) -> x.getSort() - y.getSort());
                switch (entry.getKey()) {
                    case 1:
                        timeVO.setMon(list);
                        break;
                    case 2:
                        timeVO.setThue(list);
                        break;
                    case 3:
                        timeVO.setWed(list);
                        break;
                    case 4:
                        timeVO.setThu(list);
                        break;
                    case 5:
                        timeVO.setFri(list);
                        break;
                    case 6:
                        timeVO.setSat(list);
                        break;
                    case 7:
                        timeVO.setSun(list);
                        break;
                }
            }
        }
        return timeVO;
    }

    @Override
    public void downloadTemplate(DownloadTemplateDTO dto, HttpServletResponse response) {
        List<EduClassScheduleTime> timeList = eduClassScheduleTimeService
            .list(Wrappers.<EduClassScheduleTime>lambdaQuery()
                .eq(EduClassScheduleTime::getScheduleId, dto.getScheduleId()));
        // 每天的课程节数
        Map<Integer, List<EduClassScheduleTime>> scheduleTimeList = timeList.stream()
            .collect(Collectors.groupingBy(EduClassScheduleTime::getWeekId));
        // 构建课表模板
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFSheet sheet = workbook.createSheet();
        // 设置样式
        XSSFCellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        XSSFFont font = workbook.createFont();
        font.setBold(true);
        font.setFontHeight(2D);
        cellStyle.setFont(font);
        XSSFRow row0 = sheet.createRow(0);
        XSSFRow row1 = sheet.createRow(1);
        row0.setRowStyle(cellStyle);
        row1.setRowStyle(cellStyle);

        // 班级列表
        List<EduClassScheduleTimeClass> classTimeList = eduClassScheduleTimeClassService
            .list(Wrappers.lambdaQuery(new EduClassScheduleTimeClass())
                .eq(EduClassScheduleTimeClass::getScheduleId, dto.getScheduleId()));
        List<String> className =
            classTimeList.stream().map(x -> x.getClassName()).distinct().collect(Collectors.toList());
        if (dto.getType() == 0) {
            row0.createCell(0).setCellValue("总课程表");
            createTotalTemplate(sheet, scheduleTimeList, className);
            // 文字提醒框
            XSSFTextBox textbox = sheet.createDrawingPatriarch().createTextbox(new XSSFClientAnchor(0, 0, 1023,
                255, (short) 10, 10,
                (short) 20, 20));
            textbox.setFillColor(255, 255, 255);
            textbox.addNewTextParagraph("科目：选填，系统内的科目名称");
            textbox.addNewTextParagraph("课程：选填，若不填，则上传后自动同步科目名称");
            textbox.addNewTextParagraph("任课老师：该班级的任课老师或班主任");
        } else {
            row0.createCell(0).setCellValue("班级");
            row0.createCell(1).setCellValue("课程");
            createClassTempate(sheet, scheduleTimeList, className);
        }
        ExcelUtil.export("课程表模板", "xlsx", workbook, response);
    }

    private void createClassTempate(XSSFSheet sheet, Map<Integer, List<EduClassScheduleTime>> scheduleList,
        List<String> className) {
        XSSFRow row0 = sheet.getRow(0);
        Integer maxScheduleNum = 0;
        // 第一行星期
        for (int i = 1; i <= 7; i++) {
            if (scheduleList.containsKey(i)) {
                row0.createCell(i + 1).setCellValue(FebsUtil.convertWeekCN(i));
                maxScheduleNum = Math.max(maxScheduleNum, scheduleList.get(i).size());
            }
        }
        int rowIndex = 1;
        // 使用最大节数*班级数做总节数
        for (int j = 0; j < className.size(); j++) {
            for (int i = 1; i <= maxScheduleNum; i++) {
                XSSFRow nameRow = sheet.createRow(rowIndex);
                rowIndex++;
                nameRow.createCell(0).setCellValue(className.get(j));
                nameRow.createCell(1).setCellValue(String.format("第%s节", i));
            }
        }
    }

    private void createTotalTemplate(XSSFSheet sheet, Map<Integer, List<EduClassScheduleTime>> map,
        List<String> className) {
        XSSFRow row0 = sheet.getRow(0);
        XSSFRow row1 = sheet.getRow(1);
        int colIndex = 1, rowIndex = 2;
        // 星期一
        if (map.containsKey(1)) {
            int startCol = colIndex;
            row0.createCell(colIndex).setCellValue("星期一");
            for (int i = 0; i < map.get(1).size(); i++) {
                row1.createCell(colIndex).setCellValue("第" + (i + 1) + "节");
                colIndex++;
            }
            if (colIndex - 1 > startCol) {
                CellRangeAddress region = new CellRangeAddress(0, 0, startCol, colIndex - 1);
                sheet.addMergedRegion(region);
            }
        }
        // 星期二
        if (map.containsKey(2)) {
            int startCol = colIndex;
            row0.createCell(colIndex).setCellValue("星期二");
            for (int i = 0; i < map.get(2).size(); i++) {
                row1.createCell(colIndex).setCellValue("第" + (i + 1) + "节");
                colIndex++;
            }
            if (colIndex - 1 > startCol) {
                CellRangeAddress region = new CellRangeAddress(0, 0, startCol, colIndex - 1);
                sheet.addMergedRegion(region);
            }
        }
        // 星期三
        if (map.containsKey(3)) {
            int startCol = colIndex;
            row0.createCell(colIndex).setCellValue("星期三");
            for (int i = 0; i < map.get(3).size(); i++) {
                row1.createCell(colIndex).setCellValue("第" + (i + 1) + "节");
                colIndex++;
            }
            if (colIndex - 1 > startCol) {
                CellRangeAddress region = new CellRangeAddress(0, 0, startCol, colIndex - 1);
                sheet.addMergedRegion(region);
            }
        }
        // 星期四
        if (map.containsKey(4)) {
            int startCol = colIndex;
            row0.createCell(colIndex).setCellValue("星期四");
            for (int i = 0; i < map.get(4).size(); i++) {
                row1.createCell(colIndex).setCellValue("第" + (i + 1) + "节");
                colIndex++;
            }
            if (colIndex - 1 > startCol) {
                CellRangeAddress region = new CellRangeAddress(0, 0, startCol, colIndex - 1);
                sheet.addMergedRegion(region);
            }
        }
        // 星期五
        if (map.containsKey(5)) {
            int startCol = colIndex;
            row0.createCell(colIndex).setCellValue("星期五");
            for (int i = 0; i < map.get(5).size(); i++) {
                row1.createCell(colIndex).setCellValue("第" + (i + 1) + "节");
                colIndex++;
            }
            if (colIndex - 1 > startCol) {
                CellRangeAddress region = new CellRangeAddress(0, 0, startCol, colIndex - 1);
                sheet.addMergedRegion(region);
            }
        }
        // 星期六
        if (map.containsKey(6)) {
            int startCol = colIndex;
            row0.createCell(colIndex).setCellValue("星期六");
            for (int i = 0; i < map.get(6).size(); i++) {
                row1.createCell(colIndex).setCellValue("第" + (i + 1) + "节");
                colIndex++;
            }
            if (colIndex - 1 > startCol) {
                CellRangeAddress region = new CellRangeAddress(0, 0, startCol, colIndex - 1);
                sheet.addMergedRegion(region);
            }
        }
        // 星期天
        if (map.containsKey(7)) {
            int startCol = colIndex;
            row0.createCell(colIndex).setCellValue("星期天");
            for (int i = 0; i < map.get(7).size(); i++) {
                row1.createCell(colIndex).setCellValue("第" + (i + 1) + "节");
                colIndex++;
            }
            if (colIndex - 1 > startCol) {
                CellRangeAddress region = new CellRangeAddress(0, 0, startCol, colIndex - 1);
                sheet.addMergedRegion(region);
            }
        }
        // 添加第二行数据
        for (int i = 0; i < className.size(); i++) {
            String clazz = className.get(i);
            int startRow = rowIndex;
            XSSFRow nameRow = sheet.createRow(rowIndex);
            rowIndex++;
            nameRow.createCell(0).setCellValue(clazz);
            if (i == 0) {
                nameRow.createCell(1).setCellValue("科目（添加前请删除）");
            }
            for (int j = 0; j < 2; j++) {
                XSSFRow row = sheet.createRow(rowIndex);
                if (i == 0 && j == 0) {
                    row.createCell(1).setCellValue("课程（添加前请删除）");
                } else if (i == 0 && j == 1) {
                    row.createCell(1).setCellValue("任课老师（添加前请删除）");
                }
                rowIndex++;
            }
            sheet.addMergedRegion(new CellRangeAddress(startRow, rowIndex - 1, 0, 0));
        }
        // 合并总标题
        sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, 0));
    }

    @Override
    public void importTime(String path, Long scheduleId, Integer type) {
        EduClassSchedule schedule = getById(scheduleId);
        Map<String, EduCourse> courseCache = new HashMap<>();
        Map<String, EduStaff> teacherCache = new HashMap<>();
        try {
            Workbook workbook = ExcelUtil.readExcel(new FileInputStream(path), "xlsx");
            Sheet sheet = workbook.getSheetAt(0);
            List<EduClassScheduleTimeClass> classScheduleInfo = eduClassScheduleTimeClassMapper
                .getClassScheduleInfo(scheduleId, null);
            eduClassScheduleTimeClassService.remove(Wrappers.lambdaQuery(new EduClassScheduleTimeClass())
                .eq(EduClassScheduleTimeClass::getScheduleId, scheduleId));
            Map<String, List<EduClassScheduleTimeClass>> map = classScheduleInfo.stream()
                .collect(Collectors.groupingBy(EduClassScheduleTimeClass::getClassName));
            List<EduClassScheduleTimeClass> saveTimeList = new ArrayList<>();
            // 总课表与班级模板不同
            int startRow = type == 0 ? 2 : 1;
            int rowStep = type == 0 ? 3 : 1;
            int startCol = type == 0 ? 1 : 2;
            List<ClassScheduleImportVO> cellData = new ArrayList<>();
            // 获取数据
            Row row0 = sheet.getRow(0);
            for (int i = startRow, rowNum = 0; i <= sheet.getLastRowNum(); i += rowStep, rowNum++) {
                Row row1 = sheet.getRow(i);
                Row row2 = type == 0 ? sheet.getRow(i + 1) : null;
                Row row3 = type == 0 ? sheet.getRow(i + 2) : null;
                String className = ExcelUtil.getCellValue(row1.getCell(0));
                if (StrUtil.isBlank(className)) {
                    break;
                }
                for (int j = startCol, colNum = 0; j < row0.getLastCellNum(); j++, colNum++) {
                    // 获取每堂课信息
                    String course = row1 != null ? ExcelUtil.getCellValue(row1.getCell(j)) : null;
                    String subject = row2 != null ? ExcelUtil.getCellValue(row2.getCell(j)) : null;
                    String teacherName = row3 != null ? ExcelUtil.getCellValue(row3.getCell(j)) : null;
                    // 每一格的数据
                    ClassScheduleImportVO data = new ClassScheduleImportVO();
                    data.setCourse(course);
                    data.setSubject(subject);
                    data.setTeacherName(teacherName);
                    data.setClassName(className);
                    data.setCol(colNum);
                    data.setRow(rowNum);
                    cellData.add(data);
                }
            }

            // 按班级填充数据
            for (Entry<String, List<EduClassScheduleTimeClass>> entry : map.entrySet()) {
                // 按班级导入的数据需要调转数据行列
                List<ClassScheduleImportVO> realDataList = cellData.stream()
                    .filter(x -> x.getClassName().equals(entry.getKey())).sorted((x, y) -> {
                        if (type == 0) {
                            return 1;
                        }
                        Integer score1 = x.getCol() * 10 + x.getRow();
                        Integer score2 = y.getCol() * 10 + y.getRow();
                        if (score1 > score2) {
                            return 1;
                        } else {
                            return -1;
                        }
                    }).collect(Collectors.toList());
                for (int i = 0; i < entry.getValue().size(); i++) {
                    // 获取每堂课信息
                    String course = realDataList.get(i).getCourse();
                    String subject = realDataList.get(i).getSubject();
                    String teacherName = realDataList.get(i).getTeacherName();
                    EduClassScheduleTimeClass time = entry.getValue().get(i);
                    EduClassScheduleTimeClass saveTime = new EduClassScheduleTimeClass();
                    saveTime.setClassId(time.getClassId());
                    saveTime.setClassName(time.getClassName());
                    saveTime.setScheduleId(time.getScheduleId());
                    saveTime.setScheduleTimeId(time.getScheduleTimeId());
                    EduCourse eduCourse = null;
                    if (StrUtil.isNotBlank(course)) {
                        eduCourse = courseCache.containsKey(course)
                            ? courseCache.get(course)
                            : remoteCourseService.getCourseByName(course, schedule.getSchoolId()).getData();
                        Asserts.isNotNull(eduCourse, String.format("导入失败,%s课程不存在", course));
                        courseCache.put(course, eduCourse);
                    }
                    if (StrUtil.isNotBlank(teacherName)) {
                        EduStaff teacher = teacherCache.containsKey(teacherName) ? teacherCache.get(teacherName) : null;
                        if (teacher == null) {
                            List<EduStaff> teacherList = remoteTeacherService
                                .listTeacherByClassAndName(time.getClassId(), teacherName).getData();
                            Asserts.isTrue(CollUtil.isNotEmpty(teacherList), String.format("%s教师不存在或不属于该班级",
                                teacherName));
                            Asserts.isTrue(teacherList.size() == 1, String.format("%s教师存在同名", teacherName));
                            teacher = teacherList.get(0);
                            teacherCache.put(teacherName, teacherList.get(0));
                        }
                        saveTime.setTeacherId(teacher.getId());
                        saveTime.setTeacherName(teacher.getName());
                    }
                    if (eduCourse != null) {
                        saveTime.setSubjectId(eduCourse.getId());
                        saveTime.setCourseName(StrUtil.isNotBlank(subject) ? subject : eduCourse.getCourseName());
                    }
                    saveTimeList.add(saveTime);
                }
            }
            eduClassScheduleTimeClassService.saveBatch(saveTimeList);
        } catch (Exception e) {
            log.error("import fail", e);
            String errMsg = FebsUtil.containChinese(e.getMessage()) ? e.getMessage() : "导入失败";
            throw new FebsException(errMsg);
        } finally {
            FileUtil.del(new File(path));
        }
    }
}
