package jnpf.attendance.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jnpf.attendance.mapper.AttendanceClockInResultMapper;
import jnpf.attendance.mapper.AttendanceLeaveApproveMapper;
import jnpf.attendance.mapper.StatisticsMapper;
import jnpf.attendance.service.AttendanceBaseSettingService;
import jnpf.attendance.service.AttendanceDayStatisticsService;
import jnpf.attendance.service.AttendanceRepairService;
import jnpf.attendance.service.StatisticsUtilService;
import jnpf.entity.attendance.AttendanceBaseSetting;
import jnpf.entity.attendance.AttendanceDayStatistics;
import jnpf.entity.attendance.AttendanceRepair;
import jnpf.entity.workflow.AttendanceBusinessTripApprove;
import jnpf.entity.workflow.AttendanceGoOutApprove;
import jnpf.enums.attendance.AttendanceTypeEnum;
import jnpf.enums.attendance.ClockInStatusEnum;
import jnpf.enums.attendance.LeaveUnitEnum;
import jnpf.model.attendance.model.*;
import jnpf.model.attendance.vo.attendance.DayStatisticsQueryVo;
import jnpf.model.common.DateRangeDto;
import jnpf.model.common.DateRangeDto1;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.util.ConstantUtil;
import jnpf.util.DateConvertUtil;
import jnpf.util.DateUtil;
import jnpf.util.StringUtil;
import jnpf.util.attendance.DayStatisticsUtils;
import jnpf.workflow.service.BusinessTripApproveService;
import jnpf.workflow.service.GoOutApproveService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static jnpf.util.DateUtil.dateFormat;
import static jnpf.util.DateUtil.strToDate;
import static jnpf.util.attendance.DayStatisticsUtils.*;

/**
 * 统计公共方法服务实现
 *
 * @author shitou
 * @ date 2023/11/21
 */
@Slf4j
@Service
public class StatisticsUtilServiceImpl implements StatisticsUtilService {
    @Resource
    private StatisticsMapper statisticsMapper;
    @Resource
    private GoOutApproveService goOutApproveService;
    @Resource
    private AttendanceRepairService attendanceRepairService;
    @Resource
    private AttendanceBaseSettingService baseSettingService;
    @Resource
    private AttendanceLeaveApproveMapper leaveApproveMapper;
    @Resource
    private BusinessTripApproveService businessTripApproveService;
    @Resource
    private AttendanceClockInResultMapper attendanceClockInResultMapper;
    @Resource
    private AttendanceDayStatisticsService attendanceDayStatisticsService;

    @Override
    public List<ClockInfo> getUserClockDateArrayList(String userId, DateRangeDto selecteDateRangeDto,
                                                     List<Date> dateArrayList, List<DayStatisticsQueryVo> dayStatisticsQueryVoList) {
        List<ClockInfo> clockInfoList = new ArrayList<>();
        for (Date date : dateArrayList) {
            List<DayStatisticsQueryVo> dayStatisticsQueryVos = dayStatisticsQueryVoList.stream().filter(item -> item.getDate().equals(date)).collect(Collectors.toList());
            clockInfoList.add(ClockInfo.builder().date(date).clockStatus(getClockStatus(dayStatisticsQueryVos)).build());
        }
        return clockInfoList;
    }


    @Override
    public Map<String, List<LeaveRecord>> getLeaveMap(Map<String, UserBoundVO> userMap, Map<String, BigDecimal> ratioMap,
                                                      List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<LeaveRecord>> leaveRecordMap = new HashMap<>();
        LambdaQueryWrapper<AttendanceDayStatistics> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(AttendanceDayStatistics::getUserId, AttendanceDayStatistics::getGroupId, AttendanceDayStatistics::getLeaveBatchNumber, AttendanceDayStatistics::getCustomLeaveJson);
        queryWrapper.in(AttendanceDayStatistics::getUserId, userIds);
        queryWrapper.in(AttendanceDayStatistics::getGroupId, new ArrayList<>(ratioMap.keySet()));
        queryWrapper.between(AttendanceDayStatistics::getDate, strToDate(dateRangeDto.getStartDate()), strToDate(dateRangeDto.getEndDate()));
        queryWrapper.isNotNull(AttendanceDayStatistics::getLeaveBatchNumber);
        queryWrapper.gt(AttendanceDayStatistics::getLeaveHours, BigDecimal.ZERO);
        List<AttendanceDayStatistics> dayStatisticsList = attendanceDayStatisticsService.list(queryWrapper);
        if (CollUtil.isEmpty(dayStatisticsList)) {
            return leaveRecordMap;
        }
        List<LeaveTypeJsonData> applyList = dayStatisticsList.stream().filter(a -> StringUtil.isNotEmpty(a.getCustomLeaveJson()))
                .flatMap(a -> JSONArray.parseArray(a.getCustomLeaveJson(), LeaveTypeJsonData.class).stream()).collect(Collectors.toList());
        Map<String, LeaveTypeStaDetailsModel> applyMap = CollUtil.isNotEmpty(applyList) ? applyList.stream().map(LeaveTypeJsonData::getApplyList)
                .flatMap(Collection::stream).collect(Collectors.groupingBy(
                        LeaveTypeStaDetailsModel::getApplyId,
                        Collectors.reducing(new LeaveTypeStaDetailsModel(), (model1, model2) -> {
                            LeaveTypeStaDetailsModel result = new LeaveTypeStaDetailsModel();
                            result.setApplyId(model1.getApplyId());
                            result.setLeaveDays(model1.getLeaveDays().add(model2.getLeaveDays()));
                            result.setLeaveHours(model1.getLeaveHours().add(model2.getLeaveHours()));
                            result.setLeavePayrollHours(model1.getLeavePayrollHours().add(model2.getLeavePayrollHours()));
                            return result;
                        })
                )) : new HashMap<>();
        List<String> applyAllIds = dayStatisticsList.stream().flatMap(a -> Arrays.stream(a.getLeaveBatchNumber().split(","))).collect(Collectors.toList());
        List<LeaveSituationData> leaveForApplyList = CollUtil.isEmpty(applyAllIds) ? new ArrayList<>() : leaveApproveMapper.getUserLeaveListByApplyIds(applyAllIds);
        if (CollUtil.isEmpty(leaveForApplyList)) {
            return leaveRecordMap;
        }
        Map<String, LeaveSituationData> leaveApproveMap = leaveForApplyList.stream().collect(Collectors.toMap(LeaveSituationData::getApplyId, Function.identity()));
        Map<String, List<LeaveSituationData>> userLeavedMap = leaveForApplyList.stream().collect(Collectors.groupingBy(leave -> leave.getUserId() + "&" + leave.getGroupId()));
        for (Map.Entry<String, List<LeaveSituationData>> entry : userLeavedMap.entrySet()) {
            String userAndGroupKey = entry.getKey();
            String userId = userAndGroupKey.split("&")[0];
            String groupId = userAndGroupKey.split("&")[1];
            List<LeaveSituationData> leaveList = entry.getValue();
            if (!userMap.containsKey(userId) || !ratioMap.containsKey(groupId)) {
                continue;
            }
            UserBoundVO user = userMap.get(userId);
            List<LeaveRecord> leaveRecordList = new ArrayList<>();
            List<String> applyIds = leaveList.stream().map(LeaveSituationData::getApplyId).filter(StringUtil::isNotEmpty).distinct().collect(Collectors.toList());
            Map<String, LeaveSituationData> userLeaveApproveMap = applyIds.stream().filter(leaveApproveMap::containsKey).collect(Collectors.toMap(Function.identity(), leaveApproveMap::get));
            for (Map.Entry<String, LeaveSituationData> approveEntry : userLeaveApproveMap.entrySet()) {
                LeaveSituationData approve = approveEntry.getValue();
                LeaveRecord leaveRecord = new LeaveRecord();
                leaveRecord.setUserName(user.getUserName());
                DateTimeFormatter formatterDayStr = approve.getUnit().equals(1) ? DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") : DateTimeFormatter.ofPattern("yyyy-MM-dd");
                leaveRecord.setRangeStr(DayStatisticsUtils.formatDayRange(approve.getStartTime(), approve.getEndTime(), formatterDayStr));
                leaveRecord.setLeaveDays(applyMap.get(approve.getApplyId()).getLeaveDays());
                leaveRecord.setType(approve.getLeaveName());
                leaveRecordList.add(leaveRecord);
            }
            leaveRecordMap.put(userAndGroupKey, leaveRecordList);
        }
        return leaveRecordMap;
    }

    @Override
    public Map<String, List<LateRecord>> getLateMap(Map<String, UserBoundVO> userMap, Map<String, Boolean> sealMap, List<String> groupIds,
                                                    List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<LateRecord>> lateRecordMap = new HashMap<>();
        List<ClockInResultRecord> userClockInResultList = this.statisticsMapper.getUserClockInResultList(
                List.of(AttendanceTypeEnum.ORDINARY.getCode()), userIds,
                dateRangeDto, groupIds, ClockInStatusEnum.WORK_LATE.getValue(), 0, null, null);
        if (CollUtil.isEmpty(userClockInResultList)) {
            return lateRecordMap;
        }
        //查询考勤组配置信息
        Map<String, AttendanceBaseSetting> baseSettingMap = baseSettingService.getEnableBaseSetting(userClockInResultList
                .stream().map(ClockInResultRecord::getGroupId).distinct().collect(Collectors.toList()));
        //按照用户+&考勤组分组获取数据
        Map<String, List<ClockInResultRecord>> userClockMap = userClockInResultList.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        userClockMap.forEach((userAndGroupKey, list) -> {
            String userId = userAndGroupKey.split("&")[0];
            String groupId = userAndGroupKey.split("&")[1];
            // 获取补卡次数
            AttendanceRepair repairByDate = attendanceRepairService.getAttendanceRepairByDate(DateUtil.daFormat(new Date()), userId, groupId);
            int repairNum = repairByDate != null ? repairByDate.getRepairNum() : 0;
            // 获取审批中的补卡次数
            int applyCount = attendanceClockInResultMapper.selectRepairApplyCount(userId, groupId, dateRangeDto.getStartDate(), dateRangeDto.getEndDate());
            // 是否允许补卡
            boolean isApply = (repairNum - applyCount) > 0;
            List<LateRecord> lateRecordList = new ArrayList<>();
            for (ClockInResultRecord inResultRecord : list) {
                LateRecord lateRecord = new LateRecord();
                BeanUtils.copyProperties(inResultRecord, lateRecord);
                lateRecord.setLateDate(inResultRecord.getDay());
                lateRecord.setLateMinutes(DateConvertUtil.secondConvert(inResultRecord.getAbnormalSecond(), 1, 2, null));
                lateRecord.setUserName(userMap.get(userId).getUserName());
                lateRecord.setSeal(sealMap.getOrDefault(userId, false) ? 1 : 0);
                if (baseSettingMap.containsKey(inResultRecord.getGroupId())) {
                    AttendanceBaseSetting baseSetting = baseSettingMap.get(inResultRecord.getGroupId());
                    lateRecord.setIsOperate(Objects.isNull(baseSetting.getPatchClockStatus()) || baseSetting.getPatchClockStatus().equals(0) ? 0 :
                            Objects.nonNull(baseSetting.getPatchType()) && baseSetting.getPatchType().contains("1") && isApply ? 1 : 0);
                }
                lateRecordList.add(lateRecord);
            }
            if (CollUtil.isNotEmpty(lateRecordList)) {
                lateRecordList = lateRecordList.stream().sorted(Comparator.comparing(LateRecord::getLateDate)
                        .thenComparing(LateRecord::getClockTime)).collect(Collectors.toList());
            }
            lateRecordMap.put(userAndGroupKey, lateRecordList);
        });
        return lateRecordMap;
    }

    @Override
    public Map<String, List<EarlyLeaveRecord>> getEarlyMap(Map<String, UserBoundVO> userMap, Map<String, Boolean> sealMap, List<String> groupIds,
                                                           List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<EarlyLeaveRecord>> earlyListMap = new HashMap<>();
        List<ClockInResultRecord> userClockInResultList = this.statisticsMapper.getUserClockInResultList(
                List.of(AttendanceTypeEnum.ORDINARY.getCode()), userIds,
                dateRangeDto, groupIds, ClockInStatusEnum.HOME_EARLY.getValue(), 0, null, null);
        if (CollUtil.isEmpty(userClockInResultList)) {
            return earlyListMap;
        }
        //查询考勤组配置信息
        Map<String, AttendanceBaseSetting> baseSettingMap = baseSettingService.getEnableBaseSetting(userClockInResultList
                .stream().map(ClockInResultRecord::getGroupId).distinct().collect(Collectors.toList()));
        //按照用户+&考勤组分组获取数据
        Map<String, List<ClockInResultRecord>> userClockMap = userClockInResultList.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        userClockMap.forEach((userAndGroupKey, list) -> {
            String userId = userAndGroupKey.split("&")[0];
            String groupId = userAndGroupKey.split("&")[1];
            // 获取补卡次数
            AttendanceRepair repairByDate = attendanceRepairService.getAttendanceRepairByDate(DateUtil.daFormat(new Date()), userId, groupId);
            int repairNum = repairByDate != null ? repairByDate.getRepairNum() : 0;
            // 获取审批中的补卡次数
            int applyCount = attendanceClockInResultMapper.selectRepairApplyCount(userId, groupId, dateRangeDto.getStartDate(), dateRangeDto.getEndDate());
            // 是否允许补卡
            boolean isApply = (repairNum - applyCount) > 0;
            List<EarlyLeaveRecord> earlyLeaveRecordList = CollUtil.newArrayList();
            for (ClockInResultRecord inResultRecord : list) {
                EarlyLeaveRecord earlyLeaveRecord = new EarlyLeaveRecord();
                BeanUtils.copyProperties(inResultRecord, earlyLeaveRecord);
                earlyLeaveRecord.setEarlyLeaveDate(inResultRecord.getDay());
                earlyLeaveRecord.setEarlyLeaveMinutes(DateConvertUtil.secondConvert(inResultRecord.getAbnormalSecond(), 1, 2, null));
                earlyLeaveRecord.setUserName(userMap.get(userId).getUserName());
                earlyLeaveRecord.setSeal(sealMap.getOrDefault(userId, false) ? 1 : 0);
                if (baseSettingMap.containsKey(inResultRecord.getGroupId())) {
                    AttendanceBaseSetting baseSetting = baseSettingMap.get(inResultRecord.getGroupId());
                    earlyLeaveRecord.setIsOperate(Objects.isNull(baseSetting.getPatchClockStatus()) || baseSetting.getPatchClockStatus().equals(0) ? 0 :
                            Objects.nonNull(baseSetting.getPatchType()) && baseSetting.getPatchType().contains("2") && isApply ? 1 : 0);
                }
                earlyLeaveRecordList.add(earlyLeaveRecord);
            }
            if (CollUtil.isNotEmpty(earlyLeaveRecordList)) {
                earlyLeaveRecordList = earlyLeaveRecordList.stream().sorted(Comparator.comparing(EarlyLeaveRecord::getEarlyLeaveDate)
                        .thenComparing(EarlyLeaveRecord::getClockTime)).collect(Collectors.toList());
            }
            earlyListMap.put(userAndGroupKey, earlyLeaveRecordList);
        });
        return earlyListMap;
    }

    @Override
    public Map<String, List<AbsenceCardRecord>> getAbsenceCardMap(Map<String, UserBoundVO> userMap, Map<String, Boolean> sealMap, List<String> groupIds,
                                                                  List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<AbsenceCardRecord>> absenceCardMap = new HashMap<>();
        List<ClockInResultRecord> userClockInResultList = this.statisticsMapper.getUserClockInResultList(
                List.of(AttendanceTypeEnum.ORDINARY.getCode(), AttendanceTypeEnum.WORKOVERTIME.getCode()), userIds,
                dateRangeDto, groupIds, ClockInStatusEnum.NO_CLOCK.getValue(), 0, null, null);
        if (CollUtil.isEmpty(userClockInResultList)) {
            return absenceCardMap;
        }
        //查询考勤组配置信息
        Map<String, AttendanceBaseSetting> baseSettingMap = baseSettingService.getEnableBaseSetting(userClockInResultList
                .stream().map(ClockInResultRecord::getGroupId).distinct().collect(Collectors.toList()));
        //按照用户+&考勤组分组获取数据
        Map<String, List<ClockInResultRecord>> userClockMap = userClockInResultList.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        userClockMap.forEach((userAndGroupKey, list) -> {
            String userId = userAndGroupKey.split("&")[0];
            String groupId = userAndGroupKey.split("&")[1];
            // 获取补卡次数
            AttendanceRepair repairByDate = attendanceRepairService.getAttendanceRepairByDate(DateUtil.daFormat(new Date()), userId, groupId);
            int repairNum = repairByDate != null ? repairByDate.getRepairNum() : 0;
            // 获取审批中的补卡次数
            int applyCount = attendanceClockInResultMapper.selectRepairApplyCount(userId, groupId, dateRangeDto.getStartDate(), dateRangeDto.getEndDate());
            // 是否允许补卡
            boolean isApply = (repairNum - applyCount) > 0;
            List<AbsenceCardRecord> absenceCardList = CollUtil.newArrayList();
            for (ClockInResultRecord inResultRecord : list) {
                AbsenceCardRecord absenceCardRecord = new AbsenceCardRecord();
                BeanUtils.copyProperties(inResultRecord, absenceCardRecord);
                absenceCardRecord.setAbsenceCardDate(inResultRecord.getDay());
                absenceCardRecord.setPointOfTime(inResultRecord.getClockInType().equals(ConstantUtil.ON_WORK) ?
                        inResultRecord.getInPoint() : inResultRecord.getOutPoint());
                absenceCardRecord.setUserName(userMap.get(userId).getUserName());
                absenceCardRecord.setSeal(sealMap.getOrDefault(userId, false) ? 1 : 0);
                if (baseSettingMap.containsKey(inResultRecord.getGroupId())) {
                    AttendanceBaseSetting baseSetting = baseSettingMap.get(inResultRecord.getGroupId());
                    absenceCardRecord.setIsOperate(Objects.isNull(baseSetting.getPatchClockStatus()) || baseSetting.getPatchClockStatus().equals(0) ? 0 :
                            Objects.nonNull(baseSetting.getPatchType()) && baseSetting.getPatchType().contains("3") && isApply ? 1 : 0);
                }
                absenceCardList.add(absenceCardRecord);
            }
            if (CollUtil.isNotEmpty(absenceCardList)) {
                absenceCardList = absenceCardList.stream().sorted(Comparator.comparing(AbsenceCardRecord::getAbsenceCardDate)
                        .thenComparing(AbsenceCardRecord::getPointOfTime)).collect(Collectors.toList());
            }
            absenceCardMap.put(userAndGroupKey, absenceCardList);
        });
        return absenceCardMap;
    }

    @Override
    public Map<String, List<AbsenceRecord>> getAbsenceMap(List<String> groupIds,
                                                          Map<String, Boolean> sealMap, List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<AbsenceRecord>> absenceMap = new HashMap<>();
        List<ClockInResultRecord> userClockInResultList = this.statisticsMapper.getUserClockInResultList(
                List.of(AttendanceTypeEnum.ORDINARY.getCode()), userIds,
                dateRangeDto, groupIds, null, 1, null, null);
        if (CollUtil.isEmpty(userClockInResultList)) {
            return absenceMap;
        }
        //查询考勤组配置信息
        Map<String, AttendanceBaseSetting> baseSettingMap = baseSettingService.getEnableBaseSetting(userClockInResultList
                .stream().map(ClockInResultRecord::getGroupId).distinct().collect(Collectors.toList()));
        //按照用户+&考勤组分组获取数据
        Map<String, List<ClockInResultRecord>> userClockMap = userClockInResultList.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        userClockMap.forEach((userAndGroupKey, list) -> {
            String userId = userAndGroupKey.split("&")[0];
            String groupId = userAndGroupKey.split("&")[1];
            // 获取补卡次数
            AttendanceRepair repairByDate = attendanceRepairService.getAttendanceRepairByDate(DateUtil.daFormat(new Date()), userId, groupId);
            int repairNum = repairByDate != null ? repairByDate.getRepairNum() : 0;
            // 获取审批中的补卡次数
            int applyCount = attendanceClockInResultMapper.selectRepairApplyCount(userId, groupId, dateRangeDto.getStartDate(), dateRangeDto.getEndDate());
            // 是否允许补卡
            boolean isApply = (repairNum - applyCount) > 0;
            List<AbsenceRecord> absenceRecordList = CollUtil.newArrayList();
            for (ClockInResultRecord inResultRecord : list) {
                AbsenceRecord absenceRecord = new AbsenceRecord();
                BeanUtils.copyProperties(inResultRecord, absenceRecord);
                absenceRecord.setAbsenceDate(inResultRecord.getDay());
                absenceRecord.setStartTime(inResultRecord.getInPoint());
                absenceRecord.setEndTime(inResultRecord.getOutPoint());
                absenceRecord.setIsAdminUpdate(StringUtil.isNotEmpty(inResultRecord.getAbsenceLeader()) ? 1 : 0);
                absenceRecord.setSeal(sealMap.getOrDefault(userId, false) ? 1 : 0);
                if (baseSettingMap.containsKey(inResultRecord.getGroupId())) {
                    AttendanceBaseSetting baseSetting = baseSettingMap.get(inResultRecord.getGroupId());
                    absenceRecord.setIsOperate(Objects.isNull(baseSetting.getPatchClockStatus()) || baseSetting.getPatchClockStatus().equals(0) ? 0 :
                            Objects.nonNull(baseSetting.getPatchType()) && baseSetting.getPatchType().contains("4") && isApply ? 1 : 0);
                }
                absenceRecordList.add(absenceRecord);
            }
            if (CollUtil.isNotEmpty(absenceRecordList)) {
                absenceRecordList = absenceRecordList.stream().sorted(Comparator.comparing(AbsenceRecord::getAbsenceDate)
                        .thenComparing(AbsenceRecord::getStartTime).thenComparing(AbsenceRecord::getClockInType)).collect(Collectors.toList());
            }
            absenceMap.put(userAndGroupKey, absenceRecordList);
        });
        return absenceMap;
    }

    @Override
    public Map<String, List<MakeUpCardRecord>> getMakeUpCardMap(List<String> groupIds,
                                                                List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<MakeUpCardRecord>> makeUpCardMap = new HashMap<>();
        List<ClockInResultRecord> userClockInResultList = this.statisticsMapper.getUserClockInResultList(
                List.of(AttendanceTypeEnum.ORDINARY.getCode(), AttendanceTypeEnum.WORKOVERTIME.getCode()), userIds,
                dateRangeDto, groupIds, null, null, 1, null);
        if (CollUtil.isEmpty(userClockInResultList)) {
            return makeUpCardMap;
        }
        //按照用户+&考勤组分组获取数据
        Map<String, List<ClockInResultRecord>> userClockMap = userClockInResultList.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        userClockMap.forEach((userAndGroupKey, list) -> {
            List<MakeUpCardRecord> makeUpCardRecordList = CollUtil.newArrayList();
            for (ClockInResultRecord inResultRecord : list) {
                MakeUpCardRecord makeUpCardRecord = new MakeUpCardRecord();
                BeanUtils.copyProperties(inResultRecord, makeUpCardRecord);
                makeUpCardRecord.setMakeUpCardDate(inResultRecord.getDay());
                makeUpCardRecord.setMakeUpCardTime(inResultRecord.getEffectiveTime());
                makeUpCardRecordList.add(makeUpCardRecord);
            }
            if (CollUtil.isNotEmpty(makeUpCardRecordList)) {
                makeUpCardRecordList = makeUpCardRecordList.stream().sorted(Comparator.comparing(MakeUpCardRecord::getMakeUpCardDate)
                        .thenComparing(MakeUpCardRecord::getMakeUpCardTime)).collect(Collectors.toList());
            }
            makeUpCardMap.put(userAndGroupKey, makeUpCardRecordList);
        });
        return makeUpCardMap;
    }

    @Override
    public Map<String, List<OutworkRecord>> getOutworkRecordList(List<String> groupIds,
                                                                 List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<OutworkRecord>> outworkMap = new HashMap<>();
        List<ClockInResultRecord> userClockInResultList = this.statisticsMapper.getUserClockInResultList(
                List.of(AttendanceTypeEnum.ORDINARY.getCode(), AttendanceTypeEnum.WORKOVERTIME.getCode()), userIds,
                dateRangeDto, groupIds, null, null, null, 2);
        if (CollUtil.isEmpty(userClockInResultList)) {
            return outworkMap;
        }
        //按照用户+&考勤组分组获取数据
        Map<String, List<ClockInResultRecord>> userClockMap = userClockInResultList.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        userClockMap.forEach((userAndGroupKey, list) -> {
            List<OutworkRecord> outworkRecordList = CollUtil.newArrayList();
            for (ClockInResultRecord inResultRecord : list) {
                OutworkRecord outworkRecord = new OutworkRecord();
                BeanUtils.copyProperties(inResultRecord, outworkRecord);
                outworkRecord.setOutworkDate(inResultRecord.getDay());
                outworkRecord.setOutworkTime(inResultRecord.getClockTime());
                outworkRecordList.add(outworkRecord);
            }
            if (CollUtil.isNotEmpty(outworkRecordList)) {
                outworkRecordList = outworkRecordList.stream().sorted(Comparator.comparing(OutworkRecord::getOutworkDate)
                        .thenComparing(OutworkRecord::getOutworkTime)).collect(Collectors.toList());
            }
            outworkMap.put(userAndGroupKey, outworkRecordList);
        });
        return outworkMap;
    }

    @Override
    public Map<String, List<OvertimeRecord>> getOvertimeRecordList(List<String> groupIds, List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<OvertimeRecord>> overtimeMap = new HashMap<>();
        List<UserRuleRecord> userOvertimeRecordList = this.statisticsMapper.getUserRuleRecordList(userIds,
                dateRangeDto, groupIds, List.of(AttendanceTypeEnum.WORKOVERTIME.getCode()));
        if (CollUtil.isEmpty(userOvertimeRecordList)) {
            return overtimeMap;
        }
        // 排除掉加班未结束的班次
        userOvertimeRecordList.removeIf(record -> CollUtil.isEmpty(record.getClockInResultList()) || record.getClockInResultList().size() != 2);
        userOvertimeRecordList.forEach(record -> {
            if (record.getClockInResultList() != null) {
                record.getClockInResultList().removeIf(result ->
                        Objects.equals(result.getDeleteMark(), 1) || StringUtil.isEmpty(result.getId()));
            }
        });
        // 按照用户分组获取数据
        Map<String, List<UserRuleRecord>> userClockMap = userOvertimeRecordList.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        userClockMap.forEach((userAndGroupKey, userClockList) -> {
            List<OvertimeRecord> overtimeRecordList = CollUtil.newArrayList();
            for (UserRuleRecord userRuleRecord : userClockList) {
                OvertimeRecord overtimeRecord = new OvertimeRecord();
                List<ClockInResultRecordVo> clockInResults = userRuleRecord.getClockInResultList();
                if (CollUtil.isNotEmpty(clockInResults)) {
                    DayStatisticsUtils.processClockInResults(clockInResults, userRuleRecord, overtimeRecord);
                } else {
                    DayStatisticsUtils.handleEmptyClockInResults(userRuleRecord, overtimeRecord);
                }
                overtimeRecordList.add(overtimeRecord);
            }
            if (CollUtil.isNotEmpty(overtimeRecordList)) {
                overtimeRecordList = overtimeRecordList.stream()
                        .sorted(Comparator.comparing(OvertimeRecord::getOvertimeDate)
                                .thenComparing(OvertimeRecord::getStartTime))
                        .collect(Collectors.toList());
            }
            overtimeMap.put(userAndGroupKey, overtimeRecordList);
        });
        return overtimeMap;
    }

    @SneakyThrows
    @Override
    public Map<String, List<SecondRecord>> getSecondRecordList(List<String> groupIds, List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<SecondRecord>> secondMap = new HashMap<>();
        //被借调记录
        List<UserSecondRecord> userSecondRecordList = statisticsMapper.getUserSecondRecordList(userIds, groupIds);
        if (CollUtil.isEmpty(userSecondRecordList)) {
            return secondMap;
        }
        //按照用户+&考勤组分组获取数据
        Map<String, List<UserSecondRecord>> userMap = userSecondRecordList.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        Date queryStartDate = strToDate(dateRangeDto.getStartDate());
        Date queryEndDate = strToDate(dateRangeDto.getEndDate());
        for (Map.Entry<String, List<UserSecondRecord>> entry : userMap.entrySet()) {
            List<SecondRecord> secondRecordList = new ArrayList<>();
            //处理借调记录(json对象)
            List<TimeJson> timeAllList = new ArrayList<>();
            handleDateJsonObject(dateRangeDto, timeAllList, entry.getValue());
            if (CollUtil.isEmpty(timeAllList)) {
                continue;
            }
            for (TimeJson item : timeAllList) {
                LocalDateTime startDate = LocalDateTime.parse(dateFormat(item.getStartTime()), formatter);
                LocalDateTime endDate = LocalDateTime.parse(dateFormat(item.getEndTime()), formatter);
                SecondRecord secondRecord = new SecondRecord();
                if (isCrossMonth(startDate, endDate)) {
                    Date start = queryStartDate.compareTo(item.getStartTime()) <= 0 ?
                            item.getStartTime() : queryStartDate;
                    Date end = queryEndDate.compareTo(item.getEndTime()) <= 0 ?
                            cn.hutool.core.date.DateUtil.endOfDay(item.getEndTime()) : queryEndDate;
                    secondRecord.setSecondDate(start);
                    secondRecord.setStartTime(start);
                    secondRecord.setEndTime(end);
                    secondRecord.setSecondDuration(DateConvertUtil.dateConvert(start, end, 3, 2, new BigDecimal(24)));
                } else {
                    secondRecord.setSecondDate(item.getStartTime());
                    secondRecord.setStartTime(item.getStartTime());
                    secondRecord.setEndTime(item.getEndTime());
                    secondRecord.setSecondDuration(DateConvertUtil.dateConvert(secondRecord.getStartTime(),
                            secondRecord.getEndTime(), 3, 2, new BigDecimal(24)));
                }
                secondRecordList.add(secondRecord);
            }
            if (CollUtil.isNotEmpty(secondRecordList)) {
                secondMap.put(entry.getKey(), secondRecordList);
            }
        }
        return secondMap;
    }


    @Override
    public Map<String, List<BusOrOutRecord>> getBusRecordList(List<String> groupIds, List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<BusOrOutRecord>> busMap = new HashMap<>();
        //获取日统计数据
        List<AttendanceDayStatistics> dayStatisticsList = attendanceDayStatisticsService.lambdaQuery()
                .in(AttendanceDayStatistics::getUserId, userIds)
                .in(CollUtil.isNotEmpty(groupIds), AttendanceDayStatistics::getGroupId, groupIds)
                .ge(AttendanceDayStatistics::getDate, DateUtil.strToDate(dateRangeDto.getStartDate()))
                .le(AttendanceDayStatistics::getDate, DateUtil.strToDate(dateRangeDto.getEndDate()))
                .isNotNull(AttendanceDayStatistics::getBusBatchNumber)
                .list();
        if (CollUtil.isEmpty(dayStatisticsList)) {
            return busMap;
        }
        List<String> applyList = new ArrayList<>();
        dayStatisticsList.forEach(item -> applyList.addAll(List.of(item.getBusBatchNumber().split(","))));
        List<AttendanceBusinessTripApprove> busApproveMap = businessTripApproveService.getBatchByIds(groupIds, applyList);
        if (CollUtil.isEmpty(busApproveMap)) {
            return busMap;
        }
        //按照用户+&考勤组分组
        Map<String, List<AttendanceBusinessTripApprove>> userMap = busApproveMap.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        userMap.forEach((userAndGroupKey, approveList) -> {
            List<BusOrOutRecord> busRecordList = new ArrayList<>();
            for (AttendanceBusinessTripApprove approve : approveList) {
                BusOrOutRecord busOrOutRecord = new BusOrOutRecord();
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                LocalDateTime startDate = LocalDateTime.parse(dateFormat(approve.getStartTime()), formatter);
                LocalDateTime endDate = LocalDateTime.parse(dateFormat(approve.getEndTime()), formatter);
                if (isCrossMonth(startDate, endDate)) {
                    DateRangeDto1 dateRange = getIntersection(startDate, endDate,
                            LocalDateTime.parse(dateRangeDto.getStartDate() + " 00:00:00", formatter),
                            LocalDateTime.parse(dateRangeDto.getEndDate() + " 23:59:59", formatter));
                    if (Objects.nonNull(dateRange)) {
                        busOrOutRecord.setStartTime(Date.from(dateRange.getStartDate().atZone(ZoneId.systemDefault()).toInstant()));
                        busOrOutRecord.setEndTime(Date.from(dateRange.getEndDate().atZone(ZoneId.systemDefault()).toInstant()));
                        busOrOutRecord.setBusOrOutDays(DateConvertUtil.dateConvert(busOrOutRecord.getStartTime(),
                                busOrOutRecord.getEndTime(), 3, 2, new BigDecimal(24)));
                    }
                } else {
                    busOrOutRecord.setStartTime(approve.getStartTime());
                    busOrOutRecord.setEndTime(approve.getEndTime());
                    busOrOutRecord.setBusOrOutDays(approve.getDayNum());
                }
                busRecordList.add(busOrOutRecord);
            }
            if (CollUtil.isNotEmpty(busRecordList)) {
                busRecordList = busRecordList.stream().sorted(Comparator.comparing(BusOrOutRecord::getStartTime)).collect(Collectors.toList());
            }
            busMap.put(userAndGroupKey, busRecordList);
        });
        return busMap;
    }

    @Override
    public Map<String, List<BusOrOutRecord>> getOutRecordList(Map<String, BigDecimal> ratioMap, List<String> userIds, DateRangeDto dateRangeDto) {
        Map<String, List<BusOrOutRecord>> outMap = new HashMap<>();
        //获取日统计数据
        ArrayList<String> groupIds = new ArrayList<>(ratioMap.keySet());
        List<AttendanceDayStatistics> dayStatisticsList = attendanceDayStatisticsService.lambdaQuery()
                .in(AttendanceDayStatistics::getUserId, userIds)
                .in(AttendanceDayStatistics::getGroupId, groupIds)
                .ge(AttendanceDayStatistics::getDate, DateUtil.strToDate(dateRangeDto.getStartDate()))
                .le(AttendanceDayStatistics::getDate, DateUtil.strToDate(dateRangeDto.getEndDate()))
                .isNotNull(AttendanceDayStatistics::getOutBatchNumber)
                .list();
        if (CollUtil.isEmpty(dayStatisticsList)) {
            return outMap;
        }
        List<String> applyList = new ArrayList<>();
        List<BatchNumberResult> shiftsJsonVoList = dayStatisticsList.stream()
                .map(AttendanceDayStatistics::getOutBatchNumber)
                .filter(item -> Objects.nonNull(item) && item.startsWith("[") && item.endsWith("]"))
                .flatMap(item -> JSONArray.parseArray(item, BatchNumberResult.class).stream())
                .collect(Collectors.toList());
        Map<String, Map<Date, BigDecimal>> applyMap = new HashMap<>();
        if (CollUtil.isNotEmpty(shiftsJsonVoList)) {
            shiftsJsonVoList.removeIf(item -> Objects.isNull(item.getDay()));
            applyMap = shiftsJsonVoList.stream().collect(Collectors.groupingBy(BatchNumberResult::getBatchNumberId,
                    Collectors.toMap(BatchNumberResult::getDay, BatchNumberResult::getDays, BigDecimal::add)));
        }
        dayStatisticsList.forEach(item -> applyList.addAll(parseBatchNumber(item.getOutBatchNumber())));
        List<AttendanceGoOutApprove> outList = goOutApproveService.getBatchByIds(groupIds, applyList);
        if (CollUtil.isEmpty(outList)) {
            return outMap;
        }
        //按照用户+&考勤组分组
        Map<String, List<AttendanceGoOutApprove>> outApproveMap = outList.stream()
                .collect(Collectors.groupingBy(approve -> approve.getUserId() + "&" + approve.getGroupId()));
        Map<String, Map<Date, BigDecimal>> finalApplyMap = applyMap;
        outApproveMap.forEach((userAndGroupKey, outApproveList) -> {
            List<BusOrOutRecord> outRecordList = CollUtil.newArrayList();
            for (AttendanceGoOutApprove approve : outApproveList) {
                BusOrOutRecord busOrOutRecord = new BusOrOutRecord();
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                LocalDateTime startDate = LocalDateTime.parse(dateFormat(approve.getStartTime()), formatter);
                LocalDateTime endDate = LocalDateTime.parse(dateFormat(approve.getEndTime()), formatter);
                String groupId = approve.getGroupId();
                if (isCrossMonth(startDate, endDate)) {
                    DateRangeDto1 dateRange = getIntersection(startDate, endDate,
                            LocalDateTime.parse(dateRangeDto.getStartDate() + " 00:00:00", formatter),
                            LocalDateTime.parse(dateRangeDto.getEndDate() + " 23:59:59", formatter));
                    if (Objects.nonNull(dateRange)) {
                        busOrOutRecord.setStartTime(Date.from(dateRange.getStartDate().atZone(ZoneId.systemDefault()).toInstant()));
                        busOrOutRecord.setEndTime(Date.from(dateRange.getEndDate().atZone(ZoneId.systemDefault()).toInstant()));
                    }
                } else {
                    busOrOutRecord.setStartTime(approve.getStartTime());
                    busOrOutRecord.setEndTime(approve.getEndTime());
                }
                // 日统计是否计算外出天数
                if (finalApplyMap.containsKey(approve.getId())) {
                    Map<Date, BigDecimal> dateBigDecimalMap = finalApplyMap.get(approve.getId());
                    // 获取实际时长 要在busOrOutRecord的范围内的数据汇总
                    busOrOutRecord.setBusOrOutDays(dateBigDecimalMap.entrySet().stream()
                            .filter(entry -> entry.getKey().compareTo(cn.hutool.core.date.DateUtil.beginOfDay(busOrOutRecord.getStartTime())) >= 0 &&
                                    entry.getKey().compareTo(cn.hutool.core.date.DateUtil.beginOfDay(busOrOutRecord.getEndTime())) <= 0)
                            .map(Map.Entry::getValue)
                            .reduce(BigDecimal::add)
                            .orElse(BigDecimal.ZERO));
                    outRecordList.add(busOrOutRecord);
                    continue;
                }
                BigDecimal bigDecimal = DateConvertUtil.dateConvert(busOrOutRecord.getStartTime(), busOrOutRecord.getEndTime(), 3, 2, ratioMap.get(groupId));
                if (approve.getUnit().equals(LeaveUnitEnum.DAY.getCode())) {
                    busOrOutRecord.setBusOrOutDays(bigDecimal);
                } else if (approve.getUnit().equals(LeaveUnitEnum.HOUR.getCode())) {
                    busOrOutRecord.setBusOrOutDays(bigDecimal.divide(ratioMap.get(groupId), 2, RoundingMode.HALF_UP));
                }
                outRecordList.add(busOrOutRecord);
            }
            if (CollUtil.isNotEmpty(outRecordList)) {
                outRecordList = outRecordList.stream().sorted(Comparator.comparing(BusOrOutRecord::getStartTime)).collect(Collectors.toList());
            }
            outMap.put(userAndGroupKey, outRecordList);
        });
        return outMap;
    }
}
