package com.vctgo.gateway.filter;

import com.vctgo.common.core.constant.CacheConstants;
import com.vctgo.common.core.constant.HttpStatus;
import com.vctgo.common.core.constant.TokenConstants;
import com.vctgo.common.core.utils.JwtUtils;
import com.vctgo.common.core.utils.ServletUtils;
import com.vctgo.common.core.utils.StrUtils;
import com.vctgo.common.redis.service.RedisService;
import com.vctgo.gateway.config.properties.DemoAccountProperties;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * 黑名单过滤器
 *
 * @author vctgo
 */
@Slf4j
@Component
public class DemoAccountFilter extends AbstractGatewayFilterFactory<DemoAccountFilter.Config> {

    private final static ArrayList<String> VALIDATE_URL = new ArrayList<>(Arrays.asList("/auth/platform/login",
            "/auth/kindergarten/login",
            "/auth/mini-program/login",
            "/auth/expert/login",
            "/auth/register",
            "/system/user/getInfo",
            "/auth/logout",
            "/auth/captcha/get",
            "/auth/captcha/check",
            "/auth/program/login",
            "/auth/program/smsCode",
            "/vote/v1/carouselsList",
            "/vote/v1/newsList",
            "/vote/v1/carousels",
            "/vote/v1/likeRecord",
            "/auth/kindergarten/voteLogin"));

    @Autowired
    private DemoAccountProperties demoAccountProperties;

    @Autowired
    private RedisService redisService;

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (!VALIDATE_URL.contains(exchange.getRequest().getURI().getPath())) {
                ServerHttpRequest request = exchange.getRequest();
                String token = getToken(request);
                if (StrUtils.isEmpty(token)) {
                    return unauthorizedResponse(exchange, "令牌不能为空");
                }
                Claims claims = JwtUtils.parseToken(token);
                if (claims == null) {
                    return unauthorizedResponse(exchange, "令牌已过期或验证不正确！");
                }
                String userkey = JwtUtils.getUserKey(claims);
                boolean islogin = redisService.hasKey(getTokenKey(userkey));
                if (!islogin) {
                    return unauthorizedResponse(exchange, "登录状态已过期");
                }
                String username = JwtUtils.getUserName(claims);
                if (!"GET".equals(exchange.getRequest().getMethod().name())
                        && demoAccountProperties.getAccountList().contains(username)) {
                    return unauthorizedResponse(exchange, "演示账号暂无权限");
                }
            }
            return chain.filter(exchange);
        };
    }

    private String getToken(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String token = "";
        if (headers.containsKey(TokenConstants.AUTHENTICATION)) {
            token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
        } else if (headers.containsKey(TokenConstants.WEBSOCKET_AUTHENTICATION)) {
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            token = queryParams.getFirst(TokenConstants.AUTHENTICATION);
        }

        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StrUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
            token = token.replaceFirst(TokenConstants.PREFIX, StrUtils.EMPTY);
        }
        return token;
    }

    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
        log.error("[演示账号异常处理]请求路径:{}", exchange.getRequest().getPath());
        return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.ERROR);
    }

    /**
     * 获取缓存key
     */
    private String getTokenKey(String token) {
        return CacheConstants.LOGIN_TOKEN_KEY + token;
    }

    public DemoAccountFilter() {
        super(Config.class);
    }

    public static class Config {
    }

}
