package jnpf.attendance.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jnpf.attendance.mapper.AttendanceDailyRuleMapper;
import jnpf.attendance.mapper.ClockInResultMapper;
import jnpf.attendance.service.AttenceMachineService;
import jnpf.attendance.service.AttendanceUserService;
import jnpf.attendance.service.ClockInResultService;
import jnpf.attendance.service.OvertimeRuleService;
import jnpf.attendance.service.handle.chain.RuleFilterChain;
import jnpf.base.UserInfo;
import jnpf.base.service.SuperServiceImpl;
import jnpf.entity.AttendanceGroupUser;
import jnpf.entity.attendance.AttendanceClockInResult;
import jnpf.entity.attendance.AttendanceMachineManage;
import jnpf.entity.attendance.AttendanceRuleScope;
import jnpf.entity.attendance.FtbAttendanceDailyRule;
import jnpf.enums.attendance.ScopeBizType;
import jnpf.model.attendance.vo.attendance.OvertimeRuleVo;
import jnpf.util.ConstantUtil;
import jnpf.util.DateDetail;
import jnpf.util.UserProvider;
import jnpf.util.attendance.RuleScopeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

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

/**
 * 打卡服务实现2.0
 *
 * @author yanwenfu
 * @create 2025-09-23
 */
@Slf4j
@Service
public class ClockInResultServiceImpl extends SuperServiceImpl<ClockInResultMapper, AttendanceClockInResult> implements ClockInResultService {

    @Resource
    private AttendanceDailyRuleMapper attendanceDailyRuleMapper;
    @Resource
    private RuleFilterChain ruleFilterChain;
    @Resource
    private OvertimeRuleService overtimeRuleService;
    @Resource
    private AttendanceUserService attendanceUserService;
    @Resource
    private RuleScopeUtil ruleScopeUtil;
    @Resource
    private AttenceMachineService attenceMachineService;

    @Override
    public List<FtbAttendanceDailyRule> getTodayRuleList(Date today, UserInfo userInfo) {

        // 查询今日可以打卡的出勤规则
        List<FtbAttendanceDailyRule> ruleList = attendanceDailyRuleMapper.getRuleClockTimeContainsToday(today, userInfo.getUserId());
        ruleList = clearErrorRule(ruleList, userInfo.getUserId(), today, null);
        // 查询加班规则
        Map<String, OvertimeRuleVo> map = overtimeRuleService.getEffectDetailByUserList(List.of(userInfo.getUserId()), today);
        return ruleFilterChain.execute(today, ruleList, map, userInfo);
    }

    @Override
    public List<FtbAttendanceDailyRule> clearErrorRule(List<FtbAttendanceDailyRule> ruleList, String userId, Date date, List<String> effectGroupIds) {

        if (ruleList.isEmpty()) {
            return new ArrayList<>();
        }
        if (null == userId) {
            return ruleList;
        }
        int ruleSize = ruleList.size();
        // 判断考勤组用户信息是否正常
        List<String> egIds;
        if (CollUtil.isEmpty(effectGroupIds)) {
            List<String> groupIds = ruleList.stream().distinct().map(FtbAttendanceDailyRule::getGroupId).collect(Collectors.toList());
            List<AttendanceGroupUser> groupUserList = attendanceUserService.getAttendanceGroupUsersOfSecondment(DateUtil.beginOfDay(date), DateUtil.endOfDay(date), List.of(userId), groupIds);
            egIds = groupUserList.stream().map(AttendanceGroupUser::getGroupId).collect(Collectors.toList());
        } else {
            egIds = effectGroupIds;
        }
        ruleList.removeIf(v -> !egIds.contains(v.getGroupId()));
        if (ruleList.isEmpty()) {
            return new ArrayList<>();
        }
        int currentSize = 1;
        if (ruleSize != currentSize && ruleList.get(0).getRn() != null) {
            // 给rn重新赋值
            for (FtbAttendanceDailyRule dailyRule : ruleList) {
                dailyRule.setRn(currentSize++);
            }
        }
        return ruleList;
    }

    @Override
    public List<AttendanceMachineManage> getMachineList(String userId, String tenantId) {

        List<AttendanceMachineManage> machineList = new ArrayList<>();
        List<AttendanceRuleScope> scopeList = ruleScopeUtil.selectUserEffectList(userId, ScopeBizType.ATTENDANCE_MACHINE, tenantId);
        if (null == scopeList || scopeList.isEmpty()) {
            return machineList;
        }
        List<String> ruleIds = scopeList.stream().map(AttendanceRuleScope::getRuleId).collect(Collectors.toList());
        if (ruleIds.isEmpty()) {
            return machineList;
        }
        return attenceMachineService.list(new LambdaQueryWrapper<AttendanceMachineManage>()
                .in(AttendanceMachineManage::getId, ruleIds)
                .eq(AttendanceMachineManage::getDeleteMark, ConstantUtil.NUM_FALSE));
    }

    @Override
    public Map<String, List<FtbAttendanceDailyRule>> getTodayRuleListByUser(Date today, String userId) {

        // 查询今日可以打卡的出勤规则
        List<FtbAttendanceDailyRule> ruleList = attendanceDailyRuleMapper.getRuleClockTimeContainsToday(today, userId);
        ruleList = clearErrorRule(ruleList, userId, today, null);
        if (ruleList.isEmpty()) {
            return new HashMap<>();
        }
        UserInfo user = UserProvider.getUser();
        Map<String, List<FtbAttendanceDailyRule>> returnMap = new HashMap<>();
        // 用户 -> 出勤规则列表
        ConcurrentMap<String, List<FtbAttendanceDailyRule>> map = ruleList.stream().collect(Collectors.groupingByConcurrent(FtbAttendanceDailyRule::getUserId));
        // 查询加班规则
        Map<String, OvertimeRuleVo> overtimeMap = overtimeRuleService.getEffectDetailByUserList(new ArrayList<>(map.keySet()), today);
        map.forEach((k, v) -> {
            v.sort(Comparator.comparingInt(FtbAttendanceDailyRule::getRn));
            user.setUserId(k);
            List<FtbAttendanceDailyRule> list = ruleFilterChain.execute(today, v, overtimeMap, user);
            returnMap.put(k, list);
        });
        return returnMap;
    }

    @Override
    public Map<String, List<FtbAttendanceDailyRule>> getRuleListByUserDay(Map<String, List<FtbAttendanceDailyRule>> groupedMap, Map<String, List<String>> userGroupMap) {

        if (groupedMap.isEmpty()) {
            return Map.of();
        }
        // 查询加班规则 1. 合并所有规则列表
        List<FtbAttendanceDailyRule> allRules = groupedMap.values().stream()
                .flatMap(List::stream)
                // 2. 过滤掉 targetDate != queryDate 的记录
                .filter(rule -> Objects.equals(rule.getTargetDate(), rule.getQueryDate()))
                .collect(Collectors.toList());
        // 3. 按 day 分组，并将 userId 去重收集
        Map<Date, List<String>> dayUserMap = allRules.stream()
                .collect(Collectors.groupingBy(
                        FtbAttendanceDailyRule::getDay, // 分组依据
                        Collectors.mapping(FtbAttendanceDailyRule::getUserId,
                                Collectors.collectingAndThen(Collectors.toSet(), ArrayList::new)) // 去重后转List
                ));
        // 开始查询
        Map<String, Map<String, OvertimeRuleVo>> overtimeDayMap = new HashMap<>();
        dayUserMap.forEach((day, userIds) -> {
            Map<String, OvertimeRuleVo> overtimeMap = overtimeRuleService.getEffectDetailByUserList(userIds, day);
            overtimeDayMap.put(DateDetail.getDate2Str(day, DateDetail.DF), overtimeMap);
        });
        UserInfo currUser = UserProvider.getUser();
        Map<String, List<FtbAttendanceDailyRule>> returnMap = new HashMap<>();
        groupedMap.forEach((k, v) -> {
            String[] split = k.split("_");
            String userId = split[0];
            String targetDate = split[1];
            v.sort(Comparator.comparingInt(FtbAttendanceDailyRule::getRn));
            UserInfo user = new UserInfo();
            BeanUtils.copyProperties(currUser, user);
            user.setUserId(userId);
            List<String> groupIds = userGroupMap.getOrDefault(k, List.of());
            v = clearErrorRule(v, userId, DateDetail.getStr2Date(targetDate), groupIds);
            List<FtbAttendanceDailyRule> list = ruleFilterChain.execute(DateDetail.getStr2Date(targetDate), v, overtimeDayMap.get(targetDate), user);
            DateDetail dateDetail = new DateDetail();
            dateDetail.changeDay(DateDetail.getStr2Date(targetDate));
            dateDetail.getTomorrow();
            String tomorrow = DateDetail.getDate2Str(dateDetail.getCurrentDate(), DateDetail.DF);
            List<FtbAttendanceDailyRule> crossDayList = v.stream().filter(r -> {
                        if (r.getInPoint() == null || r.getOutPoint() == null) {
                            return false;
                        }
                        String inDate = DateDetail.getDate2Str(r.getInPoint(), DateDetail.DF);
                        String outDate = DateDetail.getDate2Str(r.getOutPoint(), DateDetail.DF);
                        return tomorrow.equals(inDate) && inDate.equals(outDate);
                    }).collect(Collectors.toList());
            if (!crossDayList.isEmpty()) {
                List<FtbAttendanceDailyRule> otherList = ruleFilterChain.execute(dateDetail.getCurrentDate(), crossDayList, overtimeDayMap.get(targetDate), user);
                if (!otherList.isEmpty()) {
                    Set<String> ruleIdSet = list.stream()
                            .map(FtbAttendanceDailyRule::getId)
                            .collect(Collectors.toSet());
                    otherList.removeIf(o -> ruleIdSet.contains(o.getId()));
                    list.addAll(otherList);
                }
            }
            returnMap.put(k, list);
        });
        return returnMap;
    }
}
