package ink.wgink.common.service.rbac.impl;

import ink.wgink.common.service.rbac.IRbacService;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.pojo.bos.LoginUser;
import ink.wgink.pojo.bos.RoleGrantedAuthorityBO;
import ink.wgink.pojo.bos.UserInfoBO;
import ink.wgink.properties.AccessControlProperties;
import ink.wgink.properties.LoggingProperties;
import ink.wgink.util.log.LogUtil;
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.Service;
import org.springframework.util.AntPathMatcher;

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

/**
 * @ClassName: RbacServiceImpl
 * @Description: 权限校验
 * @Author: WangGeng
 * @Date: 2019-05-30 15:16
 * @Version: 1.0
 **/
@Service("rbacService")
public class RbacServiceImpl implements IRbacService {

    private static final Logger LOG = LoggerFactory.getLogger(RbacServiceImpl.class);
    @Autowired
    private AccessControlProperties accessControlProperties;
    @Autowired
    private LoggingProperties loggingProperties;

    /**
     * 根路径
     */
    private static final String ANT_PATH_BASE = "/";

    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        Object object = authentication.getPrincipal();
        if (object == null || "anonymousUser".equals(object.toString())) {
            return false;
        }
        boolean hasPermission = false;
        String requestURI = request.getRequestURI();
        Collection<? extends GrantedAuthority> grantedAuthorities = authentication.getAuthorities();
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        String contextPath = request.getContextPath();

        // 校验权限
        for (GrantedAuthority grantedAuthority : grantedAuthorities) {
            if (!(grantedAuthority instanceof RoleGrantedAuthorityBO)) {
                LOG.trace("角色授权格式错误：{}", grantedAuthority);
                continue;
            }
            RoleGrantedAuthorityBO roleGrantedAuthority = (RoleGrantedAuthorityBO) grantedAuthority;
            if (StringUtils.contains(roleGrantedAuthority.getRoleId(), ISystemConstant.ADMIN)) {
                LOG.trace("权限校验URI：{}，当前用户为最高管理员，有所有权限", requestURI);
                hasPermission = true;
                break;
            }
            // 放行权限
            if (hasPassPermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.trace("权限校验URI：{}，有新增权限", requestURI);
                hasPermission = true;
                break;
            }
            if (hasMenuPermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.trace("权限校验URI：{}，有菜单权限", requestURI);
                hasPermission = true;
                break;
            }
            // 是否校验增删改查权限
            if (!accessControlProperties.getRolePermission()) {
                LOG.trace("不校验URI的增、删、改、查权限");
                hasPermission = true;
                break;
            }
            // 新增权限
            if (hasInsertPermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.trace("权限校验URI：{}，有新增权限", requestURI);
                hasPermission = true;
                break;
            }
            // 删除权限
            if (hasDeletePermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.trace("权限校验URI：{}，有删除权限", requestURI);
                hasPermission = true;
                break;
            }
            // 修改权限
            if (hasUpdatePermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.trace("权限校验URI：{}，有修改权限", requestURI);
                hasPermission = true;
                break;
            }
            // 查询权限，查权限最多，最后校验
            if (hasQueryPermission(contextPath, requestURI, roleGrantedAuthority, antPathMatcher)) {
                LOG.trace("权限校验URI：{}，有查询权限", requestURI);
                hasPermission = true;
                break;
            }
        }
        if (object instanceof UserInfoBO) {
            UserInfoBO userInfoBO = (UserInfoBO) object;
            log(hasPermission, requestURI, contextPath, request, userInfoBO.getUserId(), userInfoBO.getUserName());
        }
        if (object instanceof LoginUser) {
            LoginUser loginUser = (LoginUser) object;
            log(hasPermission, requestURI, contextPath, request, loginUser.getUserId(), loginUser.getUserName());
        }
        return hasPermission;
    }

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

    /**
     * 接口的菜单权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasMenuPermission(String contextPath, String uri, RoleGrantedAuthorityBO roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        List<String> menus = roleGrantedAuthority.getMenus();
        for (String menu : menus) {
            if (antPathMatcher.match(contextPath + menu, uri)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 接口的新增权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasInsertPermission(String contextPath, String uri, RoleGrantedAuthorityBO roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        List<String> inserts = roleGrantedAuthority.getInserts();
        for (String insert : inserts) {
            if (antPathMatcher.match(contextPath + insert, uri)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 接口的删除权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasDeletePermission(String contextPath, String uri, RoleGrantedAuthorityBO roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        List<String> deletes = roleGrantedAuthority.getDeletes();
        for (String delete : deletes) {
            if (antPathMatcher.match(contextPath + delete, uri)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 接口的修改权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasUpdatePermission(String contextPath, String uri, RoleGrantedAuthorityBO roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        List<String> updates = roleGrantedAuthority.getUpdates();
        for (String update : updates) {
            if (antPathMatcher.match(contextPath + update, uri)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 接口的查询权限
     *
     * @param uri
     * @param roleGrantedAuthority
     * @param antPathMatcher
     * @return
     */
    private boolean hasQueryPermission(String contextPath, String uri, RoleGrantedAuthorityBO roleGrantedAuthority, AntPathMatcher antPathMatcher) {
        List<String> queries = roleGrantedAuthority.getQueries();
        for (String query : queries) {
            if (antPathMatcher.match(contextPath + query, uri)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 路由日志
     *
     * @param hasPermission
     * @param requestURI
     * @param contextPath
     * @param request
     * @param userId
     * @param userName
     */
    private void log(boolean hasPermission, String requestURI, String contextPath, HttpServletRequest request, String userId, String userName) {
        if (!hasPermission) {
            return;
        }
        if (loggingProperties == null || !loggingProperties.getEnableApiLog()) {
            return;
        }
        String uri = requestURI.replace(contextPath, "");
        if (!uri.startsWith("/route")) {
            return;
        }
        LogUtil.routeLog(request, uri, userId, userName);
    }

}
