package jnpf.attendance.service.handle.chain;

import jnpf.attendance.service.filter.RuleFilter;
import jnpf.attendance.service.filter.RuleFilterContext;
import jnpf.attendance.service.filter.RuleFilterResult;
import jnpf.base.UserInfo;
import jnpf.entity.attendance.AttendanceRuleScope;
import jnpf.entity.attendance.FtbAttendanceDailyRule;
import jnpf.enums.attendance.AttendanceTypeEnum;
import jnpf.enums.attendance.v2.WorkBoundaryCoverageEnum;
import jnpf.model.attendance.vo.attendance.OvertimeRuleVo;
import jnpf.util.ConstantUtil;
import jnpf.util.DateDetail;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 出勤规则过滤链执行器
 *
 * @author yanwenfu
 * @create 2025-09-25
 */
@Component
public class RuleFilterChain {

    private final List<RuleFilter> filters;

    public RuleFilterChain(List<RuleFilter> filters) {
        // 按照 getOrder 排序，保证执行顺序
        this.filters = filters.stream()
                .sorted(Comparator.comparingInt(RuleFilter::getOrder))
                .collect(Collectors.toList());
    }

    /**
     * 获取今日可打卡出勤规则+数据填写
     * @param curDate 当前日期
     * @param ruleList 出勤规则列表(当前+前一天)
     * @param map k:用户id, v:加班规则
     * @param userInfo 用户信息
     * @return java.util.List<jnpf.entity.attendance.FtbAttendanceDailyRule>
     */
    public List<FtbAttendanceDailyRule> execute(Date curDate, List<FtbAttendanceDailyRule> ruleList, Map<String, OvertimeRuleVo> map, UserInfo userInfo) {
        RuleFilterContext context = new RuleFilterContext(curDate, ruleList, map);
        stop : for (FtbAttendanceDailyRule rule : ruleList) {
            boolean keep = false;
            for (RuleFilter filter : filters) {
                RuleFilterResult result = filter.keepRule(rule, context, userInfo);
                if (result.isTerminate()) {
                    // 终止整个责任链
                    break stop;
                }
                if (result.isKeep()) {
                    // 不保留，但继续走
                    keep = true;
                    break;
                }
            }
            if (keep) {
                context.getCollectList().add(rule);
            }
        }
        // 填值
        if (context.getCollectList().isEmpty()) {
            return new ArrayList<>();
        }
        // 去除半天休
        List<String> restRuleList = context.getCollectList().stream()
                .filter(v -> DateDetail.checkSameDay(context.getCurDate(), v.getDay()) && v.getAttendanceType().equals(AttendanceTypeEnum.REST.getCode()))
                .map(FtbAttendanceDailyRule::getId)
                .collect(Collectors.toList());
        long count = context.getCollectList().stream().filter(v -> DateDetail.checkSameDay(context.getCurDate(), v.getDay())).count();
        if (!restRuleList.isEmpty() && count > 1) {
            context.getCollectList().removeIf(v -> restRuleList.contains(v.getId()));
        }
        // 今日所有需要打卡的出勤规则
        List<FtbAttendanceDailyRule> addList = new ArrayList<>();
        context.getCollectList().forEach(rule -> {
            if (rule.getAttendanceType().equals(AttendanceTypeEnum.BUSINESS_TRIP.getCode()) || rule.getAttendanceType().equals(AttendanceTypeEnum.STEP_OUT.getCode())) {
                // 外出或出差, 且无班次则设置班次类型为-1
                rule.setAttendanceType(AttendanceTypeEnum.DEFAULT.getCode());
                rule.setInStepOutType(ConstantUtil.NUM_TRUE);
                rule.setOutStepOutType(ConstantUtil.NUM_TRUE);
            }
            // 普通班次 且 允许无需审批的加班 缺卡时间延后 与加班下班相同
            if (rule.getAttendanceType().equals(AttendanceTypeEnum.ORDINARY.getCode()) && rule.getOvertime().equals(ConstantUtil.NUM_TRUE)) {
                outLackSet(rule, context);
            }
            // 请假
            if (rule.getInUnbounded().equals(WorkBoundaryCoverageEnum.LEAVE_COVERED.getCode())) {
                rule.setMsg("请假回岗");
                // rule.setInLackPoint(rule.getInPoint());
            }
            if (rule.getOutUnbounded().equals(WorkBoundaryCoverageEnum.LEAVE_COVERED.getCode())) {
                rule.setMsg("请假离岗");
                // rule.setOutLackPoint(rule.getOutPoint());
            }
            // 借调
            if (rule.getInUnbounded().equals(WorkBoundaryCoverageEnum.SECONDMENT_COVERED.getCode())) {
                rule.setInHideStatus(ConstantUtil.STR_TRUE);
                // 在这个时间点生成无需打卡 -2
                rule.setOnWorkIgnore(true);
                rule.setInLackPoint(rule.getInPoint());
            }
            if (rule.getOutUnbounded().equals(WorkBoundaryCoverageEnum.SECONDMENT_COVERED.getCode())) {
                rule.setOutHideStatus(ConstantUtil.STR_TRUE);
                // 在这个时间点生成无需打卡 -2
                rule.setOffWorkIgnore(true);
                rule.setOutLackPoint(rule.getOutPoint());
            }
            // 加班
            if (rule.getInUnbounded().equals(WorkBoundaryCoverageEnum.OVERTIME_COVERED.getCode())) {
                rule.setInHideStatus(ConstantUtil.STR_TRUE);
                // 在这个时间点生成无需打卡 -2
                rule.setOnWorkIgnore(true);
                rule.setClockStartPoint(rule.getInPoint());
                rule.setInLackPoint(rule.getInPoint());
                setAnother(ConstantUtil.ON_WORK, context, rule, addList);
            }
            if (rule.getOutUnbounded().equals(WorkBoundaryCoverageEnum.OVERTIME_COVERED.getCode())) {
                rule.setOutHideStatus(ConstantUtil.STR_TRUE);
                // 在这个时间点生成无需打卡 -2
                rule.setOffWorkIgnore(true);
                rule.setOutLackPoint(rule.getOutPoint());
                setAnother(ConstantUtil.OFF_WORK, context, rule, addList);
            }
            // 晚走晚到
            if (rule.getInUnbounded().equals(WorkBoundaryCoverageEnum.LATE_LEAVE_LATE_ARRIVE.getCode())) {
                // 在这个时间点生成无需打卡 -2
                rule.setOnWorkIgnore(true);
            }
            if (rule.getOutUnbounded().equals(WorkBoundaryCoverageEnum.LATE_LEAVE_LATE_ARRIVE.getCode())) {
                // 在这个时间点生成无需打卡 -2
                rule.setOffWorkIgnore(true);
            }
            if (rule.getAttendanceType().equals(AttendanceTypeEnum.WORKOVERTIME.getCode())) {
                // 按审批时长申请计算 无需打卡
                if (rule.getOvertimeRuleDetail().getCalcMethod().equals(1)) {
                    rule.setOnWorkIgnore(true);
                    rule.setOffWorkIgnore(true);
                }
                // 查询上一个班次的下班时间
                inLackSet(rule, context);
                // 查询下一个班次的上班时间
                outLackSet(rule, context);
            }
        });
        context.getCollectList().addAll(addList);
        context.getCollectList().sort(
                Comparator.comparing(
                        FtbAttendanceDailyRule::getInPoint,
                        Comparator.nullsLast(Comparator.naturalOrder())
                )
        );
        return context.getCollectList();
    }

    private void outLackSet(FtbAttendanceDailyRule rule, RuleFilterContext context) {
        // 加班 或 普班无审批加班
        if (null == rule.getOutLackPoint() || (rule.getAttendanceType().equals(AttendanceTypeEnum.ORDINARY.getCode()) && rule.getOvertime().equals(ConstantUtil.NUM_TRUE))) {
            if (rule.getRn() - 1 > 0) {
                FtbAttendanceDailyRule nextRule = context.getRuleList().get(rule.getRn() - 2);
                if (null != nextRule.getInPoint()) {
                    rule.setOutLackPoint(nextRule.getInPoint());
                } else {
                    if (!DateDetail.checkSameDay(rule.getDay(), context.getCurDate()) && DateDetail.checkSameDay(rule.getOutPoint(), context.getCurDate())) {
                        // 昨天的班下班时间在今天, 所以缺卡时间最多到今晚24点
                        rule.setOutLackPoint(DateDetail.endOfDay(rule.getOutPoint()));
                    } else {
                        rule.setOutLackPoint(DateDetail.getNextDay(rule.getOutPoint()));
                    }
                }
            } else {
                // 无下一个班次 暂时设置为24小时后, 明天会重新获取
                rule.setOutLackPoint(DateDetail.getNextDay(rule.getOutPoint()));
            }
        }
    }

    private void inLackSet(FtbAttendanceDailyRule rule, RuleFilterContext context) {
        if (null == rule.getInLackPoint()) {
            if (rule.getRn() + 1 < context.getRuleList().size()) {
                FtbAttendanceDailyRule dailyRule = context.getRuleList().get(rule.getRn());
                rule.setClockStartPoint(null == dailyRule.getOutPoint() ? DateDetail.beginOfDay(rule.getInPoint()) : dailyRule.getOutPoint());
            } else {
                // 无上一个班次 设置为上班时间的0点
                rule.setClockStartPoint(DateDetail.beginOfDay(rule.getInPoint()));
            }
            rule.setInLackPoint(rule.getOutPoint());
        }
    }

    /**
     * 设置无需打卡时间
     */
    private void setAnother(Integer workStatus, RuleFilterContext context, FtbAttendanceDailyRule rule, List<FtbAttendanceDailyRule> addList) {

        FtbAttendanceDailyRule dailyRule = context.getMap().get(rule.getId());
        if (null != dailyRule) {
            FtbAttendanceDailyRule r = context.getCollectList().stream().filter(v -> v.getId().equals(dailyRule.getId())).findFirst().orElse(null);
            if (null == r) {
                if (workStatus.equals(ConstantUtil.ON_WORK)) {
                    dailyRule.setOutHideStatus(ConstantUtil.STR_TRUE);
                    dailyRule.setOffWorkIgnore(true);
                } else {
                    dailyRule.setInHideStatus(ConstantUtil.STR_TRUE);
                    dailyRule.setOnWorkIgnore(true);
                }
                addList.add(dailyRule);
            }
        }
    }
}
