package com.xjrsoft.module.job;

import cn.hutool.core.util.IdUtil;
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.xjrsoft.common.mybatis.SqlRunnerAdapter;
import com.xjrsoft.common.utils.WeChatUtil;
import com.xjrsoft.module.attendance.entity.AttendanceRuleDetails;
import com.xjrsoft.module.attendance.entity.AttendanceUserRelation;
import com.xjrsoft.module.attendance.mapper.AttendanceUserRelationMapper;
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.service.IWeChatService;
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.text.SimpleDateFormat;
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年5月21日
 */
@Component
@Slf4j
public class AttenDanceWarnNoticeTask {
    @Autowired
    private IAttendanceRuleCategoryService categoryService;

    @Autowired
    private IXjrUserService userService;

    @Autowired
    private AttendanceUserRelationMapper relationMapper;

    @Autowired
    private IWeChatService weChatService;

    @Autowired
    private WeChatUtil weChatUtil;

    @Autowired
    private IHolidayDateService holidayDateService;

    @Scheduled(cron = "0 */1 * * * ?")
    public void execute(){
        RefreshConnectionPool();
    }
    public void RefreshConnectionPool() {
        String active = SpringUtil.getActiveProfile();
        if("dev".equals(active)){
            log.info("测试环境，无法执行数据推送");
            return;
        }
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String timeStr = now.format(formatter);

        //查询今天所有的考勤规则
        List<AttendanceRuleDetails> ruleDetailsList = categoryService.getTodayRules();
        if(ruleDetailsList.isEmpty()){
            log.info("未能查询出规则，不进行推送");
            return;
        }
        Map<Long, AttendanceRuleDetails> ruleDetailsMap = new HashMap<>();
        for (AttendanceRuleDetails ruleDetail : ruleDetailsList) {
            ruleDetailsMap.put(ruleDetail.getAttendanceRuleCategoryId(), ruleDetail);
        }

        //查询出所有人的考勤规则
        List<AttendanceUserRelation> relationList = relationMapper.selectList(
                new QueryWrapper<AttendanceUserRelation>().lambda()
                        .eq(AttendanceUserRelation::getDeleteMark, 0)
        );
        Map<Long, AttendanceRuleDetails> allTodyRule = new HashMap<>();
        for (AttendanceUserRelation userRelation : relationList) {
            allTodyRule.put(userRelation.getUserId(), ruleDetailsMap.get(userRelation.getAttendanceRuleCategoryId()));
        }

        Map<Long, AttendanceUserRelation> allUserStatus = new HashMap<>();
        for (AttendanceUserRelation userRelation : relationList) {
            allUserStatus.put(userRelation.getUserId(), userRelation);
        }

        //根据规则中的用户id查询出所有绑定了微信的学生和教师
        if(allTodyRule.isEmpty()){
            log.info("未能查询出规则，不进行推送");
            return;
        }

        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("SUNDAY".equals(dayOfWeek) || "SATURDAY".equals(dayOfWeek)){
            log.info("非工作日，不需要提醒");
            return;
        }

        //获取最近的时间
        LocalDateTime recentlyTime = getRecentlyTime(ruleDetailsList, now);
        if(recentlyTime == null){
            log.info("未到时间，不进行推送");
            return;
        }
        long minute = Math.abs(ChronoUnit.MINUTES.between(now, recentlyTime));
        if(minute >= 1){
            log.info("未到时间，不进行推送");
            return;
        }
        String wechatTemplate = weChatUtil.getAttenDanceWarnTemplate();
//        List<WechatMessageLog> list = wechatMessageLogService.list(
//                new QueryWrapper<WechatMessageLog>().lambda()
//                        .select(WechatMessageLog.class, x -> VoToColumnUtil.fieldsToColumns(WechatMessageLog.class).contains(x.getProperty()))
//                        .eq(WechatMessageLog::getSendTime, recentlyTime)
//                        .eq(WechatMessageLog::getTemplateId, wechatTemplate)
//        );
//        if(!list.isEmpty()){//已经推送过，不再进行推送
//            return;
//        }

        LocalDateTime attendanceTime = getAttendanceTime(ruleDetailsList, now);

        //判断所有当前时间下，哪几条规则需要进行提醒
        Map<Long, Integer> ruleDetailsJudgeMap = judgeExecuteNotice(now, ruleDetailsList, recentlyTime);

        List<XjrUser> userList = getUserList(allTodyRule.keySet());
        for (XjrUser xjrUser : userList) {
            AttendanceRuleDetails ruleDetails = allTodyRule.get(xjrUser.getId());
            if(ruleDetails == null){
                continue;
            }
            Integer status = ruleDetailsJudgeMap.get(ruleDetails.getId());
            if(status == 0){
                continue;
            }

            WeChatSendMessageDto weChatSendMessageDto = new WeChatSendMessageDto();
            weChatSendMessageDto.setUserId(xjrUser.getOpenId());
            weChatSendMessageDto.setTemplateId(wechatTemplate);
            weChatSendMessageDto.setMsgId(IdUtil.getSnowflakeNextId() + "");
            JSONObject paramJson = new JSONObject();


            JSONObject time5 = new JSONObject();
            time5.put("value", attendanceTime.format(formatter));
            paramJson.put("time5", time5);

            JSONObject thing8 = new JSONObject();
            thing8.put("value", xjrUser.getName());
            paramJson.put("thing8", thing8);

            JSONObject time9 = new JSONObject();
            time9.put("value", timeStr);
            paramJson.put("time9", time9);

            JSONObject const12 = new JSONObject();
            if(status == 1){
                const12.put("value", "待打卡");
            }else{
                const12.put("value", "未打卡");
            }
            //查询这个用户最新的考勤状态
            AttendanceUserRelation userRelation = allUserStatus.get(xjrUser.getId());
            if(userRelation != null
                    && userRelation.getAttendanceTime() !=null
                    && userRelation.getAttendanceTime().toLocalDate().isEqual(now.toLocalDate())
                    && "到校".equals(userRelation.getAttendanceStatus())){
                const12.put("value", "已打卡");
            }
            paramJson.put("const12", const12);

            weChatSendMessageDto.setContent(paramJson);
            weChatService.sendTemplateMessage(weChatSendMessageDto);

        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
        String table = "wechat_message_log";
        long id = IdUtil.getSnowflakeNextId();
        Entity entity = Entity.create(table);
        entity.set("id", id);
        entity.set("content", "消息推送人数：" + userList.size());
        entity.set("create_date", sdf.format(new Date()));
        entity.set("template_id", wechatTemplate);
        entity.set("send_time", recentlyTime);
        SqlRunnerAdapter.db().dynamicInsert(table, entity);

    }

    /**
     * 判断所有规则，哪些需要发送
     * @param judgeTime
     * @param ruleDetailsList
     * @return 0：不需要提醒，1：待打卡，2：未打卡
     */
    Map<Long, Integer> judgeExecuteNotice(LocalDateTime judgeTime, List<AttendanceRuleDetails> ruleDetailsList, LocalDateTime recentlyTime){
        Map<Long, Integer> result = new HashMap<>();
        for (AttendanceRuleDetails ruleDetails : ruleDetailsList) {
            if(ruleDetails.getIsAllowInOutSchool() != null && ruleDetails.getIsAllowInOutSchool() == 1
                    && ruleDetails.getIsAttendance() != null && ruleDetails.getIsAttendance() == 1){
                if(ruleDetails.getAmStartTime() != null){
                    LocalDateTime amStartTime = judgeTime.with(ruleDetails.getAmStartTime().toLocalTime());
                    Long amBetween = Math.abs(ChronoUnit.MINUTES.between(judgeTime, amStartTime));
                    if(ruleDetails.getAgoMinutes() != null && recentlyTime.isEqual(amStartTime.plusMinutes(- ruleDetails.getAgoMinutes()))
                            && amBetween >= ruleDetails.getAgoMinutes() - 1 && amBetween <= ruleDetails.getAgoMinutes() + 1){//相差分钟数小于0
                        result.put(ruleDetails.getId(), 1);
                    }else if(ruleDetails.getOverMinutes() != null && recentlyTime.isEqual(amStartTime.plusMinutes(ruleDetails.getOverMinutes()))
                            && amBetween >= ruleDetails.getOverMinutes() - 1 && amBetween <= ruleDetails.getOverMinutes() + 1){
                        result.put(ruleDetails.getId(), 2);
                    }
                }
                if(ruleDetails.getPmStartTime() != null){
                    LocalDateTime pmStartTime = judgeTime.with(ruleDetails.getPmStartTime().toLocalTime());
                    Long pmBetween = Math.abs(ChronoUnit.MINUTES.between(judgeTime, pmStartTime));
                    if(ruleDetails.getAgoMinutes() != null && recentlyTime.isEqual(pmStartTime.plusMinutes(- ruleDetails.getAgoMinutes()))
                            && pmBetween >= ruleDetails.getAgoMinutes() - 1 && pmBetween <= ruleDetails.getAgoMinutes() + 1){//相差分钟数小于0
                        result.put(ruleDetails.getId(), 1);
                    }else if(ruleDetails.getOverMinutes() != null && recentlyTime.isEqual(pmStartTime.plusMinutes(ruleDetails.getOverMinutes()))
                            && pmBetween >= ruleDetails.getOverMinutes() - 1 && pmBetween <= ruleDetails.getOverMinutes() + 1){
                        result.put(ruleDetails.getId(), 2);
                    }
                }

                if(ruleDetails.getEveningStartTime() != null){
                    LocalDateTime eveningStartTime = judgeTime.with(ruleDetails.getEveningStartTime().toLocalTime());
                    Long eveningBetween = Math.abs(ChronoUnit.MINUTES.between(judgeTime, eveningStartTime));
                    if(ruleDetails.getAgoMinutes() != null && recentlyTime.isEqual(eveningStartTime.plusMinutes(- ruleDetails.getAgoMinutes()))
                            && eveningBetween >= ruleDetails.getAgoMinutes() - 1 && eveningBetween <= ruleDetails.getAgoMinutes() + 1){//相差分钟数小于0
                        result.put(ruleDetails.getId(), 1);
                    }else if(ruleDetails.getOverMinutes() != null && recentlyTime.isEqual(eveningStartTime.plusMinutes(ruleDetails.getOverMinutes()))
                            && eveningBetween >= ruleDetails.getOverMinutes() - 1 && eveningBetween <= ruleDetails.getOverMinutes() + 1){
                        result.put(ruleDetails.getId(), 2);
                    }
                }

                if(!result.containsKey(ruleDetails.getId())){
                    result.put(ruleDetails.getId(), 0);
                }
            }else{
                result.put(ruleDetails.getId(), 0);
            }
        }
        return result;
    }

    /**
     * 查询出所有用户信息（openId不为空的）
     * @param userIds 需要插叙的userid
     * @return 用户信息
     */
    List<XjrUser> getUserList(Set<Long> userIds){
        return userService.list(
            new QueryWrapper<XjrUser>().lambda()
            .in(XjrUser::getId, userIds)
            .isNotNull(XjrUser::getOpenId)
        );
    }

    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());
                    if(ruleDetail.getAgoMinutes() != null){
                        result.add(amStartTime.plusMinutes(- ruleDetail.getAgoMinutes()));
                    }

                    if(ruleDetail.getOverMinutes() != null){
                        result.add(amStartTime.plusMinutes(ruleDetail.getOverMinutes()));
                    }

                }
                if(ruleDetail.getPmStartTime() != null){
                    LocalDateTime pmStartTime = now.with(ruleDetail.getPmStartTime().toLocalTime());
                    if(ruleDetail.getAgoMinutes() != null){
                        result.add(pmStartTime.plusMinutes(- ruleDetail.getAgoMinutes()));
                    }

                    if(ruleDetail.getOverMinutes() != null){
                        result.add(pmStartTime.plusMinutes(ruleDetail.getOverMinutes()));
                    }
                }

                if(ruleDetail.getEveningStartTime() != null){
                    LocalDateTime eveningStartTime = now.with(ruleDetail.getEveningStartTime().toLocalTime());
                    if(ruleDetail.getAgoMinutes() != null){
                        result.add(eveningStartTime.plusMinutes(- ruleDetail.getAgoMinutes()));
                    }

                    if(ruleDetail.getOverMinutes() != null){
                        result.add(eveningStartTime.plusMinutes(ruleDetail.getOverMinutes()));
                    }
                }
            }
        }
        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 getAttendanceTime(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));
    }

}
