package jnpf.attendance.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.nlf.calendar.Holiday;
import jnpf.attendance.mapper.AttendanceFestivalRulesMapper;
import jnpf.attendance.service.*;
import jnpf.base.UserInfo;
import jnpf.base.service.SuperServiceImpl;
import jnpf.entity.AttendanceGroup;
import jnpf.entity.AttendanceGroupUser;
import jnpf.entity.attendance.AttendanceFestivalRules;
import jnpf.entity.attendance.AttendanceRuleScope;
import jnpf.enums.attendance.ScopeBizType;
import jnpf.util.HttpUtil;
import jnpf.model.attendance.dto.AttendanceFestivalRulesDto;
import jnpf.model.attendance.dto.FestivalDto;
import jnpf.model.attendance.dto.FestivalRulesQueryDto;
import jnpf.model.attendance.vo.attendance.AttendanceFestivalRulesVo;
import jnpf.permission.V2UserApi;
import jnpf.util.*;
import jnpf.util.attendance.RuleScopeUtil;
import jnpf.util.context.ThreadContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author panpan
 */
@Service
@Slf4j
public class AttendanceFestivalRulesServiceImpl extends SuperServiceImpl<AttendanceFestivalRulesMapper, AttendanceFestivalRules> implements AttendanceFestivalRulesService {

    @Resource
    private AttendanceFestivalRulesMapper attendanceFestivalRulesMapper;

    @Resource
    private RuleScopeUtil ruleScopeUtil;

    @Resource
    private UserProvider userProvider;

    @Resource
    private RuleScopeService ruleScopeService;

    @Autowired
    private AttendanceDailyRuleService attendanceDailyRuleService;
    @Resource
    private AttendanceGroupService attendanceGroupService;

    @Resource
    private V2UserApi v2UserApi;
    @Resource
    private ThreadPoolExecutor attendanceRuleThreadPool;
    @Autowired
    private AttendanceUserService attendanceUserService;

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(AttendanceFestivalRulesDto festivalRulesDto) throws Exception {
        String userId = userProvider.get().getUserId();
        List<AttendanceFestivalRules> list = lambdaQuery().eq(AttendanceFestivalRules::getName, festivalRulesDto.getName())
                .eq(AttendanceFestivalRules::getYear, festivalRulesDto.getYear())
                .eq(AttendanceFestivalRules::getDeleteMark, 0)
                .list();
        Assert.isTrue(CollUtil.isEmpty(list), "该名称已存在");
        Date date = new Date();
        AttendanceFestivalRules entity = BeanUtil.toBean(festivalRulesDto, AttendanceFestivalRules.class);
        entity.setCreatorUserId(userId);
        entity.setCreatorTime(date);
        entity.setLastModifyUserId(userId);
        entity.setLastModifyTime(date);
        attendanceFestivalRulesMapper.insert(entity);
        List<AttendanceRuleScope> ruleScopeList = ruleScopeUtil.getRuleScopeList(entity.getId(), entity.getScopeOfAdaptation(), MutablePair.of(festivalRulesDto.getOrganizeList(), festivalRulesDto.getUserIdList()), ScopeBizType.FESTIVAL_RULES);
        ruleScopeUtil.saveBatch(ruleScopeList);
    }

    private List<String> getUserIds(List<String> organizeList, List<String> userIdList, AttendanceFestivalRules entity) {
        List<Date> dateList = StringUtil.isNotEmpty(entity.getFestivalDate()) ? Arrays.stream(entity.getFestivalDate().split(",")).map(DateDetail::getStr2Date10).collect(Collectors.toList()) : new ArrayList<>();
        Date start = dateList.stream().min(Comparator.naturalOrder()).orElse(null);
        if (null == start) {
            return new ArrayList<>();
        }
        Date end = dateList.stream().max(Comparator.naturalOrder()).orElse(null);
        return attendanceUserService.getUserIds(organizeList, userIdList, start, end, entity.getScopeOfAdaptation());
    }

    // ahua
    private void autoRest(AttendanceFestivalRules newEntity, AttendanceFestivalRules oldEntity, List<String> newUserIds, List<String> oldUserIds){
        Date start = new Date();
        Date end = jnpf.util.DateUtil.getEndDayOfMonth();
        if (Objects.nonNull(oldEntity) && StringUtil.isNotEmpty(oldEntity.getFestivalDate())) {
            List<Date> dateList = Arrays.stream(oldEntity.getFestivalDate().split(",")).map(DateDetail::getStr2Date10).collect(Collectors.toList());
            start = Collections.min(dateList);
            end = Collections.max(dateList);
        }
        if (Objects.nonNull(newEntity) && StringUtil.isNotEmpty(newEntity.getFestivalDate())) {
            if (Objects.equals(newEntity.getState(), 0)) {
                return;
            }
            if (Objects.equals(newEntity.getAutoScheduling(), 0)) {
                return;
            }
            List<Date> dateList = Arrays.stream(newEntity.getFestivalDate().split(",")).map(DateDetail::getStr2Date10).collect(Collectors.toList());
            List<Date> collect1 = dateList.stream().filter(date -> jnpf.util.DateUtil.getDayEnd().after(date)).collect(Collectors.toList());
            Assert.isFalse(CollUtil.isNotEmpty(collect1), "请选择小于今天日期的节日日期");
            Date min = Collections.min(dateList);
            start = start.after(min) ? start : min;
            Date max = Collections.max(dateList);
            end = end.before(max) ? max : end;
        }
        Date finalStart = start;
        Date finalEnd = end;
        CompletableFuture.runAsync(ThreadContext.wrap(() -> {
            attendanceDailyRuleService.addHolidayDailyRule(finalStart, finalEnd, attendanceGroupService.queryDropList(), newEntity, oldEntity, newUserIds, oldUserIds);
        }), attendanceRuleThreadPool);
    }

    @Transactional
    @Override
    public void statutoryUpdate(String year) {
        List<AttendanceFestivalRules> list1 = lambdaQuery()
                .likeRight(AttendanceFestivalRules::getEndDate, year)
                .eq(AttendanceFestivalRules::getDeleteMark, Boolean.FALSE)
                .list();
        Map<String, AttendanceFestivalRules> collect1 = list1.stream().collect(Collectors.toMap(AttendanceFestivalRules::getName, entity -> entity, (r1, r2) -> r1));
        List<Holiday> holidays = getHolidaysFromApi(year);
        holidays.addAll(getHolidaysFromApi(DateDetail.getPreviousYear(year)));
        Map<String, List<Holiday>> collect = holidays.stream().filter(festival -> festival.getTarget().contains(year)).collect(Collectors.groupingBy(Holiday::getName));
        List<AttendanceFestivalRules> entities = CollUtil.newArrayList();
        UserInfo user = UserProvider.getUser();

        collect.forEach((name, list) -> {
            List<Holiday> workHolidays = list.stream().filter(Holiday::isWork).collect(Collectors.toList());
            List<Holiday> notWorkHolidays = list.stream().filter(holiday -> !holiday.isWork()).collect(Collectors.toList());
            Holiday firstHoliday = notWorkHolidays.stream().sorted(Comparator.comparing(Holiday::getDay)).reduce((h1, h2) -> h1).orElse(null);
            Holiday lastHoliday = notWorkHolidays.stream().sorted(Comparator.comparing(Holiday::getDay)).reduce((h1, h2) -> h2).orElse(null);
            AttendanceFestivalRules attendanceFestivalRules = new AttendanceFestivalRules();
            attendanceFestivalRules.setId(RandomUtil.uuId());
            attendanceFestivalRules.setYear(year);
            attendanceFestivalRules.setName(name);
            attendanceFestivalRules.setCreatorTime(new Date());
            attendanceFestivalRules.setCreatorUserId(user.getUserId());
            attendanceFestivalRules.setTenantId(user.getTenantId());
            attendanceFestivalRules.setAutoScheduling(1);
            attendanceFestivalRules.setUpdateState(0);
            attendanceFestivalRules.setState(0);
            attendanceFestivalRules.setScopeOfAdaptation(0);
            AttendanceFestivalRules orDefault = collect1.getOrDefault(name, attendanceFestivalRules);
            orDefault.setStartDate(jnpf.util.DateUtil.stringToDates(firstHoliday.getDay()));
            orDefault.setEndDate(jnpf.util.DateUtil.stringToDates(lastHoliday.getDay()));
            String compensateInDate = workHolidays.stream().map(Holiday::getDay).reduce((r1, r2) -> r1 + "," + r2).orElse("");
            orDefault.setCompensateInDate(compensateInDate);
            entities.add(orDefault);
        });
        // 处理每年同日更新的配置
        List<AttendanceFestivalRules> oldRules = lambdaQuery()
                .likeRight(AttendanceFestivalRules::getEndDate, DateUtil.format(DateUtil.offsetMonth(DateUtil.parse(year + "-01-01 00:00:00", "yyyy-01-01 00:00:00"), -12), "yyyy"))
                .eq(AttendanceFestivalRules::getUpdateState, 1)
                .eq(AttendanceFestivalRules::getDeleteMark, Boolean.FALSE)
                .list();
        oldRules.forEach(entity -> {
            entity.setId(RandomUtil.uuId());
            entity.setDeleteTime(new Date());
            entity.setYear(year);
            entity.setCreatorTime(new Date());
            entity.setCreatorUserId(user.getUserId());
            entity.setStartDate(DateUtil.offsetMonth(entity.getStartDate(), 12));
            entity.setEndDate(DateUtil.offsetMonth(entity.getEndDate(), 12));
            entity.setYear(DateUtil.format(new Date(), "yyyy"));
            entity.setScopeOfAdaptation(0);
            entity.setState(0);
            entity.setCompensateInDate(StringUtil.isEmpty(entity.getCompensateInDate()) ? null : Arrays.stream(entity.getCompensateInDate().split(",")).map(date -> jnpf.util.DateUtil.daFormat(DateUtil.offsetMonth(DateUtil.parse(date, "yyyy-MM-dd"), 12))).collect(Collectors.joining(",")));
            entity.setFestivalDate(StringUtil.isEmpty(entity.getFestivalDate()) ? "" : jnpf.util.DateUtil.daFormat(DateUtil.offsetMonth(jnpf.util.DateUtil.stringToDates(entity.getFestivalDate()), 12)));
        });
        oldRules.removeIf(rule -> list1.stream().anyMatch(vo -> StringUtil.equals(vo.getName(), rule.getName())));
        entities.addAll(oldRules);
        saveOrUpdateBatch(entities.stream().sorted(Comparator.comparing(AttendanceFestivalRules::getStartDate)).collect(Collectors.toList()));
        ruleScopeService.saveBatch(entities.stream().map(entity ->
                ruleScopeUtil.getRuleScope(entity.getId(), null, ConstantUtil.RULE_SCOPE_ALL, ScopeBizType.FESTIVAL_RULES)
        ).collect(Collectors.toList()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(AttendanceFestivalRulesDto festivalRulesDto) throws Exception {
        List<AttendanceFestivalRules> list = lambdaQuery().eq(AttendanceFestivalRules::getName, festivalRulesDto.getName())
                .eq(AttendanceFestivalRules::getYear, festivalRulesDto.getYear())
                .eq(AttendanceFestivalRules::getDeleteMark, 0)
                .list();
        Assert.isFalse(CollUtil.isNotEmpty(list) && list.stream().noneMatch(entity -> entity.getId().equals(festivalRulesDto.getId())), "该名称已存在");
        AttendanceFestivalRules oldEntity = attendanceFestivalRulesMapper.selectById(festivalRulesDto.getId());
        String userId = userProvider.get().getUserId();
        Date date = new Date();
        AttendanceFestivalRules entity = BeanUtil.toBean(festivalRulesDto, AttendanceFestivalRules.class);
        entity.setLastModifyUserId(userId);
        entity.setLastModifyTime(date);
        entity.setTenantId(UserProvider.getUser().getTenantId());
        entity.setDeleteMark(0);
        entity.setState(oldEntity.getState());
        attendanceFestivalRulesMapper.updateById(entity);
        MutablePair<List<String>, List<String>> listListMutablePair = ruleScopeUtil.selectScopeList(festivalRulesDto.getId(), ScopeBizType.FESTIVAL_RULES);
        ruleScopeUtil.updateRuleScopeList(entity.getId(), oldEntity.getScopeOfAdaptation(), entity.getScopeOfAdaptation(), MutablePair.of(festivalRulesDto.getOrganizeList(), festivalRulesDto.getUserIdList()), ScopeBizType.FESTIVAL_RULES);
        List<String> oldUserIds = getUserIds(Objects.isNull(listListMutablePair) ? null : listListMutablePair.getLeft(), Objects.isNull(listListMutablePair) ? null : listListMutablePair.getRight(), oldEntity);
        List<String> userIds = getUserIds(festivalRulesDto.getOrganizeList(), festivalRulesDto.getUserIdList(), entity);
        autoRest(entity, oldEntity, userIds, oldUserIds);
    }

    @Override
    public AttendanceFestivalRulesVo detail(String id) {
        AttendanceFestivalRulesVo bean = BeanUtil.toBean(attendanceFestivalRulesMapper.selectById(id), AttendanceFestivalRulesVo.class);
        // 当适配范围为指定组织或成员时查询适配范围
        if (bean.getScopeOfAdaptation().equals(1)) {
            MutablePair<List<String>, List<String>> pair = ruleScopeUtil.selectScopeList(id, ScopeBizType.FESTIVAL_RULES);
            if (null != pair) {
                bean.setOrganizeList(pair.getLeft());
                bean.setUserIdList(pair.getRight());
            }
        }
        return bean;
    }

    @Override
    public void delete(String id) {
        AttendanceFestivalRules byId = getById(id);
        MutablePair<List<String>, List<String>> listListMutablePair = ruleScopeUtil.selectScopeList(id, ScopeBizType.FESTIVAL_RULES);
        List<String> oldUserIds = getUserIds(Objects.isNull(listListMutablePair) ? null : listListMutablePair.getLeft(), Objects.isNull(listListMutablePair) ? null : listListMutablePair.getRight(), byId);
        attendanceFestivalRulesMapper.deleteById(id);
        ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
                .eq(AttendanceRuleScope::getRuleId, id)
                .eq(AttendanceRuleScope::getBizType, ScopeBizType.FESTIVAL_RULES));
        autoRest(null, byId, List.of(), oldUserIds);
    }

    @Override
    public void updateState(AttendanceFestivalRulesDto festivalRulesDto) {
        AttendanceFestivalRules byId = getById(festivalRulesDto.getId());
        attendanceFestivalRulesMapper.updateState(festivalRulesDto.getId(), festivalRulesDto.getState(), userProvider.get().getId());
        MutablePair<List<String>, List<String>> listListMutablePair = ruleScopeUtil.selectScopeList(festivalRulesDto.getId(), ScopeBizType.FESTIVAL_RULES);
        List<String> oldUserIds = getUserIds(Objects.isNull(listListMutablePair) ? null : listListMutablePair.getLeft(), Objects.isNull(listListMutablePair) ? null : listListMutablePair.getRight(), byId);

            // 查询自动排休逻辑对应的人员列表
            if (Objects.equals(festivalRulesDto.getState(), 1)) {
                byId.setState(1);
                autoRest(byId, null, oldUserIds, List.of());
            } else {
                autoRest(null, byId, List.of(), oldUserIds);
            }

    }

    @Override
    public boolean checkFestivalRules(Date date, String userId) {
        List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(Collections.singletonList(userId), ScopeBizType.FESTIVAL_RULES, ConstantUtil.NUM_FALSE);
        if (CollUtil.isEmpty(attendanceRuleScopes)) {
            return false;
        }
        List<AttendanceFestivalRules> rules = getRules(date, attendanceRuleScopes);
        return CollUtil.isNotEmpty(rules);
    }

    @Override
    public Map<String, Map<String, FestivalDto>> checkFestivalRulesBatch(Date date, List<String> userIds) {

        DateDetail dateDetail = new DateDetail(date);
        Date today = dateDetail.getCurrentDate();
        String endDate = DateDetail.getDate2Str(today, DateDetail.DF);
        dateDetail.getYesterday();
        Date yesterday = dateDetail.getCurrentDate();
        String startDate = DateDetail.getDate2Str(yesterday, DateDetail.DF);
        List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(userIds, ScopeBizType.FESTIVAL_RULES, ConstantUtil.NUM_FALSE);
        ConcurrentMap<String, List<AttendanceRuleScope>> map = attendanceRuleScopes.stream().collect(Collectors.groupingByConcurrent(AttendanceRuleScope::getUserId));
        // 一次查出所有涉及的节假日规则
        List<AttendanceFestivalRules> allRules;
        if (map.isEmpty()) {
            allRules = List.of();
        } else {
            List<String> rules = attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).distinct().collect(Collectors.toList());
            allRules = attendanceFestivalRulesMapper.selectList(
                    new QueryWrapper<AttendanceFestivalRules>()
                            .lambda()
                            .eq(AttendanceFestivalRules::getDeleteMark, ConstantUtil.NUM_FALSE)
                            .eq(AttendanceFestivalRules::getState, ConstantUtil.NUM_TRUE)
                            .le(AttendanceFestivalRules::getStartDate, endDate)
                            .ge(AttendanceFestivalRules::getEndDate, startDate)
                            .in(AttendanceFestivalRules::getId, rules)
            );
        }
        ConcurrentMap<String, List<AttendanceFestivalRules>> ruleMap = allRules.stream().collect(Collectors.groupingByConcurrent(AttendanceFestivalRules::getId));
        // 返回内容
        Map<String, Map<String, FestivalDto>> returnMap = new HashMap<>();
        userIds.forEach(user -> {
            List<AttendanceRuleScope> scopeList = map.getOrDefault(user, List.of());
            Map<String, FestivalDto> valueMap = new HashMap<>();
            if (scopeList.isEmpty()) {
                valueMap.put(startDate, new FestivalDto(Boolean.FALSE, ""));
                valueMap.put(endDate, new FestivalDto(Boolean.FALSE, ""));
                returnMap.put(user, valueMap);
            } else {
                List<String> userRules = scopeList.stream()
                        .map(AttendanceRuleScope::getRuleId)
                        .distinct()
                        .collect(Collectors.toList());
                // 判断[昨天]是否匹配节假日
                valueMap.put(startDate, checkFestivalByDay(yesterday, ruleMap, userRules));
                // 判断[今天]是否匹配节假日
                valueMap.put(endDate, checkFestivalByDay(today, ruleMap, userRules));
                returnMap.put(user, valueMap);
            }
        });
        return returnMap;
    }

    private FestivalDto checkFestivalByDay(Date day, ConcurrentMap<String, List<AttendanceFestivalRules>> ruleMap, List<String> userRules) {
        FestivalDto festivalDto = new FestivalDto(false, "");
        DateTime day1 = DateUtil.beginOfDay(day);
        // 节日集合
        List<AttendanceFestivalRules> festivalRulesList = new ArrayList<>();
        // 过滤节日对象，有值时拼接节日名称
        userRules.forEach(id -> {
            List<AttendanceFestivalRules> list = ruleMap.get(id);
            if (list == null || list.isEmpty()) return;
            List<AttendanceFestivalRules> collect = list.stream().filter(r -> DateDetail.checkTimeBetween(day1, r.getStartDate(), r.getEndDate())).collect(Collectors.toList());
            if (collect.isEmpty()) return;
            festivalRulesList.addAll(collect);
        });
        // 去重festivalRulesList 用@拼接节日名称
        festivalDto.setFestival(!festivalRulesList.isEmpty());
        festivalDto.setFestivalStr(festivalRulesList.stream().map(AttendanceFestivalRules::getName).collect(Collectors.joining("@")));
        return festivalDto;
    }

    /**
     * 获取未删除且启用的规则
     * @param date  日期
     * @param attendanceRuleScopes 规则范围
     * @return 规则列表
     */
    private List<AttendanceFestivalRules> getRules(Date date, List<AttendanceRuleScope> attendanceRuleScopes) {
        return attendanceFestivalRulesMapper.selectList(new QueryWrapper<AttendanceFestivalRules>()
                .lambda()
                .eq(AttendanceFestivalRules::getDeleteMark, 0)
                .eq(AttendanceFestivalRules::getState, 1)
                .and(true, x -> x.le(AttendanceFestivalRules::getEndDate, date))
                .and(true, x -> x.ge(AttendanceFestivalRules::getStartDate, date))
                .in(CollUtil.isNotEmpty(attendanceRuleScopes), AttendanceFestivalRules::getId, attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).distinct().collect(Collectors.toList()))
        );
    }

    /**
     * 获取未删除且启用的规则
     * @param attendanceRuleScopes 规则范围
     * @return 规则列表
     */
    private List<AttendanceFestivalRules> getRules(Date start, Date end, List<AttendanceRuleScope> attendanceRuleScopes) {
        return attendanceFestivalRulesMapper.selectList(new QueryWrapper<AttendanceFestivalRules>()
                .lambda()
                .eq(AttendanceFestivalRules::getDeleteMark, 0)
                .eq(AttendanceFestivalRules::getState, 1)
                .and(x -> x.ge(AttendanceFestivalRules::getEndDate, start))
                .and(x -> x.le(AttendanceFestivalRules::getStartDate, end))
                .in(CollUtil.isNotEmpty(attendanceRuleScopes), AttendanceFestivalRules::getId, attendanceRuleScopes.stream().map(AttendanceRuleScope::getRuleId).distinct().collect(Collectors.toList()))
        );
    }

    @Override
    public Map<String, Boolean> batchCheckFestivalRules(Date date, List<String> userIds) {
        Map<String, Boolean> map = new HashMap<>();
        List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(userIds, ScopeBizType.FESTIVAL_RULES, ConstantUtil.NUM_FALSE);
        if (CollUtil.isEmpty(attendanceRuleScopes)) {
            userIds.forEach(x -> map.put(x, false));
            return map;
        }
        List<AttendanceFestivalRules> rules = getRules(date, attendanceRuleScopes);
        // 命中且启用的节日规则Id集合
        List<String> festivalRules = rules.stream().map(AttendanceFestivalRules::getId).distinct().collect(Collectors.toList());
        // 获取用户对应的启动中的规则
        Map<String, List<AttendanceRuleScope>> scopeMap = attendanceRuleScopes.stream().collect(Collectors.groupingBy(AttendanceRuleScope::getUserId));
        userIds.forEach(userId -> {
            List<AttendanceRuleScope> scopes = scopeMap.get(userId);
            if (CollUtil.isEmpty(scopes)) {
                // 未命中规则
                map.put(userId, false);
            } else {
                List<String> list = scopes.stream().map(AttendanceRuleScope::getRuleId).distinct().collect(Collectors.toList());
                list.retainAll(festivalRules);
                map.put(userId, !list.isEmpty());
            }
        });
        return map;
    }

    @Override
    public Map<String, List<AttendanceFestivalRules>> batchGetFestivalRulesByUserIds(Date start, Date end, List<String> userIds) {
        Map<String, List<AttendanceFestivalRules>> map = new HashMap<>();
        List<AttendanceRuleScope> attendanceRuleScopes = ruleScopeUtil.selectUserEffectListBatch(userIds, ScopeBizType.FESTIVAL_RULES, ConstantUtil.NUM_FALSE);
        if (CollUtil.isEmpty(attendanceRuleScopes)) {
            return map;
        }
        List<AttendanceFestivalRules> rules = getRules(start, end, attendanceRuleScopes);
        // 获取用户对应的启动中的规则
        Map<String, List<AttendanceRuleScope>> scopeMap = attendanceRuleScopes.stream().collect(Collectors.groupingBy(AttendanceRuleScope::getUserId));
        userIds.forEach(userId -> {
            List<AttendanceRuleScope> scopes = scopeMap.get(userId);
            if (CollUtil.isEmpty(scopes)) {
                // 未命中规则
                return;
            }
            map.put(userId, rules.stream().filter(rule -> scopes.stream().anyMatch(scope -> StringUtil.equals(scope.getRuleId(), rule.getId()))).collect(Collectors.toList()));
        });
        return map;
    }

    /**
     * 查询节假日信息并返回Holiday对象列表
     * @param year 年份
     * @return Holiday对象列表
     */
    public List<Holiday> getHolidaysFromApi(String year) {
        // 验证输入参数
        if (StringUtil.isEmpty(year)) {
            log.warn("Year parameter is empty, returning empty holiday list");
            return Collections.emptyList();
        }
        List<Holiday> holidayList = new ArrayList<>();

        try {
            // 构建API URL
            String apiUrl = "https://publicapi.xiaoai.me/holiday/year?date=" + year;

            // 调用API获取响应
            String response = HttpUtil.sendGet(apiUrl);

            if (StringUtil.isEmpty(response)) {
                log.error("API returned empty response for year: {}", year);
                return Collections.emptyList();
            }

            // 解析JSON响应
            JSONObject jsonObject = JSONObject.parseObject(response);

            // 检查响应状态
            if (jsonObject != null && jsonObject.getInteger("code") == 0) {
                JSONArray dataArray = jsonObject.getJSONArray("data");

                if (dataArray != null && !dataArray.isEmpty()) {
                    // 遍历节假日数据
                    for (int i = 0; i < dataArray.size(); i++) {
                        JSONObject holidayJson = dataArray.getJSONObject(i);

                        if (holidayJson != null) {
                            // 只包含节假日(daytype=1)和调休日(daytype=3)
                            Integer daytype = holidayJson.getInteger("daytype");
                            if (daytype == 1 || daytype == 3) {
                                // 创建Holiday对象
                                Holiday holiday = new Holiday();

                                // 设置属性
                                holiday.setDay(holidayJson.getString("date"));

                                // 提取节假日名称，移除"调休"后缀，确保同个节假日的名称一致
                                String holidayName = holidayJson.getString("holiday");
                                if (holidayName != null && holidayName.contains("调休")) {
                                    holidayName = holidayName.replace("调休", "").trim();
                                }
                                holiday.setName(holidayName);

                                holiday.setWork(daytype == 3); // daytype=3表示调休日（需上班）
                                holiday.setTarget(year); // 设置目标年份

                                // 添加到列表
                                holidayList.add(holiday);
                            }
                        }
                    }

                } else {
                    log.warn("No holiday data found for year: {}", year);
                }
            } else {
                int code = jsonObject != null ? jsonObject.getInteger("code") : -1;
                String message = jsonObject != null ? jsonObject.getString("msg") : "Unknown error";
                log.error("API returned error for year {}: code={}, message={}", year, code, message);
            }
        } catch (Exception e) {
            log.error("Error fetching holidays from API for year {}: {}", year, e.getMessage(), e);
        }

        return holidayList;
    }
}
