package com.rongwei.trainingcommon.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.rongwei.rwadmincommon.system.domain.SysRoleDo;
import com.rongwei.rwadmincommon.system.domain.SysUserDo;
import com.rongwei.rwadmincommon.system.domain.SysUserRoleDo;
import com.rongwei.rwadmincommon.system.service.SysConfigFeignService;
import com.rongwei.rwadmincommon.system.service.SysRoleService;
import com.rongwei.rwadmincommon.system.service.SysUserRoleService;
import com.rongwei.rwadmincommon.system.service.SysUserService;
import com.rongwei.rwcommon.base.BaseDo;
import com.rongwei.rwcommon.utils.StringUtils;
import com.rongwei.rwcommon.vo.MailDo;
import com.rongwei.training.domain.EmpExamDo;
import com.rongwei.training.domain.PlanDo;
import com.rongwei.training.domain.PlanPaperDo;
import com.rongwei.training.vo.WordEmpInfoVo;
import com.rongwei.trainingcommon.sys.dao.TraningCommonDao;
import com.rongwei.trainingcommon.sys.service.EmpExamService;
import com.rongwei.trainingcommon.sys.service.PlanPaperService;
import com.rongwei.trainingcommon.sys.service.PlanService;
import com.rongwei.trainingcommon.sys.service.SendMailService;
import com.spire.doc.*;
import com.spire.doc.documents.*;
import com.spire.doc.fields.TextRange;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.awt.*;
import java.io.File;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
 * SendMailServiceImpl class
 *
 * @author XH
 * @date 2020/11/27
 */
@Service("sendMailService")
@Slf4j
public class SendMailServiceImpl implements SendMailService {
    // 培训计划已发布
    private static final String TRAINING_PLAN_PUBLISH = "y";
    // 培训管理员角色名称
    private static final String TRAIN_ADMINISTRATOR_ROLE_NAME = "培训管理员";
    // 邮箱配置信息
    private static final String MAIL_SETTING_CONFIG_CODE = "mailSetting";
    // word表格头
    private static final String[] WORD_TABLE_HEADER = {"序号", "工号", "姓名", "部门", "岗位", "联系方式", "培训课程名称", "未考试试卷名称"};
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private PlanService planService;
    @Autowired
    private EmpExamService empExamService;
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysConfigFeignService sysConfigFeignService;
    @Autowired
    private PlanPaperService planPaperService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private TraningCommonDao commonDao;

    /**
     * 邮件标题： 培训开始提醒
     * 邮件内容： 您好，XXX培训将于XXXX年XX月XX日开始进行，请及时参加学习。学习地址：http://www.xxxx.com
     */
    @Override
    public void startReminderEmail() {
        List<PlanDo> planDoList = planService.list(new LambdaQueryWrapper<PlanDo>().eq(BaseDo::getDeleted, "0").eq(PlanDo::getPublish, TRAINING_PLAN_PUBLISH));
        if (planDoList.isEmpty()) {
            return;
        }
        log.info("获取到培训计划：{}", planDoList);
        // 筛选在3天以内就要开始的培训计划
        List<PlanDo> list = planDoList.parallelStream()
                .filter(planDo -> planDo.getTrainingstarttime() != null && this.aFewDaysApart(planDo.getTrainingstarttime()) == 3)
                .collect(Collectors.toList());
        if (list.isEmpty()) {
            return;
        }
        // 培训计划Id
        List<String> planIdList = list.stream().map(PlanDo::getId).distinct().collect(Collectors.toList());
        log.info("获取到培训计划Id:{}", planIdList);
        // 根据培训计划发送培训开始提醒邮件
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
        planIdList.forEach(planId -> {
            PlanDo planInfo = planDoList.stream().filter(planDo -> planDo.getId().equals(planId)).findFirst().orElse(null);
            if (planInfo == null) {
                log.info("无法获取培训信息");
                return;
            }
            log.info("");
            // 根据planId获取对应的 员工Id
            Set<String> empIdsFromPlan = planService.getPlanUserIdsFromPlan(planInfo);
            if (empIdsFromPlan.isEmpty()) {
                log.info("无法获取员工信息");
                return;
            }
            // 获取员工信息
            List<SysUserDo> empInfo = sysUserService.list(new LambdaQueryWrapper<SysUserDo>().eq(BaseDo::getDeleted, "0").in(SysUserDo::getId, empIdsFromPlan));
            // 员工email
            Set<String> empMail = empInfo.stream().filter(user -> StringUtils.isNotBlank(user.getEmail())).map(SysUserDo::getEmail).collect(Collectors.toSet());
            StringBuilder mailContent = new StringBuilder("您好，");
            mailContent.append(planInfo.getCoursename()).append("培训将于").append(simpleDateFormat.format(planInfo.getTrainingstarttime())).append("开始进行");
            mailContent.append("，请及时参加学习。");
            mailContent.append("详见智能安全监控平台：http://192.168.0.205:8000");
            String[] reMails = empMail.stream().toArray(String[]::new);
            sendMail(reMails, "培训开始提醒邮件", mailContent.toString());
            log.info("发送:{},给:{}", "培训开始提醒邮件", empInfo);
        });
    }


    @Override
    public void endReminderEmail(boolean sendToTrainAdministrator) {
        // 获取培训计划
        List<PlanDo> planDoList = planService.list(new LambdaQueryWrapper<PlanDo>().eq(BaseDo::getDeleted, "0").eq(PlanDo::getPublish, TRAINING_PLAN_PUBLISH));
        if (planDoList.isEmpty()) {
            return;
        }
        log.info("获取到培训计划：{}", planDoList);
        // 根据培训计划获取对应的试卷信息
        List<PlanPaperDo> planPaperList = planPaperService.list(new LambdaQueryWrapper<PlanPaperDo>()
                .eq(BaseDo::getDeleted, "0")
                .in(PlanPaperDo::getTrainingplanid, planDoList.stream().map(PlanDo::getId).collect(Collectors.toSet())));
        log.info("获取到的试卷信息：{}", planPaperList);
        // 筛选出距离截止日期还有3天的试卷信息
        List<PlanPaperDo> closePlanPaper = planPaperList
                .parallelStream()
                .filter(planPaper -> planPaper.getDeadline() != null && this.aFewDaysApart(planPaper.getDeadline()) == 3)
                .collect(Collectors.toList());
        if (closePlanPaper.isEmpty()) {
            return;
        }
        // 即将截止的试卷Id
        Set<String> paperId = closePlanPaper.stream().map(PlanPaperDo::getPaperid).collect(Collectors.toSet());
        // 试卷所属的培训计划
        Set<String> planId = closePlanPaper.stream().map(PlanPaperDo::getTrainingplanid).collect(Collectors.toSet());
        /*****获取没有参与考试的培训员工******/
        List<EmpExamDo> toTestInfoList = empExamService.list(new LambdaQueryWrapper<EmpExamDo>()
                .eq(BaseDo::getDeleted, "0").eq(EmpExamDo::getExamstatus, "待考试").in(EmpExamDo::getPlanid, planId).in(EmpExamDo::getPaperid, paperId));
        if (toTestInfoList.isEmpty()) {
            return;
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
        // 按照培训计划ID 进行分组
        Map<String, List<EmpExamDo>> infoMap = toTestInfoList.stream()
                .filter(examPaperDo -> examPaperDo.getExamdeadtime() != null)
                .collect(Collectors.groupingBy(EmpExamDo::getPlanid, Collectors.toList()));
        // 发送考试截止提醒邮件
        if (!sendToTrainAdministrator) {
            log.info("准备发送考试截止提醒邮件");
            this.endReminderEmail(infoMap, planDoList, simpleDateFormat);
        }
        // 发送未考试人员提醒邮件
        if (sendToTrainAdministrator) {
            log.info("准备发送未考试人员提醒邮件");
            this.sendMailForTrainAdministrator(infoMap, planDoList, simpleDateFormat);
        }
    }

    /**
     * 	邮件标题：“考试截止提醒”
     * 	邮件内容：“您好，XXX培训考试将于XXXX年XX月XX日截止，请尽快参加考试。考试地址：http://www.xxxx.com”
     */
    public void endReminderEmail(Map<String, List<EmpExamDo>> infoMap, List<PlanDo> planDoList, SimpleDateFormat simpleDateFormat) {
        infoMap.forEach((k, v) -> {
            log.info("准备开始考试截止提醒邮件");
            PlanDo planDo = planDoList.stream().filter(planInfo -> planInfo.getId().equals(k)).findFirst().orElse(null);
            if (planDo == null) {
                return;
            }
            Set<String> empIdList = v.stream().map(EmpExamDo::getEmpid).collect(Collectors.toSet());
            if (empIdList.isEmpty()) {
                return;
            }
            List<SysUserDo> empList = sysUserService.list(new LambdaQueryWrapper<SysUserDo>().eq(BaseDo::getDeleted, "0").in(SysUserDo::getId, empIdList));
            if (empList.isEmpty()) {
                return;
            }
            String[] reMails = empList.stream().filter(empInfo -> StringUtils.isNotBlank(empInfo.getEmail())).map(SysUserDo::getEmail).distinct().toArray(String[]::new);
            StringBuilder mailContent = new StringBuilder("您好，");
            mailContent.append(planDo.getCoursename()).append("培训考试将于").append(simpleDateFormat.format(v.get(0).getExamdeadtime())).append("截止");
            mailContent.append("，请尽快参加考试。");
            mailContent.append("详见智能安全监控平台：http://192.168.0.205:8000");
            sendMail(reMails, "考试截止提醒邮件", mailContent.toString());
            log.info("发送:{},给:{}", "考试截止提醒邮件", empList);
        });
    }

    /**
     * 	邮件标题：“未考试人员提醒”
     * 	邮件内容：“您好，XXX培训考试将于XXXX年XX月XX日截止，附件人员是暂未参加考试人员，请及时督促员工学习考试。”
     */
    public void sendMailForTrainAdministrator(Map<String, List<EmpExamDo>> infoMap, List<PlanDo> planDoList, SimpleDateFormat simpleDateFormat) {
        // 获取 培训培训管理员信息
        List<SysRoleDo> list = sysRoleService.list(new LambdaQueryWrapper<SysRoleDo>()
                .eq(BaseDo::getDeleted, "0").eq(SysRoleDo::getName, TRAIN_ADMINISTRATOR_ROLE_NAME));
        if (list.isEmpty()) {
            log.info("无法获取培训管理员角色");
            return;
        }
        // 获取培训管理员角色下的员工
        List<SysUserRoleDo> sysUserRoleDoList = sysUserRoleService.list(new LambdaQueryWrapper<SysUserRoleDo>()
                .eq(BaseDo::getDeleted, "0").in(SysUserRoleDo::getRoleid, list.stream().map(SysRoleDo::getId).collect(Collectors.toSet())));
        Set<String> trainAdministratorIds = sysUserRoleDoList.stream().map(SysUserRoleDo::getUserid).collect(Collectors.toSet());
        if (trainAdministratorIds.isEmpty()) {
            log.info("没有员工属于培训管理员");
            return;
        }
        // 获取培训管理员信息
        Set<String> trainAdministratorMailSet = sysUserService.list(new LambdaQueryWrapper<SysUserDo>().eq(BaseDo::getDeleted, "0").in(SysUserDo::getId, trainAdministratorIds)).stream()
                .filter(userDo -> StringUtils.isNotBlank(userDo.getEmail()))
                .map(SysUserDo::getEmail)
                .collect(Collectors.toSet());
        if (trainAdministratorMailSet.isEmpty()) {
            log.info("培训管理员邮箱为空");
            return;
        }
        log.info("收件人：{}", trainAdministratorMailSet);
        for (Map.Entry<String, List<EmpExamDo>> entry : infoMap.entrySet()) {
            String k = entry.getKey();
            List<EmpExamDo> v = entry.getValue();
            PlanDo planDo = planDoList.stream().filter(planInfo -> planInfo.getId().equals(k)).findFirst().orElse(null);
            if (planDo == null) {
                log.info("培训计划为空");
                continue;
            }
            List<String> empIdList = v.stream().map(EmpExamDo::getEmpid).distinct().collect(Collectors.toList());
            if (empIdList.isEmpty()) {
                log.info("员工Id为空");
                continue;
            }
            // 根据 员工ID 获取员工所属的角色和部门
            List<WordEmpInfoVo> empInfo = commonDao.getEmpInfo(empIdList);
            log.info("获取到员工信息:{}", empInfo);
            // 邮箱正文
            StringBuilder mailContent = new StringBuilder("您好，");
            mailContent.append(planDo.getCoursename()).append("培训考试将于").append(simpleDateFormat.format(v.get(0).getExamdeadtime())).append("截止");
            mailContent.append("，附件人员是暂未参加考试人员，请及时督促员工学习考试。");
            mailContent.append("详见智能安全监控平台：http://192.168.0.205:8000");
            // 创建附件
            String accessoryName = createAccessory(v.get(0), empInfo, planDo);
            File file = new File(accessoryName);
            sendMail(trainAdministratorMailSet.stream().toArray(String[]::new), "未考试人员提醒", mailContent.toString(), new File[]{file});
            // MailUtil.send(mailAccount,trainAdministratorMailSet, "未考试人员提醒", mailContent.toString(),false, file);
            log.info("发送:{},给:{}", "未考试人员提醒,成功。", trainAdministratorMailSet);
            // 保存发送邮件的历史记录
            if (file.exists()) {
                log.info("删除附件");
                file.delete();
            }
        }
    }

    /**
     * 	邮件标题：“未考试人员提醒”
     * 	邮件内容：“您好，XXX培训考试将于XXXX年XX月XX日截止，附件人员是暂未参加考试人员，请及时督促员工学习考试。”
     *
     * @return
     */
    @Override
    public void dontTakeExamMail() {

    }

    /**
     * 发送邮件
     *
     * @param reMails     收件人
     * @param subject     主题
     * @param mailContent 内容
     */
    private void sendMail(String[] reMails, String subject, String mailContent) {
        sendMail(reMails, subject, mailContent, null);
    }

    /**
     * 发送邮件
     *
     * @param reMails     收件人
     * @param subject     主题
     * @param mailContent 内容
     */
    private void sendMail(String[] reMails, String subject, String mailContent, File[] files) {
        MailDo mail = new MailDo();
        mail.setSubject(subject);
        mail.setReceiveEmail(reMails);
        mail.setContent(mailContent);
        if (files != null) {
            mail.setFileList(Arrays.asList(files));
            // 邮件发送服务调用
            sysConfigFeignService.sendMailWithAccessory(mail);
        } else {
            sysConfigFeignService.sendTextMail(mail);
        }
    }

    /**
     * 校验日期相隔几天
     *
     * @param startDate
     * @return
     */
    public long aFewDaysApart(Date startDate) {
        Calendar startInstance = Calendar.getInstance();
        startInstance.setTime(startDate);
        LocalDate startLocalDate = LocalDate.of(startInstance.get(Calendar.YEAR), startInstance.get(Calendar.MONTH) + 1, startInstance.get(Calendar.DAY_OF_MONTH));
        return startLocalDate.toEpochDay() - LocalDate.now().toEpochDay();
    }

    /**
     * 创建未考试人员附件
     *
     * @param empExamDo 试卷信息
     * @param empInfo   员工信息
     * @return
     */
    private String createAccessory(EmpExamDo empExamDo, List<WordEmpInfoVo> empInfo, PlanDo planDo) {
        log.info("开始创建未考试人员附件");
        Document doc = new Document();
        Section sec = doc.addSection();
        Table table = sec.addTable(true);
        table.resetCells(empInfo.size() + 1, WORD_TABLE_HEADER.length);
        TableRow row = table.getRows().get(0);
        row.isHeader(true);
        row.setHeight(20);
        row.setHeightType(TableRowHeightType.Exactly);
        row.getRowFormat().setBackColor(Color.white);
        // 组织表格需要的数据
        String[][] data = new String[empInfo.size()][WORD_TABLE_HEADER.length];
        for (int i = 0; i < empInfo.size(); i++) {
            WordEmpInfoVo wordEmpInfoVo = empInfo.get(i);
            String[] dataArray = {String.valueOf(i + 1), wordEmpInfoVo.getAccount(),
                    wordEmpInfoVo.getEmpName(), wordEmpInfoVo.getOrgName(),
                    wordEmpInfoVo.getRoleName(),
                    StringUtils.isNotBlank(wordEmpInfoVo.getMobile()) ? wordEmpInfoVo.getMobile() : "",
                    planDo.getCoursename(),
                    empExamDo.getPapername()};
            data[i] = dataArray;
        }

        // 创建表头
        for (int i = 0; i < WORD_TABLE_HEADER.length; i++) {
            row.getCells().get(i).getCellFormat().setVerticalAlignment(VerticalAlignment.Middle);
            Paragraph p = row.getCells().get(i).addParagraph();
            p.getFormat().setHorizontalAlignment(HorizontalAlignment.Center);
            TextRange range1 = p.appendText(WORD_TABLE_HEADER[i]);
            range1.getCharacterFormat().setFontName("Arial");
            range1.getCharacterFormat().setFontSize(12f);
            range1.getCharacterFormat().setBold(true);
            range1.getCharacterFormat().setTextColor(Color.BLACK);
        }
        // 填充数据
        for (int r = 0; r < data.length; r++) {
            TableRow dataRow = table.getRows().get(r + 1);
            dataRow.setHeight(25);
            dataRow.setHeightType(TableRowHeightType.Exactly);
            dataRow.getRowFormat().setBackColor(Color.white);
            for (int c = 0; c < data[r].length; c++) {
                dataRow.getCells().get(c).getCellFormat().setVerticalAlignment(VerticalAlignment.Middle);
                TextRange range2 = dataRow.getCells().get(c).addParagraph().appendText(data[r][c]);
                range2.getOwnerParagraph().getFormat().setHorizontalAlignment(HorizontalAlignment.Center);
                range2.getCharacterFormat().setFontName("Arial");
                range2.getCharacterFormat().setFontSize(10f);
            }
        }
        // 自适应列宽
        table.autoFit(AutoFitBehaviorType.Auto_Fit_To_Contents);
        // 设置边框样式
        table.getTableFormat().getBorders().setBorderType(BorderStyle.Thick_Thin_Large_Gap);
        doc.saveToFile("未考试人员附件列表.docx", FileFormat.Docx_2013);
        log.info("附件创建结束");
        return "未考试人员附件列表.docx";
    }
}
