package com.cm.common.plugin.oauth.service.rbac.impl;

import com.cm.common.config.properties.AccessControl;
import com.cm.common.plugin.oauth.service.rbac.IClientRbacService;
import com.cm.common.pojo.bos.PermissionBO;
import com.cm.common.pojo.bos.RoleGrantedAuthority;
import com.cm.common.pojo.bos.RoleMenuBO;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: ClientRbacServiceImpl
 * @Description: 客户端RBAC权限校验
 * @Author: WangGeng
 * @Date: 2019/11/11 3:27 下午
 * @Version: 1.0
 **/
@Component("clientRbacService")
public class ClientRbacServiceImpl implements IClientRbacService {

    private static final Logger LOG = LoggerFactory.getLogger(ClientRbacServiceImpl.class);
    /**
     * 根路径
     */
    private static final String ANT_PATH_BASE = "/";
    @Autowired
    private AccessControl accessControl;

    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        boolean hasPermission = false;
        Object principal = authentication.getPrincipal();
        if (Objects.isNull(principal) || StringUtils.equals("anonymousUser", principal.toString())) {
            return false;
        }
        String requestURI = request.getRequestURI();
        Collection<? extends GrantedAuthority> grantedAuthorities = authentication.getAuthorities();
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        String contextPath = request.getContextPath();
        for (GrantedAuthority grantedAuthority : grantedAuthorities) {
            RoleGrantedAuthority roleGrantedAuthority = (RoleGrantedAuthority) grantedAuthority;
            if (StringUtils.contains(roleGrantedAuthority.getAuthority(), "_ALL")) {
                LOG.debug("权限校验URI：{}，当前用户为最高管理员，有所有权限", requestURI);
                hasPermission = true;
                break;
            }
            // 放行权限
            if (hasPassPermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.debug("权限校验URI：{}，有新增权限", requestURI);
                hasPermission = true;
                break;
            }
            // 新增权限
            if (hasSavePermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.debug("权限校验URI：{}，有新增权限", requestURI);
                hasPermission = true;
                break;
            }
            // 删除权限
            if (hasDeletePermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.debug("权限校验URI：{}，有删除权限", requestURI);
                hasPermission = true;
                break;
            }
            // 修改权限
            if (hasUpdatePermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.debug("权限校验URI：{}，有修改权限", requestURI);
                hasPermission = true;
                break;
            }
            // 查询权限
            if (hasQueryPermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.debug("权限校验URI：{}，有查询权限", requestURI);
                hasPermission = true;
                break;
            }
        }
        return hasPermission;
    }

    /**
     * 放行权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasPassPermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        if ((contextPath + ANT_PATH_BASE).equals(uri)) {
            return true;
        }
        List<String> passPaths = accessControl.getPassPaths();
        for (String passPath : passPaths) {
            if (antPathMatcher.match(contextPath + passPath, uri)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 接口的新增权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasSavePermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        // 匹配接口权限
        for (PermissionBO permissionBO : roleGrantedAuthority.getPermissionInsert()) {
            if (antPathMatcher.match(contextPath + permissionBO.getPermissionUrl(), uri)) {
                return true;
            }
        }
        List<String> savePaths = accessControl.getSavePaths();
        // 匹配接口
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getApiSaveMenu()) {
            for (String savePath : savePaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getApiPrefix() + savePath, uri)) {
                    return true;
                }
            }
        }
        // 匹配资源
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getResourceSaveMenu()) {
            for (String savePath : savePaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getResourcePrefix() + savePath, uri)) {
                    return true;
                }
            }
        }
        // 匹配路由
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getRouteSaveMenu()) {
            for (String savePath : savePaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getRoutePrefix() + savePath, uri)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 接口的删除权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasDeletePermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        // 匹配接口权限
        for (PermissionBO permissionBO : roleGrantedAuthority.getPermissionDelete()) {
            if (antPathMatcher.match(contextPath + permissionBO.getPermissionUrl(), uri)) {
                return true;
            }
        }
        List<String> deletePaths = accessControl.getDeletePaths();
        // 匹配接口
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getApiDeleteMenu()) {
            for (String deletePath : deletePaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getApiPrefix() + deletePath, uri)) {
                    return true;
                }
            }
        }
        // 匹配资源
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getResourceDeleteMenu()) {
            for (String deletePath : deletePaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getResourcePrefix() + deletePath, uri)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 接口的修改权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasUpdatePermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        // 匹配接口权限
        for (PermissionBO permissionBO : roleGrantedAuthority.getPermissionUpdate()) {
            if (antPathMatcher.match(contextPath + permissionBO.getPermissionUrl(), uri)) {
                return true;
            }
        }
        List<String> updatePaths = accessControl.getUpdatePaths();
        // 匹配接口
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getApiUpdateMenu()) {
            for (String updatePath : updatePaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getApiPrefix() + updatePath, uri)) {
                    return true;
                }
            }
        }
        // 匹配资源
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getResourceUpdateMenu()) {
            for (String updatePath : updatePaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getResourcePrefix() + updatePath, uri)) {
                    return true;
                }
            }
        }
        // 匹配路由
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getRouteUpdateMenu()) {
            for (String updatePath : updatePaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getRoutePrefix() + updatePath, uri)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 接口的查询权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasQueryPermission(String contextPath, String uri, RoleGrantedAuthority roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        // 匹配接口权限
        for (PermissionBO permissionBO : roleGrantedAuthority.getPermissionQuery()) {
            if (antPathMatcher.match(contextPath + permissionBO.getPermissionUrl(), uri)) {
                return true;
            }
        }
        List<String> queryPaths = accessControl.getQueryPaths();
        // 匹配接口
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getApiQueryMenu()) {
            for (String queryPath : queryPaths) {
                String queryAntPath = contextPath + roleMenuBO.getApiPrefix() + queryPath;
                if (antPathMatcher.match(queryAntPath, uri)) {
                    return true;
                }
            }
        }
        // 匹配资源
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getResourceQueryMenu()) {
            for (String queryPath : queryPaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getResourcePrefix() + queryPath, uri)) {
                    return true;
                }
            }
        }
        // 匹配路由
        for (RoleMenuBO roleMenuBO : roleGrantedAuthority.getRouteQueryMenu()) {
            for (String queryPath : queryPaths) {
                if (antPathMatcher.match(contextPath + roleMenuBO.getRoutePrefix() + queryPath, uri)) {
                    return true;
                }
            }
        }
        return false;
    }

}
