package com.njcn.gateway.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.JWSObject;
import com.njcn.common.pojo.constant.SecurityConstants;
import com.njcn.common.pojo.dto.UserTokenInfo;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gateway.enums.GateWayEnum;
import com.njcn.gateway.utils.IpUtils;
import com.njcn.gateway.utils.ResponseUtils;
import com.njcn.redis.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * 全局过滤器
 *
 * @author hongawen
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private final static List<String> USER_AGENT_IP = Arrays.asList("/pqs-auth/auth/getImgCode", "/pqs-auth/oauth/token", "/user-boot/user/generateSm2Key", "/user-boot/user/updateFirstPassword", "/user-boot/appUser/resetPsd","/pqs-auth/oauth/lnLogin","/pqs-auth/oauth/lnCheck","/pqs-auth/oauth/lnRefreshToken");

    private final RedisUtil redisUtil;

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String realIp = IpUtils.getRealIpAddress(request);
        // 非JWT或者JWT为空不作处理
        String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_KEY);
        String path = request.getURI().getPath();
        if (StrUtil.isBlank(token) || !token.startsWith(SecurityConstants.AUTHORIZATION_PREFIX)) {
//            String path = request.getURI().getPath();
            //未认证且这些请求需要
            if (USER_AGENT_IP.contains(path)) {
                request = exchange.getRequest().mutate()
                        .header(SecurityConstants.REQUEST_HEADER_KEY_CLIENT_REAL_IP, realIp)
                        .build();
                return chain.filter(exchange.mutate().request(request).build());
            }
            return chain.filter(exchange);
        }
        // 解析JWT获取jti，以jti为key判断redis的黑名单列表是否存在，存在拦截响应token失效
        token = token.replace(SecurityConstants.AUTHORIZATION_PREFIX, Strings.EMPTY);
        JWSObject jwsObject = JWSObject.parse(token);
        String payload = jwsObject.getPayload().toString();
        JSONObject jsonObject = JSONUtil.parseObj(payload);
        String userIndex = jsonObject.getStr(SecurityConstants.USER_INDEX_KEY);
        String jti = jsonObject.getStr(SecurityConstants.JWT_JTI);
        String exp = jsonObject.getStr(SecurityConstants.JWT_EXP);
        LocalDateTime expTime = LocalDateTimeUtil.of(Long.parseLong(exp + "000"));
        if(expTime.isBefore(LocalDateTimeUtil.now())){
            return ResponseUtils.writeErrorInfo(response, GateWayEnum.ACCESS_TOKEN_EXPIRE_JWT);
        }
        String blackUserKey = SecurityConstants.TOKEN_BLACKLIST_PREFIX + userIndex;
        List<UserTokenInfo> blackUsers = (List<UserTokenInfo>) redisUtil.getObjectByKey(blackUserKey);
        if (CollUtil.isNotEmpty(blackUsers)) {
            for (UserTokenInfo blackUser : blackUsers) {
                //存在当前的刷新token，则抛出业务异常
                if(blackUser.getAccessTokenJti().equalsIgnoreCase(jti)){
                    return ResponseUtils.writeErrorInfo(response, GateWayEnum.TOKEN_EXPIRE_JWT);
                }
            }
        }
        // 存在token且不是黑名单，request写入JWT的载体信息
        request = exchange.getRequest().mutate()
                .header(SecurityConstants.JWT_PAYLOAD_KEY, URLEncoder.encode(payload, StandardCharsets.UTF_8.toString()))
                .header(SecurityConstants.REQUEST_HEADER_KEY_CLIENT_REAL_IP, realIp)
                .build();
        exchange = exchange.mutate().request(request).build();
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

}
