package com.engine.salary.entity.salaryBill.bo;

import com.engine.salary.entity.salaryBill.dto.SalarySendUserInfoDTO;
import com.engine.salary.entity.salaryBill.po.SalarySendRangeObj;
import com.engine.salary.enums.salarysend.SalarySendRangeTargetTypeEnum;
import com.engine.salary.enums.salarysend.SalarySendRangeTypeEnum;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;

/**
 * @Description: 工资单发放范围
 */
@NoArgsConstructor
public class SalarySendRangeBO {
    private static final SalarySendRangeItem EMPTY_ITEM = new SalarySendRangeItem(null, null, Collections.emptySet());

    private static final Integer INCLUDED = 1;
    private static final Integer EXCLUDED = -1;
    private static final Integer NOT_IN = 0;

    /**
     * 索引map
     */
    private final Map<SalarySendRangeTargetTypeEnum, Map<SalarySendRangeTypeEnum, SalarySendRangeItem>> indexMap
            = new HashMap<>(6);

    /**
     * 排除所有人
     */
    private boolean isExcludeAll = false;

    /**
     * 增加所有人
     */
    private boolean isIncludeAll = false;

    public boolean putNew(SalarySendRangeObj salarySendRangeObj) {
        //如果已经设置了排除所有人 或者 添加所有人， 直接跳过处理
        if (isExcludeAll) {
            return true;
        }
        Integer rangeType = salarySendRangeObj.getRangeType();
        SalarySendRangeTypeEnum rangeTypeE = SalarySendRangeTypeEnum.fromValue(rangeType);

        Integer targetType = salarySendRangeObj.getTargetType();
        SalarySendRangeTargetTypeEnum targetTypeE = SalarySendRangeTargetTypeEnum.fromValue(targetType);

        if (rangeTypeE == null || targetTypeE == null) {
            return false;
        }
        //所有人 筛选
        if (targetTypeE == SalarySendRangeTargetTypeEnum.ALL) {
            isIncludeAll = rangeTypeE == SalarySendRangeTypeEnum.INCLUDE_OBJ;
            isExcludeAll = rangeTypeE == SalarySendRangeTypeEnum.EXCLUDE_OBJ;
        }
        //未存入时插入
        indexMap.computeIfAbsent(targetTypeE, k -> {
            Map<SalarySendRangeTypeEnum, SalarySendRangeItem> value = new HashMap<>();
            SalarySendRangeItem rangeItem =
                    new SalarySendRangeItem(rangeTypeE, targetTypeE, Sets.newHashSet(salarySendRangeObj.getTargetId()));
            value.put(rangeTypeE, rangeItem);
            return value;
        });
        //已存在时更新
        indexMap.computeIfPresent(targetTypeE, (k, v) -> {
            SalarySendRangeItem item = v.get(rangeTypeE);
            if (null == item) {
                item = new SalarySendRangeItem(rangeTypeE, targetTypeE, Sets.newHashSet(salarySendRangeObj.getTargetId()));
            } else {
                item.getTargetIds().add(salarySendRangeObj.getTargetId());
            }
            v.put(rangeTypeE, item);
            return v;
        });
        return true;
    }

    private Set<Long> getTargetIds(SalarySendRangeTargetTypeEnum targetType, SalarySendRangeTypeEnum rangeType) {
        Map<SalarySendRangeTypeEnum, SalarySendRangeItem> itemMap = indexMap.get(targetType);
        if (itemMap != null) {
            SalarySendRangeItem item = itemMap.getOrDefault(rangeType, EMPTY_ITEM);
            return item.getTargetIds();
        }
        return Collections.emptySet();
    }

    /**
     * 判断是否在人员范围内
     * 如果已经有 所有人 的范围设置，不判断是否有其他范围直接返回
     *
     * @param targetType 目标类型
     * @param targetId   目标id
     * @return 是否在范围内
     */
    private int doJudgeInRange(SalarySendRangeTargetTypeEnum targetType, Long targetId) {
        //如果所有人范围已经设置
        if (isExcludeAll) {
            return EXCLUDED;
        }
        Set<Long> includeTargetIds = getTargetIds(targetType, SalarySendRangeTypeEnum.INCLUDE_OBJ);
        Set<Long> excludeTargetIds = getTargetIds(targetType, SalarySendRangeTypeEnum.EXCLUDE_OBJ);
        if (excludeTargetIds.contains(targetId)) {
            return EXCLUDED;
        } else if (includeTargetIds.contains(targetId)) {
            return INCLUDED;
        } else {
            //默认值是是否选择所有人
            return isIncludeAll ? INCLUDED : NOT_IN;
        }
    }

    private int doJudgeInRange(SalarySendRangeTargetTypeEnum targetType, Collection<Long> targetIds) {
        if (CollectionUtils.isEmpty(targetIds)) {
            return NOT_IN;
        }
        //如果所有人范围已经设置
        if (isExcludeAll) {
            return EXCLUDED;
        }
        if (isIncludeAll) {
            return INCLUDED;
        }
        Set<Long> includeTargetIds = getTargetIds(targetType, SalarySendRangeTypeEnum.INCLUDE_OBJ);
        Set<Long> excludeTargetIds = getTargetIds(targetType, SalarySendRangeTypeEnum.EXCLUDE_OBJ);
        if (targetIds.stream().anyMatch(excludeTargetIds::contains)) {
            return EXCLUDED;
        } else if (targetIds.stream().anyMatch(includeTargetIds::contains)) {
            return INCLUDED;
        } else {
            return NOT_IN;
        }
    }

    public boolean judgeInRange(SalarySendUserInfoDTO dto) {
        //如果所有人范围已经设置
        if (isExcludeAll) {
            return false;
        }
        Integer isInRange = NOT_IN;
        // 默认值为是否选择所有人
        boolean result = isIncludeAll;
        for (SalarySendRangeTargetTypeEnum targetType : SalarySendRangeTargetTypeEnum.values()) {
            switch (targetType) {
                case DEPT:
                    isInRange = doJudgeInRange(targetType, dto.getDepartmentId());
                    break;
                case USER:
                    isInRange = doJudgeInRange(targetType, dto.getResourceId());
                    break;
                case POSITION:
                    isInRange = doJudgeInRange(targetType, dto.getJobTitle());
                    break;
//                case TAX_AGENT:
//                    isInRange = doJudgeInRange(targetType, dto.getTaxAgentIds());
//                    break;
                case SUB_COMPANY:
                    isInRange = doJudgeInRange(targetType, dto.getSubCompanyId());
                    break;
            }
            if (isInRange.equals(EXCLUDED)) {
                //如果被排除，返回false
                return false;
            } else {
                // 只要没有被排除，在范围内即为true
                result |= isInRange.equals(INCLUDED);
            }
        }
        return result;
    }

    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    private static class SalarySendRangeItem {
        private SalarySendRangeTypeEnum rangeType;

        private SalarySendRangeTargetTypeEnum targetType;

        private Set<Long> targetIds;
    }
}
