package jnpf.attendance.service.handle.rule;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import jnpf.attendance.service.RuleProcessor;
import jnpf.constants.AttendanceConstant;
import jnpf.entity.attendance.FtbAttendanceDailyRule;
import jnpf.entity.attendance.LeaveParam;
import jnpf.enums.attendance.AttendanceTypeEnum;
import jnpf.enums.attendance.LeaveUnitEnum;
import jnpf.enums.attendance.v2.WorkBoundaryCoverageEnum;
import jnpf.model.attendance.vo.DailyRuleResultVo;
import jnpf.util.DateDetail;
import jnpf.util.DateUtil;
import jnpf.util.JsonUtil;
import jnpf.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 处理历史普通排班
 */
@Component
@Slf4j
public class OrdinaryRuleProcessor extends RuleProcessor {
    @Override
    public void ruleArrangementHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<FtbAttendanceDailyRule> currDailyRule, List<DailyRuleResultVo> resultList) {
        Map<Integer, List<FtbAttendanceDailyRule>> collect = hisDailyRules.stream().collect(Collectors.groupingBy(FtbAttendanceDailyRule::getAttendanceType));
        List<FtbAttendanceDailyRule> ftbAttendanceDailyRules = collect.get(AttendanceTypeEnum.ORDINARY.getCode());
        if (CollUtil.isNotEmpty(ftbAttendanceDailyRules)) {
            Iterator<FtbAttendanceDailyRule> iterator = currDailyRule.iterator();
            while (iterator.hasNext()) {
                FtbAttendanceDailyRule rule = iterator.next();
                //新增排休
                if (restHandle(hisDailyRules, ftbAttendanceDailyRules, rule, resultList)) {
                    continue;
                }
                //新增上班
                if (ordinaryHandle(hisDailyRules, ftbAttendanceDailyRules, rule, resultList)) {
                    continue;
                }
                //新增加班
                if (workOvertimeHandle(hisDailyRules, ftbAttendanceDailyRules, rule, resultList)) {
                    continue;
                }
                //新增请假
                if (leaveHandle(hisDailyRules, ftbAttendanceDailyRules, rule, resultList)) {
                    continue;
                }
                //新增外出、出差
                if (stepOutHandle(hisDailyRules, ftbAttendanceDailyRules, rule, resultList)) {
                    continue;
                }
                //新增清除
                clearHandle(hisDailyRules, ftbAttendanceDailyRules, rule, resultList);
            }
        }
        if (Objects.nonNull(nextRuleProcessor)) {
            nextRuleProcessor.ruleArrangementHandle(hisDailyRules, currDailyRule, resultList);
        }
    }

    /**
     * 新增外出、出差申请
     *
     * @param hisDailyRules           历史规则
     * @param ftbAttendanceDailyRules 新增规则集合
     * @param rule                    当前规则
     */
    private Boolean stepOutHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<FtbAttendanceDailyRule> ftbAttendanceDailyRules, FtbAttendanceDailyRule rule, List<DailyRuleResultVo> resultList) {
        //新增外出
        if (!Objects.equals(AttendanceTypeEnum.BUSINESS_TRIP.getCode(), rule.getAttendanceType()) && !Objects.equals(AttendanceTypeEnum.STEP_OUT.getCode(), rule.getAttendanceType())) {
            return Boolean.FALSE;
        }
        ftbAttendanceDailyRules.forEach(oldRule -> {
            //新加入数据不做处理
            rule.setShiftId(oldRule.getShiftId());
            rule.setPeriodId(oldRule.getPeriodId());
            //原小时外出没命中已有班次数据，隐藏外出申请
            if (rule.getOutPoint().before(oldRule.getInPoint())
                    || rule.getInPoint().after(oldRule.getOutPoint())
                    || rule.getInPoint().after(oldRule.getInPoint()) && rule.getOutPoint().before(oldRule.getOutPoint())) {
                if (!rule.getIsInsert()) {
                    if (!hisDailyRules.contains(rule)) {
                        hisDailyRules.add(rule);
                    }
                }
                log.error("新增排班数据未命中小时外出申请或者外出数据完全被排班数据覆盖，过滤{}", JSON.toJSONString(rule));
                resultList.add(DailyRuleResultVo.successNailBuild(rule));
                return;
            }
            rule.insertTrue();
            if (!hisDailyRules.contains(rule)) {
                hisDailyRules.add(rule);
            }
            rule.setApplyViewEnable(0);
            oldRule.setApplyViewEnable(rule.getAttendanceType());
            //原小时外出数据覆盖现在新增排班数据上班时间
            if (!rule.getInPoint().after(oldRule.getInPoint())) {
                oldRule.setInStepOutType(1);
                oldRule.setInStepOutApplyId(rule.getApplyId());
            }
            //原小时外出数据覆盖现在新增排班数据下班时间
            if (!rule.getOutPoint().before(oldRule.getOutPoint())) {
                oldRule.setOutStepOutType(1);
                oldRule.setOutStepOutApplyId(rule.getApplyId());
            }
            log.error("新增外出、出差申请标记原普班时段{}", JSON.toJSONString(rule));
            resultList.add(DailyRuleResultVo.successBuild(oldRule, rule));
        });
        return Boolean.TRUE;
    }

    /**
     * 新增清除
     *
     * @param hisDailyRules           历史规则
     * @param ftbAttendanceDailyRules 新增规则集合
     * @param rule                    当前规则
     */
    private Boolean clearHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<FtbAttendanceDailyRule> ftbAttendanceDailyRules, FtbAttendanceDailyRule rule, List<DailyRuleResultVo> resultList) {
        //新增清除
        if (!Objects.equals(AttendanceTypeEnum.CLEAR.getCode(), rule.getAttendanceType())) {
            return Boolean.FALSE;
        }
        hisDailyRules.removeAll(ftbAttendanceDailyRules);
        ftbAttendanceDailyRules.forEach(oldRule -> resultList.add(DailyRuleResultVo.successBuild(oldRule)));
        return Boolean.TRUE;
    }

    /**
     * 新增请假
     *
     * @param hisDailyRules           历史规则
     * @param ftbAttendanceDailyRules 新增规则集合
     * @param rule1                   当前规则
     */
    private Boolean leaveHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<FtbAttendanceDailyRule> ftbAttendanceDailyRules, FtbAttendanceDailyRule rule1, List<DailyRuleResultVo> resultList) {
        //新增请假
        if (!Objects.equals(AttendanceTypeEnum.LEAVE.getCode(), rule1.getAttendanceType())) {
            return Boolean.FALSE;
        }
        boolean isMultipleRules = ftbAttendanceDailyRules.size() > 1;
        ftbAttendanceDailyRules.forEach(oldRule -> {
            daysLeaveHandle(hisDailyRules, oldRule, rule1, resultList, isMultipleRules);
            houseLeaveHandle(hisDailyRules, oldRule, rule1, resultList);
        });
        return Boolean.TRUE;
    }

    /**
     * 小时请假
     *
     * @param hisDailyRules
     * @param ordinaryRule
     * @param rule
     * @param resultList
     */
    private void houseLeaveHandle(List<FtbAttendanceDailyRule> hisDailyRules, FtbAttendanceDailyRule ordinaryRule, FtbAttendanceDailyRule rule, List<DailyRuleResultVo> resultList) {
        if (Objects.nonNull(rule.getApplyUnit()) && !Objects.equals(rule.getApplyUnit(), LeaveUnitEnum.HOUR.getCode())) {
            return;
        }
        //新加入数据不做处理
        if (ordinaryRule.getIsInsert()) {
            return;
        }
        //请假处于排班时段中间a
        if (DateDetail.checkTimeBetween(rule.getApplyStart(), ordinaryRule.getInPoint(), ordinaryRule.getOutPoint())
                && DateDetail.checkTimeBetween(rule.getApplyEnd(), ordinaryRule.getInPoint(), ordinaryRule.getOutPoint())) {
            FtbAttendanceDailyRule oldRule1 = BeanUtil.toBean(ordinaryRule, FtbAttendanceDailyRule.class);
            rule = getFtbAttendanceDailyRule(ordinaryRule, rule);
            ordinaryRule.setEarlyPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getOutPoint(), ordinaryRule.getEarlyPoint(), rule.getApplyStart()));
            ordinaryRule.setOutLackPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getOutPoint(), ordinaryRule.getOutLackPoint(), rule.getApplyStart()));
            ordinaryRule.setOutPoint(rule.getApplyStart());
            ordinaryRule.setOutUnbounded(WorkBoundaryCoverageEnum.LEAVE_COVERED.getCode());
            ordinaryRule.calInLackPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getInLackPoint(), ordinaryRule.getInPoint()));
            ordinaryRule.calLatePoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getLatePoint(), ordinaryRule.getInPoint()));
            getBreakTimeByOrdinary(ordinaryRule, rule);
            oldRule1.setClockStartPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getClockStartPoint(), rule.getApplyEnd()));
            oldRule1.calInLackPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getInLackPoint(), rule.getApplyEnd()));
            oldRule1.calLatePoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getLatePoint(), rule.getApplyEnd()));
            oldRule1.setInPoint(rule.getApplyEnd());
            oldRule1.setInUnbounded(WorkBoundaryCoverageEnum.LEAVE_COVERED.getCode());
            oldRule1.setId(RandomUtil.uuId());
            getBreakTimeByOrdinary(oldRule1, rule);
            houseLeaveDayProcess(rule, ordinaryRule.getOriginValidDuration(), ordinaryRule.getPeriodWorkDay(), ordinaryRule.getPayrollHours());
            oldRule1.insertTrue();
            if (DateUtil.dateDiff(oldRule1.getOutPoint(), oldRule1.getInPoint()) != 0) {
                hisDailyRules.add(oldRule1);
            }
            if (DateUtil.dateDiff(ordinaryRule.getOutPoint(), ordinaryRule.getInPoint()) == 0) {
                hisDailyRules.remove(ordinaryRule);
            }
            if (!hisDailyRules.contains(rule)) {
                hisDailyRules.add(rule);
            }
            log.error("新增请假处于排班时段中间{}", JSON.toJSONString(rule));
            resultList.add(DailyRuleResultVo.successBuild(ordinaryRule, rule));
            return;
        }
        //请假时间完全覆盖原来上班时段
        if (DateDetail.checkTimeBetween(ordinaryRule.getInPoint(), rule.getApplyStart(), rule.getApplyEnd())
                && DateDetail.checkTimeBetween(ordinaryRule.getOutPoint(), rule.getApplyStart(), rule.getApplyEnd())) {
            rule = getFtbAttendanceDailyRule(ordinaryRule, rule);
            hisDailyRules.remove(ordinaryRule);
            rule.setInPoint(ordinaryRule.getInPoint());
            rule.setOutPoint(ordinaryRule.getOutPoint());
            if (!hisDailyRules.contains(rule)) {
                hisDailyRules.add(rule);
            }
            getBreakTimeByOrdinary(ordinaryRule, rule);
            houseLeaveDayProcess(rule, ordinaryRule.getOriginValidDuration(), ordinaryRule.getPeriodWorkDay(), ordinaryRule.getPayrollHours());
            log.error("新增请假时间完全覆盖原来上班时段{}", JSON.toJSONString(rule));
            resultList.add(DailyRuleResultVo.successBuild(ordinaryRule, rule));
            return;
        }
        //部分覆盖
        //开始部分覆盖
        if (DateDetail.checkTimeBetweenR(rule.getApplyEnd(), ordinaryRule.getInPoint(), ordinaryRule.getOutPoint())) {
            rule = getFtbAttendanceDailyRule(ordinaryRule, rule);
            rule.setInPoint(ordinaryRule.getInPoint());
            ordinaryRule.calInLackPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getInLackPoint(), rule.getApplyEnd()));
            ordinaryRule.setClockStartPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getClockStartPoint(), rule.getApplyEnd()));
            ordinaryRule.calLatePoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getLatePoint(), rule.getApplyEnd()));
            ordinaryRule.setInPoint(rule.getApplyEnd());

            ordinaryRule.setInUnbounded(WorkBoundaryCoverageEnum.LEAVE_COVERED.getCode());
            if (!hisDailyRules.contains(rule)) {
                hisDailyRules.add(rule);
                rule.insertTrue();
            }
            getBreakTimeByOrdinary(ordinaryRule, rule);
            houseLeaveDayProcess(rule, ordinaryRule);
            log.error("新增请假时间开始部分覆盖原来上班时段{}", JSON.toJSONString(rule));
            resultList.add(DailyRuleResultVo.successBuild(ordinaryRule, rule));
            return;
        }
        //结束部分覆盖
        if (DateDetail.checkTimeBetweenL(rule.getApplyStart(), ordinaryRule.getInPoint(), ordinaryRule.getOutPoint())) {
            rule = getFtbAttendanceDailyRule(ordinaryRule, rule);
            rule.setOutPoint(ordinaryRule.getOutPoint());
            ordinaryRule.setEarlyPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getOutPoint(), ordinaryRule.getEarlyPoint(), rule.getApplyStart()));
            ordinaryRule.setOutLackPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getOutPoint(), ordinaryRule.getOutLackPoint(), rule.getApplyStart()));
            ordinaryRule.setOutPoint(rule.getApplyStart());
            ordinaryRule.setOutUnbounded(WorkBoundaryCoverageEnum.LEAVE_COVERED.getCode());
            ordinaryRule.calInLackPoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getInLackPoint(), ordinaryRule.getInPoint()));
            ordinaryRule.calLatePoint(DateDetail.getNewDateByPeriod(ordinaryRule.getInPoint(), ordinaryRule.getLatePoint(), ordinaryRule.getInPoint()));
            if (!hisDailyRules.contains(rule)) {
                hisDailyRules.add(rule);
                rule.insertTrue();
            }
            getBreakTimeByOrdinary(ordinaryRule, rule);
            houseLeaveDayProcess(rule, ordinaryRule);
            log.error("新增请假时间结束部分覆盖原来上班时段{}", JSON.toJSONString(rule));
            resultList.add(DailyRuleResultVo.successBuild(ordinaryRule, rule));
            return;
        }

        //完全没交集
        if (!hisDailyRules.contains(rule) && !rule.getIsInsert()) {
            rule.setShiftId(ordinaryRule.getShiftId());
            rule.setPeriodId(ordinaryRule.getPeriodId());
            rule.setApplyViewEnable(2);
            hisDailyRules.add(rule);
            log.error("新增请假与原排班时段中间没交集{}", JSON.toJSONString(rule));
        }
    }

    private FtbAttendanceDailyRule getFtbAttendanceDailyRule(FtbAttendanceDailyRule ordinaryRule, FtbAttendanceDailyRule rule) {
        if (rule.getIsInsert()) {
            rule = BeanUtil.toBean(rule, FtbAttendanceDailyRule.class);
            rule.setId(RandomUtil.uuId());
            rule.setSelfGroup(ordinaryRule.getSelfGroup());
        }
        rule.setApplyViewEnable(1);
        rule.setFixedMark(ordinaryRule.getFixedMark());
        rule.setBreakEnable(ordinaryRule.getBreakEnable());
        rule.setSchedulesType(ordinaryRule.getSchedulesType());
        rule.setShiftId(ordinaryRule.getShiftId());
        rule.setPeriodId(ordinaryRule.getPeriodId());
        rule.setInPoint(rule.getApplyStart());
        rule.setOutPoint(rule.getApplyEnd());
        rule.setOriginInPoint(ordinaryRule.getOriginInPoint());
        rule.setOriginOutPoint(ordinaryRule.getOriginOutPoint());
        rule.insertTrue();
        return rule;
    }

    /**
     * 天、半天请假
     *
     * @param hisDailyRules
     * @param oldRule
     * @param leaveRule
     * @param resultList
     */
    private void daysLeaveHandle(List<FtbAttendanceDailyRule> hisDailyRules, FtbAttendanceDailyRule oldRule, FtbAttendanceDailyRule leaveRule, List<DailyRuleResultVo> resultList, Boolean isMultipleRules) {
        if (Objects.isNull(leaveRule.getApplyUnit()) || Objects.equals(leaveRule.getApplyUnit(), LeaveUnitEnum.HOUR.getCode())) {
            return;
        }
        LeaveParam leaveParam = JsonUtil.getJsonToBean(leaveRule.getLeaveParam(), LeaveParam.class);
        //是否为半天假
        //开始日期
        //是否为全天班
        halfDayLeave(hisDailyRules, oldRule, resultList, leaveRule, leaveParam);
        //全天请假
        dayLeave(hisDailyRules, oldRule, resultList, leaveRule);
        //休息时间处理
        //如果开始时间处于休息时间中间，开始时间变更为休息时间的开始时间
        if (Objects.nonNull(oldRule.getBreakStartPoint()) && DateDetail.checkTimeBetween(leaveRule.getInPoint(), oldRule.getOriginBreakStartPoint(), oldRule.getOriginBreakEndPoint())) {
            leaveRule.setInPoint(oldRule.getOriginBreakStartPoint());
            leaveRule.setBreakStartPoint(oldRule.getOriginBreakStartPoint());
            oldRule.setOutPoint(oldRule.getOriginBreakStartPoint());
            oldRule.setBreakStartPoint(null);
            oldRule.setBreakEndPoint(null);
            log.error("新增请假开始时间调整为休息开始时间{}", JSON.toJSONString(oldRule.getOriginBreakStartPoint()));
        }
        //如果结束时间处理休息时间中间，结束时间变更为休息时间的结束时间
        if (Objects.nonNull(oldRule.getBreakStartPoint()) && DateDetail.checkTimeBetween(leaveRule.getOutPoint(), oldRule.getOriginBreakStartPoint(), oldRule.getOriginBreakEndPoint())) {
            leaveRule.setOutPoint(oldRule.getOriginBreakEndPoint());
            leaveRule.setBreakEndPoint(oldRule.getOriginBreakEndPoint());
            oldRule.setInPoint(oldRule.getOriginBreakEndPoint());
            oldRule.setBreakStartPoint(null);
            oldRule.setBreakEndPoint(null);
            log.error("新增请假结束时间调整为休息结束时间{}", JSON.toJSONString(oldRule.getOriginBreakEndPoint()));
        }
    }

    private void dayLeave(List<FtbAttendanceDailyRule> hisDailyRules, FtbAttendanceDailyRule oldRule, List<DailyRuleResultVo> resultList, FtbAttendanceDailyRule leaveRule) {
        if (!Objects.equals(leaveRule.getApplyUnit(), LeaveUnitEnum.DAY.getCode())) {
            return;
        }
        //全天请假
        leaveRule = getFtbAttendanceDailyRule(oldRule, leaveRule);
        hisDailyRules.remove(oldRule);
        leaveRule.setInPoint(oldRule.getInPoint());
        leaveRule.setOutPoint(oldRule.getOutPoint());
        //如果为全天班，则请假时长为时段工时
        leaveRule.setLeaveDay(!Objects.equals(oldRule.getSchedulesType(), 0) ? oldRule.getPeriodWorkDay() : BigDecimal.ONE);
        leaveRule.setPayrollHours(oldRule.getPayrollHours());
        if (!hisDailyRules.contains(leaveRule)) {
            hisDailyRules.add(leaveRule);
        }
        log.error("新增全天请假时间覆盖原来班时段{}", JSON.toJSONString(leaveRule));
        resultList.add(DailyRuleResultVo.successBuild(oldRule, leaveRule));
    }

    private void halfDayLeave(List<FtbAttendanceDailyRule> hisDailyRules, FtbAttendanceDailyRule oldRule, List<DailyRuleResultVo> resultList, FtbAttendanceDailyRule leaveRule, LeaveParam leaveParam) {
        //是否为半天假
        if (!Objects.equals(leaveRule.getApplyUnit(), LeaveUnitEnum.HALF_DAY.getCode())) {
            return;
        }
        //划线排班，隐藏半天请假
        if (Objects.equals(oldRule.getFixedMark(), 2)) {
            log.error("划线排班，隐藏半天请假{}", JSON.toJSONString(oldRule));
            leaveRule.setApplyViewEnable(0);
            return;
        }
        if (isNotBetweenByHalfDayLeave(oldRule, leaveParam, leaveRule)) {
            //完全没交集
            if (!hisDailyRules.contains(leaveRule)) {
                leaveRule.setApplyViewEnable(2);
                leaveRule.setFixedMark(oldRule.getFixedMark());
                hisDailyRules.add(leaveRule);
            }
            return;
        }
        leaveRule = getFtbAttendanceDailyRule(oldRule, leaveRule);
        if (Objects.equals(oldRule.getSchedulesType(), 0)) {
            Date leaveDate = getLeaveDate(oldRule);
            boolean isMorningHalfDay = isHalfDayLeave(oldRule, leaveParam, 1);
            boolean isAfternoonHalfDay = isHalfDayLeave(oldRule, leaveParam, 2);

            //请假开始是否为上半天请假
            if (isMorningHalfDay && !isAfternoonHalfDay) {
                if (leaveDate.before(oldRule.getInPoint())) {
                    return;
                }
                //请假开始时间为开始日期当天排班开始时间
                leaveRule.setInPoint(oldRule.getOriginInPoint());
                leaveRule.setOutPoint(leaveDate);
                leaveRule.setSchedulesType(oldRule.getSchedulesType());
                leaveRule.setLeaveDay(BigDecimal.valueOf(0.5));
                leaveRule.setSchedulesType(1);
                leaveRule.insertTrue();
                getBreakTimeByOrdinary(oldRule, leaveRule);
                housePayrollHoursProcess(leaveRule, oldRule.getPayrollHours(),oldRule.getOriginValidDuration());
                if (leaveDate.compareTo(oldRule.getOutPoint()) == 0) {
                    log.error("新增下半天请假时间覆盖原来全天班时段{}", JSON.toJSONString(leaveRule));
                    if (!hisDailyRules.contains(leaveRule)) {
                        hisDailyRules.add(leaveRule);
                    }
                    resultList.add(DailyRuleResultVo.successBuild(oldRule, leaveRule));
                    hisDailyRules.remove(oldRule);
                    return;
                }
                oldRule.calInLackPoint(DateDetail.getNewDateByPeriod(oldRule.getInPoint(), oldRule.getInLackPoint(), leaveDate));
                oldRule.calLatePoint(DateDetail.getNewDateByPeriod(oldRule.getInPoint(), oldRule.getLatePoint(), leaveDate));
                oldRule.setInPoint(leaveDate);
                oldRule.setInUnbounded(WorkBoundaryCoverageEnum.LEAVE_COVERED.getCode());
                oldRule.setSchedulesType(2);
                housePayrollHoursProcess(oldRule, oldRule.getPayrollHours(), oldRule.getOriginValidDuration());
                log.error("新增上半天请假时间覆盖原来全天班时段{}", JSON.toJSONString(leaveRule));
                if (!hisDailyRules.contains(leaveRule)) {
                    hisDailyRules.add(leaveRule);
                }
                resultList.add(DailyRuleResultVo.successBuild(oldRule, leaveRule));
            } else if (!isMorningHalfDay && isAfternoonHalfDay) {
                if (leaveDate.after(oldRule.getOutPoint())) {
                    return;
                }
                leaveRule.setInPoint(leaveDate);
                leaveRule.setSchedulesType(oldRule.getSchedulesType());
                leaveRule.setOutPoint(oldRule.getOriginOutPoint());
                leaveRule.setLeaveDay(BigDecimal.valueOf(0.5));
                leaveRule.setSchedulesType(2);
                leaveRule.insertTrue();
                getBreakTimeByOrdinary(oldRule, leaveRule);
                housePayrollHoursProcess(leaveRule, oldRule.getPayrollHours(), oldRule.getOriginValidDuration());
                if (leaveDate.compareTo(oldRule.getInPoint()) == 0) {
                    log.error("新增下半天请假时间覆盖原来全天班时段{}", JSON.toJSONString(leaveRule));
                    if (!hisDailyRules.contains(leaveRule)) {
                        hisDailyRules.add(leaveRule);
                    }
                    resultList.add(DailyRuleResultVo.successBuild(oldRule, leaveRule));
                    hisDailyRules.remove(oldRule);
                    return;
                }
                oldRule.setEarlyPoint(DateDetail.getNewDateByPeriod(oldRule.getOutPoint(), oldRule.getEarlyPoint(), leaveDate));
                oldRule.setOutLackPoint(DateDetail.getNewDateByPeriod(oldRule.getOutPoint(), oldRule.getOutLackPoint(), leaveDate));
                oldRule.setOutPoint(leaveDate);
                oldRule.setOutUnbounded(WorkBoundaryCoverageEnum.LEAVE_COVERED.getCode());
                oldRule.setSchedulesType(1);
                housePayrollHoursProcess(oldRule, oldRule.getPayrollHours(), oldRule.getOriginValidDuration());
                log.error("新增下半天请假时间覆盖原来全天班时段{}", JSON.toJSONString(leaveRule));
                if (!hisDailyRules.contains(leaveRule)) {
                    hisDailyRules.add(leaveRule);
                }
                resultList.add(DailyRuleResultVo.successBuild(oldRule, leaveRule));
            } else {
                leaveRule.setInPoint(oldRule.getInPoint());
                leaveRule.setOutPoint(oldRule.getOutPoint());
                leaveRule.setSchedulesType(oldRule.getSchedulesType());
                getBreakTimeByOrdinary(oldRule, leaveRule);
                houseLeaveDayProcess(leaveRule, oldRule);
                leaveRule.insertTrue();
                hisDailyRules.remove(oldRule);
                if (!hisDailyRules.contains(leaveRule)) {
                    hisDailyRules.add(leaveRule);
                }
                getBreakTimeByOrdinary(oldRule, leaveRule);
                resultList.add(DailyRuleResultVo.successBuild(oldRule, leaveRule));
                log.error("新增请假时间完全覆盖全天班{}", JSON.toJSONString(oldRule));
            }
            return;
        }
        //是否为半天班
        if (!leaveRule.getSchedulesType().equals(oldRule.getSchedulesType())) {
            return;
        }
        //请假开始时间为开始日期当天排班开始时间
        leaveRule.setInPoint(oldRule.getInPoint());
        leaveRule.setOutPoint(oldRule.getOutPoint());
        leaveRule.setSchedulesType(oldRule.getSchedulesType());
        if (!hisDailyRules.contains(leaveRule)) {
            leaveRule.insertTrue();
            hisDailyRules.add(leaveRule);
        }
        getBreakTimeByOrdinary(oldRule, leaveRule);
        leaveRule.setLeaveDay(BigDecimal.valueOf(0.5));
        leaveRule.setPayrollHours(oldRule.getPayrollHours());
        hisDailyRules.remove(oldRule);
        log.error("新增半天请假时间覆盖原来半天班时段{}", JSON.toJSONString(leaveRule));
        resultList.add(DailyRuleResultVo.successBuild(oldRule, leaveRule));

    }

    // 提取辅助方法，减少重复逻辑
    private boolean isHalfDayLeave(FtbAttendanceDailyRule oldRule, LeaveParam leaveParam, int timeType) {
        // 开始日期且时间类型匹配
        boolean isStartDateMatch = Objects.equals(leaveParam.getStartTimeType(), timeType) &&
                oldRule.getDay().compareTo(leaveParam.getStart()) == 0;

        // 中间日期
        boolean isMiddleDate = (Objects.equals(timeType, 2) || oldRule.getDay().after(leaveParam.getStart())) &&
                (Objects.equals(timeType, 1) || oldRule.getDay().before(leaveParam.getEnd()));

        // 结束日期且时间类型匹配
        boolean isEndDateMatch = Objects.equals(leaveParam.getEndTimeType(), timeType) &&
                oldRule.getDay().compareTo(leaveParam.getEnd()) == 0;

        return isStartDateMatch || isMiddleDate || isEndDateMatch;
    }

    /**
     * 半天请假是否班次未命中请假
     *
     * @param oldRule2
     * @param leaveParam
     * @param leaveRule
     * @return
     */
    private static boolean isNotBetweenByHalfDayLeave(FtbAttendanceDailyRule oldRule2, LeaveParam leaveParam, FtbAttendanceDailyRule leaveRule) {
        return !DateDetail.checkTimeBetween(oldRule2.getDay(), leaveParam.getStart(), leaveParam.getEnd())
                || !Objects.equals(oldRule2.getSchedulesType(), 0) && leaveParam.getStartTimeType() > oldRule2.getSchedulesType() && oldRule2.getDay().compareTo(leaveParam.getStart()) == 0
                || !Objects.equals(oldRule2.getSchedulesType(), 0) && leaveParam.getEndTimeType() < oldRule2.getSchedulesType() && oldRule2.getDay().compareTo(leaveParam.getEnd()) == 0;
    }

    /**
     * 计算半天请假全天班中间临界请假时间
     *
     * @param ftbAttendanceDailyRule
     * @return
     */
    private Date getLeaveDate(FtbAttendanceDailyRule ftbAttendanceDailyRule) {
        int ruleMinute = DateDetail.calculateMinuteDiff(ftbAttendanceDailyRule.getOriginInPoint(), ftbAttendanceDailyRule.getOriginOutPoint());
        Date dateTime = cn.hutool.core.date.DateUtil.offsetMinute(ftbAttendanceDailyRule.getOriginInPoint(), ruleMinute / 2);
        return dateTime;
    }

    /**
     * 新增请假的休息时间处理
     *
     * @param leaveRule    原请假规则
     * @param ordinaryRule 新增正常排班规则
     */
    private void getBreakTimeByOrdinary(FtbAttendanceDailyRule ordinaryRule, FtbAttendanceDailyRule leaveRule) {
        if (!Objects.equals(ordinaryRule.getBreakEnable(), 1) || Objects.isNull(ordinaryRule.getBreakStartPoint())) {
            return;
        }
        leaveRule.setBreakStartPoint(leaveRule.getInPoint().after(ordinaryRule.getBreakEndPoint())|| leaveRule.getOutPoint().before(ordinaryRule.getBreakStartPoint()) ? null : leaveRule.getInPoint().after(ordinaryRule.getBreakStartPoint()) ? leaveRule.getInPoint() : ordinaryRule.getBreakStartPoint());
        leaveRule.setBreakEndPoint(leaveRule.getInPoint().after(ordinaryRule.getBreakEndPoint()) || leaveRule.getOutPoint().before(ordinaryRule.getBreakStartPoint()) ? null : leaveRule.getOutPoint().before(ordinaryRule.getBreakEndPoint()) ? leaveRule.getOutPoint() : ordinaryRule.getBreakEndPoint());
        //休息结束时间小于排班上班时间，或者休息开始时间大于排班下班时间
        if (ordinaryRule.getBreakEndPoint().compareTo(ordinaryRule.getInPoint()) <= 0 || ordinaryRule.getBreakStartPoint().compareTo(ordinaryRule.getOutPoint()) >= 0) {
            ordinaryRule.setBreakStartPoint(null);
            ordinaryRule.setBreakEndPoint(null);
            ordinaryRule.setBreakEnable(0);
            return;
        }
        //请假时间完全覆盖原来休息时段
        if (DateDetail.checkTimeBetween(ordinaryRule.getBreakStartPoint(), leaveRule.getInPoint(), leaveRule.getOutPoint())
                && DateDetail.checkTimeBetween(ordinaryRule.getBreakEndPoint(), leaveRule.getInPoint(), leaveRule.getOutPoint())) {
            ordinaryRule.setBreakStartPoint(null);
            ordinaryRule.setBreakEndPoint(null);
            ordinaryRule.setBreakEnable(0);
            return;
        }
        //休息时间完全覆盖请假时间
        if (DateDetail.checkTimeBetween(leaveRule.getInPoint(), ordinaryRule.getBreakStartPoint(), ordinaryRule.getBreakEndPoint())
                && DateDetail.checkTimeBetween(leaveRule.getOutPoint(), ordinaryRule.getBreakStartPoint(), ordinaryRule.getBreakEndPoint())) {
            if (leaveRule.getInPoint().compareTo(ordinaryRule.getBreakStartPoint()) >= 0) {
                ordinaryRule.setBreakEndPoint(leaveRule.getInPoint());
                leaveRule.setBreakStartPoint(leaveRule.getInPoint());
            }
            if (leaveRule.getOutPoint().compareTo(ordinaryRule.getBreakEndPoint()) <= 0) {
                leaveRule.setBreakEndPoint(leaveRule.getOutPoint());
                ordinaryRule.setBreakEndPoint(leaveRule.getOutPoint());
            }
            return;
        }
        //部分覆盖
        //开始部分覆盖
        if (DateDetail.checkTimeBetween(leaveRule.getOutPoint(), ordinaryRule.getBreakStartPoint(), ordinaryRule.getBreakEndPoint())) {
            leaveRule.setBreakEndPoint(leaveRule.getOutPoint());
            ordinaryRule.setBreakStartPoint(leaveRule.getOutPoint());
            return;
        }
        //结束部分覆盖
        if (DateDetail.checkTimeBetween(leaveRule.getInPoint(), ordinaryRule.getBreakStartPoint(), ordinaryRule.getBreakEndPoint())) {
            ordinaryRule.setBreakEndPoint(leaveRule.getInPoint());
            leaveRule.setBreakStartPoint(leaveRule.getInPoint());
        }
    }

    private void houseLeaveDayProcess(FtbAttendanceDailyRule leaveRule, FtbAttendanceDailyRule ordinaryRule) {
        houseLeaveDayProcess(leaveRule, ordinaryRule.getOriginValidDuration(), ordinaryRule.getPeriodWorkDay(), ordinaryRule.getPayrollHours());
    }

    private void houseLeaveDayProcess(FtbAttendanceDailyRule leaveRule, Integer validDuration, BigDecimal periodWorkDay, BigDecimal payrollHours) {
        leaveRule.calValidDuration();
        leaveRule.setLeaveDay(Objects.isNull(periodWorkDay) ? null : BigDecimal.valueOf(leaveRule.getValidDuration())
                .divide(BigDecimal.valueOf(validDuration), 4, RoundingMode.HALF_UP)
                .multiply(periodWorkDay).setScale(2, RoundingMode.HALF_UP));
        housePayrollHoursProcess(leaveRule, payrollHours, validDuration);
    }

    private void housePayrollHoursProcess(FtbAttendanceDailyRule leaveRule, BigDecimal payrollHours, Integer validDuration) {
        leaveRule.calValidDuration();
        leaveRule.setPayrollHours(Objects.isNull(payrollHours) ? null : BigDecimal.valueOf(leaveRule.getValidDuration())
                .divide(BigDecimal.valueOf(validDuration), 4, RoundingMode.HALF_UP)
                .multiply(payrollHours).setScale(2, RoundingMode.HALF_UP));
    }

    /**
     * 新增上班
     *
     * @param hisDailyRules           历史规则
     * @param ftbAttendanceDailyRules 新增规则集合
     * @param rule                    当前规则
     */
    private Boolean ordinaryHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<FtbAttendanceDailyRule> ftbAttendanceDailyRules, FtbAttendanceDailyRule rule, List<DailyRuleResultVo> resultList) {
        //新增上班
        if (!Objects.equals(AttendanceTypeEnum.ORDINARY.getCode(), rule.getAttendanceType()) || rule.getIsInsert()) {
            return Boolean.FALSE;
        }
        if (hisDailyRules.containsAll(ftbAttendanceDailyRules)) {
            if (Objects.equals(rule.getFixedMark(), 1)) {
                ftbAttendanceDailyRules = ftbAttendanceDailyRules.stream().filter(rule1 -> !Objects.equals(rule1.getFixedMark(), 3)).collect(Collectors.toList());
            }
            hisDailyRules.removeAll(ftbAttendanceDailyRules);
        }
        if (CollUtil.isNotEmpty(ftbAttendanceDailyRules)) {
            if (!hisDailyRules.contains(rule)) {
                if(ftbAttendanceDailyRules.stream().anyMatch(vo->Objects.equals(vo.getApplyViewEnable(),3))){
                    rule.setApplyViewEnable(3);
                }
                hisDailyRules.add(rule);
            }
        }
        ftbAttendanceDailyRules.forEach(oldRule -> {
            resultList.add(DailyRuleResultVo.successBuild(oldRule, rule));
        });
        return Boolean.TRUE;
    }

    /**
     * 新增排休
     *
     * @param hisDailyRules           历史规则
     * @param ftbAttendanceDailyRules 新增规则集合
     * @param rule                    当前规则
     */
    private Boolean restHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<FtbAttendanceDailyRule> ftbAttendanceDailyRules, FtbAttendanceDailyRule rule, List<DailyRuleResultVo> resultList) {
        //新增排休
        if (!Objects.equals(AttendanceTypeEnum.REST.getCode(), rule.getAttendanceType())
                && !Objects.equals(AttendanceTypeEnum.HOLIDAYS.getCode(), rule.getAttendanceType())
                && !Objects.equals(AttendanceTypeEnum.DEDUCTION_HOLIDAYS.getCode(), rule.getAttendanceType())
                && !Objects.equals(AttendanceTypeEnum.EXCHANGE_HOLIDAYS.getCode(), rule.getAttendanceType())) {
            return Boolean.FALSE;
        }
        if (Objects.equals(rule.getFixedMark(), 1)) {
            ftbAttendanceDailyRules = ftbAttendanceDailyRules.stream().filter(rule1 -> !Objects.equals(rule1.getFixedMark(), 3)).collect(Collectors.toList());
        }
        ftbAttendanceDailyRules.forEach(oldRule -> {
            //新加入数据不做处理
            if (oldRule.getIsInsert()) {
                return;
            }
            hisDailyRules.remove(oldRule);
        });
        if (CollUtil.isNotEmpty(ftbAttendanceDailyRules)) {
            rule.insertTrue();
            if (!hisDailyRules.contains(rule)) {
                hisDailyRules.add(rule);
            }
        }
        log.error("新增排休覆盖原来上班时段{},目标结果为{}", JSON.toJSONString(rule), JSON.toJSONString(hisDailyRules));
        ftbAttendanceDailyRules.forEach(oldRule -> {
            resultList.add(DailyRuleResultVo.successRestBuild(oldRule, rule, rule.getPeriodWorkDay()));
        });
        return Boolean.TRUE;
    }

    /**
     * 新增加班
     *
     * @param hisDailyRules           历史规则
     * @param ftbAttendanceDailyRules 新增规则集合
     * @param rule                    当前规则
     */
    private Boolean workOvertimeHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<FtbAttendanceDailyRule> ftbAttendanceDailyRules, FtbAttendanceDailyRule rule, List<DailyRuleResultVo> resultList) {
        //新增加班
        if (!Objects.equals(AttendanceTypeEnum.WORKOVERTIME.getCode(), rule.getAttendanceType())) {
            return Boolean.FALSE;
        }
        ftbAttendanceDailyRules.forEach(oldRule -> {
            //新加入数据不做处理
            if (oldRule.getIsInsert()) {
                return;
            }
            rule.setSelfGroup(oldRule.getSelfGroup());
            rule.setFixedMark(oldRule.getFixedMark());
            //完全覆盖
            if (DateDetail.checkTimeBetween(rule.getApplyStart(), oldRule.getInPoint(), oldRule.getOutPoint())
                    && DateDetail.checkTimeBetween(rule.getApplyEnd(), oldRule.getInPoint(), oldRule.getOutPoint())) {
                log.error(AttendanceConstant.getApplyResult(rule.getUserId(), AttendanceConstant.ORDINARY));
                resultList.add(DailyRuleResultVo.failBuild(oldRule, rule, AttendanceTypeEnum.ORDINARY.getCode(), AttendanceTypeEnum.WORKOVERTIME.getCode()));
                return;
            }
            //部分覆盖
            //开始部分覆盖
            if (DateDetail.checkTimeBetweenR(rule.getApplyEnd(), oldRule.getInPoint(), oldRule.getOutPoint())) {
                log.error(AttendanceConstant.getApplyResult(rule.getUserId(), AttendanceConstant.ORDINARY));
                resultList.add(DailyRuleResultVo.failBuild(oldRule, rule, AttendanceTypeEnum.ORDINARY.getCode(), AttendanceTypeEnum.WORKOVERTIME.getCode()));
                return;
            }
            //结束部分覆盖
            if (DateDetail.checkTimeBetweenL(rule.getApplyStart(), oldRule.getInPoint(), oldRule.getOutPoint())) {
                log.error(AttendanceConstant.getApplyResult(rule.getUserId(), AttendanceConstant.ORDINARY));
                resultList.add(DailyRuleResultVo.failBuild(oldRule, rule, AttendanceTypeEnum.ORDINARY.getCode(), AttendanceTypeEnum.WORKOVERTIME.getCode()));
                return;
            }
            //加班时间完全覆盖原来上班时段
            if (DateDetail.checkBetween(oldRule.getInPoint(), rule.getApplyStart(), rule.getApplyEnd())
                    && DateDetail.checkBetween(oldRule.getOutPoint(), rule.getApplyStart(), rule.getApplyEnd())) {
                log.error(AttendanceConstant.getApplyResult(rule.getUserId(), AttendanceConstant.ORDINARY));
                resultList.add(DailyRuleResultVo.failBuild(oldRule, rule, AttendanceTypeEnum.ORDINARY.getCode(), AttendanceTypeEnum.WORKOVERTIME.getCode()));
                return;
            }
            //加班与普班时间边界连接
            //加班结束时间与上班时段开始时间相同
            if (oldRule.getInPoint().compareTo(rule.getApplyEnd()) == 0) {
                oldRule.setInUnbounded(WorkBoundaryCoverageEnum.OVERTIME_COVERED.getCode());
                rule.setOutUnbounded(WorkBoundaryCoverageEnum.OVERTIME_COVERED.getCode());
            }
            //加班开始时间与上班时段结束时间相同
            if (oldRule.getOutPoint().compareTo(rule.getApplyStart()) == 0) {
                oldRule.setOutUnbounded(WorkBoundaryCoverageEnum.OVERTIME_COVERED.getCode());
                rule.setInUnbounded(WorkBoundaryCoverageEnum.OVERTIME_COVERED.getCode());
            }
            rule.insertTrue();
            //完全没交集
            if (!hisDailyRules.contains(rule)) {
                hisDailyRules.add(rule);
            }
            log.error("新增加班与原排班时段中间没交集{}", JSON.toJSONString(rule));
            resultList.add(DailyRuleResultVo.successNailBuild(rule));
        });
        return Boolean.TRUE;
    }
}
