package com.bonus.system.att.tasks;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.bonus.common.core.utils.DateTimeHelper;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.system.att.dao.AttGroupDao;
import com.bonus.system.att.dao.AttSourceDataDao;
import com.bonus.system.att.entity.*;
import com.bonus.system.att.utils.AddressCoordinateFormatUtil;
import com.bonus.system.att.utils.WorkdayCalculator;
import com.bonus.system.holiday.dao.HolidayDao;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import javax.annotation.Resource;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author zys
 * 考勤定时器
 */
@Configuration
@EnableScheduling
@Slf4j
@EnableAsync
public class AttTasks {

    @Resource(name = "attSourceDataDao")
    private AttSourceDataDao attSourceDataDao;
    @Resource(name = "sqlSessionTemplate")
    private SqlSessionTemplate sqlSessionTemplate;

    @Resource(name = "attGroupDao")
    private AttGroupDao attGroupDao;

    @Resource(name = "HolidayDao")
    private HolidayDao holidayDao;


    private static final int BATCH_SIZE = 10; // 每批次处理的日期数量
    private static final int THREAD_POOL_SIZE = 5; // 线程池大小
    private volatile boolean executed = false; // 标志位，表示任务是否已经执行过

//        @Scheduled(cron = "0 0/10 * * * ?")
//    @Scheduled(initialDelay = 60000, fixedDelay = 60000 * 10)
    @Async
    public void getAttTasks() {
        log.info("--------考勤定时器开启------");
        String today = DateUtil.today();
        pushAttData(today, 1);
        log.info("--------考勤定时器完毕------");
    }

    /**
     * 历史考勤数据
     */
//    @Scheduled(initialDelay = 6000,fixedDelay = 60000 * 30)
        @Scheduled(cron = "0 50 19 * * ?")
    @Async
    public void getHisAttTasks() {
        log.info("--------考勤定时器开启------");
        if (executed) {
            return; // 如果任务已经执行过，直接返回
        }
        executed = true; // 设置标志位，表示任务已经执行过
        String startDate = "2025-01-01";
        String endDate = "2025-01-31";
        List<String> dateList = getStrDateListBetween(startDate, endDate);
//        List<String> dateList = new ArrayList<>();
//        dateList.add("2024-10-18");
        hisAttPush(dateList);
        log.info("--------考勤定时器完毕------");
    }

    /**
     * 防止黔送离线数据每月前三天晚上将数据重新执行
     */
    @Scheduled(cron = "0 30 22 1-3 * *")
    @Async
    public void getHisMonthAttTask() {
        // 获取当前日期
        LocalDate today = LocalDate.now();
        // 获取上个月的YearMonth实例
        YearMonth previousMonth = YearMonth.from(today).minusMonths(1);
        // 上个月的第一天
        LocalDate startOfPreviousMonth = previousMonth.atDay(1);
        // 上个月的最后一天
        LocalDate endOfPreviousMonth = previousMonth.atEndOfMonth();
        String startDate = startOfPreviousMonth.toString();
        String endDate = endOfPreviousMonth.toString();
        List<String> dateList = getStrDateListBetween(startDate, endDate);
        hisAttPush(dateList);
        log.info("--------考勤定时器完毕------");
    }

    /**
     * 凌晨0-5点得到数据，算作昨天，将昨天的重新执行
     */
    @Scheduled(cron = "0 5 6 * * ?")
    @Async
    public void getHisYesterdayAttTask() {
        // 获取今天的日期
        LocalDate today = LocalDate.now();
        // 计算昨天的日期
        LocalDate yesterday = today.minusDays(1);
        String startDate = yesterday.toString();
        String endDate = yesterday.toString();
        List<String> dateList = getStrDateListBetween(startDate, endDate);
        hisAttPush(dateList);
        log.info("--------考勤定时器完毕------");
    }


    public void hisAttPush(List<String> dateList) {
        // 创建固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        // 分批处理日期列表
        for (int i = 0; i < dateList.size(); i += BATCH_SIZE) {
            int end = Math.min(i + BATCH_SIZE, dateList.size());
            List<String> batch = dateList.subList(i, end);
            // 提交任务到线程池
            executorService.submit(() -> {
                for (String date : batch) {
                    try {
                        delHisData(date);
                        pushAttData(date, 2);
                    } catch (Exception e) {
                        // 记录异常并继续处理下一个日期
                        System.err.println("Error processing date: " + date + ", error: " + e.getMessage());
                    }
                }
            });
        }
        // 关闭线程池
        executorService.shutdown();
        try {
            // 等待所有任务完成
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void delHisData(String date) {
        //查询基础数据是否完备
        int i = attSourceDataDao.getFirstAttendanceData(date);
        if(i > 0){
            attSourceDataDao.updateHisData(date);
        }else{
            attSourceDataDao.delHisData(date);
        }
    }


    /**
     * 推送考勤数据
     *
     * @param pushDate 时间
     * @param pushType 1:自动推送 2:手动
     */
    private void pushAttData(String pushDate, int pushType) {
        //获取黔送云的考勤数据
        getQsyAttendanceData(pushDate, pushType);
        //获取考勤机中的考勤数据
        getMachineAttendanceData(pushDate, pushType);
//        //查出每一个考勤组的应出勤天数以及考勤组的规则
        List<AttGroupBean> groupList = getGroupData(pushDate);
//        //新增默认考勤数据到考勤数据和修改表
        insertAttData(groupList, pushDate, pushType);
        //更新请假数据
        updateLeaveData(pushDate);
        //修改考勤数据
        updateAttData(groupList, pushDate, pushType);
//        根据考勤数据生成报表数据
        createReportData(groupList, pushDate, pushType);
    }

    /**
     * 获取黔送云的考勤数据
     */
    private void getQsyAttendanceData(String pushDate, int pushType) {
        List<AttSourceDataBean> attSourceList = Optional.ofNullable(attSourceDataDao.getQsyAttendances(pushDate, pushType)).
                orElseGet(ArrayList::new);
        if (!attSourceList.isEmpty()) {
            attSourceList.forEach(c -> {
                String address;
                try {
                    address = AddressCoordinateFormatUtil.
                            coordinateToAddress(c.getAttLon(), c.getAttLat());
                }catch (IllegalArgumentException e){
                    address = "地址未转化成功";
                }
                c.setAttAddress(address);
                        if ("0".equals(c.getAttType())) {
                            int i = checkTime(c.getAttCurrentTime());
                            c.setAttType(String.valueOf(i));
                        }
                        //将00:00:00 至 04:59:59 时间变成att_current_day上一天的
                        changeAttCurrentDay(c);
                    }
            );
            //新增考勤到考勤来源表(数据过多时分批次插入)
            insertAttSourceData(attSourceList);
        }
    }

    /**
     * 将00:00:00 至 04:59:59 时间变成att_current_day上一天的
     *
     * @param c
     */
    public void changeAttCurrentDay(AttSourceDataBean c) {
        // 定义时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 解析传入的时间字符串
        LocalTime time = LocalTime.parse(c.getAttCurrentTime(), formatter);
        // 定义早上5点和中午12点的时间
        LocalTime morningStart = LocalTime.of(0, 0);
        LocalTime noonEnd = LocalTime.of(5, 0);
        // 判断时间是否在0:00到05:00之间
        if (time.isAfter(morningStart) && !time.isAfter(noonEnd)) {
            //获取前一天时间
            String afterDate = DateUtils.getAfterDate(c.getAttCurrentDay());
            c.setAttCurrentDay(afterDate);
        }
    }

    public void insertAttSourceData(List<AttSourceDataBean> attSourceList) {
        if (attSourceList == null || attSourceList.isEmpty()) {
            return;
        }
        int batchSize = 3000;
        int totalBatches = (int) Math.ceil((double) attSourceList.size() / batchSize);
        try {
            for (int i = 0; i < totalBatches; i++) {
                int fromIndex = i * batchSize;
                int toIndex = Math.min(fromIndex + batchSize, attSourceList.size());
                List<AttSourceDataBean> batch = attSourceList.subList(fromIndex, toIndex);
                attSourceDataDao.insertAttSourceData(batch);
                if ((i + 1) % 100 == 0 || i + 1 == totalBatches) {
                    sqlSessionTemplate.flushStatements(); // 提交事务
                    sqlSessionTemplate.clearCache(); // 清理缓存
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Error replacing data", e);
        }
    }


    /**
     * 获取考勤机的考勤数据
     */
    private void getMachineAttendanceData(String pushDate, int pushType) {
        List<AttSourceDataBean> attSourceList = Optional.ofNullable(attSourceDataDao.getMachineAttendances(pushDate, pushType)).
                orElseGet(ArrayList::new);
        attSourceList.forEach(c -> {
            if ("0".equals(c.getAttType())) {
                int i = checkTime(c.getAttCurrentTime());
                c.setAttType(String.valueOf(i));
            }
            //将00:00:00 至 04:59:59 时间变成att_current_day上一天的
            changeAttCurrentDay(c);
        });
        //新增考勤到考勤来源表(数据过多时分批次插入)
        insertAttSourceData(attSourceList);
    }

    public int checkTime(String currentTime) {
        if (currentTime == null || currentTime.isEmpty()) {
            // 如果输入为空或为空字符串，则返回默认值（例如2）
            return 1;
        }
        try {
            // 定义时间格式
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            // 解析传入的时间字符串
            LocalTime time = LocalTime.parse(currentTime, formatter);
            // 定义早上5点和中午12点的时间
            LocalTime morningStart = LocalTime.of(5, 0);
            LocalTime noonEnd = LocalTime.of(12, 0);
            // 判断时间是否在5:00到12:00之间
            if (time.isAfter(morningStart) && !time.isAfter(noonEnd)) {
                return 1; // 在早上5点到12点之间
            } else {
                return 2; // 其他时间
            }
        } catch (DateTimeParseException e) {
            // 如果时间格式不正确，返回默认值（例如2）
            e.printStackTrace();
            return 1;
        }
    }

    /**
     * 更新所有的请假数据
     */
    private void updateLeaveData(String pushDate) {
        /**
         * 查出在考勤组人员的请假数据
         */
        List<LeaveBean> leaveList = Optional.ofNullable(attSourceDataDao.getLeaveData(pushDate)).
                orElseGet(ArrayList::new);
        if (!leaveList.isEmpty()) {
            leaveList.forEach(leaveData -> threadLeaveData(leaveData, pushDate));
        }
    }

    /**
     * 根据请假数据去更新考勤数据
     */
    private void threadLeaveData(LeaveBean c, String pushDate) {
        //1.将请假区间变成日期集合
//        if(c.getUserId() == 263){
//            System.out.println("米娜");
//        }
        List<String> dateLists = getStrDateListBetween(c.getLeaveStartDate(), c.getLeaveEndDate());
        if (!dateLists.isEmpty()) {
//            //根据开始时间、结束时间获取节假日的日期集合
            // 1 表示固定打卡
            if("1".equals(c.getAttType())){
                //只有固定打卡蒋周六日不算在请假轮休天数中
                List<String> holidayList = holidayDao.getHolidayDataByType(c.getLeaveStartDate(), c.getLeaveEndDate());
                // 使用Stream API去除A中包含在B中的元素
                dateLists = dateLists.stream().filter(element -> !holidayList.contains(element)).collect(Collectors.toList());
            }
            dateLists.forEach(v -> {
                boolean tf = false;
                AttDataBean toWorkBean = new AttDataBean(v, c.getAttStatus(), "1",
                        c.getOrgId(), c.getUserId());
                AttDataBean offWorkBean = new AttDataBean(v, c.getAttStatus(), "2",
                        c.getOrgId(), c.getUserId());
                //开始和结束的那天是否只请了半天
                if (c.getLeaveStartDate().equals(v)) {
                    if (c.getLeaveStartInterval().equals("2")) {
                        replaceHolidayData(offWorkBean, 0);
                    } else {
                        tf = true;
                    }
                } else if (c.getLeaveEndDate().equals(v)) {
                    if (c.getLeaveEndInterval().equals("1")) {
                        replaceHolidayData(toWorkBean, 0);
                    } else {
                        tf = true;
                    }
                } else {
                    tf = true;
                }
                if (tf) {
                    replaceHolidayData(toWorkBean, 0);
                    replaceHolidayData(offWorkBean, 0);
                }
            });
        }
    }

    public static List<String> getStrDateListBetween(String startDateString, String endDateString) {
        List<String> dateList = new ArrayList<>();
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            // 解析开始日期和结束日期
            Date startDate = dateFormat.parse(startDateString);
            Date endDate = dateFormat.parse(endDateString);
            // 创建 Calendar 实例并设置为开始日期
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(startDate);
            // 循环直到当前日期超过结束日期
            while (!calendar.getTime().after(endDate)) {
                // 将日期格式化为字符串，并添加到列表中
                dateList.add(dateFormat.format(calendar.getTime()));
                // 将日期加一天
                calendar.add(Calendar.DAY_OF_MONTH, 1);
            }
        } catch (ParseException ignored) {
        }
        return dateList;
    }

    /**
     * 查出每一个考勤组的应出勤天数以及考勤组的规则
     *
     * @return 考勤组集合
     */
    public List<AttGroupBean> getGroupData(String pushDate) {
        //查出考勤组的数据
        List<AttGroupBean> groupList = Optional.ofNullable(attGroupDao.selectAttGroupList(new AttGroupBean())).
                orElseGet(ArrayList::new);
        if (groupList.isEmpty()) {
            return new ArrayList<>();
        }
        //查询当月否有节假日或补班
        List<Holiday> holidays = attSourceDataDao.selectHolidayByMonth(pushDate);
        groupList.forEach(c -> {
            //应考勤天
            List<String> attDayList = WorkdayCalculator.getWorkDay(c.getAttDay(),
                    Integer.parseInt(c.getAttType()), holidays,pushDate);
            c.setAttWorkDayBean(new PersonAttWorkDayBean(c.getGroupId(), attDayList.size(), attDayList));
        });
        return groupList;
    }

    /**
     * 新增默认考勤数据到考勤数据和修改表
     *
     * @param groupList 考勤组集合
     */
    private void insertAttData(List<AttGroupBean> groupList, String pushDate, int pushType) {
        //获取所有的用户
        // 2025-01-17 添加时间，查出每个人在对应时间存在的考勤组
        List<AttDataBean> listPerson = attSourceDataDao.getAllPerson(pushDate);
        if (!listPerson.isEmpty()) {
            listPerson.forEach(v -> {
                groupList.stream()
                        .filter(c -> Objects.equals(v.getGroupId(), c.getGroupId()) && !c.getAttWorkDayBean()
                                .getAttDayList().contains(pushDate))
                        .findFirst()
                        .ifPresent(c -> v.setAttStatus("11"));
                v.setAttCurrentDay(pushDate);
                replaceAttData(v, pushDate);
            });
        }
    }

    /**
     * 新增修改考勤数据
     */
    private void replaceAttData(AttDataBean v, String pushDate) {
        //查询考勤模版数据是否已经插入
        Boolean isAttExist = attSourceDataDao.selectAttIsExist(v);
        if (!isAttExist) {
            //查询能修改的考勤模版数据是否已经插入
            // 判断 pushDate 是否在今天之前
            boolean isPastDate = isBeforeToday(pushDate);
            if (isPastDate) {
                //不存在且在今天之前
                //之前处理过节假日的数据，只有是默认数据才能将旷工状态写入
                if (v.getAttStatus().equals("0")) {
                    v.setAttStatus("3");
                }
            }else{
                //不存在就在今天
                //逻辑跟修改相同，第一次就不去麻烦根据时间判断状态了
            }
            attSourceDataDao.insertAttData(v);
        } else {
            if (!v.getAttStatus().equals("0")) {
                attSourceDataDao.updateAttData(v, 0);
            }
            if (v.getAttStatus().equals("0")) {
                //将未打卡的全部置为旷工
                changeAbsenteeAttStatus(v, 0, 0, pushDate);
            }
        }
        //查询能修改的考勤模版数据是否已经插入
        Boolean isAttUpdateExist = attSourceDataDao.selectAttUpdateIsExist(v);
        if (!isAttUpdateExist) {
            // 判断 pushDate 是否在今天之前
            boolean isPastDate = isBeforeToday(pushDate);
            if (isPastDate) {
                //之前处理过节假日的数据，只有是默认数据才能将旷工状态写入
                if (v.getAttStatus().equals("0")) {
                    v.setAttStatus("3");
                }
            }
            attSourceDataDao.insertAttUpdateData(v);
        } else {
            if (!v.getAttStatus().equals("0")) {
                attSourceDataDao.updateAttUpdateData(v, 0);
            } else {
                //将未打卡的全部置为旷工
                changeAbsenteeAttStatus(v, 0, 1, pushDate);
            }
        }
    }


    /**
     * 新增修改请假数据（更新考勤表）
     */
    private void replaceHolidayData(AttDataBean v, int type) {
        //查询考勤模版数据是否已经插入
        Boolean isAttExist = attSourceDataDao.selectAttIsExist(v);
        if (!isAttExist) {
            //没有基础数据，插入基础数据并改状态为对应请假状态
            attSourceDataDao.insertAttData(v);
        } else {
            if (!v.getAttStatus().equals("0")) {
                //改状态为对应请假状态
                attSourceDataDao.updateAttData(v, type);
            }
        }
        //查询能修改的考勤模版数据是否已经插入
        Boolean isAttUpdateExist = attSourceDataDao.selectAttUpdateIsExist(v);
        if (!isAttUpdateExist) {
            //没有基础数据，插入基础数据并改状态为对应请假状态
            attSourceDataDao.insertAttUpdateData(v);
        } else {
            if (!v.getAttStatus().equals("0")) {
                //改状态为对应请假状态
                attSourceDataDao.updateAttUpdateData(v, type);
            }
        }
    }


    /**
     * 将未打卡的全部置为旷工
     *
     * @param v        考勤数据
     * @param type     0表示考勤数据，1表示考勤修改数据
     * @param update   0表示修改考勤数据，1表示修改考勤修改数据
     * @param pushDate 推送时间
     */
    private void changeAbsenteeAttStatus(AttDataBean v, int type, int update, String pushDate) {
        //将未打卡的全部置为旷工
        // 判断 pushDate 是否在今天之前
        boolean isPastDate = isBeforeToday(pushDate);
        if (isPastDate) {
            // 如果 pushDate 是过去的时间，直接执行原逻辑
                v.setAttStatus("3");
                if (update == 0) {
                    attSourceDataDao.updateAttData(v, type);
                } else if (update == 1) {
                    attSourceDataDao.updateAttUpdateData(v, type);
                }
        } else {
            LocalTime currentTime;
            // 使用当前时间
            currentTime = LocalTime.now();
            // 定义中午 12 点
            LocalTime noon = LocalTime.of(12, 0);
            // 定义下午 22 点
            LocalTime eightPM = LocalTime.of(22, 0);
            // 当前时间逻辑
            if (currentTime.isAfter(noon)) {
                if ("1".equals(v.getAttType())) {
                    v.setAttStatus("3");
                    if (update == 0) {
                        attSourceDataDao.updateAttData(v, type);
                    } else if (update == 1) {
                        attSourceDataDao.updateAttUpdateData(v, type);
                    }
                }
            }
            if (currentTime.isAfter(eightPM)) {
                if ("2".equals(v.getAttType())) {
                    v.setAttStatus("3");
                    if (update == 0) {
                        attSourceDataDao.updateAttData(v, type);
                    } else if (update == 1) {
                        attSourceDataDao.updateAttUpdateData(v, type);
                    }
                }
            }
        }
    }

    /**
     * 判断传入的日期是否在今天的日期之前
     *
     * @param dateStr 传入的日期字符串，格式为 "yyyy-MM-dd"
     * @return 如果传入的日期在今天的日期之前，返回 true；否则返回 false
     */
    public boolean isBeforeToday(String dateStr) {
        if (dateStr == null || dateStr.isEmpty()) {
            return false;
        }
        try {
            LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            LocalDate today = LocalDate.now();
            return date.isBefore(today);
        } catch (Exception e) {
            // 如果日期格式不正确，返回 false
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 修改考勤数据
     *
     * @param groupList 考勤组集合
     */
    private void updateAttData(List<AttGroupBean> groupList, String pushDate, int pushType) {
        //查出最近三天的人员考勤记录
        AtomicReference<List<AttSourceDataBean>> sourceDataList = new AtomicReference<>(attSourceDataDao.getSourceAttData(pushDate, pushType));
        //根据考勤组的规则和请假记录算出人员的考勤状态
        groupList.forEach(c -> {
            List<AttSourceDataBean> list = Collections.emptyList();
            //考勤规则里面的attType 1 固定打卡  2 自由打卡
            if ("1".equals(c.getAttType())) {
                list = GroupFixedData(sourceDataList.get().stream()
                        .filter(a -> "1".equals(a.getGroupType()) &&
                                a.getGroupId() == c.getGroupId())
                        .collect(Collectors.toList()), c);
            } else if ("2".equals(c.getAttType())) {
                list = getDataList(sourceDataList.get().stream()
                        .filter(a -> "2".equals(a.getGroupType()) &&
                                a.getGroupId() == c.getGroupId())
                        .collect(Collectors.toList()), c, true);
            }
            //修改考勤记录 att_data
            if (!list.isEmpty()) {
                if ("1".equals(c.getAttType())) {
                    //固定打卡
                    // 定义时间格式
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                    // 移除移除 attType 为 2 且 attCurrentTime 在上午 5 点到 12 点之间的元素
                    list.removeIf(record -> {
                        String attType = record.getAttType();
                        String attCurrentTime = record.getAttCurrentTime();
                        if (!"".equals(attCurrentTime)) {
                            if (attCurrentTime.contains("  ")) {
                                attCurrentTime = attCurrentTime.split("  ")[0];
                            }
                            LocalDateTime time = LocalDateTime.parse(attCurrentTime, formatter);
                            // 提取时间部分
                            LocalTime localTime = time.toLocalTime();
                            boolean condition1 = "2".equals(attType) && localTime.isAfter(LocalTime.of(5, 0)) && localTime.isBefore(LocalTime.of(12, 0));
                            boolean condition2 = "1".equals(attType) && localTime.isAfter(LocalTime.of(12, 0)) && localTime.isBefore(LocalTime.of(23, 59));
                            return condition1 || condition2;
                        } else {
                            return false;
                        }
                    });
                    // 插入剩余的元素
                    attSourceDataDao.updateAttStatusData(list);
                } else if ("2".equals(c.getAttType())) {
                    //自由打卡
                    attSourceDataDao.updateAttStatusData(list);
                }
            }
        });
    }

    /**
     * 固定考勤数据计算
     *
     * @param list         考勤来源数据
     * @param attGroupBean 考勤组数据
     * @return 考勤数据
     */
    private List<AttSourceDataBean> GroupFixedData(List<AttSourceDataBean> list,
                                                   AttGroupBean attGroupBean) {
        List<AttSourceDataBean> newList = getDataList(list, attGroupBean, false);
        Iterator<AttSourceDataBean> iterator = newList.iterator();
        while (iterator.hasNext()) {
            if (iterator.next() == null) {
                iterator.remove();
            }
        }
        processAttendanceStatus(newList, attGroupBean.getToWorkTime(), attGroupBean.getOffWorkTime(), attGroupBean.getLateMinute(),
                attGroupBean.getAbsenteeismLateMinute(), attGroupBean.getLeaveMinute(), attGroupBean.getAbsenteeismLeaveMinute());
        return newList;
    }

    /**
     * 上下班考勤计算
     *
     * @param list         考勤来源数据
     * @param attGroupBean 考勤组数据
     * @param tf           固定|自由
     * @return 考勤数据
     */
    private List<AttSourceDataBean> getDataList(List<AttSourceDataBean> list,
                                                AttGroupBean attGroupBean,
                                                Boolean tf) {
        List<AttSourceDataBean> newList = new ArrayList<>();
        //分组排序(将每个人每天在自己考勤组的数据分组)
        Map<String, List<AttSourceDataBean>> groupedItems = list.stream()
                .collect(Collectors.groupingBy(
                        c -> c.getUserId() + "|" + c.getOrgId() + "|" + c.getAttCurrentDay(),
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                v -> {
                                    v.sort(Comparator.comparing(AttSourceDataBean::getAttCurrentTime));
                                    return v;
                                }
                        )
                ));
        if (!tf) {
            //固定打卡，以最靠近上班打卡时间前的出闸机的时间为基准，去除掉之前的所有打卡数据
            processGroupedItems(groupedItems, attGroupBean);
        }
        groupedItems.forEach((c, v) -> {
            //第一次上班时间
            AttSourceDataBean frontToWorkBean = v.stream()
                    .filter(a -> a.getAttType().equals("1"))
                    .collect(Collectors.toList())
                    .stream().min(Comparator.comparing(AttSourceDataBean::getAttCurrentTime)).orElse(null);
            //最新一次下班时间
            AttSourceDataBean backOffWorkBean = v.stream()
                    .filter(a -> a.getAttType().equals("2"))
                    .collect(Collectors.toList())
                    .stream().max(Comparator.comparing(AttSourceDataBean::getAttCurrentTime)).orElse(null);
            if (tf) {
                if(v!=null&&v.size()>0&&v.get(0).getName().equals("任荣辉")){
                    System.out.println("11111111111");
                }
                //自由打卡不需要去除数据
                getFreeAttData(newList, frontToWorkBean, backOffWorkBean, attGroupBean);
            } else {
                if (backOffWorkBean != null) {
                    //最新一次下班时间插入list
                    newList.add(backOffWorkBean);
                }
//                if(v.get(0).getName().equals("肖阳")){
//                    System.out.println("c.getAttCurrentTime() = ");
//                }
                // 处理逻辑 出入异常的逻辑
//                try {
//                    Map<Boolean, List<AttSourceDataBean>> result = checkAdjacentAttType1WithGapAndCheckTypesBefore(v, attGroupBean.getEntryAbnormalMinute());
//                    if (result.containsKey(true)) {
//                        List<AttSourceDataBean> matchingItems = result.get(true);
//                        frontToWorkBean = matchingItems.get(0);
//                        frontToWorkBean.setAbnormalAttTime(matchingItems.get(1).getAttCurrentTime());
//                        frontToWorkBean.setAbnormalAttAddress(matchingItems.get(1).getAttAddress());
//                        frontToWorkBean.setAttStatus(8);
//                    }
//                }catch(Exception e){
//                    e.printStackTrace();
//                    log.error("出入异常处理报错");
//                }
                //没有下班卡则添加第一次打卡数据
                newList.add(frontToWorkBean);
                //获取工作异常
                LocalTime lastOutTime = null;
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
                try {
                    //存储工作异常的list
                    List<AttSourceDataBean> longBreakRecords = new ArrayList<>();
                    for (int i = 0; i < v.size(); i++) {
                        AttSourceDataBean record = v.get(i);
                        // 如果是“出”的记录，保存时间
                        if ("2".equals(record.getAttType())) {
                            lastOutTime = LocalDateTime.parse(record.getAttCurrentTime(), dateTimeFormatter).toLocalTime();
                            if(lastOutTime.isAfter(LocalTime.parse(attGroupBean.getOffWorkTime(), timeFormatter))){
                                //进在下班之后，不考虑
                                break;
                            }
                        }
                        // 如果是“进”的记录并且之前有“出”的记录
                        else if ("1".equals(record.getAttType()) && lastOutTime != null) {
                            LocalTime inTime = LocalDateTime.parse(record.getAttCurrentTime(), dateTimeFormatter).toLocalTime();
                            // 计算实际工作时间（不包括午休时间）
//                        if(record.getName().equals("何波")){
//                            System.out.println("11111111111");
//                        }
                            // 出-》进 的时间在上班时间前，去除
                            if(inTime.isBefore(LocalTime.parse(attGroupBean.getToWorkTime(), timeFormatter))){
                                //进在上班之后，不考虑
                                break;
                            }
                            Duration workDuration = calculateWorkDuration(lastOutTime, inTime, attGroupBean);
                            // 如果工作时间外出超过了规定时间，记录下这对“出”和“进”
                            if (workDuration != null && !workDuration.isNegative() && workDuration.toMinutes() > attGroupBean.getWorkAbnormalMinute()) {
                                AttSourceDataBean longBreakRecord = new AttSourceDataBean();
                                longBreakRecord.setUserId(record.getUserId());
                                longBreakRecord.setName(record.getName());
                                longBreakRecord.setOrgId(attGroupBean.getOrgId());
                                longBreakRecord.setAttCurrentDay(record.getAttCurrentDay());
                                longBreakRecord.setAttCurrentTime(v.get(i - 1).getAttCurrentTime() + "  " + record.getAttCurrentTime());
                                longBreakRecord.setAttAddress(v.get(i - 1).getAttAddress() + "  " + record.getAttAddress());
                                longBreakRecords.add(longBreakRecord);
                            }
                            // 更新lastOutTime为null，因为已经处理了这一对
                            lastOutTime = null;
                        }
                    }
                    //判断有没有临时外出请假等等
                    if(!longBreakRecords.isEmpty()){
                        int x = attSourceDataDao.getLeaveDataByUserId(longBreakRecords.get(0));
                        if(x==0){
                            attSourceDataDao.insertWorkAbnormal(longBreakRecords);
                        }
                    }
                }catch(Exception e){
                    e.printStackTrace();
                    log.error("工作异常处理报错");
                }

            }
        });
        return newList;
    }

    private static Duration calculateWorkDuration(LocalTime outTime, LocalTime inTime, AttGroupBean group) {
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
            // 解析上班、下班、午休开始、结束时间
            LocalTime offWorkTime = LocalTime.parse(group.getOffWorkTime(), formatter);
            LocalTime breakStartTime = LocalTime.parse(group.getBreakStartTime(), formatter);
            LocalTime breakEndTime = LocalTime.parse(group.getBreakEndTime(), formatter);
            // 如果出勤时间在午休时间内，调整出勤时间为午休结束时间
            if (outTime.isAfter(breakStartTime) && outTime.isBefore(breakEndTime)) {
                outTime = breakEndTime;
            }
            // 如果进入时间在午休时间内，调整进入时间为午休开始时间
            if (inTime.isAfter(breakStartTime) && inTime.isBefore(breakEndTime)) {
                inTime = breakStartTime;
            }
            // 如果进入时间在下班时间内，调整进入时间为下班时间
            if (inTime.isAfter(offWorkTime)) {
                inTime = offWorkTime;
            }
            // 计算总的工作时间
            Duration totalDuration = Duration.between(outTime, inTime);
            // 如果时间段跨越了午休时间，减去午休时长
            if (outTime.isBefore(breakEndTime) && inTime.isAfter(breakStartTime)) {
                Duration breakDuration = Duration.between(breakStartTime, breakEndTime);
                totalDuration = totalDuration.minus(breakDuration);
            }
            return totalDuration;
        } catch (DateTimeParseException e) {
            // 处理解析异常，例如记录日志或返回默认值
            System.err.println("时间解析错误: " + e.getMessage());
            return null; // 或者返回一个默认的 LocalDateTime
        }
    }

    /**
     * 根据getAttCurrentTime对List<AttSourceDataBean>进行正序排序。
     * 遍历排序后的列表，找到最早相邻且间隔时间大于2分钟的两个attType = 1的数据项。
     * 检查这两个数据项之前的数据集合是否同时包含attType = 1和attType = 2的数据。
     *
     * @param items
     * @param entryAbnormalMinute
     * @return
     */
    private Map<Boolean, List<AttSourceDataBean>> checkAdjacentAttType1WithGapAndCheckTypesBefore(List<AttSourceDataBean> items, Long entryAbnormalMinute) {
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        // 步骤1：根据 getAttCurrentTime 排序
        items.sort(Comparator.comparing(AttSourceDataBean::getAttCurrentTime));
        // 步骤2：找到最早相邻且间隔时间大于2分钟的两个 attType = 1 的数据项
        AttSourceDataBean firstAttType1 = null;
        AttSourceDataBean secondAttType1 = null;
        for (int i = 0; i < items.size() - 1; i++) {
            if ("1".equals(items.get(i).getAttType()) && "1".equals(items.get(i + 1).getAttType())) {
                // 提取时间部分
                String firstTimeStr = items.get(i).getAttCurrentTime().substring(11);
                String secondTimeStr = items.get(i + 1).getAttCurrentTime().substring(11);
                LocalTime firstTime = LocalTime.parse(firstTimeStr, timeFormatter);
                LocalTime secondTime = LocalTime.parse(secondTimeStr, timeFormatter);
                Duration duration = Duration.between(firstTime, secondTime);
                if (duration.toMinutes() > entryAbnormalMinute) {
                    firstAttType1 = items.get(i);
                    secondAttType1 = items.get(i + 1);
                    break;
                }
            }
        }
        // 如果没有找到符合条件的两个 attType = 1 的数据项，直接返回 false
        if (firstAttType1 == null || secondAttType1 == null) {
            Map<Boolean, List<AttSourceDataBean>> result = new HashMap<>();
            result.put(false, Collections.emptyList());
            return result;
        }

        // 步骤3：检查这两个数据项之前的数据集合是否同时包含 attType = 1 和 attType = 2 的数据
        int indexFirstAttType1 = items.indexOf(firstAttType1);
        List<AttSourceDataBean> dataBeforeFirstAttType1 = items.subList(0, indexFirstAttType1);

        Set<String> attTypesBefore = dataBeforeFirstAttType1.stream()
                .map(AttSourceDataBean::getAttType)
                .collect(Collectors.toSet());

        boolean containsBothTypes = attTypesBefore.contains("1") && attTypesBefore.contains("2");
        // 返回结果
        Map<Boolean, List<AttSourceDataBean>> result = new HashMap<>();
        if (containsBothTypes) {
            result.put(true, Arrays.asList(firstAttType1, secondAttType1));
        } else {
            result.put(false, Collections.emptyList());
        }
        return result;
    }

    /**
     * 固定打卡清除数据
     *
     * @param groupedItems
     */
    private void processGroupedItems(Map<String, List<AttSourceDataBean>> groupedItems, AttGroupBean attGroupBean) {
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        for (Map.Entry<String, List<AttSourceDataBean>> entry : groupedItems.entrySet()) {
            String key = entry.getKey();
            List<AttSourceDataBean> items = entry.getValue();
            if (items.isEmpty()) {
                continue; // 如果列表为空，跳过
            }
            //五点之前的数据属于过来当天的下班打卡，不计入
            LocalTime noonEnd = LocalTime.of(5, 0);
            // 获取上班时间并转换为 LocalTime 以便比较
            LocalTime toWorkTime = LocalTime.parse(attGroupBean.getToWorkTime(), timeFormatter);
            // 初始化变量来保存最接近上班时间的数据项及其索引
            AttSourceDataBean closestBeforeToWork = null;
            int closestIndex = -1;
            // 遍历并查找最接近上班时间且 attType=2 的数据项
            //前面已经排过序了，取第一个就可以了
            for (int i = 0; i < items.size(); i++) {
                AttSourceDataBean item = items.get(i);
//                System.out.println(item.getName());
//                if("王义".equals(item.getName())){
//                    System.out.println(111);
//                }
                if ("2".equals(item.getAttType())) {
                    // 只提取时间部分进行比较
                    LocalTime recordTime = LocalTime.parse(item.getAttCurrentTime().substring(11), timeFormatter);
                    // 检查是否在上班时间之前并且是目前找到的最接近上班时间的
                    if (recordTime.isBefore(toWorkTime) && recordTime.isAfter(noonEnd) &&
                            (closestBeforeToWork == null ||
                                    recordTime.isAfter(LocalTime.parse(closestBeforeToWork.getAttCurrentTime().substring(11), timeFormatter)))) {
                        closestBeforeToWork = item;
                        closestIndex = i;
                    }
                }
            }

            // 如果找到了符合条件的数据项，则移除它之前的所有数据，包括它自己
            if (closestIndex != -1) {
                List<AttSourceDataBean> filteredItems = items.stream()
                        .skip(closestIndex) // 跳过最接近上班时间的数据项及其之前的所有数据
                        .collect(Collectors.toList());
                // 更新原始映射中的列表
                groupedItems.put(key, filteredItems);
            } else {
                // 如果没有找到符合条件的数据项，可以选择保留所有数据或清空列表
                // groupedItems.put(key, Collections.emptyList()); // 清空列表
            }
        }
    }

    /**
     * 获取自由考勤数据
     *
     * @param newList         考勤数据
     * @param frontToWorkBean 第一次上班考勤数据
     * @param backOffWorkBean 最后一次下班考勤数据
     * @param attGroupBean    考勤组数据
     */
    private void getFreeAttData(List<AttSourceDataBean> newList,
                                AttSourceDataBean frontToWorkBean,
                                AttSourceDataBean backOffWorkBean,
                                AttGroupBean attGroupBean) {
        // todayClockNum 自由打卡每天打卡次数
        if (attGroupBean.getTodayClockNum() == 2) {
            if (frontToWorkBean != null) {
                frontToWorkBean.setAttStatus(1);
                newList.add(frontToWorkBean);
            }
            if (backOffWorkBean != null) {
                if (frontToWorkBean != null) {
                    long minutesDiff = DateUtil.between(
                            DateUtils.parseDate(frontToWorkBean.getAttCurrentTime()),
                            DateUtils.parseDate(backOffWorkBean.getAttCurrentTime()),
                            DateUnit.MINUTE
                    );
                    if (minutesDiff >= attGroupBean.getAttendanceDuration()) {
                        backOffWorkBean.setAttStatus(1);
                    } else {
                        if (minutesDiff > attGroupBean.getAbsenteeismLateMinute()) {
                            backOffWorkBean.setAttStatus(1);
                        }else if (minutesDiff > attGroupBean.getAbsenteeismLeaveMinute()) {
                            backOffWorkBean.setAttStatus(4);
                        }else{
                            backOffWorkBean.setAttStatus(3);
                        }
                    }
                    newList.add(backOffWorkBean);
                } else {
                    backOffWorkBean.setAttStatus(1);
                    newList.add(backOffWorkBean);
                }
            }
        } else {
            if (frontToWorkBean != null) {
//                if(frontToWorkBean.getName().equals("位兆虎")){
//                    System.out.println(1);
//                }
                frontToWorkBean.setAttStatus(1);
                AttSourceDataBean bean;
                try {
                    bean = (AttSourceDataBean) BeanUtils.cloneBean(frontToWorkBean);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                bean.setAttCurrentTime("");
                bean.setAttType("2");
                newList.add(bean);
                newList.add(frontToWorkBean);
            } else if (backOffWorkBean != null) {
                backOffWorkBean.setAttStatus(1);
                AttSourceDataBean bean;
                try {
                    bean = (AttSourceDataBean) BeanUtils.cloneBean(backOffWorkBean);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                bean.setAttCurrentTime("");
                bean.setAttType("1");
                newList.add(bean);
                newList.add(backOffWorkBean);
            }

        }
    }

    /**
     * 根据时间段处理上下班
     *
     * @param bean
     * @param startHour
     * @param startMinute
     * @param endHour
     * @param endMinute
     * @param oldAttType
     * @param newAttType
     * @return
     */
    private void dealWithDateToAttType(AttSourceDataBean bean, int startHour, int startMinute, int endHour, int endMinute, String oldAttType, String newAttType) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String attType = bean.getAttType();
        String attCurrentTime = bean.getAttCurrentTime();
        if (!"".equals(attCurrentTime)) {
            if (attCurrentTime.contains("  ")) {
                attCurrentTime = attCurrentTime.split("  ")[0];
            }
            LocalDateTime time = LocalDateTime.parse(attCurrentTime, formatter);
            // 提取时间部分
            LocalTime localTime = time.toLocalTime();
            if (oldAttType.equals(attType) && localTime.isAfter(LocalTime.of(startHour, startMinute)) && localTime.isBefore(LocalTime.of(startMinute, endMinute))) {
                bean.setAttType(newAttType);
            }
        }
    }


    /**
     * 处理打卡状态
     *
     * @param newList                考勤数据
     * @param toWorkTime             上班时间
     * @param offWorkTime            下班时间
     * @param lateMinute             迟到时间
     * @param absenteeismLateMinute  旷工迟到分钟
     * @param leaveMinute            早退分钟
     * @param absenteeismLeaveMinute 旷工早退分钟
     */
    private void processAttendanceStatus(List<AttSourceDataBean> newList, String toWorkTime,
                                         String offWorkTime, long lateMinute,
                                         long absenteeismLateMinute, long leaveMinute,
                                         long absenteeismLeaveMinute) {
        newList.forEach(c -> {
//            if(c.getName().equals("肖阳")){
//                System.out.println("c.getAttCurrentTime() = " + c.getAttCurrentTime());
//            }
            System.out.println(c.getName());
            int attStatus = calculateStatus(
                    c.getAttType(),
                    c.getAttCurrentTime(),
                    "1".equals(c.getAttType()) ? toWorkTime : offWorkTime,
                    "1".equals(c.getAttType()) ? lateMinute : leaveMinute,
                    "1".equals(c.getAttType()) ? absenteeismLateMinute : absenteeismLeaveMinute
            );
            System.out.println(1111);
            // 如打卡状态为正常，检查是否有异常出入时间
            if (attStatus == 1 && c.getAttStatus() != null && c.getAttStatus() == 8) {
                attStatus = 8; // 异常
                boolean b = DateTimeHelper.compareTime2(c.getAttCurrentTime(), c.getAbnormalAttTime());
                String time = "";
                String address = "";
                if (b) {
                    time = c.getAttCurrentTime() + "  " + c.getAbnormalAttTime();
                    address = c.getAttAddress() + "  " + c.getAbnormalAttAddress();
                } else {
                    time = c.getAbnormalAttTime() + "  " + c.getAttCurrentTime();
                    address = c.getAbnormalAttAddress() + "  " + c.getAttAddress();
                }
                c.setAttCurrentTime(time);
                c.setAttAddress(address);
            }
            System.out.println(2222);
            c.setAttStatus(attStatus);
        });
    }


    /**
     * 计算时间差并返回状态
     *
     * @param attType              考勤类型
     * @param attTime              考勤时间
     * @param standardTime         上、下班时间
     * @param lateThreshold        迟到、早退时间
     * @param absenteeismThreshold 旷工时间
     * @return 考勤状态
     */
    private int calculateStatus(String attType, String attTime, String standardTime,
                                long lateThreshold, long absenteeismThreshold) {
        int status = 1;
        // 创建两个时间点
        Date date1 = DateUtils.parseDate(DateUtils.getYyyyMmDd(attTime) + " " + standardTime);
        Date date2 = DateUtils.parseDate(attTime);
        if ("2".equals(attType)) {
            String startTime = DateUtils.getYyyyMmDd(attTime) + " 00:00:00";
            String endTime = DateUtils.getYyyyMmDd(attTime) + " 04:59:59";
            if (DateUtils.getTimeIsRange(attTime, startTime, endTime)) {
                date1 = DateUtils.parseDate(DateUtils.getAfterDate(DateUtils.getYyyyMmDd(attTime)) + " " + standardTime);
            }
        }
        if ("1".equals(attType)) { // 上班打卡
            // 计算时间差
            // 减 1 原因：30上班，30:59不算迟到，类似于默认可以迟到一分钟
            double difference = (date2.getTime() - date1.getTime()) / 60000.0 - 1;
            if (difference > 0) {
                if (difference > absenteeismThreshold) {
                    status = 3; // 旷工
                } else if (difference > lateThreshold) {
                    status = 2; // 迟到
                }
            }
        } else if ("2".equals(attType)) { // 下班打卡
            // 计算时间差
            double difference = (date2.getTime() - date1.getTime()) / 60000.0;
            if (difference < 0) {
                if (Math.abs(difference) > lateThreshold) {
                    status = 4; // 早退
                }
                if (Math.abs(difference) > absenteeismThreshold) {
                    status = 3; // 旷工
                }
            }
        }
        return status; // 正常
    }

    /**
     * 根据考勤数据生成报表数据
     *
     * @param groupList 考勤组集合
     */
    public void createReportData(List<AttGroupBean> groupList, String pushDate, int pushType) {
        //日报表查询
        List<AttDayReportBean> dayReportList = attSourceDataDao.selectAttDayReport(pushDate);
        //日报表新增
        attSourceDataDao.insertAttDayReport(dayReportList);
        List<AttMonthReportBean> monthReportList = attSourceDataDao.selectAttMonthReport(pushDate);
        //人员对应出月出勤天数
        monthReportList.forEach(c ->
                groupList.stream()
                        .filter(v -> c.getGroupId() != null && c.getGroupId().equals(v.getGroupId()))
                        .findFirst()
                        .ifPresent(v -> c.setRequiredDays(v.getAttWorkDayBean().getRequiredDays()))
        );
        //先将本月数据删除，重新生成
        attSourceDataDao.deleteAttMonthReport(pushDate);
        //月报表新增
        attSourceDataDao.insertAttMonthReport(monthReportList);
    }

    /**
     * 根据当前日期计算目标日期
     *
     * @param now 当前日期时间
     * @return 目标日期
     */
    public static LocalDate getTargetDate(LocalDateTime now) {
        // 获取当前日期的年份和月份
        YearMonth currentYearMonth = YearMonth.from(now);

        // 获取当前日期的月初日期
        LocalDate firstDayOfMonth = currentYearMonth.atDay(1);

        // 判断是否是当月
        if (now.getMonth().equals(firstDayOfMonth.getMonth())) {
            // 如果是当月，返回当前日期的日期部分
            return now.toLocalDate();
        } else {
            // 如果是之前月份，返回该月份的最后一天
            return currentYearMonth.atEndOfMonth();
        }
    }

}