package jnpf.attendance.service.handle.rule;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import jnpf.attendance.service.RuleProcessor;
import jnpf.constants.AttendanceConstant;
import jnpf.entity.attendance.FtbAttendanceDailyRule;
import jnpf.enums.attendance.AttendanceTypeEnum;
import jnpf.model.attendance.vo.DailyRuleResultVo;
import jnpf.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 处理历史出差、外出申请
 */
@Component
@Slf4j
public class StepOutRuleProcessor extends RuleProcessor {
    @Override
    public void ruleArrangementHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<FtbAttendanceDailyRule> currDailyRule, List<DailyRuleResultVo> resultList) {

        Map<Integer, List<FtbAttendanceDailyRule>> collect = hisDailyRules.stream().collect(Collectors.groupingBy(FtbAttendanceDailyRule::getAttendanceType));
        List<FtbAttendanceDailyRule> ftbAttendanceDailyRules = CollUtil.newArrayList();
        if (collect.containsKey(AttendanceTypeEnum.BUSINESS_TRIP.getCode())) {
            ftbAttendanceDailyRules.addAll(collect.get(AttendanceTypeEnum.BUSINESS_TRIP.getCode()));
        }
        if (collect.containsKey(AttendanceTypeEnum.STEP_OUT.getCode())) {
            ftbAttendanceDailyRules.addAll(collect.get(AttendanceTypeEnum.STEP_OUT.getCode()));
        }
        if (CollUtil.isNotEmpty(ftbAttendanceDailyRules)) {
            currDailyRule.forEach(rule -> {
                //新增清除
                if (clearHandle(hisDailyRules, ftbAttendanceDailyRules, rule, resultList)) {
                    return;
                }
                if (!hisDailyRules.contains(rule) && !rule.getIsInsert()) {
                    hisDailyRules.add(rule);
                }

            });
            Iterator<FtbAttendanceDailyRule> iterator = currDailyRule.iterator();
            while (iterator.hasNext()) {
                FtbAttendanceDailyRule rule = iterator.next();
                //新增清除
                if (Objects.equals(AttendanceTypeEnum.CLEAR.getCode(), rule.getAttendanceType())) {
                    continue;
                }
                daysStepOutHandle(hisDailyRules, resultList, ftbAttendanceDailyRules, rule);
                housesStepOutHandle(hisDailyRules, resultList, ftbAttendanceDailyRules, rule);
            }
        }
        if (Objects.nonNull(nextRuleProcessor)) {
            nextRuleProcessor.ruleArrangementHandle(hisDailyRules, currDailyRule, resultList);
        }
    }

    /**
     * 处理单位为天的外出、出差规则冲突
     * 该方法用于处理在已有的日常规则结果列表中，新加入的外出或出差规则与已有规则之间的冲突
     * 如果新规则与已有规则冲突且为外出或出差类型，则记录错误日志并添加失败结果到列表中
     * 如果不冲突，则更新新旧规则的申请查看权限，并添加成功结果到列表中
     *
     * @param resultList              日常规则结果列表，用于存储处理结果
     * @param ftbAttendanceDailyRules 已有的日常规则列表，用于比较和更新
     * @param rule                    新加入的日常规则，用于检查冲突和更新
     */
    private void daysStepOutHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<DailyRuleResultVo> resultList, List<FtbAttendanceDailyRule> ftbAttendanceDailyRules, FtbAttendanceDailyRule rule) {
        List<FtbAttendanceDailyRule> collect = ftbAttendanceDailyRules.stream().filter(oldRule -> Objects.equals(oldRule.getApplyUnit(), 2) || Objects.isNull(oldRule.getApplyUnit())).collect(Collectors.toList());
        collect.forEach(oldRule -> {
            if (StringUtil.equals(oldRule.getId(), rule.getId())) {
                return;
            }
            if (Objects.equals(AttendanceTypeEnum.STEP_OUT.getCode(), rule.getAttendanceType()) || Objects.equals(AttendanceTypeEnum.BUSINESS_TRIP.getCode(), rule.getAttendanceType())) {
                log.error(AttendanceConstant.getSchedulingResult(rule.getUserId(), rule.getDay(), AttendanceTypeEnum.getMsg(oldRule.getAttendanceType()), AttendanceTypeEnum.getMsg(rule.getAttendanceType())));
                resultList.add(DailyRuleResultVo.failBuild(oldRule, rule, oldRule.getAttendanceType(), rule.getAttendanceType()));
                log.error("新增外出、出差申请与原外出、出差申请冲突{}", JSON.toJSONString(rule));
                return;
            }
            rule.setApplyViewEnable(oldRule.getAttendanceType());
            rule.setInStepOutType(1);
            rule.setOutStepOutType(1);
            rule.setInStepOutApplyId(oldRule.getApplyId());
            rule.setOutStepOutApplyId(oldRule.getApplyId());
            oldRule.setApplyViewEnable(0);
            log.error("原外出、出差申请标记新增排班时段{}", JSON.toJSONString(rule));
            if (!oldRule.getIsInsert() && !hisDailyRules.contains(rule) && !rule.getIsInsert()) {
                hisDailyRules.add(rule);
                rule.insertTrue();
            }
            resultList.add(DailyRuleResultVo.successBuild(oldRule, rule));
        });
    }

    /**
     * 处理单位为小时的外出、出差规则冲突
     * 该方法用于处理在已有的日常规则结果列表中，新加入的外出或出差规则与已有规则之间的冲突
     * 如果新规则与已有规则冲突且为外出或出差类型，则记录错误日志并添加失败结果到列表中
     * 如果不冲突，则更新新旧规则的申请查看权限，并添加成功结果到列表中
     *
     * @param resultList              日常规则结果列表，用于存储处理结果
     * @param ftbAttendanceDailyRules 已有的日常规则列表，用于比较和更新
     * @param rule                    新加入的日常规则，用于检查冲突和更新
     */
    private void housesStepOutHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<DailyRuleResultVo> resultList, List<FtbAttendanceDailyRule> ftbAttendanceDailyRules, FtbAttendanceDailyRule rule) {
        List<FtbAttendanceDailyRule> collect = ftbAttendanceDailyRules.stream().filter(oldRule -> Objects.equals(oldRule.getApplyUnit(), 1) && !oldRule.equals(rule)).collect(Collectors.toList());
        collect.forEach(stepOutRule -> {
            if (Objects.equals(rule.getAttendanceType(), AttendanceTypeEnum.STEP_OUT.getCode()) || Objects.equals(rule.getAttendanceType(), AttendanceTypeEnum.BUSINESS_TRIP.getCode())) {
                if (!stepOutRule.getOutPoint().before(rule.getInPoint()) && !stepOutRule.getInPoint().after(rule.getOutPoint())) {
                    resultList.add(DailyRuleResultVo.failBuild(stepOutRule, rule, stepOutRule.getAttendanceType(), rule.getAttendanceType()));
                    log.error("新增小时外出申请与原外出申请冲突{}", JSON.toJSONString(rule));
                    return;
                }
            }
            if(Objects.equals(rule.getAttendanceType(), AttendanceTypeEnum.REST.getCode()) && Objects.isNull(rule.getInPoint()) ){
                rule.setApplyViewEnable(0);
                if (!hisDailyRules.contains(rule) && !rule.getIsInsert()) {
                    hisDailyRules.add(rule);
                    rule.insertTrue();
                }
                return;
            }
            //原小时外出没命中已有班次数据，隐藏外出申请
            if (stepOutRule.getOutPoint().before(rule.getInPoint())
                    || stepOutRule.getInPoint().after(rule.getOutPoint())
                    || stepOutRule.getInPoint().after(rule.getInPoint()) && stepOutRule.getOutPoint().before(rule.getOutPoint())) {
                if (!stepOutRule.getIsInsert() && !hisDailyRules.contains(rule) && !rule.getIsInsert()) {
                    hisDailyRules.add(rule);
                    rule.insertTrue();
                }
                log.error("排班数据未命中小时外出申请或者外出数据完全被排班数据覆盖，过滤{}", JSON.toJSONString(rule));
                resultList.add(DailyRuleResultVo.successNailBuild(rule));
                return;
            }
            stepOutRule.setApplyViewEnable(0);
            rule.setApplyViewEnable(stepOutRule.getAttendanceType());
            //原小时外出数据覆盖现在新增排班数据上班时间
            if (!stepOutRule.getInPoint().after(rule.getInPoint())) {
                rule.setInStepOutType(1);
                rule.setInStepOutApplyId(stepOutRule.getApplyId());
            }
            //原小时外出数据覆盖现在新增排班数据下班时间
            if (!stepOutRule.getOutPoint().before(rule.getOutPoint())) {
                rule.setOutStepOutType(1);
                rule.setOutStepOutApplyId(stepOutRule.getApplyId());
            }
            if (!stepOutRule.getIsInsert() && !hisDailyRules.contains(rule) && !rule.getIsInsert()) {
                hisDailyRules.add(rule);
                rule.insertTrue();
            }
            log.error("新增外出、出差申请标记排班时段{}", JSON.toJSONString(rule));
            resultList.add(DailyRuleResultVo.successBuild(stepOutRule, rule));
        });
    }

    /**
     * 新增清除
     *
     * @param hisDailyRules           历史规则
     * @param ftbAttendanceDailyRules 新增规则集合
     * @param rule                    当前规则
     */
    private Boolean clearHandle(List<FtbAttendanceDailyRule> hisDailyRules, List<FtbAttendanceDailyRule> ftbAttendanceDailyRules, FtbAttendanceDailyRule rule, List<DailyRuleResultVo> resultList) {
        //新增清除
        if (!Objects.equals(AttendanceTypeEnum.CLEAR.getCode(), rule.getAttendanceType())) {
            return Boolean.FALSE;
        }
        ftbAttendanceDailyRules.forEach(oldRule -> oldRule.setApplyViewEnable(oldRule.getAttendanceType()));
        return Boolean.TRUE;
    }
}
