package cn.iocoder.yudao.module.system.service.holiday.holidayuserrecord;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.system.api.holiday.dto.CreateUserHolidayDTO;
import cn.iocoder.yudao.module.system.api.holiday.dto.OvertimeIncreasesHolidayDTO;
import cn.iocoder.yudao.module.system.controller.admin.holiday.holidaysetting.dto.RecalculateAssignedDTO;
import cn.iocoder.yudao.module.system.controller.admin.holiday.holidaysetting.vo.DetermineHolidayBalanceSettingSwitchVO;
import cn.iocoder.yudao.module.system.controller.admin.holiday.holidayuserrecord.vo.HolidayUserRecordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.holiday.holidayuserrecord.vo.HolidayUserRecordSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.holiday.holidaybalancesetting.HolidayBalanceSettingDO;
import cn.iocoder.yudao.module.system.dal.dataobject.holiday.holidaysetting.HolidaySettingDO;
import cn.iocoder.yudao.module.system.dal.dataobject.holiday.holidaysettingrange.HolidaySettingRangeDO;
import cn.iocoder.yudao.module.system.dal.dataobject.holiday.holidayuser.HolidayUserDO;
import cn.iocoder.yudao.module.system.dal.dataobject.holiday.holidayuserrecord.HolidayUserRecordDO;
import cn.iocoder.yudao.module.system.dal.dataobject.holiday.holidayworkingage.HolidayWorkingAgeDO;
import cn.iocoder.yudao.module.system.dal.dataobject.holidayuseremploymentrecord.HolidayUserEmploymentRecordDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.holiday.holidayuser.HolidayUserMapper;
import cn.iocoder.yudao.module.system.dal.mysql.holiday.holidayuserrecord.HolidayUserRecordMapper;
import cn.iocoder.yudao.module.system.dal.mysql.holidayuseremploymentrecord.HolidayUserEmploymentRecordMapper;
import cn.iocoder.yudao.module.system.service.holiday.holidaysetting.HolidaySettingService;
import cn.iocoder.yudao.module.system.service.holiday.holidaysettingrange.HolidaySettingRangeService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.Period;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

/**
 * 员工假期记录 Service 实现类
 *
 * @author 艾楷
 */
@Service
@Validated
@Slf4j
public class HolidayUserRecordServiceImpl implements HolidayUserRecordService {

    @Resource
    private HolidayUserRecordMapper holidayUserRecordMapper;
    @Resource
    private HolidayUserMapper holidayUserMapper;

    @Resource
    private AdminUserService adminUserService;
    @Resource
    private HolidaySettingRangeService holidaySettingRangeService;
    @Resource
    @Lazy
    private HolidaySettingService holidaySettingService;
    @Resource
    @Lazy
    private HolidayUserEmploymentRecordMapper holidayUserEmploymentRecordMapper;

    @Override
    public void createHolidayUserRecord(HolidayUserRecordSaveReqVO createReqVO) {
        // 获取当前登录用户信息
        AdminUserDO myself = adminUserService.getUser(getLoginUserId());
        // -- 计算获取每个人的假期额度
        Long userId = createReqVO.getUserId();
        AdminUserDO targetUser = adminUserService.getUser(userId);
        LocalDateTime now = LocalDateTime.now();
        Map<Long, BigDecimal> userQuotaMap = new HashMap<>();
        List<Long> userIds = Collections.singletonList(userId);
        HolidaySettingDO holidaySetting = holidaySettingService.getHolidaySetting(createReqVO.getHolidaySettingId());
        HolidayBalanceSettingDO holidayBalanceSettingDO = holidaySetting.getHolidayBalanceSettingDO();
        userQuotaMap.put(userId, createReqVO.getHolidayBalance());
        // -- 获取用户的假期表
        Map<Long, HolidayUserDO> holidayUserDOMap = this.getHolidayUserMap(userIds, holidaySetting, holidayBalanceSettingDO);
        List<HolidayUserRecordDO> newHolidayUserRecordDOList = new ArrayList<>();
        List<HolidayUserDO> newHolidayUserDOList = new ArrayList<>();
        String remark = myself.getNickname() + " 为" + targetUser.getNickname() + (createReqVO.getDirection() == 0 ? "增加" : "减去");
        this.calculateUserHolidays(userQuotaMap, userId, holidayUserDOMap, holidaySetting, holidayBalanceSettingDO, newHolidayUserRecordDOList,
                newHolidayUserDOList, now, remark, createReqVO.getDirection(), createReqVO.getReason(), false);

        if (CollUtil.isNotEmpty(newHolidayUserRecordDOList)) {
            holidayUserRecordMapper.insertBatch(newHolidayUserRecordDOList);
        }
        if (CollUtil.isNotEmpty(newHolidayUserDOList)) {
            holidayUserMapper.insertOrUpdateBatch(newHolidayUserDOList);
        }
        if (createReqVO.getDirection() == 1) {
            //减去用户记录中的剩余余额
            List<HolidayUserRecordDO> editList = this.subHolidayUserRecord(userId, createReqVO.getHolidaySettingId(), createReqVO.getHolidayBalance());
            if (CollUtil.isNotEmpty(editList)) {
                holidayUserRecordMapper.updateBatch(editList);
            }
        }
    }


    private List<HolidayUserRecordDO> addHolidayUserRecord(Long userId, Long holidaySettingId, BigDecimal addAmount) {
        List<HolidayUserRecordDO> editList = new ArrayList<>();
        // -- 扣除用户记录中的余额 - （获取没有过期的余额列表 - 并且余额 > 0 - 按照过期时间升序 从最快过期的开始扣）
        List<HolidayUserRecordDO> holidayUserRecordDOS = holidayUserRecordMapper.selectList(new LambdaQueryWrapper<HolidayUserRecordDO>()
                .eq(HolidayUserRecordDO::getUserId, userId)
                .eq(HolidayUserRecordDO::getHolidaySettingId, holidaySettingId)
                .orderByDesc(HolidayUserRecordDO::getExpiredTime)
        );
        // 过滤出不想等的 余额
        holidayUserRecordDOS = holidayUserRecordDOS.stream().filter(record -> record.getRemainingBalance().compareTo(record.getHolidayBalance()) != 0).collect(Collectors.toList());
        for (HolidayUserRecordDO record : holidayUserRecordDOS) {
            BigDecimal remaining = record.getRemainingBalance();
            BigDecimal holidayBalance = record.getHolidayBalance();
            // 计算可以加到的金额
            BigDecimal newBalance = remaining.add(addAmount);
            if (newBalance.compareTo(holidayBalance) > 0) {
                // 如果加上addAmount后超过holidayBalance
                addAmount = newBalance.subtract(holidayBalance); // 计算需要继续加的部分
                record.setRemainingBalance(holidayBalance); // 设置余额为holidayBalance
                editList.add(record);
            } else {
                // 如果没有超过，直接加上
                record.setRemainingBalance(newBalance);
                editList.add(record);
                break; // 结束循环
            }
        }
        return editList;
    }


    private List<HolidayUserRecordDO> subHolidayUserRecord(Long userId, Long holidaySettingId, BigDecimal subAmount) {
        List<HolidayUserRecordDO> editList = new ArrayList<>();
        // -- 扣除用户记录中的余额 - （获取没有过期的余额列表 - 并且余额 > 0 - 按照过期时间升序 从最快过期的开始扣）
        List<HolidayUserRecordDO> holidayUserRecordDOS = holidayUserRecordMapper.selectList(new LambdaQueryWrapper<HolidayUserRecordDO>()
                .eq(HolidayUserRecordDO::getUserId, userId)
                .eq(HolidayUserRecordDO::getHolidaySettingId, holidaySettingId)
                .gt(HolidayUserRecordDO::getRemainingBalance, BigDecimal.ZERO)
                .orderByAsc(HolidayUserRecordDO::getExpiredTime)
        );

        for (HolidayUserRecordDO record : holidayUserRecordDOS) {
            BigDecimal remaining = record.getRemainingBalance();
            if (remaining.compareTo(subAmount) >= 0) {
                remaining = remaining.subtract(subAmount);
                record.setRemainingBalance(remaining);
                editList.add(record);
                break; // 余额足够，结束循环
            } else {
                subAmount = subAmount.subtract(remaining);
                record.setRemainingBalance(BigDecimal.ZERO);
                editList.add(record);
            }
        }
        return editList;
    }

    @Override
    public void createUserHoliday(CreateUserHolidayDTO dto) {
        // -- 计算获取每个人的假期额度
        AdminUserDO targetUser = adminUserService.getUser(dto.getUserId());
        LocalDateTime now = LocalDateTime.now();
        Map<Long, BigDecimal> userQuotaMap = new HashMap<>();
        List<Long> userIds = Collections.singletonList(dto.getUserId());
        HolidaySettingDO holidaySetting = holidaySettingService.getHolidaySetting(dto.getHolidaySettingId());
        HolidayBalanceSettingDO holidayBalanceSettingDO = holidaySetting.getHolidayBalanceSettingDO();
        userQuotaMap.put(dto.getUserId(), dto.getHolidayBalance());
        // -- 获取用户的假期表
        Map<Long, HolidayUserDO> holidayUserDOMap = this.getHolidayUserMap(userIds, holidaySetting, holidayBalanceSettingDO);
        List<HolidayUserRecordDO> newHolidayUserRecordDOList = new ArrayList<>();
        List<HolidayUserDO> newHolidayUserDOList = new ArrayList<>();
        String remark = targetUser.getNickname() + "使用了 ";
        this.calculateUserHolidays(userQuotaMap, dto.getUserId(), holidayUserDOMap, holidaySetting, holidayBalanceSettingDO,
                newHolidayUserRecordDOList, newHolidayUserDOList, now, remark, dto.getDirection(), dto.getReason(), false);

        List<HolidayUserRecordDO> editList = new ArrayList<>();
        if (dto.getDirection() == 1) {
            //减去用户记录中的剩余余额
            List<HolidayUserRecordDO> holidayUserRecordDOS = this.subHolidayUserRecord(dto.getUserId(), dto.getHolidaySettingId(), dto.getHolidayBalance());
            editList.addAll(holidayUserRecordDOS);
        } else {
            //加上用户记录中的剩余余额
            List<HolidayUserRecordDO> holidayUserRecordDOS = this.addHolidayUserRecord(dto.getUserId(), dto.getHolidaySettingId(), dto.getHolidayBalance());
            editList.addAll(holidayUserRecordDOS);
        }
        if (CollUtil.isNotEmpty(editList)) {
            holidayUserRecordMapper.updateBatch(editList);
        }
        if (CollUtil.isNotEmpty(newHolidayUserRecordDOList)) {
            holidayUserRecordMapper.insertBatch(newHolidayUserRecordDOList);
        }
        if (CollUtil.isNotEmpty(newHolidayUserDOList)) {
            holidayUserMapper.insertOrUpdateBatch(newHolidayUserDOList);
        }
    }

    @Override
    public void beOverdue(List<HolidayUserRecordDO> holidayUserRecords) {
        if (CollectionUtil.isEmpty(holidayUserRecords)) {
            return;
        }
        List<Long> holidayUserIds = holidayUserRecords.stream().map(HolidayUserRecordDO::getHolidayUserId).distinct().collect(Collectors.toList());
        List<HolidayUserDO> holidayUserDOS = holidayUserMapper.selectBatchIds(holidayUserIds);
        Map<Long, HolidayUserDO> holidayUserDOMap = holidayUserDOS.stream().collect(Collectors.toMap(HolidayUserDO::getId, Function.identity()));
        // -- 获取用户的假期表
        Map<Long, List<HolidayUserRecordDO>> collect = holidayUserRecords.stream().collect(Collectors.groupingBy(HolidayUserRecordDO::getHolidayUserId));
        List<HolidayUserDO> editList = new ArrayList<>();
        for (Map.Entry<Long, List<HolidayUserRecordDO>> entry : collect.entrySet()) {
            List<HolidayUserRecordDO> holidayUserRecordDOS = entry.getValue();
            BigDecimal reduce = holidayUserRecordDOS.stream().map(HolidayUserRecordDO::getRemainingBalance).reduce(BigDecimal.ZERO, BigDecimal::add);
            HolidayUserDO holidayUserDO = holidayUserDOMap.get(entry.getKey());
            BigDecimal holidayBalance = holidayUserDO.getHolidayBalance().add(reduce.negate());
            holidayBalance = holidayBalance.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : holidayBalance;
            holidayUserDO.setHolidayBalance(holidayBalance);
            editList.add(holidayUserDO);
        }
        // --
        holidayUserRecords.forEach(holidayUserRecordDO -> holidayUserRecordDO.setExpiredDeductionFlag(1));
        holidayUserRecordMapper.updateBatch(holidayUserRecords);
        if (CollUtil.isNotEmpty(editList)) {
            holidayUserMapper.updateBatch(editList);
        }
    }


    @Override
    public List<HolidayUserRecordDO> getHolidayUserRecord(Long userId, Long holidaySettingId) {
        return holidayUserRecordMapper.selectList(new LambdaQueryWrapper<HolidayUserRecordDO>()
                .eq(HolidayUserRecordDO::getUserId, userId)
                .eq(HolidayUserRecordDO::getHolidaySettingId, holidaySettingId)
                .orderByDesc(HolidayUserRecordDO::getCreateTime));
    }

    @Override
    public PageResult<HolidayUserRecordDO> getHolidayUserRecordPage(HolidayUserRecordPageReqVO pageReqVO) {
        return holidayUserRecordMapper.selectPage(pageReqVO);
    }

    @Override
    public void init(HolidaySettingDO holidaySetting, List<HolidaySettingRangeDO> holidaySettingRangeDOS, HolidayBalanceSettingDO holidayBalanceSettingDO, List<HolidayWorkingAgeDO> holidayWorkingAgeDOS) {
        // -- 先查询出所有用户
        List<AdminUserDO> users = this.getUsersByRange(holidaySetting, holidaySettingRangeDOS);
        // -- 计算获取每个人的假期额度
        Map<Long, Map<LocalDateTime, BigDecimal>> userQuotaMap = this.getQuotaAllMap(users, holidayBalanceSettingDO, holidayWorkingAgeDOS);
        // -- 获取用户的假期表
        List<Long> userIds = users.stream().map(AdminUserDO::getId).collect(Collectors.toList());
        Map<Long, HolidayUserDO> holidayUserDOMap = this.getHolidayUserMap(userIds, holidaySetting, holidayBalanceSettingDO);
        List<HolidayUserRecordDO> newHolidayUserRecordDOList = new ArrayList<>();
        List<HolidayUserDO> newHolidayUserDOList = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        for (Long userId : userIds) {
            String remark = "系统按照规则自动 增加";
            this.calculateUserAllHolidays(userQuotaMap, userId, holidayUserDOMap, holidaySetting, holidayBalanceSettingDO, newHolidayUserRecordDOList, newHolidayUserDOList, now, remark, 0, null);
        }
        // TODO: 2024/10/23 这里可能还会慢
        if (CollUtil.isNotEmpty(newHolidayUserRecordDOList)) {
            holidayUserRecordMapper.insertBatch(newHolidayUserRecordDOList);
        }
        if (CollUtil.isNotEmpty(newHolidayUserDOList)) {
            holidayUserMapper.insertOrUpdateBatch(newHolidayUserDOList);
        }

        // -- 如果是每年按照员工入职时间计算 - 则这里还需要记录在 kq_holiday_user_employment_record 表中
        if (holidayBalanceSettingDO.getType() == 2 && holidayBalanceSettingDO.getIssueTimeType() == 1 && CollUtil.isNotEmpty(newHolidayUserDOList)) {
            List<HolidayUserEmploymentRecordDO> newHolidayUserEmploymentRecordDOList = new ArrayList<>();
            String year = String.valueOf(now.getYear());
            newHolidayUserDOList.forEach(
                    a -> {
                        if (BigDecimal.ZERO.compareTo(a.getHolidayBalance()) > 0) {
                            newHolidayUserEmploymentRecordDOList.add(new HolidayUserEmploymentRecordDO().setUserId(a.getUserId())
                                    .setHolidaySettingId(holidaySetting.getId())
                                    .setYearNum(year));
                        }
                    }
            );
            if (CollUtil.isNotEmpty(newHolidayUserEmploymentRecordDOList)) {
                holidayUserEmploymentRecordMapper.insertBatch(newHolidayUserEmploymentRecordDOList);
            }
        }
    }


    @Override
    public void grant(HolidaySettingDO holidaySetting, List<HolidaySettingRangeDO> holidaySettingRangeDOS,
                      HolidayBalanceSettingDO holidayBalanceSettingDO, List<HolidayWorkingAgeDO> holidayWorkingAgeDOS, Boolean recordZero) {
        // -- 先查询出所有用户
        List<AdminUserDO> users = this.getUsersByRange(holidaySetting, holidaySettingRangeDOS);
        // -- 计算获取每个人的假期额度
        Map<Long, BigDecimal> userQuotaMap = this.getQuotaMap(users, holidayBalanceSettingDO, holidayWorkingAgeDOS);
        log.info("计算获取每个人的假期额度 userQuotaMap:{}", userQuotaMap);
        // -- 获取用户的假期表
        List<Long> userIds = users.stream().map(AdminUserDO::getId).collect(Collectors.toList());
        Map<Long, AdminUserDO> userMap = users.stream().collect(Collectors.toMap(AdminUserDO::getId, a -> a));
        Map<Long, HolidayUserDO> holidayUserDOMap = this.getHolidayUserMap(userIds, holidaySetting, holidayBalanceSettingDO);
        List<HolidayUserRecordDO> newHolidayUserRecordDOList = new ArrayList<>();
        List<HolidayUserDO> newHolidayUserDOList = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        // -- 如果是按照每年员工入职时间计算的话 -
        if (holidayBalanceSettingDO.getType() == 2 && holidayBalanceSettingDO.getIssueTimeType() == 1
                && CollUtil.isNotEmpty(userIds)) {
            String year = String.valueOf(now.getYear());
            List<Long> effectiveUserIds = userIds;
            // 判断 员工是否发放过年假了 - 下次发放时间设置为明年1月1号 - 如果 用户入职时间 小于 当前时间
            List<HolidayUserEmploymentRecordDO> records = holidayUserEmploymentRecordMapper.selectList(new LambdaQueryWrapper<HolidayUserEmploymentRecordDO>()
                    .in(HolidayUserEmploymentRecordDO::getUserId, userIds)
                    .eq(HolidayUserEmploymentRecordDO::getHolidaySettingId, holidaySetting.getId())
                    .eq(HolidayUserEmploymentRecordDO::getYearNum, year));
            List<Long> issuedUserIds = records.stream().map(HolidayUserEmploymentRecordDO::getUserId).collect(Collectors.toList());
            effectiveUserIds.removeAll(issuedUserIds);
            if (CollUtil.isEmpty(effectiveUserIds)) {
                return;
            }
            List<Long> notOnTimeUserIds = new ArrayList<>();
            for (Long effectiveUserId : effectiveUserIds) {
                if (userMap.get(effectiveUserId).getEntryDate() != null && userMap.get(effectiveUserId).getEntryDate().withYear(now.getYear()).isAfter(now)) {
                    notOnTimeUserIds.add(effectiveUserId);
                }
            }
            effectiveUserIds.removeAll(notOnTimeUserIds);
            if (CollUtil.isEmpty(effectiveUserIds)) {
                return;
            }

            userIds = effectiveUserIds;
        }
        for (Long userId : userIds) {
            String remark = "系统按照规则自动 增加";
            this.calculateUserHolidays(userQuotaMap, userId, holidayUserDOMap, holidaySetting, holidayBalanceSettingDO,
                    newHolidayUserRecordDOList, newHolidayUserDOList, now, remark, 0, null, recordZero);
        }
        // TODO: 2024/10/23 这里可能还会慢
        if (CollUtil.isNotEmpty(newHolidayUserRecordDOList)) {
            holidayUserRecordMapper.insertBatch(newHolidayUserRecordDOList);
        }
        if (CollUtil.isNotEmpty(newHolidayUserDOList)) {
            holidayUserMapper.insertOrUpdateBatch(newHolidayUserDOList);
        }
        // -- 如果是每年按照员工入职时间计算 - 则这里还需要记录在 kq_holiday_user_employment_record 表中
        if (holidayBalanceSettingDO.getType() == 2 && holidayBalanceSettingDO.getIssueTimeType() == 1 && CollUtil.isNotEmpty(newHolidayUserDOList)) {
            List<HolidayUserEmploymentRecordDO> newHolidayUserEmploymentRecordDOList = new ArrayList<>();
            String year = String.valueOf(now.getYear());
            newHolidayUserDOList.forEach(
                    a -> {
                        newHolidayUserEmploymentRecordDOList.add(new HolidayUserEmploymentRecordDO().setUserId(a.getUserId())
                                .setHolidaySettingId(holidaySetting.getId())
                                .setYearNum(year));
                    }
            );
            if (CollUtil.isNotEmpty(newHolidayUserEmploymentRecordDOList)) {
                holidayUserEmploymentRecordMapper.insertBatch(newHolidayUserEmploymentRecordDOList);
            }
        }
    }

    private List<AdminUserDO> getUsersByRange(HolidaySettingDO
                                                      holidaySetting, List<HolidaySettingRangeDO> holidaySettingRangeDOS) {
        List<AdminUserDO> users;
        if (holidaySetting.getApplicationScope() == 0) {
            users = adminUserService.getAllList(0, 1, null);
        } else {
            users = holidaySettingRangeService.getUsersByRange(holidaySettingRangeDOS);
        }
        return users;
    }

    private void calculateUserAllHolidays(Map<Long, Map<LocalDateTime, BigDecimal>> userQuotaMap, Long userId, Map<Long, HolidayUserDO> holidayUserDOMap,
                                          HolidaySettingDO holidaySetting, HolidayBalanceSettingDO holidayBalanceSettingDO,
                                          List<HolidayUserRecordDO> newHolidayUserRecordDOList, List<HolidayUserDO> newHolidayUserDOList,
                                          LocalDateTime now, String remark, Integer direction, String reason) {
        Map<LocalDateTime, BigDecimal> stringBigDecimalMap = userQuotaMap.get(userId);
        if (MapUtil.isNotEmpty(stringBigDecimalMap)) {
            HolidayUserDO holidayUserDO = holidayUserDOMap.get(userId);
            List<HolidayUserRecordDO> items = new ArrayList<>();
            for (Map.Entry<LocalDateTime, BigDecimal> localDateTimeBigDecimalEntry : stringBigDecimalMap.entrySet()) {
                HolidayUserRecordDO holidayUserRecordDO = new HolidayUserRecordDO();
                holidayUserRecordDO.setUserId(userId);
                holidayUserRecordDO.setHolidayUserId(holidayUserDO.getId());
                holidayUserRecordDO.setHolidaySettingId(holidaySetting.getId());
                holidayUserRecordDO.setHolidayBalanceSettingId(holidayBalanceSettingDO.getId());
                holidayUserRecordDO.setDirection(direction);
                holidayUserRecordDO.setHolidayBalance(localDateTimeBigDecimalEntry.getValue());
                Map<Long, LocalDateTime> longLocalDateTimeMap = this.validityPeriodCalculation(localDateTimeBigDecimalEntry.getKey(), userId, holidayBalanceSettingDO);
                LocalDateTime expiredTime = longLocalDateTimeMap.get(userId);
                holidayUserRecordDO.setReleaseTime(localDateTimeBigDecimalEntry.getKey());
                holidayUserRecordDO.setExpiredTime(expiredTime);
                LocalDateTime expirationReminderTime = expiredTime == null ? null : expiredTime.minusDays(holidayBalanceSettingDO.getReminderTimeNum());
                holidayUserRecordDO.setExpirationReminderTime(expirationReminderTime);
                holidayUserRecordDO.setExpirationReminderFlag(holidayBalanceSettingDO.getExpirationReminderFlag() == 0 ? 1 : 0);
                holidayUserRecordDO.setRemainingBalance(localDateTimeBigDecimalEntry.getValue());
                holidayUserRecordDO.setRemark(remark + localDateTimeBigDecimalEntry.getValue() + (holidaySetting.getMinUnit() == 3 ? "小时" : "天") + holidaySetting.getName());
                holidayUserRecordDO.setReason(reason);
                // -- 如果是0的话就不记录了
                if (BigDecimal.ZERO.compareTo(holidayUserRecordDO.getHolidayBalance()) == 0
                        || (expiredTime != null && expiredTime.isBefore(now))) {
                    continue;
                }
                newHolidayUserRecordDOList.add(holidayUserRecordDO);
                items.add(holidayUserRecordDO);
            }
            BigDecimal holidayBalance = holidayUserDO.getHolidayBalance().add(items.stream().filter(a -> a.getExpiredTime() == null || a.getExpiredTime().isAfter(now))
                    .map(product -> {
                        if (product.getDirection() == 0) {
                            return product.getHolidayBalance();
                        } else {
                            return product.getHolidayBalance().negate();
                        }
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
            // --- 员工假期
            newHolidayUserDOList.add(
                    holidayUserDO.setHolidayBalance(
                            holidayBalance.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : holidayBalance
                    )
            );
        }
    }


    private void calculateUserHolidays(Map<Long, BigDecimal> userQuotaMap, Long userId, Map<Long, HolidayUserDO> holidayUserDOMap,
                                       HolidaySettingDO holidaySetting, HolidayBalanceSettingDO holidayBalanceSettingDO,
                                       List<HolidayUserRecordDO> newHolidayUserRecordDOList, List<HolidayUserDO> newHolidayUserDOList,
                                       LocalDateTime now, String remark, Integer direction, String reason, Boolean recordZero) {
        BigDecimal quota = userQuotaMap.get(userId);
        // -- 如果是0的话就不记录了 -- 如果不记录0 并且是0 直接return
        log.info("假期额度分配数量quota为:{},recordZero为:{}", quota, recordZero);
        if (quota == null || (BigDecimal.ZERO.compareTo(quota) == 0 && !recordZero)) {
            return;
        }
        HolidayUserDO holidayUserDO = holidayUserDOMap.get(userId);
        HolidayUserRecordDO holidayUserRecordDO = new HolidayUserRecordDO();
        holidayUserRecordDO.setUserId(userId);
        holidayUserRecordDO.setHolidayUserId(holidayUserDO.getId());
        holidayUserRecordDO.setHolidaySettingId(holidaySetting.getId());
        holidayUserRecordDO.setHolidayBalanceSettingId(holidayBalanceSettingDO.getId());
        holidayUserRecordDO.setDirection(direction);
        holidayUserRecordDO.setHolidayBalance(quota);
        Map<Long, LocalDateTime> longLocalDateTimeMap = this.validityPeriodCalculation(now, userId, holidayBalanceSettingDO);
        LocalDateTime expiredTime = longLocalDateTimeMap.get(userId);
        if (direction == 0) {
            holidayUserRecordDO.setReleaseTime(now);
            holidayUserRecordDO.setExpiredTime(expiredTime);
            LocalDateTime expirationReminderTime = expiredTime == null ? null : expiredTime.minusDays(holidayBalanceSettingDO.getReminderTimeNum());
            holidayUserRecordDO.setExpirationReminderTime(expirationReminderTime);
            holidayUserRecordDO.setExpirationReminderFlag(holidayBalanceSettingDO.getExpirationReminderFlag() == 0 ? 1 : (expirationReminderTime == null ? 1 : 0));
            holidayUserRecordDO.setRemainingBalance(quota);
        } else {
            // -- 如果是扣除or提醒的话 这里直接是1 避免计算过期的时候再把他查出来
            holidayUserRecordDO.setExpirationReminderFlag(1);
            holidayUserRecordDO.setExpiredDeductionFlag(1);
        }
        //quota保留两位小数
        holidayUserRecordDO.setRemark(remark + quota.setScale(2, RoundingMode.HALF_UP) + (holidaySetting.getMinUnit() == 3 ? "小时" : "天") + holidaySetting.getName());
        holidayUserRecordDO.setReason(reason);
        newHolidayUserRecordDOList.add(holidayUserRecordDO);
        BigDecimal holidayBalance = holidayUserDO.getHolidayBalance().add(direction == 0 ? quota : quota.negate());
        // --- 员工假期
        newHolidayUserDOList.add(
                holidayUserDO.setHolidayBalance(
                        holidayBalance.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : holidayBalance
                )
        );
    }

    @Override
    public void clear(Long holidaySettingId, List<HolidaySettingRangeDO> holidaySettingRangeDOS) {
        List<Long> userIds = new ArrayList<>();
        if (CollUtil.isNotEmpty(holidaySettingRangeDOS)) {
            userIds = holidaySettingRangeService.getUsersByRange(holidaySettingRangeDOS).stream().map(AdminUserDO::getId).collect(Collectors.toList());
        }
        if (CollUtil.isEmpty(userIds)) {
            return;
        }
        // 清空请假数
        holidayUserMapper.delete(new LambdaQueryWrapper<HolidayUserDO>()
                .eq(HolidayUserDO::getHolidaySettingId, holidaySettingId)
                .in(CollUtil.isNotEmpty(userIds), HolidayUserDO::getUserId, userIds));
        // 清空记录 -
        holidayUserRecordMapper.delete(new LambdaQueryWrapper<HolidayUserRecordDO>()
                .in(CollUtil.isNotEmpty(userIds), HolidayUserRecordDO::getUserId, userIds)
                .eq(HolidayUserRecordDO::getHolidaySettingId, holidaySettingId));
        // 清空记录 -
        holidayUserEmploymentRecordMapper.delete(new LambdaQueryWrapper<HolidayUserEmploymentRecordDO>()
                .eq(HolidayUserEmploymentRecordDO::getHolidaySettingId, holidaySettingId)
                .in(CollUtil.isNotEmpty(userIds), HolidayUserEmploymentRecordDO::getUserId, userIds));
    }

    @Override
    public void recalculateAssigned(RecalculateAssignedDTO dto) {
        // 如果有变动 -
        DetermineHolidayBalanceSettingSwitchVO settingSwitch = dto.getDetermineHolidayBalanceSettingSwitchVO();
        if (settingSwitch.isChangeFlag()) {
            if (settingSwitch.isSwitchFlag()) {
                // 如果是开启状态 说明老的没开启 - 新的开启了 那么计算现有的人员即可 -
                this.init(dto.getHolidaySetting(), dto.getPersonnelChangesVO().getNewAllList(), dto.getHolidayBalanceSetting(), dto.getHolidayWorkingAges());
            } else {
                // 如果关闭状态 - 则把所有老的都关闭 -
                this.clear(dto.getHolidaySetting().getId(), dto.getPersonnelChangesVO().getOldAllList());
            }
        } else {
            // -- 首先清空去除掉的人员
            this.clear(dto.getHolidaySetting().getId(), dto.getPersonnelChangesVO().getDelList());
            // -- 再处理现有的人员 - 这里可能就不是直接清空 - 而是记录 （并且之前的请假记录要保留 - 不能应为改动了发放数据 而导致用户多出假期 - ）
            // -- 3.假期发放方式调整 历史余额将会被清空 按新的方式重新发送
            // -- 4.发放日期调整  修改发放日期后假期余额将按新规则重新计算
            // -- 5.更改额度配置规则 修改余额发放方式后，假期余额将按新规则重新发放。
            // -- 发放天数调整 - 不清空 - 只重新计算
            if (settingSwitch.isAdjustTypeFlag() || settingSwitch.isDateAdjustFlag() || settingSwitch.isQuotaRuleFlag() || settingSwitch.isQuotaFlag() || CollUtil.isNotEmpty(dto.getPersonnelChangesVO().getSaveList())) {
                this.clear(dto.getHolidaySetting().getId(), dto.getPersonnelChangesVO().getNewAllList());
                this.init(dto.getHolidaySetting(), dto.getPersonnelChangesVO().getNewAllList(), dto.getHolidayBalanceSetting(), dto.getHolidayWorkingAges());
            }
        }
    }

    @Override
    public Map<Long, LocalDateTime> validityPeriodCalculation(LocalDateTime
                                                                      now, List<Long> usersIds, HolidayBalanceSettingDO holidayBalanceSettingDO) {
        Map<Long, LocalDateTime> map = new HashMap<>();
        for (Long userId : usersIds) {
            Map<Long, LocalDateTime> longLocalDateTimeMap = this.validityPeriodCalculation(now, userId, holidayBalanceSettingDO);
            map.putAll(longLocalDateTimeMap);
        }
        return map;
    }

    @Override
    public Map<Long, LocalDateTime> validityPeriodCalculation(LocalDateTime now, Long userId, HolidayBalanceSettingDO holidayBalanceSettingDO) {
        LocalDateTime localDateTime = null;
        Map<Long, LocalDateTime> map = new HashMap<>();
        switch (holidayBalanceSettingDO.getValidityPeriod()) {
            case 1:
                localDateTime = now.plusMonths(1);
                break;
            case 2:
                localDateTime = now.plusYears(1);
                break;
            case 3:
                // 判断用户入职是否是闰年
                boolean leap = now.getMonth() == Month.FEBRUARY
                        && now.getDayOfMonth() == 29
                        && Year.isLeap(now.getYear());
                int dayOfMonth = now.getDayOfMonth();
                // 如果是闰年的29号入职 并且当明年不是闰年 则日期 -1
                if (leap && !Year.isLeap(now.plusYears(1).getYear())) {
                    dayOfMonth = dayOfMonth - 1;
                }
                localDateTime = now.plusYears(1).withMonth(now.getMonthValue()).withDayOfMonth(dayOfMonth);
                break;
            case 4:
                localDateTime = now.with(TemporalAdjusters.lastDayOfYear());
                break;
            case 5:
                if (StringUtils.isNotEmpty(holidayBalanceSettingDO.getFixedEveryYearInvalid())) {
                    try {
                        // 将当前年份设置到解析的日期上
                        localDateTime = LocalDateTime.parse(holidayBalanceSettingDO.getFixedEveryYearInvalid(), DateTimeFormatter.ofPattern("MM-dd")).withYear(now.getYear());
                    } catch (DateTimeParseException e) {
                        // -- 这里不做任何处理 - 如果日期格式错了那么就默认为null;
                        localDateTime = null;
                    }
                }
                break;
            case 6:
                localDateTime = null;
                break;
            case 7:
                localDateTime = now.plusDays(holidayBalanceSettingDO.getFixedEveryMonthInvalid());
                break;
            case 8:
                localDateTime = LocalDateTimeUtils.getQuarterEnd(now);
                break;
            case 9:
                localDateTime = now.plusDays(holidayBalanceSettingDO.getAfterFewDays());
                break;
            default:
        }
        if (holidayBalanceSettingDO.getExtensionAllowedFlag() != null &&
                holidayBalanceSettingDO.getExtensionAllowedFlag() == 1 && localDateTime != null) {
            localDateTime = localDateTime.plusDays(holidayBalanceSettingDO.getExtendNum());
        }
        map.put(userId, localDateTime);
        return map;
    }


    private Map<Long, HolidayUserDO> getHolidayUserMap(List<Long> userIds, HolidaySettingDO
            holidaySetting, HolidayBalanceSettingDO holidayBalanceSettingDO) {
        // -- 先通过用户ids 获取列表
        List<HolidayUserDO> holidayUserDOS = holidayUserMapper.selectList(new LambdaQueryWrapper<HolidayUserDO>()
                .in(CollUtil.isNotEmpty(userIds), HolidayUserDO::getUserId, userIds)
                .eq(HolidayUserDO::getHolidaySettingId, holidaySetting.getId())
                .eq(HolidayUserDO::getHolidayBalanceSettingId, holidayBalanceSettingDO.getId()));
        Map<Long, HolidayUserDO> holidayUserDOMap = holidayUserDOS.stream().collect(Collectors.toMap(HolidayUserDO::getUserId, holidayUserDO -> holidayUserDO));
        for (Long userId : userIds) {
            if (!holidayUserDOMap.containsKey(userId)) {
                HolidayUserDO holidayUserDO = new HolidayUserDO(IdWorker.getId(), userId, holidaySetting.getId(), holidayBalanceSettingDO.getId());
                holidayUserDOMap.put(userId, holidayUserDO);
            }
        }
        return holidayUserDOMap;
    }

    /**
     * 获取到用户假期额度 - 包含历史
     *
     * @param users
     * @param holidayBalanceSettingDO
     * @param holidayWorkingAgeDOS
     * @return
     */
    private Map<Long, Map<LocalDateTime, BigDecimal>> getQuotaAllMap
    (List<AdminUserDO> users, HolidayBalanceSettingDO
            holidayBalanceSettingDO, List<HolidayWorkingAgeDO> holidayWorkingAgeDOS) {
        Map<Long, Map<LocalDateTime, BigDecimal>> quotaMap = new HashMap<>();
        LocalDateTime now = LocalDateTime.now();
        // -- 自动发放只有按照每月固定的
        if (holidayBalanceSettingDO.getType() == 1) {
            for (AdminUserDO user : users) {
                if (user.getEntryDate() == null) {
                    user.setEntryDate(user.getCreateTime());
//                    continue;
                }
                List<LocalDateTime> theDayOfEachMonthSoFar = LocalDateTimeUtils.getTheDayOfEachMonthSoFar(user.getEntryDate(), now, holidayBalanceSettingDO.getIssueTimeType());
                Map<LocalDateTime, BigDecimal> map = theDayOfEachMonthSoFar.stream()
                        .collect(Collectors.toMap(
                                localDateTime -> localDateTime,
                                localDateTime -> new BigDecimal(holidayBalanceSettingDO.getQuota().toString())
                        ));
                quotaMap.put(user.getId(), map);
            }
        } else if (holidayBalanceSettingDO.getType() == 2) {
            for (AdminUserDO user : users) {
                if (user.getEntryDate() == null) {
                    user.setEntryDate(user.getCreateTime());
//                    continue;
                }
                List<LocalDateTime> theDayOfEachMonthSoFar;
                if (holidayBalanceSettingDO.getIssueTimeType() == 1) {
                    theDayOfEachMonthSoFar = LocalDateTimeUtils.getTheCurrentDateOfEachYearToACertainTime(user.getEntryDate(), now);
                } else {
                    theDayOfEachMonthSoFar = LocalDateTimeUtils.getTheDayOfEachYeasSoFar(user.getEntryDate(), now);
                }
                if (holidayBalanceSettingDO.getQuotaRule() == 1) {
                    Map<LocalDateTime, BigDecimal> map = theDayOfEachMonthSoFar.stream()
                            .collect(Collectors.toMap(
                                    localDateTime -> localDateTime,
                                    localDateTime -> new BigDecimal(holidayBalanceSettingDO.getQuota().toString())
                            ));
                    quotaMap.put(user.getId(), map);
                } else {
                    Map<Integer, Integer> holidayWorkingAgeMap = holidayWorkingAgeDOS.stream().collect(Collectors.toMap(HolidayWorkingAgeDO::getYears, HolidayWorkingAgeDO::getNum));
                    Map<LocalDateTime, BigDecimal> map = new HashMap<>();
                    for (LocalDateTime localDateTime : theDayOfEachMonthSoFar) {
                        int years = Period.between(user.getEntryDate().toLocalDate(), localDateTime.toLocalDate()).getYears();
                        Integer num = holidayWorkingAgeMap.get(years);
                        map.put(localDateTime, new BigDecimal(num.toString()));
                    }
                    quotaMap.put(user.getId(), map);
                }
            }
        } else if (holidayBalanceSettingDO.getType() == 3) {
            for (AdminUserDO user : users) {
                quotaMap.put(user.getId(), Collections.singletonMap(now, new BigDecimal(holidayBalanceSettingDO.getQuota().toString())));
            }
        } else {
            // --- 如果都不是都话 默认为0
            quotaMap = users.stream()
                    .collect(Collectors.toMap(
                            AdminUserDO::getId,
                            user -> Collections.singletonMap(now, BigDecimal.ZERO),
                            (oldValue, newValue) -> oldValue, // 保留第一个值
                            HashMap::new
                    ));
        }
        return quotaMap;
    }

    /**
     * 获取到用户假期额度 - (本次)
     *
     * @param users
     * @param holidayBalanceSettingDO
     * @param holidayWorkingAgeDOS
     * @return
     */
    @Override
    public Map<Long, BigDecimal> getQuotaMap(List<AdminUserDO> users, HolidayBalanceSettingDO
            holidayBalanceSettingDO, List<HolidayWorkingAgeDO> holidayWorkingAgeDOS) {
        Map<Long, BigDecimal> quotaMap = new HashMap<>();
        LocalDateTime now = LocalDateTime.now();
        // -- 自动发放只有按照每月固定的
        if (holidayBalanceSettingDO.getType() == 1) {
            for (AdminUserDO user : users) {
                if (user.getEntryDate() == null) {
                    user.setEntryDate(user.getCreateTime());
//                    continue;
                }
                quotaMap.put(user.getId(), new BigDecimal(holidayBalanceSettingDO.getQuota().toString()));
            }
        } else if (holidayBalanceSettingDO.getType() == 2) {
            for (AdminUserDO user : users) {
                if (user.getEntryDate() == null) {
                    user.setEntryDate(user.getCreateTime());
//                    continue;
                }
                if (holidayBalanceSettingDO.getQuotaRule() == 1) {
                    // TODO: 2024/11/6 // -- actualWorkFlag 没有判断这个 - 暂时去掉
                    quotaMap.put(user.getId(), new BigDecimal(holidayBalanceSettingDO.getQuota().toString()));
                } else {
                    Map<Integer, Integer> holidayWorkingAgeMap = holidayWorkingAgeDOS.stream().collect(Collectors.toMap(HolidayWorkingAgeDO::getYears, HolidayWorkingAgeDO::getNum));
                    int years = Period.between(user.getEntryDate().toLocalDate(), now.toLocalDate()).getYears();
                    Integer num = holidayWorkingAgeMap.get(years);
                    quotaMap.put(user.getId(), new BigDecimal(num.toString()));
                }
            }
        } else {
            // --- 如果都不是都话 默认为0
            quotaMap = users.stream()
                    .collect(Collectors.toMap(
                            AdminUserDO::getId,
                            user -> BigDecimal.ZERO
                    ));
        }
        return quotaMap;
    }

    @Override
    public List<HolidayUserRecordDO> getHolidayBeOverdue(LocalDateTime now) {
        return holidayUserRecordMapper.selectList(
                new LambdaQueryWrapper<HolidayUserRecordDO>()
                        .gt(HolidayUserRecordDO::getRemainingBalance, 0)
                        .lt(HolidayUserRecordDO::getExpiredTime, now)
                        .eq(HolidayUserRecordDO::getExpiredDeductionFlag, 0)
        );
    }

    @Override
    public List<HolidayUserRecordDO> getHolidayRemind(LocalDateTime now) {
        return holidayUserRecordMapper.selectList(
                new LambdaQueryWrapper<HolidayUserRecordDO>()
                        .gt(HolidayUserRecordDO::getRemainingBalance, 0)
                        .isNotNull(HolidayUserRecordDO::getExpirationReminderTime)
                        .lt(HolidayUserRecordDO::getExpirationReminderTime, now)
                        .eq(HolidayUserRecordDO::getExpirationReminderFlag, 0)
        );
    }

    @Override
    public void editReminder(List<HolidayUserRecordDO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        list.forEach(a -> a.setExpirationReminderFlag(1));
        holidayUserRecordMapper.updateBatch(list);
    }

    @Override
    public void overtimeIncreasesHoliday(OvertimeIncreasesHolidayDTO dto) {
        Long userId = dto.getUserId();
        List<HolidayUserDO> holidayUserDOS = holidayUserMapper.selectList(new LambdaQueryWrapper<HolidayUserDO>().eq(HolidayUserDO::getUserId, userId)
                .eq(HolidayUserDO::getHolidaySettingId, dto.getHolidaySettingId()));
        // 过滤掉不适用的
        if (CollectionUtils.isEmpty(holidayUserDOS)) {
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        Map<Long, BigDecimal> userQuotaMap = new HashMap<>();
        List<Long> userIds = Collections.singletonList(userId);
        HolidaySettingDO holidaySetting = holidaySettingService.getHolidaySetting(dto.getHolidaySettingId());

        Integer minUnit = holidaySetting.getMinUnit();
        // -- 加班时间单位转换假期时间单位 - ？
        BigDecimal holidayBalance = this.overtimeUnitToHolidayUnitConversion(dto, minUnit);

        HolidayBalanceSettingDO holidayBalanceSettingDO = holidaySetting.getHolidayBalanceSettingDO();
        userQuotaMap.put(userId, holidayBalance);
        // -- 获取用户的假期表
        Map<Long, HolidayUserDO> holidayUserDOMap = this.getHolidayUserMap(userIds, holidaySetting, holidayBalanceSettingDO);
        List<HolidayUserRecordDO> newHolidayUserRecordDOList = new ArrayList<>();
        List<HolidayUserDO> newHolidayUserDOList = new ArrayList<>();
        String remark = "加班" + (dto.getDirection() == 0 ? "增加" : "减去");
        this.calculateUserHolidays(userQuotaMap, userId, holidayUserDOMap, holidaySetting, holidayBalanceSettingDO,
                newHolidayUserRecordDOList, newHolidayUserDOList, now, remark, dto.getDirection(), dto.getReason(), false);
        if (CollUtil.isNotEmpty(newHolidayUserRecordDOList)) {
            holidayUserRecordMapper.insertBatch(newHolidayUserRecordDOList);
        }
        if (CollUtil.isNotEmpty(newHolidayUserDOList)) {
            holidayUserMapper.insertOrUpdateBatch(newHolidayUserDOList);
        }
    }

    /**
     * 加班单位转换为 假期单位 计算时长
     *
     * @param dto
     * @param minUnit 请假最小单位 1按天 2按半天 3按小时
     * @return
     */
    private BigDecimal overtimeUnitToHolidayUnitConversion(OvertimeIncreasesHolidayDTO dto, Integer minUnit) {
        BigDecimal holidayBalance = dto.getHolidayBalance();
        if (dto.getUnit() == 1) {
            if (minUnit == 1 || minUnit == 2) {
                // 转换为天 -
                holidayBalance = dto.getHolidayBalance().divide(dto.getConversion(), 2, RoundingMode.HALF_UP);
            }
        } else {
            // -- 转换为小时
            if (minUnit == 3) {
                holidayBalance = dto.getHolidayBalance().multiply(dto.getConversion());
            }
        }
        return holidayBalance;
    }
}
