package com.apobates.forum.strategy.exposure;

import com.apobates.forum.event.elderly.ForumActionEnum;
import com.apobates.forum.member.entity.Member;
import com.apobates.forum.member.entity.MemberRoleEnum;
import com.apobates.forum.member.entity.MemberStatusEnum;
import com.apobates.forum.strategy.exception.VerificaFailException;
import com.apobates.forum.utils.lang.TriPredicate;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.ImmutableTriple;

/**
 * 抽像的检测器主体
 * 
 * @param <T>
 * @author xiaofanku
 * @since 20200803
 */
public abstract class AbstractDetectionStrategy<T> {
    public final static EnumSet<MemberRoleEnum> EMPTY_ROLES=EnumSet.noneOf(MemberRoleEnum.class);
    public final static EnumSet<MemberRoleEnum> MODERATOR_ROLES=EnumSet.of(MemberRoleEnum.ADMIN, MemberRoleEnum.MASTER, MemberRoleEnum.BM);
    
    public static EnumSet<MemberRoleEnum> allowRoles(MemberRoleEnum... roles){
        return EnumSet.copyOf(Arrays.asList(roles));
    }
    /**
     * 检测入口,依次执行: 
     *  getEntityStrategy, 子类负责实现
     *  getMemberCondition, 默认的,不执行任何检测, 子类可以覆盖实现
     *  getActionCondition 进行昵名和允许的角色检测, 子类可以覆盖实现
     * 若不希望执行以上某个检测可以在子类中覆盖该方法
     * 
     * @param entitiy 实体
     * @param member 当前会员操作者
     * @param forumAction 当前会员进行的操作
     * @param actionAllowRoles 当前操作允许的会员角色集合
     * @return
     * @throws VerificaFailException 
     */
    public ComplexDetectionStrategy<T> detect(T entitiy, Member member, ForumActionEnum forumAction, Set<MemberRoleEnum> actionAllowRoles)throws VerificaFailException{
        DetectionStrategyChain<T> dsc = DetectionStrategyChain.<T>begin()
                    .entity(getEntityStrategy(), ImmutablePair.of(entitiy, forumAction))
                    .member(getMemberCondition(), member)
                    .action(getActionCondition(), ImmutableTriple.of(member, forumAction, actionAllowRoles));
        if(dsc.getBreakException().isPresent()){
            throw dsc.getBreakException().get();
        }
        return new ComplexDetectionStrategy<>(entitiy, member, forumAction, actionAllowRoles);
    }
    
    protected Predicate<Member> getMemberCondition()throws VerificaFailException{
        return (Member culpritor) -> {
            if(MemberStatusEnum.GROUND == culpritor.getStatus()){
                throw new VerificaFailException("您已经被限制访问了");
            }
            return true;
        };
    }
    
    protected TriPredicate<Member, ForumActionEnum, Set<MemberRoleEnum>> getActionCondition(){
        return (Member culpritor, ForumActionEnum action, Set<MemberRoleEnum> allowRoles)->{
            //匿名会员检查
            if (!action.isSupportGuest() && culpritor.getId() < 1) {
                throw new VerificaFailException(String.format("暂不支持匿名会员的%s操作", action.getTitle()));
            }
            // B操作者检查{0: 是否支持匿名|1:角色检查|2:身份检查}
            if(checkMemberRoles(culpritor.getMrole(), action, allowRoles)){
                return true;
            }
            throw new VerificaFailException("操作因权限检测而中断");
        };
    }
    
    protected abstract BiPredicate<T, ForumActionEnum> getEntityStrategy()throws VerificaFailException;
    
    // 检查操作者的角色
    private static boolean checkMemberRoles(MemberRoleEnum memberRole, ForumActionEnum forumAction, Set<MemberRoleEnum> actionAllowRoles) throws VerificaFailException {
        boolean isIntercept = true; 
        Set<MemberRoleEnum> ar = actionAllowRoles;
        //
        if (ar.isEmpty()) { //不限角色,只要不是管理操作
            isIntercept = !isContinueManagerAction(forumAction, memberRole);
        } else {
            if (ar.contains(memberRole)) {
                isIntercept = !isContinueManagerAction(forumAction, memberRole);
            }
        }
        // 限制角色的操作
        if (isIntercept) {
            throw new VerificaFailException(forumAction.getTitle() + "操作需要的角色您暂时不具备");
        }
        return true;
    }
    
    // 这个操作是否是只有管理员能干
    private static boolean isManagerAction(ForumActionEnum action) {
        if (ForumActionEnum.POSTS_EDIT == action) { // 作者和版主都可以
            return false;
        }
        Set<ForumActionEnum> ma = ForumActionEnum.getModeratorActions();
        return ma.contains(action);
    }
    
    private static boolean isContinueManagerAction(ForumActionEnum action, MemberRoleEnum memberRole) {
        //操作是否受限:当前会员的角色对应的操作是否包含当前操作
        //是版主的操作 && 当前的会员是版主
        //A版主的版主可以操作B版的版主操作吗?缺少针对当前版块的检查
        if (isManagerAction(action)) {
            return MemberRoleEnum.getManagerRoles().contains(memberRole);
        }
        return true;
    }
}