package com.tofly.toflygateway.filter;

import com.tofly.common.core.constant.SecurityConstants;
import com.tofly.common.core.util.HttpURL;
import com.tofly.common.core.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 登陆接口默认添加clientid
 * 需要在网关进行配置对哪一个服务进行过滤
 */
@Component
@Slf4j
public class LoginParamFilter extends AbstractGatewayFilterFactory {

    @Autowired
    private ValueOperations<String, String> valueOperations;

    @Value("${tofly.client_Id:tofly}")
    private String client_Id;
    @Value("${tofly.client_secret:tofly}")
    private String client_secret;
    @Value("${tofly.grant_type:password}")
    private String grant_type;

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            // 不是登录请求直接放行
            if (!StringUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_LOGIN_URL)
                    && !StringUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL)) {
                return chain.filter(exchange);
            }

            URI uri = exchange.getRequest().getURI();
            String queryParam = uri.getRawQuery();

            Map<String, String> paramMap = getParameterMap(queryParam);
            if (StringUtil.isEmpty(paramMap.get("grant_type"))) {
                paramMap.put("grant_type", grant_type);
            }
            if (StringUtil.isEmpty(paramMap.get("client_id"))) {
                paramMap.put("client_id", client_Id);
            }
            if (StringUtil.isEmpty(paramMap.get("client_secret"))) {
                paramMap.put("client_secret", client_secret);
            }

            String project = paramMap.get("project");
            if (!StringUtil.isEmpty(project)) {
                tryLock(SecurityConstants.REDIS_LOGIN_LOCK, project);
            }

            log.info("登录请求入参 =============================> :{}", HttpURL.asUrlParams(paramMap));
            URI newUri = UriComponentsBuilder.fromUri(uri)
                    .replacePath(SecurityConstants.OAUTH_TOKEN_URL)
                    .replaceQuery(HttpURL.asUrlParams(paramMap))
                    .build(true)
                    .toUri();

            ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(newUri).build();
            return chain.filter(exchange.mutate().request(newRequest).build());
        };
    }

    public Boolean tryLock(String key, String value) {
        try {
            Boolean ret = valueOperations.setIfAbsent(key, value, SecurityConstants.REDIS_LOCK_TIME, TimeUnit.MILLISECONDS);
            while (!ret) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ret = valueOperations.setIfAbsent(key, value, SecurityConstants.REDIS_LOCK_TIME, TimeUnit.MILLISECONDS);
            }
            return true;

        } finally {
            valueOperations.setIfAbsent(key, value);
        }

    }

    private Map<String, String> getParameterMap(String queryParam) {
        Map<String, String> map = new HashMap<>();
        if (StringUtil.isEmpty(queryParam)) {
            return map;
        }

        String[] params = queryParam.split("&");
        for (String param : params) {
            String[] p = param.split("=");
            if (p.length == 2) {
                map.put(p[0], p[1]);
            }
        }
        return map;
    }

}
