package com.xjrsoft.module.job;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.xjrsoft.common.mybatis.SqlRunnerAdapter;
import com.xjrsoft.common.utils.VoToColumnUtil;
import com.xjrsoft.common.utils.WeChatUtil;
import com.xjrsoft.config.CommonPropertiesConfig;
import com.xjrsoft.module.attendance.entity.AttendanceMessageSet;
import com.xjrsoft.module.attendance.entity.AttendanceMessageUserRelation;
import com.xjrsoft.module.attendance.entity.AttendanceRuleDetails;
import com.xjrsoft.module.attendance.service.IAttendanceMessageSetService;
import com.xjrsoft.module.attendance.service.IAttendanceRuleCategoryService;
import com.xjrsoft.module.concat.service.IXjrUserService;
import com.xjrsoft.module.holiday.entity.HolidayDate;
import com.xjrsoft.module.holiday.service.IHolidayDateService;
import com.xjrsoft.module.organization.dto.WeChatSendMessageDto;
import com.xjrsoft.module.organization.entity.UserRoleRelation;
import com.xjrsoft.module.organization.service.IWeChatService;
import com.xjrsoft.module.outint.entity.StudentOutInRecord;
import com.xjrsoft.module.outint.entity.TeacherOutInRecord;
import com.xjrsoft.module.outint.service.IStudentOutInRecordService;
import com.xjrsoft.module.outint.service.ITeacherOutInRecordService;
import com.xjrsoft.module.student.entity.BaseStudent;
import com.xjrsoft.module.teacher.entity.BaseTeacher;
import com.xjrsoft.module.teacher.entity.XjrUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 考勤消息通知，给领导推送每个时段的考勤情况
 * @author dzx
 * @date 2024年6月7日
 */
@Component
@Slf4j
public class AttendanceMessageTask {
    @Autowired
    private IAttendanceMessageSetService messageSetService;

    @Autowired
    private IAttendanceRuleCategoryService ruleCategoryService;

    @Autowired
    private IHolidayDateService holidayDateService;

    @Autowired
    private IXjrUserService userService;

    @Autowired
    private IStudentOutInRecordService studentOutInRecordService;

    @Autowired
    private ITeacherOutInRecordService teachertOutInRecordService;

    @Autowired
    private IWeChatService weChatService;

    @Autowired
    private CommonPropertiesConfig commonPropertiesConfig;

    @Autowired
    private WeChatUtil weChatUtil;

    @Scheduled(cron = "0 */1 * * * ?")
    public void RefreshConnectionPool() {
        String active = SpringUtil.getActiveProfile();
        if(!"prod".equals(active)){
            log.info("非正式环境，无法执行数据推送");
            return;
        }
        doExecute();
    }

    public void doExecute(){
        LocalDateTime now = LocalDateTime.now();
        HolidayDate holidayDate = holidayDateService.getOne(
            new QueryWrapper<HolidayDate>().lambda()
            .eq(HolidayDate::getDate, now.toLocalDate())
        );
        //查询到了数据，且当天不是“普通工作日”或“需要补班的工作日”
        if(holidayDate != null && holidayDate.getWay() != null && holidayDate.getWay() != 0 && holidayDate.getWay() != 2){
            log.info("非工作日，不需要提醒");
            return;
        }
        //未查询到数据
        String dayOfWeek = now.getDayOfWeek().name();
        if(holidayDate == null || "SUNDAY".equals(dayOfWeek) || "SATURDAY".equals(dayOfWeek)){
            log.info("非工作日，不需要提醒");
//            return;
        }
        //查询今天的考勤规则
        List<AttendanceRuleDetails> ruleDetails = ruleCategoryService.getTodayRules();
        List<AttendanceMessageSet> list = messageSetService.list();
        //判断是上午还是下午
        Integer timePeriod = null;
        String timePeriodStr = null;
        if(now.getHour() <= 12){
            timePeriod = 1;
            timePeriodStr = "上午考勤";
        }else if(now.getHour() <= 18){
            timePeriod = 2;
            timePeriodStr = "下午考勤";
        }else{
            timePeriod = 3;
            timePeriodStr = "晚上考勤";
        }

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        int userCount = 0;
        Set<Integer> roleTypes = list.stream().map(AttendanceMessageSet::getRoleType).collect(Collectors.toSet());

        //查询需要通知的人
        String table = "wechat_message_log";
        boolean isInsert = true;
        for (AttendanceMessageSet messageSet : list) {
            LocalDateTime recentlyTime = null;
            String characterKey = "character_string18";
            String wechatTemplate = weChatUtil.getAttendanceMsgLateTemplate();
            if(messageSet.getMessageCategory() != null && messageSet.getMessageCategory() == 1){
                wechatTemplate = weChatUtil.getAttendanceMsgLateTemplate();
                characterKey = "character_string18";
                //获取最近的时间
                recentlyTime = getRecentlyTime(ruleDetails, now);
                if(recentlyTime.getHour() != now.getHour() || (recentlyTime.getHour() == now.getHour() && recentlyTime.getMinute() != now.getMinute())){
                    log.info("未到时间，不进行推送");
                    isInsert = false;
                    continue;
                }
                Entity entity = Entity.create(table);
                entity.set("send_time", recentlyTime);
                entity.set("template_id", wechatTemplate);
                String sql = "SELECT COUNT(*) FROM wechat_message_log WHERE delete_mark = 0 AND template_id = '" + wechatTemplate
                        + "' AND send_time = '" + recentlyTime.format(formatter) + "'";
                long count = SqlRunnerAdapter.db().selectCount(sql);
                if(count > 0){//已经推送过，不再进行推送
                    log.info("已推送过，不进行推送");
                    isInsert = false;
                    continue;
                }
            }else if(messageSet.getMessageCategory() != null && messageSet.getMessageCategory() == 3){
                wechatTemplate = weChatUtil.getAttendanceMsgAbsenceTemplate();
                recentlyTime = getRecentlyOverTime(ruleDetails, now);
                characterKey = "character_string36";
                if(recentlyTime.getHour() != now.getHour() || (recentlyTime.getHour() == now.getHour() && recentlyTime.getMinute() != now.getMinute())){
                    log.info("未到时间，不进行推送");
                    isInsert = false;
                    continue;
                }
                String sql = "SELECT COUNT(*) FROM wechat_message_log WHERE delete_mark = 0 AND template_id = '" + wechatTemplate
                        + "' AND send_time = '" + recentlyTime.format(formatter) + "'";
                long count = SqlRunnerAdapter.db().selectCount(sql);
                if(count > 0){//已经推送过，不再进行推送
                    log.info("已推送过，不进行推送");
                    isInsert = false;
                    continue;
                }
            }

            if(!messageSet.getTimePeriod().contains(timePeriod + "")){
                isInsert = false;
                continue;
            }

            List<XjrUser> userList = userService.list(
                    new MPJLambdaWrapper<XjrUser>().distinct()
                            .select(XjrUser::getId)
                            .select(XjrUser.class, x -> VoToColumnUtil.fieldsToColumns(XjrUser.class).contains(x.getProperty()))
                            .leftJoin(UserRoleRelation.class, UserRoleRelation::getUserId, XjrUser::getId)
                            .leftJoin(AttendanceMessageUserRelation.class, AttendanceMessageUserRelation::getUserId, XjrUser::getId)
                            .in(UserRoleRelation::getRoleId, roleTypes)
                            .eq(AttendanceMessageUserRelation::getAttendanceMessageSetId, messageSet.getId())
            );

            //没有需要通知的，直接跳过
            if(userList.isEmpty()){
                isInsert = false;
                continue;
            }
            if(recentlyTime == null){
                isInsert = false;
                continue;
            }

            String format = recentlyTime.format(formatter);

            userCount += userList.size();
            if(messageSet.getRoleType() != null && messageSet.getRoleType() == 1){
                //教师总人数
                long teacherCout = userService.count(
                        new MPJLambdaWrapper<XjrUser>()
                                .leftJoin(BaseTeacher.class, BaseTeacher::getUserId, XjrUser::getId)
                );
                //查询进入记录，就是实际到校的人数
                List<TeacherOutInRecord> outInRecords = teachertOutInRecordService.list(
                        new MPJLambdaWrapper<TeacherOutInRecord>()
                                .le(TeacherOutInRecord::getRecordTime, recentlyTime)
                                .eq(TeacherOutInRecord::getStatus, 1)
                                .eq("DATE_FORMAT(record_time, '%Y-%m-%d')", recentlyTime.toLocalDate())
                );
                List<Long> collect = outInRecords.stream().map(TeacherOutInRecord::getUserId).collect(Collectors.toList());

                WeChatSendMessageDto weChatSendMessageDto = new WeChatSendMessageDto();
                weChatSendMessageDto.setTemplateId(wechatTemplate);
                JSONObject paramJson = new JSONObject();

                JSONObject thing6 = new JSONObject();
                thing6.put("value", "教职工");
                paramJson.put("thing6", thing6);

                JSONObject time11 = new JSONObject();
                time11.put("value", format);
                paramJson.put("time11", time11);

                JSONObject const23 = new JSONObject();
                const23.put("value", timePeriodStr);
                paramJson.put("const23", const23);

                //迟到人数或者缺勤人数
                JSONObject character_string18 = new JSONObject();
                character_string18.put("value", teacherCout - collect.size());
                paramJson.put(characterKey, character_string18);

                JSONObject character_string16 = new JSONObject();
                character_string16.put("value", teacherCout);
                paramJson.put("character_string16", character_string16);

                weChatSendMessageDto.setContent(paramJson);
                weChatSendMessageDto.setUrl(StrUtil.format("{}pages/attendance/teacher/index", commonPropertiesConfig.getDomainApp()));

                for (XjrUser xjrUser : userList) {
                    weChatSendMessageDto.setMsgId(IdUtil.getSnowflakeNextId() + "");
                    weChatSendMessageDto.setUserId(xjrUser.getOpenId());
                    //weChatService.sendTemplateMessage(weChatSendMessageDto);
                }
            }else if(messageSet.getRoleType() != null && messageSet.getRoleType() == 2){
                //教师总人数
                long teacherCout = userService.count(
                        new MPJLambdaWrapper<XjrUser>()
                                .leftJoin(BaseStudent.class, BaseStudent::getUserId, XjrUser::getId)
                );
                //查询进入记录，就是实际到校的人数
                long outInRecords = studentOutInRecordService.count(
                        new MPJLambdaWrapper<StudentOutInRecord>()
                                .le(StudentOutInRecord::getRecordTime, recentlyTime)
                                .eq(StudentOutInRecord::getStatus, 1)
                );

                WeChatSendMessageDto weChatSendMessageDto = new WeChatSendMessageDto();
                weChatSendMessageDto.setTemplateId(wechatTemplate);
                JSONObject paramJson = new JSONObject();

                JSONObject thing6 = new JSONObject();
                thing6.put("value", "学生");
                paramJson.put("thing6", thing6);

                JSONObject time11 = new JSONObject();
                time11.put("value", format);
                paramJson.put("time11", time11);

                JSONObject const23 = new JSONObject();
                const23.put("value", timePeriodStr);
                paramJson.put("const23", const23);

                JSONObject character_string18 = new JSONObject();
                character_string18.put("value", teacherCout - outInRecords);
                paramJson.put(characterKey, character_string18);

                JSONObject character_string16 = new JSONObject();
                character_string16.put("value", teacherCout);
                paramJson.put("const3", character_string16);

                weChatSendMessageDto.setContent(paramJson);
                weChatSendMessageDto.setUrl(StrUtil.format("{}pages/attendance/class/index", commonPropertiesConfig.getDomainApp()));

                for (XjrUser xjrUser : userList) {
                    weChatSendMessageDto.setMsgId(IdUtil.getSnowflakeNextId() + "");
                    weChatSendMessageDto.setUserId(xjrUser.getOpenId());
                    //weChatService.sendTemplateMessage(weChatSendMessageDto);
                }
                isInsert = true;
            }

            if(isInsert){
                Entity entity = Entity.create(table);
                entity.set("content", "消息推送人数：" + userCount);
                entity.set("create_date", LocalDateTime.now());
                entity.set("template_id", wechatTemplate);
                entity.set("send_time", recentlyTime);
                SqlRunnerAdapter.db().dynamicInsert(table, entity);
            }

        }
    }

    LocalDateTime getRecentlyTime(List<AttendanceRuleDetails> ruleDetails, LocalDateTime now){
        List<LocalDateTime> result = new ArrayList<>();
        for (AttendanceRuleDetails ruleDetail : ruleDetails) {
            if(ruleDetail.getIsAllowInOutSchool() != null && ruleDetail.getIsAllowInOutSchool() == 1
                    && ruleDetail.getIsAttendance() != null && ruleDetail.getIsAttendance() == 1){
                if(ruleDetail.getAmStartTime() != null){
                    LocalDateTime amStartTime = now.with(ruleDetail.getAmStartTime().toLocalTime());
                    result.add(amStartTime);
                }
                if(ruleDetail.getPmStartTime() != null){
                    LocalDateTime pmStartTime = now.with(ruleDetail.getPmStartTime().toLocalTime());
                    result.add(pmStartTime);
                }

                if(ruleDetail.getEveningStartTime() != null){
                    LocalDateTime eveningStartTime = now.with(ruleDetail.getEveningStartTime().toLocalTime());
                    result.add(eveningStartTime);
                }
            }
        }
        if(result.isEmpty()){
            return null;
        }
        Map<Long, LocalDateTime> timeMap = new HashMap<>();
        for (LocalDateTime localDateTime : result) {
            long between = ChronoUnit.SECONDS.between(now, localDateTime);
            timeMap.put(Math.abs(between), localDateTime);
        }
        List<Long> collect = timeMap.keySet().stream().collect(Collectors.toList());
        Collections.sort(collect, Long::compare);

        return timeMap.get(collect.get(0));
    }

    LocalDateTime getRecentlyOverTime(List<AttendanceRuleDetails> ruleDetails, LocalDateTime now){
        List<LocalDateTime> result = new ArrayList<>();
        for (AttendanceRuleDetails ruleDetail : ruleDetails) {
            if(ruleDetail.getIsAllowInOutSchool() != null && ruleDetail.getIsAllowInOutSchool() == 1
                    && ruleDetail.getIsAttendance() != null && ruleDetail.getIsAttendance() == 1){
                if(ruleDetail.getAmStartTime() != null){
                    LocalDateTime amStartTime = now.with(ruleDetail.getAmStartTime().toLocalTime());
                    if(ruleDetail.getOverMinutes() != null){
                        amStartTime.plusMinutes(ruleDetail.getOverMinutes());
                    }
                    result.add(amStartTime);
                }
                if(ruleDetail.getPmStartTime() != null){
                    LocalDateTime pmStartTime = now.with(ruleDetail.getPmStartTime().toLocalTime());
                    if(ruleDetail.getOverMinutes() != null){
                        pmStartTime.plusMinutes(ruleDetail.getOverMinutes());
                    }
                    result.add(pmStartTime);
                }

                if(ruleDetail.getEveningStartTime() != null){
                    LocalDateTime eveningStartTime = now.with(ruleDetail.getEveningStartTime().toLocalTime());
                    if(ruleDetail.getOverMinutes() != null){
                        eveningStartTime.plusMinutes(ruleDetail.getOverMinutes());
                    }
                    result.add(eveningStartTime);
                }
            }
        }
        if(result.isEmpty()){
            return null;
        }
        Map<Long, LocalDateTime> timeMap = new HashMap<>();
        for (LocalDateTime localDateTime : result) {
            long between = ChronoUnit.SECONDS.between(now, localDateTime);
            timeMap.put(Math.abs(between), localDateTime);
        }
        List<Long> collect = timeMap.keySet().stream().collect(Collectors.toList());
        Collections.sort(collect, Long::compare);

        return timeMap.get(collect.get(0));
    }


}
