package com.artfess.base.security;

import com.artfess.base.cache.annotation.CachePut;
import com.artfess.base.constants.CacheKeyConst;
import com.artfess.base.jwt.JwtTokenHandler;
import com.artfess.base.util.AuthenticationUtil;
import com.artfess.base.util.BeanUtils;
import com.artfess.base.util.ContextThread;
import com.artfess.base.util.StringUtil;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;


@Service
public class HtInvocationSecurityMetadataSourceService  implements FilterInvocationSecurityMetadataSource, ContextThread {
	// security认证对象的线程变量 Authentication
	private static ThreadLocal<HashMap<String, Collection<ConfigAttribute>>> mapThreadLocal = new ThreadLocal<HashMap<String, Collection<ConfigAttribute>>>();

    @Resource
    private MethodAuthService methodAuthService;
    @Resource
    JwtTokenHandler jwtTokenHandler;

    /**
     * 加载权限表中所有权限
     *
     * 同一个方法可能多个角色都具有访问权限
     *
     */
	public void loadResourceDefine(){
		HashMap<String, Collection<ConfigAttribute>> map = getMapThreadLocal();
        Collection<ConfigAttribute> array;
        ConfigAttribute cfg;
        List<HashMap<String, String>> methodAuth = methodAuthService.getMethodAuth();
        if (BeanUtils.isEmpty(methodAuth)) {
			return;
		}
        for(HashMap<String, String> mapAuth : methodAuth) {
            array = new ArrayList<ConfigAttribute>();
            String roleAlias = mapAuth.get("roleAlias");
            String key = mapAuth.get("methodRequestUrl");
            if(StringUtil.isEmpty(roleAlias) || StringUtil.isEmpty(key) ) {
            	continue;
            }
            cfg = new SecurityConfig(roleAlias);

			if(map.containsKey(key)){
               array = map.get(key);
            }
        	array.add(cfg);
            map.put(key, array);
        }
    }

    @CachePut(value = CacheKeyConst.EIP_SYS_DATA_PERMISSION, key="#key")
    protected String putDataPermissionInCache(String key, String data) {
    	return data;
    }

    /**
     * 此方法是为了判定用户请求的url 是否在权限表中，如果在权限表中，则返回给 decide 方法，用来判定用户是否有此权限。如果不在权限表中则放行。
     * 返回空 ， 则不用经过 decide 方法 判断权限， 直接具有访问权限了
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {

        AntPathRequestMatcher matcher;
        String resUrl;
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        // 匿名访问不需要获取权限信息
        if(AuthenticationUtil.isAnonymous(authentication)) return null;

        // object 中包含用户请求的request 信息
        HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
        // 判断请求是否从feign过来的
        String requestHeader = request.getHeader("Proxy-Authorization");
		if (StringUtil.isNotEmpty(requestHeader) && requestHeader.startsWith("Bearer ") &&  jwtTokenHandler.validateFeignToken(requestHeader.substring(7))) {
			return null;
		}

        if(object instanceof FilterInvocation){
    		FilterInvocation filterInvocation = (FilterInvocation) object;
    		String method = filterInvocation.getRequest().getMethod();
    		resUrl = filterInvocation.getRequestUrl();
    		// options 返回null
    		if(HttpMethod.OPTIONS.matches(method)){
    			return null;
    		}
    	}

        loadResourceDefine();
        // 从线程中获取保证线程安全
        HashMap<String, Collection<ConfigAttribute>> map = getMapThreadLocal();
        for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
            resUrl = iter.next();
            matcher = new AntPathRequestMatcher(resUrl);
            if(matcher.matches(request)) {
                return map.get(resUrl);
            }
        }

        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return Collections.emptyList();
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    private static HashMap<String, Collection<ConfigAttribute>> getMapThreadLocal(){
    	HashMap<String, Collection<ConfigAttribute>> hashMap = mapThreadLocal.get();
    	if(BeanUtils.isEmpty(hashMap)){
    		hashMap = new HashMap<String, Collection<ConfigAttribute>>();
    		mapThreadLocal.set(hashMap);
    	}
    	return hashMap;
    }

    /**
     * 清空线程变量中的权限数据
     */
    public static void clearMapThreadLocal() {
    	HashMap<String, Collection<ConfigAttribute>> hashMap = mapThreadLocal.get();
		if(hashMap!=null) {
			hashMap.clear();
		}
    }

	@Override
	public void cleanAll() {
		clearMapThreadLocal();
	}
}
