package cn.reghao.tnb.data.app.security;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import jakarta.annotation.PostConstruct;
import java.util.Map;
import java.util.Objects;

/**
 * @author reghao
 * @date 2024-11-14 17:36:10
 */
@Component
@Slf4j
public class CustomReactiveAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    /**
     * 此处保存的是资源对应的权限，可以从数据库中获取
     */
    private static final Map<String, String> resourceMap = Maps.newConcurrentMap();
    // 带通配符的可以使用这个进行匹配
    private final PathMatcher pathMatcher = new AntPathMatcher();
    private final String authorities = "ROLE_USER";

    @PostConstruct
    public void initAuthMap() {
        resourceMap.put("/user/findAllUsers", "user.userInfo");
        resourceMap.put("/user/addUser", "ROLE_ADMIN");
        resourceMap.put("/api/content/**", "ROLE_ANONYMOUS");
        resourceMap.put("/api/user/**", "ROLE_ANONYMOUS");
        resourceMap.put("/api/user/message/unread", "user.userInfo");
        resourceMap.put("/api/content/video/hot", "ROLE_USER");
        resourceMap.put("api/content/site/notice", "ROLE_ANONYMOUS");
    }

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        ServerHttpRequest request = exchange.getRequest();
        // option 请求，全部放行
        if (request.getMethod() == HttpMethod.OPTIONS) {
            return Mono.just(new AuthorizationDecision(true));
        }

        String path = request.getURI().getPath();
        for (String pattern : resourceMap.keySet()) {
            boolean matched = pathMatcher.match(pattern, path);
            if (matched) {
                String authorities = resourceMap.get(pattern);
            }
        }

        //String authorities = resourceMap.get(path);
        log.info("gateway 访问路径 [{}] 所需要的权限是 [{}]", path, authorities);

        // 不在权限范围内的url，全部拒绝
        /*if (!StringUtils.hasText(authorities)) {
            return Mono.just(new AuthorizationDecision(false));
        }*/

        return authentication
                .filter(Authentication::isAuthenticated)
                .filter(a -> a instanceof JwtAuthenticationToken)
                .cast(JwtAuthenticationToken.class)
                .doOnNext(token -> {
                    System.out.println(token.getToken().getHeaders());
                    System.out.println(token.getTokenAttributes());
                })
                .flatMapIterable(AbstractAuthenticationToken::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(authority -> Objects.equals(authority, authorities))
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }
}
