package jnpf.attendance.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jnpf.attendance.mapper.AttendanceLeaveGrantSettingMapper;
import jnpf.attendance.mapper.AttendanceLeaveRulesMapper;
import jnpf.attendance.mapper.AttendanceUserBalanceMapper;
import jnpf.attendance.service.AttendanceLeaveRulesService;
import jnpf.attendance.service.AttendanceUserBalanceRecordService;
import jnpf.attendance.service.RuleScopeService;
import jnpf.base.ActionResult;
import jnpf.base.UserInfo;
import jnpf.entity.attendance.*;
import jnpf.enums.attendance.LeaveUnitEnum;
import jnpf.enums.attendance.ScopeBizType;
import jnpf.exception.ApproveException;
import jnpf.model.attendance.dto.AttendanceLeaveRulesDto;
import jnpf.model.attendance.dto.AttendanceLeaveRulesQueryDto;
import jnpf.model.attendance.vo.attendance.*;
import jnpf.permission.V2UserApi;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.util.ConstantUtil;
import jnpf.util.FtbUtil;
import jnpf.util.StringUtil;
import jnpf.util.UserProvider;
import jnpf.util.attendance.ExpiresTimeUtil;
import jnpf.util.attendance.RuleScopeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author panpan
 */
@Service
@Slf4j
public class AttendanceLeaveRulesServiceImpl implements AttendanceLeaveRulesService {

    @Resource
    private AttendanceLeaveRulesMapper attendanceLeaveRulesMapper;

    @Resource
    private AttendanceLeaveGrantSettingMapper attendanceLeaveGrantSettingMapper;

    @Resource
    @Lazy
    private RuleScopeUtil ruleScopeUtil;

    @Resource
    private UserProvider userProvider;

    @Resource
    @Lazy
    private RuleScopeService ruleScopeService;

    @Resource
    @Lazy
    private AttendanceUserBalanceRecordService attendanceUserBalanceRecordService;

    @Resource
    @Lazy
    private V2UserApi v2UserApi;

    @Resource
    @Lazy
    private ExpiresTimeUtil expiresTimeUtil;

    @Resource
    @Lazy
    private AttendanceUserBalanceMapper attendanceUserBalanceMapper;


    @Override
    public PageInfo<AttendanceLeaveRulesVo> list(AttendanceLeaveRulesQueryDto attendanceLeaveRulesQueryDto) {
        PageHelper.startPage(attendanceLeaveRulesQueryDto.getCurrentPage(), attendanceLeaveRulesQueryDto.getPageSize());
        PageInfo<AttendanceLeaveRulesVo> leaveRulesVoPageInfo = new PageInfo<>(attendanceLeaveRulesMapper.list(attendanceLeaveRulesQueryDto.getLeaveTypeId(), attendanceLeaveRulesQueryDto.getIText()));
        // 当适配范围为指定组织或成员时查询适配范围
        if (null != leaveRulesVoPageInfo.getList() && !leaveRulesVoPageInfo.getList().isEmpty()) {
            // 过滤出适配范围为指定组织或成员的规则id集合
            List<String> scopeOfAdaptationIds = leaveRulesVoPageInfo.getList().stream().filter(v -> Objects.equals(v.getScopeOfAdaptation(), 1)).map(AttendanceLeaveRulesVo::getId).collect(Collectors.toList());
            if (!scopeOfAdaptationIds.isEmpty()) {
                // 拼接适配范围
                Map<String, MutablePair<List<String>, List<String>>> stringMutablePairMap = ruleScopeUtil.selectScopeListBatch(scopeOfAdaptationIds, ScopeBizType.LEAVE_RULES);
                // 遍历vo列表，根据规则id设置适配范围
                for (AttendanceLeaveRulesVo v : leaveRulesVoPageInfo.getList()) {
                    MutablePair<List<String>, List<String>> pair = stringMutablePairMap.get(v.getId());
                    if (null != pair) {
                        v.setOrganizeNum(pair.getLeft().size());
                        v.setUserIdNum(pair.getRight().size());
                    }
                }
            }
            leaveRulesVoPageInfo.getList().stream().filter(v -> Objects.equals(v.getScopeOfAdaptation(), -1)).forEach(vo -> vo.setScopeOfAdaptation(null));
        }
        return leaveRulesVoPageInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(AttendanceLeaveRulesDto attendanceLeaveRulesDto) throws Exception {
        // 通过传入的类型修改同类型下的假期范围 新增的范围为全部清空其他
        if (0 == attendanceLeaveRulesDto.getScopeOfAdaptation()) {
            attendanceLeaveRulesMapper.updateByLeaveTypeId(attendanceLeaveRulesDto.getLeaveTypeId());
        }
        String userId = userProvider.get().getUserId();
        Date date = new Date();
        AttendanceLeaveRules entity = BeanUtil.toBean(attendanceLeaveRulesDto, AttendanceLeaveRules.class);
        entity.setCreatorUserId(userId);
        entity.setCreatorTime(date);
        entity.setLastModifyUserId(userId);
        entity.setLastModifyTime(date);
        entity.setType(0);
        attendanceLeaveRulesMapper.insert(entity);
        if (null != attendanceLeaveRulesDto.getLeaveGrantSetting()) {
            // 保存余额发放规则详情
            AttendanceLeaveGrantSetting bean = BeanUtil.toBean(attendanceLeaveRulesDto.getLeaveGrantSetting(), AttendanceLeaveGrantSetting.class);
            bean.setLeaveRulesId(entity.getId());
            bean.setLeaveTypeId(entity.getLeaveTypeId());
            attendanceLeaveGrantSettingMapper.insert(bean);
        }
        // 保存适配范围
        List<AttendanceRuleScope> ruleScopeList = ruleScopeUtil.getRuleScopeList(entity.getId(), entity.getScopeOfAdaptation(), MutablePair.of(attendanceLeaveRulesDto.getOrganizeList(), attendanceLeaveRulesDto.getUserIdList()), ScopeBizType.LEAVE_RULES, entity.getLeaveTypeId());
        ruleScopeUtil.saveBatch(ruleScopeList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(AttendanceLeaveRulesDto attendanceLeaveRulesDto) throws Exception {
        AttendanceLeaveRules oldEntity = attendanceLeaveRulesMapper.selectById(attendanceLeaveRulesDto.getId());
        // 通过传入的类型修改同类型下的假期范围 新增的范围为全部清空其他
        if (0 == attendanceLeaveRulesDto.getScopeOfAdaptation()) {
            attendanceLeaveRulesMapper.updateByLeaveTypeId(attendanceLeaveRulesDto.getLeaveTypeId());
        }
        UserInfo userInfo = userProvider.get();
        String userId = userInfo.getUserId();
        Date date = new Date();
        AttendanceLeaveRules entity = BeanUtil.toBean(attendanceLeaveRulesDto, AttendanceLeaveRules.class);
        entity.setLastModifyUserId(userId);
        entity.setLastModifyTime(date);
        entity.setDeleteMark(0);
        attendanceLeaveRulesMapper.updateById(entity);
        // 删除旧余额发放规则详情（为更新做准备）
        AttendanceLeaveGrantSetting attendanceLeaveGrantSetting = new AttendanceLeaveGrantSetting();
        attendanceLeaveGrantSetting.setDeleteMark(1);
        attendanceLeaveGrantSettingMapper.update(attendanceLeaveGrantSetting, new UpdateWrapper<AttendanceLeaveGrantSetting>().eq("F_LeaveRulesId", entity.getId()));
        if (null != attendanceLeaveRulesDto.getLeaveGrantSetting()) {
            // 保存余额发放规则详情
            AttendanceLeaveGrantSetting bean = BeanUtil.toBean(attendanceLeaveRulesDto.getLeaveGrantSetting(), AttendanceLeaveGrantSetting.class);
            bean.setLeaveRulesId(entity.getId());
            bean.setLeaveTypeId(oldEntity.getLeaveTypeId());
            attendanceLeaveGrantSettingMapper.insert(bean);
        }
        ruleScopeUtil.updateRuleScopeList(entity.getId(), oldEntity.getScopeOfAdaptation(), entity.getScopeOfAdaptation(), MutablePair.of(attendanceLeaveRulesDto.getOrganizeList(), attendanceLeaveRulesDto.getUserIdList()), ScopeBizType.LEAVE_RULES, oldEntity.getLeaveTypeId());
    }

    private void clearRecord(String ruleId, List<String> userIds, String ruleName, List<AttendanceUserBalanceDetailVo> list, String userId, boolean isClear) {
        // 获取今天的日期 格式：yyyy-mm-dd
        String today = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        List<AttendanceBalanceRecordEntity> records = attendanceUserBalanceRecordService.list(new QueryWrapper<AttendanceBalanceRecordEntity>()
                .lambda()
                .eq(AttendanceBalanceRecordEntity::getObjectId, ruleId)
                .in(AttendanceBalanceRecordEntity::getUserId, userIds)
                .eq(AttendanceBalanceRecordEntity::getType, 2)
                .eq(AttendanceBalanceRecordEntity::getDeleteMark, 0)
                .eq(AttendanceBalanceRecordEntity::getState, 0)
                .eq(AttendanceBalanceRecordEntity::getIsOver, 0)
        );
        // 先根据userId再根据objectId过滤出objectId假类型数据并分组计算余额
        Map<String, List<AttendanceBalanceRecordEntity>> userBalanceMap = records.stream().collect(Collectors.groupingBy(AttendanceBalanceRecordEntity::getUserId));
        userBalanceMap.forEach((uId, value) -> {
            // 计算余额
            BigDecimal total = value.stream().map(AttendanceBalanceRecordEntity::getBalance).reduce(BigDecimal.ZERO, BigDecimal::add);
            AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                    .id(FtbUtil.getId())
                    .userId(uId)
                    .objectId(ruleId)
                    .content(getContent(ruleName, today, total, isClear))
                    .quota(total)
                    .build();
            list.add(detailVo);
            value.forEach(record -> {
                record.setIsOver(1);
                record.setState(2);
                record.setLastModifyUserId(userId);
                record.setLastModifyTime(new Date());
            });
        });
        attendanceUserBalanceRecordService.saveBatch(records);
    }

    @NotNull
    private static String getContent(String ruleName, String today, BigDecimal total, boolean isClear) {
        if (isClear) {
            // 系统已将yyyy-mm-dd发放方式更改为“不发放”后剩余余额的X.X天/小时清除
            return "系统已将" + today + "发放方式更改为“不发放”后剩余余额的" + total.setScale(1, RoundingMode.HALF_UP) + "天清除";
        } else {
            return "系统已将" + today + "未使用的余额" + total.setScale(1, RoundingMode.HALF_UP) + "天，因" + ruleName + "适配范围中被移除，停用余额";
        }
    }

    private List<String> getUserIds(Integer scopeOfAdaptation, List<String> userIds, String tenantId, MutablePair<List<String>, List<String>> pair) {
        if (scopeOfAdaptation.equals(0)) {
            userIds = v2UserApi.getAllUserInfoBatch(null, tenantId).getData().stream().map(UserBoundVO::getId).collect(Collectors.toList());
        } else {
            if (null != pair) {
                if (null != pair.getRight() && !pair.getRight().isEmpty()) {
                    userIds.addAll(pair.getRight());
                }
                if (null != pair.getLeft() && !pair.getLeft().isEmpty()) {
                    ActionResult<List<UserBoundVO>> result = v2UserApi.listTargetOrganizesOrHaveChild(pair.getLeft(), false, null, tenantId);
                    userIds.addAll(result.getData().stream().map(UserBoundVO::getId).collect(Collectors.toList()));
                }
            }
        }
        return userIds;
    }

    @Override
    public AttendanceLeaveRulesVo detail(String id) {
        AttendanceLeaveRulesVo bean = BeanUtil.toBean(attendanceLeaveRulesMapper.selectById(id), AttendanceLeaveRulesVo.class);
        // 余额发放规则详情
        AttendanceLeaveGrantSetting attendanceLeaveGrantSetting = attendanceLeaveGrantSettingMapper.selectOne(new LambdaQueryWrapper<AttendanceLeaveGrantSetting>()
                .eq(AttendanceLeaveGrantSetting::getLeaveRulesId, id)
                .eq(AttendanceLeaveGrantSetting::getDeleteMark, 0));
        if (null != attendanceLeaveGrantSetting) {
            bean.setLeaveGrantSetting(BeanUtil.toBean(attendanceLeaveGrantSetting, AttendanceLeaveGrantSettingVo.class));
        }
        if (Objects.equals(bean.getScopeOfAdaptation(), -1)) {
            bean.setScopeOfAdaptation(null);
        }
        // 当适配范围为指定组织或成员时查询适配范围
        if (Objects.equals(bean.getScopeOfAdaptation(), 1)) {
            MutablePair<List<String>, List<String>> pair = ruleScopeUtil.selectScopeList(id, ScopeBizType.LEAVE_RULES);
            if (null != pair) {
                bean.setOrganizeList(pair.getLeft());
                bean.setOrganizeNum(pair.getLeft().size());
                bean.setUserIdList(pair.getRight());
                bean.setUserIdNum(pair.getRight().size());
            }
        }
        return bean;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String id) {
        // 删除适配范围
        ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
                .eq(AttendanceRuleScope::getRuleId, id)
                .eq(AttendanceRuleScope::getBizType, ScopeBizType.LEAVE_RULES));
        // 删除余额发放规则详情
// 删除旧余额发放规则详情（为更新做准备）
        AttendanceLeaveGrantSetting attendanceLeaveGrantSetting = new AttendanceLeaveGrantSetting();
        attendanceLeaveGrantSetting.setDeleteMark(1);
        attendanceLeaveGrantSettingMapper.update(attendanceLeaveGrantSetting, new UpdateWrapper<AttendanceLeaveGrantSetting>().eq("F_LeaveRulesId", id));
        // 删除假期
        AttendanceLeaveRules attendanceLeaveRules = new AttendanceLeaveRules();
        attendanceLeaveRules.setId(id);
        attendanceLeaveRules.setDeleteMark(1);
        attendanceLeaveRulesMapper.updateById(attendanceLeaveRules);
    }

    @Override
    public void updateState(AttendanceLeaveRulesDto attendanceLeaveRulesDto) {
        attendanceLeaveRulesMapper.updateState(attendanceLeaveRulesDto.getId(), attendanceLeaveRulesDto.getState(), userProvider.get().getId());
    }

    @Override
    public List<AttendanceLeaveRulesVo> getUserLeaveList(List<AttendanceLeaveType> leaveTypes) {
        List<AttendanceLeaveRulesVo> rulesVoList = new ArrayList<>();
        String userId = userProvider.get().getUserId();
        List<String> leaveTypeIds = leaveTypes.stream().map(AttendanceLeaveType::getId).collect(Collectors.toList());
        //  查询请假类型  未命中的不返回
        List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(Collections.singletonList(userId), ScopeBizType.LEAVE_RULES, ConstantUtil.NUM_TRUE, leaveTypeIds);
        if (null == attendanceRuleScopes || attendanceRuleScopes.isEmpty()) {
            return rulesVoList;
        }
        List<AttendanceLeaveRules> rules = attendanceLeaveRulesMapper.selectList(new QueryWrapper<AttendanceLeaveRules>()
                .lambda()
                .eq(AttendanceLeaveRules::getDeleteMark, 0)
                .eq(AttendanceLeaveRules::getState, 1)
                .ne(AttendanceLeaveRules::getScopeOfAdaptation, -1)
                .in(AttendanceLeaveRules::getId, attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).distinct().collect(Collectors.toList()))
                .orderByAsc(AttendanceLeaveRules::getCreatorTime)
        );
        // 过滤出发放余额类型的规则Id
        // 过滤出命中规则对应的假期类型 理论上命中的请假类型只有一个
        Map<String, AttendanceLeaveRules> rulesMap = rules.stream().collect(Collectors.toMap(AttendanceLeaveRules::getLeaveTypeId, a -> a, (k1, k2) -> k1));
        leaveTypes.forEach(leaveType -> {
            // 确保和类型查出的顺序一致
            AttendanceLeaveRules attendanceLeaveRules = rulesMap.get(leaveType.getId());
            if (null != attendanceLeaveRules) {
                AttendanceLeaveRulesVo bean = BeanUtil.toBean(attendanceLeaveRules, AttendanceLeaveRulesVo.class);
                bean.setLeaveTypeName(leaveType.getName());
                rulesVoList.add(bean);
            }
        });
        List<AttendanceBalanceRecordEntity> records = attendanceUserBalanceRecordService.list(new QueryWrapper<AttendanceBalanceRecordEntity>()
                .lambda()
                .and(x -> x.in(StringUtil.isNotEmpty(leaveTypeIds), AttendanceBalanceRecordEntity::getObjectId, leaveTypeIds)
                        .or()
                        .isNull(AttendanceBalanceRecordEntity::getObjectId))
                .eq(AttendanceBalanceRecordEntity::getUserId, userId)
                .eq(AttendanceBalanceRecordEntity::getDeleteMark, 0)
                .eq(AttendanceBalanceRecordEntity::getState, 0)
                .eq(AttendanceBalanceRecordEntity::getIsOver, 0)
        );
        // 过滤出objectId为空的存休数据并计算余额
        BigDecimal balance = records.stream()
                .filter(x -> x.getType().equals(3))
                .map(AttendanceBalanceRecordEntity::getBalance)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 在list中过滤出存休并将余额赋值到对应的假期中
        rulesVoList.stream().filter(x -> x.getBuiltIn().equals(1)).forEach(x -> x.setBalance(balance));
        // 其他的过滤出objectId不为空的假类型数据并分组计算余额
        Map<String, BigDecimal> map = records.stream()
                .filter(x -> x.getType().equals(2) && StringUtil.isNotEmpty(x.getObjectId()))
                .collect(Collectors.groupingBy(AttendanceBalanceRecordEntity::getObjectId, Collectors.reducing(BigDecimal.ZERO, AttendanceBalanceRecordEntity::getBalance, BigDecimal::add)));
        // 遍历vo列表，将余额设置到对应的假期中
        rulesVoList.forEach(x -> {
            x.setBalance(map.getOrDefault(x.getLeaveTypeId(), BigDecimal.ZERO));
        });
        return rulesVoList;
    }

    @Override
    public void autoGrantBalance(String tenantId) {
        // 查出所有启用中的假期且发放余额，且发放方式为自动发放的规则列表，
        List<AttendanceLeaveRules> rules = attendanceLeaveRulesMapper.selectList(new QueryWrapper<AttendanceLeaveRules>()
                .lambda()
                .eq(AttendanceLeaveRules::getDeleteMark, 0)
                .eq(AttendanceLeaveRules::getState, 1)
                .ne(AttendanceLeaveRules::getScopeOfAdaptation, -1)
                .eq(AttendanceLeaveRules::getDistributeBalance, 1)
        );
        if(CollUtil.isEmpty(rules)){
            return;
        }
        // 查出对应的发放规则详情
        List<AttendanceLeaveGrantSetting> settings = attendanceLeaveGrantSettingMapper.selectList(new QueryWrapper<AttendanceLeaveGrantSetting>()
                .lambda()
                .eq(AttendanceLeaveGrantSetting::getDeleteMark, 0)
                .eq(AttendanceLeaveGrantSetting::getGrantType, 0)
                .in(AttendanceLeaveGrantSetting::getLeaveRulesId, rules.stream().map(AttendanceLeaveRules::getId).collect(Collectors.toList()))
        );
        // 获取今天的年月日
        LocalDate today = LocalDate.now();
        // 今天的年月日  格式yyyy-MM-dd
        String todayStr = today.toString();
        int year = today.getYear();
        int month = today.getMonthValue();
        int day = today.getDayOfMonth();
        String monthday = month + "-" + day;
        // 当前月份 最后一天的日
        int monthLastDay = LocalDate.of(year, month, day).with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
        // 需要发劵的用户余额记录
        List<AttendanceBalanceRecordEntity> balanceRecordList = new ArrayList<>();
        // 消费记录
        List<AttendanceUserBalanceDetailVo> balanceDetailVoList = new ArrayList<>();
        // 需要查找的规则集合
        List<String> allRuleIds = new ArrayList<>();
        // 生日规则集合
        List<String> birthRuleIds = new ArrayList<>();
        // 入职规则集合
        List<String> onboardingRuleIds = new ArrayList<>();
        // 需要处理的规则集合
        HashMap<String, LeaveRuleBalanceVo> leaveRuleBalanceVoMap = new HashMap<>();
        for (AttendanceLeaveGrantSetting setting : settings) {
            checkGrantTimeType(setting, day, monthLastDay, allRuleIds, monthday, birthRuleIds, onboardingRuleIds, leaveRuleBalanceVoMap, month);
        }
        if(CollUtil.isEmpty(allRuleIds)){
            return;
        }
        // 通过规则Id找下面的人
        List<AttendanceRuleScope> scopes = ruleScopeService.list(new QueryWrapper<AttendanceRuleScope>()
                .lambda()
                .eq(AttendanceRuleScope::getBizType, ScopeBizType.LEAVE_RULES.getValue())
                .in(AttendanceRuleScope::getRuleId, allRuleIds)
        );
        // 查询全部用户
        ActionResult<List<UserBoundVO>> allUserInfoBatch = v2UserApi.getAllUserInfoBatch(List.of(), tenantId);
        if (allUserInfoBatch.getCode() != 200 || allUserInfoBatch.getData() == null) {
            return;
        }
        List<UserBoundVO> allUser = allUserInfoBatch.getData();
        // 根据加强类型Id过滤出map
        Map<String, List<AttendanceRuleScope>> expandMap = scopes.stream().collect(Collectors.groupingBy(AttendanceRuleScope::getExpand));
        expandMap.forEach((expand, expandList) -> {
            // 类型list  数据示列 ：规则1 类型1 范围全部  ；规则2 类型1 范围 指定组织 ；规则3 类型1 范围 指定用户1 ；规则4 类型1 范围 指定用户2
            // 优先级 ： 用户 > 组织 > 全部
            // 指定人的ids
            List<String> userIdsByExpand = expandList.stream().filter(x -> x.getScopeType() == 1).map(AttendanceRuleScope::getScopeValue).collect(Collectors.toList());
            List<String> orgIdsByExpand = expandList.stream().filter(x -> x.getScopeType() == 2).map(AttendanceRuleScope::getScopeValue).collect(Collectors.toList());
            // 指定组织下的用户ids排除指定人的ids
            List<String> orgUserIdsByExpand = allUser.stream().filter(x -> !userIdsByExpand.contains(x.getId()) && orgIdsByExpand.contains(x.getOrganizeId())).map(UserBoundVO::getId).collect(Collectors.toList());
            boolean isAll = expandList.stream().anyMatch(x -> x.getScopeType() == 3);
            // 全部对应的剩余用户
            List<String> allUserIdsByExpand = new ArrayList<>();
            if (isAll) {
                allUserIdsByExpand = allUser.stream().filter(x -> !userIdsByExpand.contains(x.getId()) && !orgUserIdsByExpand.contains(x.getId())).map(UserBoundVO::getId).collect(Collectors.toList());
            }
            Map<String, List<AttendanceRuleScope>> scopeMap = expandList.stream().collect(Collectors.groupingBy(AttendanceRuleScope::getRuleId));
            List<String> finalAllUserIdsByExpand = allUserIdsByExpand;
            scopeMap.forEach((ruleId, list) -> {
                // 本规则命中的用户信息
                List<UserBoundVO> userList = new ArrayList<>();
                // 规则对应的公式仅司龄发放有效
                List<LeaveRuleFormulaVo> formulaList = new ArrayList<>();
                if (birthRuleIds.contains(ruleId)) {
                    // 处理每年成员生日发放
                    // 拿到规则下对应的人
                    // 是否有全部范围的数据
                    boolean b = list.stream().anyMatch(x -> x.getScopeType() == 3);
                    if (b) {
                        // 比对月日相同的用户
                        userList = allUser.stream().filter(x -> finalAllUserIdsByExpand.contains(x.getId()) && expiresTimeUtil.checkMonthDay(x.getBirthday(), today)).collect(Collectors.toList());
                    } else {
                        List<String> userIds = list.stream().filter(x -> x.getScopeType() == 1).map(AttendanceRuleScope::getScopeValue).collect(Collectors.toList());
                        List<String> orgIds = list.stream().filter(x -> x.getScopeType() == 2).map(AttendanceRuleScope::getScopeValue).collect(Collectors.toList());
                        // 获取符合的组织用户
                        userList = allUser.stream().filter(x -> orgIds.contains(x.getOrganizeId()) && !userIdsByExpand.contains(x.getId()) && expiresTimeUtil.checkMonthDay(x.getBirthday(), today)).collect(Collectors.toList());
                        // 过滤该规则指定的用户
                        userList.addAll(allUser.stream().filter(x -> userIds.contains(x.getId()) && expiresTimeUtil.checkMonthDay(x.getBirthday(), today)).collect(Collectors.toList()));
                    }
                } else if (onboardingRuleIds.contains(ruleId)) {
                    // 处理每年成员入职日期发放
                    // 是否有全部范围的数据
                    boolean b = list.stream().anyMatch(x -> x.getScopeType() == 3);
                    if (b) {
                        // 比对月日相同的用户
//                        userList = allUser.stream().filter(x -> expiresTimeUtil.checkMonthDay(x.getEntryDate(), today)).collect(Collectors.toList());
                        userList = allUser.stream().filter(x -> finalAllUserIdsByExpand.contains(x.getId()) && expiresTimeUtil.checkMonthDay(x.getEntryDate(), today)).collect(Collectors.toList());
                    } else {
                        List<String> userIds = list.stream().filter(x -> x.getScopeType() == 1).map(AttendanceRuleScope::getScopeValue).collect(Collectors.toList());
                        List<String> orgIds = list.stream().filter(x -> x.getScopeType() == 2).map(AttendanceRuleScope::getScopeValue).collect(Collectors.toList());
                        // 获取符合的用户
                        // 获取符合的组织用户
                        userList = allUser.stream().filter(x -> orgIds.contains(x.getOrganizeId()) && !userIdsByExpand.contains(x.getId()) && expiresTimeUtil.checkMonthDay(x.getEntryDate(), today)).collect(Collectors.toList());
                        // 过滤该规则指定的用户
                        userList.addAll(allUser.stream().filter(x -> userIds.contains(x.getId()) && expiresTimeUtil.checkMonthDay(x.getEntryDate(), today)).collect(Collectors.toList()));
                    }
                } else {
                    // 每月指定日期发放、每月指定日期发放
                    // 是否有全部范围的数据
                    boolean b = list.stream().anyMatch(x -> x.getScopeType() == 3);
                    if (b) {
                        // 比对月日相同的用户
                        userList = allUser.stream().filter(x -> finalAllUserIdsByExpand.contains(x.getId())).collect(Collectors.toList());
                    } else {
                        List<String> userIds = list.stream().filter(x -> x.getScopeType() == 1).map(AttendanceRuleScope::getScopeValue).collect(Collectors.toList());
                        List<String> orgIds = list.stream().filter(x -> x.getScopeType() == 2).map(AttendanceRuleScope::getScopeValue).collect(Collectors.toList());
                        // 获取符合的用户
                        // 获取符合的组织用户
                        userList = allUser.stream().filter(x -> orgIds.contains(x.getOrganizeId()) && !userIdsByExpand.contains(x.getId())).collect(Collectors.toList());
                        // 过滤该规则指定的用户
                        userList.addAll(allUser.stream().filter(x -> userIds.contains(x.getId())).collect(Collectors.toList()));
                    }
                }
                // 组装劵信息及券使用记录
                LeaveRuleBalanceVo ruleBalanceVo = leaveRuleBalanceVoMap.get(ruleId);
                setParm(expand, ruleBalanceVo, formulaList, userList, balanceRecordList, todayStr, balanceDetailVoList);
            });
        });
        if (!balanceRecordList.isEmpty()) {
            // 发放劵
            attendanceUserBalanceRecordService.saveBatch(balanceRecordList);
        }
        if (!balanceDetailVoList.isEmpty()) {
            // 消费记录 仅记录生成劵的使用记录
            attendanceUserBalanceMapper.batchAddBalanceDetail(balanceDetailVoList);
        }
    }

    @Override
    public void deleteByTypeId(String id) {

        // 删除余额发放规则详情
        AttendanceLeaveGrantSetting attendanceLeaveGrantSetting = new AttendanceLeaveGrantSetting();
        attendanceLeaveGrantSetting.setLeaveTypeId(id);
        attendanceLeaveGrantSetting.setDeleteMark(1);
        attendanceLeaveGrantSettingMapper.updateById(attendanceLeaveGrantSetting);
        // 删除假期
        AttendanceLeaveRules attendanceLeaveRules = new AttendanceLeaveRules();
        attendanceLeaveRules.setLeaveTypeId(id);
        attendanceLeaveRules.setDeleteMark(1);
        attendanceLeaveRulesMapper.updateById(attendanceLeaveRules);
        // 删除适配范围
        ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
                .eq(AttendanceRuleScope::getExpand, id)
                .eq(AttendanceRuleScope::getBizType, ScopeBizType.LEAVE_RULES));
    }

    @Override
    public AttendanceLeaveRulesVo getUserLeaveDetail(AttendanceLeaveType vo,String userId) throws ApproveException {
        if (null == userId) {
             userId = userProvider.get().getUserId();
        }
        //  查询请假类型  未命中的不返回
        List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(Collections.singletonList(userId), ScopeBizType.LEAVE_RULES, ConstantUtil.NUM_TRUE, Collections.singletonList(vo.getId()));
        if (attendanceRuleScopes.isEmpty()) {
            throw new ApproveException("未找到命中的类型");
        }
        List<AttendanceLeaveRules> rules = attendanceLeaveRulesMapper.selectList(new QueryWrapper<AttendanceLeaveRules>()
                .lambda()
                .eq(AttendanceLeaveRules::getDeleteMark, 0)
                .eq(AttendanceLeaveRules::getState, 1)
                .ne(AttendanceLeaveRules::getScopeOfAdaptation, -1)
                .in(AttendanceLeaveRules::getId, attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).distinct().collect(Collectors.toList()))
                .orderByAsc(AttendanceLeaveRules::getCreatorTime)
        );
        if (rules.isEmpty()) {
            throw new ApproveException("未找到命中的规则");
        }
        // 一个人一个请假类型，只能命中一个规则
        AttendanceLeaveRulesVo bean = BeanUtil.toBean(rules.get(0), AttendanceLeaveRulesVo.class);
        bean.setUnitName(LeaveUnitEnum.getMsgByCode(bean.getUnit()));
        bean.setLeaveTypeName(vo.getName());
        return bean;
    }

    private void setParm(String ruleId, LeaveRuleBalanceVo ruleBalanceVo, List<LeaveRuleFormulaVo> formulaList, List<UserBoundVO> userList, List<AttendanceBalanceRecordEntity> balanceRecordList, String todayStr, List<AttendanceUserBalanceDetailVo> balanceDetailVoList) {
        if (ruleBalanceVo.getGrantDurationType() == 2) {
            // 计算余额发放方式为司龄的余额
            formulaList = getFormulaList(ruleBalanceVo.getFormulaJson(), formulaList);
            // 计算司龄
            List<LeaveRuleFormulaVo> finalFormulaList = formulaList;
            userList.forEach(user -> {
                AttendanceBalanceRecordEntity balanceRecord = new AttendanceBalanceRecordEntity();
                // 计算司龄月份数
                Integer seniorityMonth = expiresTimeUtil.calculateSeniorityMonth(user.getEntryDate());
                // 进行范围命中并返回对应的日期
                for (LeaveRuleFormulaVo vo : finalFormulaList) {
                    if (seniorityMonth >= vo.getMin() && seniorityMonth < vo.getMax()) {
                        balanceRecord.setBalance(new BigDecimal(vo.getDay().toString()));
                        balanceRecord.setTotal(new BigDecimal(vo.getMonth().toString()));
                    }
                }
                balanceRecord.setUserId(user.getId());
                balanceRecord.setType(2);
                balanceRecord.setObjectId(ruleId);// 类型Id
                balanceRecord.setUnit(2);
                balanceRecord.setExpireTime(ruleBalanceVo.getExpireTime());
                balanceRecordList.add(balanceRecord);
                // 组织劵日志
                AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                        .id(FtbUtil.getId())
                        .objectId(ruleId)
                        .userId(user.getId())
                        // 系统在yyyy-mm-dd，按规则自动发放余额X.X天/小时
                        .content("系统在" + todayStr + "，按规则自动发放余额" + balanceRecord.getBalance().setScale(1, RoundingMode.HALF_UP) + "天")
                        .quota(balanceRecord.getBalance())
                        .build();
                balanceDetailVoList.add(detailVo);
            });
        } else {
            userList.forEach(user -> {
                AttendanceBalanceRecordEntity balanceRecord = new AttendanceBalanceRecordEntity();
                balanceRecord.setBalance(ruleBalanceVo.getBalance());
                balanceRecord.setTotal(ruleBalanceVo.getBalance());
                balanceRecord.setUserId(user.getId());
                balanceRecord.setType(2);
                balanceRecord.setObjectId(ruleId);
                balanceRecord.setUnit(2);
                balanceRecord.setExpireTime(ruleBalanceVo.getExpireTime());
                balanceRecordList.add(balanceRecord);
                // 组织劵日志
                AttendanceUserBalanceDetailVo detailVo = AttendanceUserBalanceDetailVo.builder()
                        .id(FtbUtil.getId())
                        .objectId(ruleId)
                        .userId(user.getId())
                        // 系统在yyyy-mm-dd，按规则自动发放余额X.X天/小时
                        .content("系统在" + todayStr + "，按规则自动发放余额" + ruleBalanceVo.getBalance().setScale(1, RoundingMode.HALF_UP) + "天")
                        .quota(ruleBalanceVo.getBalance())
                        .build();
                balanceDetailVoList.add(detailVo);
            });
        }
    }

    /**
     * 设置规则余额信息
     *
     * @param setting               请假发放设置
     * @param day                   当前日期
     * @param monthLastDay          当前月份最后一天
     * @param allRuleIds            所有规则集合
     * @param monthday              当前日期 MM-dd
     * @param birthRuleIds          生日规则集合
     * @param onboardingRuleIds     入职规则集合
     * @param leaveRuleBalanceVoMap 请假余额映射
     */
    private void checkGrantTimeType(AttendanceLeaveGrantSetting setting, int day, int monthLastDay, List<String> allRuleIds, String monthday, List<String> birthRuleIds, List<String> onboardingRuleIds, HashMap<String, LeaveRuleBalanceVo> leaveRuleBalanceVoMap, int month) {
        LeaveRuleBalanceVo leaveRuleBalanceVo = new LeaveRuleBalanceVo();
        // 1.每月发放2.每年指定日期发放3.每年成员生日发放4.每年成员入职日期发放
        // 处理每月发放
        if (setting.getGrantTimeType() == 1) {
            // 检查是否是当前月份对应的日期，如果当前日期为月底最后一天，设置的日期数大于今天也能命中
            if (day == setting.getMonthDay() || (monthLastDay == day && setting.getMonthDay() >= day)) {
                allRuleIds.add(setting.getLeaveRulesId());
                setRuleBalanceVo(setting, leaveRuleBalanceVo);
            }
        } else if (setting.getGrantTimeType() == 2) {
            // 处理每年指定日期发放 2-29
            String[] split = setting.getSpecifyDay().split("-");
            int inMonth = Integer.parseInt(split[0]);
            int inDay = Integer.parseInt(split[1]);
            if (monthday.equals(setting.getSpecifyDay()) || (inMonth == month && monthLastDay == day && inDay >= day)) {
                allRuleIds.add(setting.getLeaveRulesId());
                setRuleBalanceVo(setting, leaveRuleBalanceVo);
            }
        } else if (setting.getGrantTimeType() == 3) {
            birthRuleIds.add(setting.getLeaveRulesId());
            allRuleIds.add(setting.getLeaveRulesId());
            // 处理每年成员生日发放 // 计算发放时长
            setRuleBalanceVo(setting, leaveRuleBalanceVo);
        } else if (setting.getGrantTimeType() == 4) {
            onboardingRuleIds.add(setting.getLeaveRulesId());
            allRuleIds.add(setting.getLeaveRulesId());
            // 处理每年成员入职日期发放 // 计算发放时长
            setRuleBalanceVo(setting, leaveRuleBalanceVo);
        }
        leaveRuleBalanceVoMap.put(setting.getLeaveRulesId(), leaveRuleBalanceVo);
    }

    private static List<LeaveRuleFormulaVo> getFormulaList(String formulaJson, List<LeaveRuleFormulaVo> formulaList) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            formulaList = objectMapper.readValue(formulaJson, new TypeReference<List<LeaveRuleFormulaVo>>() {
            });
            formulaList.sort(Comparator.comparingInt(LeaveRuleFormulaVo::getMonth));
            Integer min = 0;
            for (LeaveRuleFormulaVo v : formulaList) {
                // 计算司龄 min max
                if ("司龄<".equals(v.getTitle())) {
                    v.setMin(0);
                    v.setMax(v.getMonth());
                    min = v.getMonth();
                } else if ("司龄≥".equals(v.getTitle())) {
                    v.setMin(min);
                    // 设置最大边界值
                    v.setMax(999999999);
                } else {
                    v.setMin(min);
                    v.setMax(v.getMonth());
                    min = v.getMonth();
                }
            }
        } catch (JsonProcessingException e) {
            log.error("假期司龄规则JSON解析失败: " + formulaJson, e);
        }
        return formulaList;
    }

    private void setRuleBalanceVo(AttendanceLeaveGrantSetting setting, LeaveRuleBalanceVo leaveRuleBalanceVo) {
        leaveRuleBalanceVo.setRuleId(setting.getLeaveRulesId());
        leaveRuleBalanceVo.setGrantDurationType(setting.getGrantDurationType());
        // 计算发放时长
        if (setting.getGrantDurationType() == 1) {
            leaveRuleBalanceVo.setBalance(new BigDecimal(setting.getSpecifyDayNum().toString()));
        } else if (setting.getGrantDurationType() == 2) {
            leaveRuleBalanceVo.setFormulaJson(setting.getFormulaJson());
        }
        // 计算过期时间
        leaveRuleBalanceVo.setExpireTime(expiresTimeUtil.getExpiresTime(0 == setting.getExpiredType() ? 0 : 1, setting.getExpiredDayNum(), null));
    }

}
