package com.m2pool.gateway.filter;

import com.m2pool.common.core.constant.*;
import com.m2pool.common.core.text.Convert;
import com.m2pool.common.core.utils.OpenApiJwtUtils;
import com.m2pool.common.core.utils.ServletUtils;
import com.m2pool.common.core.utils.StringUtils;
import com.m2pool.common.core.utils.ip.IpUtils;
import com.m2pool.common.redis.service.RedisService;
import com.m2pool.gateway.config.properties.ApiKeyProperties;
import com.m2pool.system.api.model.OpenApiKeyInfo;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import sun.net.util.IPAddressUtil;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description apiKey鉴权
 * @Date 2024/6/12 14:13
 * @Author dy
 */
@Component
public class ApiKeyFilter implements GlobalFilter,Ordered
{
    private static final Logger log = LoggerFactory.getLogger(ApiKeyFilter.class);

    private static final Map<String, AtomicInteger> dayLimitCounter = new ConcurrentHashMap<>();
    private static final Map<String, AtomicInteger> secondLimitCounter = new ConcurrentHashMap<>();
    private static final int MAX_REQUESTS_SECOND = 5;
    private static final int MAX_REQUESTS_DAY = 5000;

    @Autowired
    private RedisService redisService;
    @Autowired
    private ApiKeyProperties apiKeyProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
    {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest.Builder mutate = request.mutate();

        String url = request.getURI().getPath();

        // 跳过不需要验证的路径 只验证/oapi/** 对应的接口
        if (!StringUtils.matches(url, apiKeyProperties.getMatchUrls()))
        {
            return chain.filter(exchange);
        }
        String jwtApiKey = getApiKey(request);
        System.out.println("API-KEY:"+jwtApiKey);
        if (StringUtils.isEmpty(jwtApiKey))
        {
            return unauthorizedResponse(exchange, "api-key不能为空");
        }
        Claims claims = OpenApiJwtUtils.parseApiKey(jwtApiKey);
        System.out.println("API-KEY解析的身份信息:"+claims);
        if (claims == null)
        {
            System.out.println("claim null");
            return unauthorizedResponse(exchange, "api-key已过期或格式不正确！",HttpStatus.TOKENEXPIRES);
        }
        String apiKey = OpenApiJwtUtils.getApiKey(claims);
        boolean isSign = redisService.hasKey(getApiKeyRedisKey(apiKey));
        if (!isSign)
        {
            return unauthorizedResponse(exchange, "api-key已过期",HttpStatus.LOGINEXPIRES);
        }

        OpenApiKeyInfo info = redisService.getCacheObject(getApiKeyRedisKey(apiKey));
        String apiIp = info.getApiIp();
        System.out.println("绑定的ip:"+apiIp);
        String username = OpenApiJwtUtils.getUserName(claims);
        if (StringUtils.isEmpty(apiIp) || StringUtils.isEmpty(username))
        {
            return unauthorizedResponse(exchange, "api-key验证失败");
        }

        //todo 验证ip
        String ip = getIP(request);
        System.out.println("当前ip:"+ip);
        if(!ip.contains(apiIp)){
            return unauthorizedResponse(exchange, "当前ip与API-KEY绑定的ip不一致");
        }
        //AtomicInteger counter = dayLimitCounter.get(apiKey);
        //if (counter == null) {
        //    counter = new AtomicInteger(0);
        //    dayLimitCounter.put(apiKey, counter);
        //}
        ////判断当前分钟
        //
        //if (counter.incrementAndGet() > MAX_REQUESTS_DAY) {
        //    return unauthorizedResponse(exchange, "Too many requests from this api-key",HttpStatus.TOO_MANY_REQUESTS);
        //}

        // 设置用户信息到请求
        addHeader(mutate, OpenApiKeyConstants.API_KEY, apiKey);
        addHeader(mutate, OpenApiKeyConstants.API_IP, apiIp);
        addHeader(mutate, OpenApiKeyConstants.API_USER, username);
        // 内部请求来源参数清除
        removeHeader(mutate, OpenApiKeyConstants.FROM_SOURCE);
        System.out.println("ApiKeyFilter走到最后了");
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }

    private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value)
    {
        if (value == null)
        {
            return;
        }
        String valueStr = value.toString();
        String valueEncode = ServletUtils.urlEncode(valueStr);
        mutate.header(name, valueEncode);
    }

    private void removeHeader(ServerHttpRequest.Builder mutate, String name)
    {
        mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
    }

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

    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg,int code)
    {
        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
        return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, code);
    }

    /**
     * 获取缓存key
     */
    private String getApiKeyRedisKey(String apiKey)
    {
        return CacheConstants.OPEN_API_KEY + apiKey;
    }

    /**
     * 获取请求token
     */
    private String getApiKey(ServerHttpRequest request)
    {
        String apiKey = request.getHeaders().getFirst(TokenConstants.API_KEY);
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        //if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
        //{
        //    token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
        //}
        return apiKey;
    }

    @Override
    public int getOrder()
    {
        return -101;
    }

    // 多次反向代理后会有多个ip值 的分割符
    private final static String IP_UTILS_FLAG = ",";
    // 未知IP
    private final static String UNKNOWN = "unknown";
    // 本地 IP
    private final static String LOCALHOST_IP = "0:0:0:0:0:0:0:1";
    private final static String LOCALHOST_IP1 = "127.0.0.1";

    private static String getIP(ServerHttpRequest request){
        // 根据 HttpHeaders 获取 请求 IP地址
        String ip = request.getHeaders().getFirst("X-Forwarded-For");
        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("x-forwarded-for");
            if (ip != null && ip.length() != 0 && !UNKNOWN.equalsIgnoreCase(ip)) {
                // 多次反向代理后会有多个ip值，第一个ip才是真实ip
                if (ip.contains(IP_UTILS_FLAG)) {
                    ip = ip.split(IP_UTILS_FLAG)[0];
                }
            }
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("X-Real-IP");
        }
        //兼容k8s集群获取ip
        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
            if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) {
                //根据网卡取本机配置的IP
                InetAddress iNet = null;
                try {
                    iNet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    log.error("getClientIp error: ", e);
                }
                ip = iNet.getHostAddress();
            }
        }
        if(StringUtils.isNull(ip)){
            ip="";
        }
        return ip;
    }

}

