package cn.myapps.gateway;

import com.auth0.jwt.JWT;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriTemplate;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus;

@Component
public class CustomRedirectToGatewayFilterFactory extends RedirectToGatewayFilterFactory {
    public static final String ACCESS_TOKEN = "accessToken";
    public static final String ADMIN_TOKEN = "adminToken";

    private static final Map<String, UriTemplate> urlTemplateContainer = new ConcurrentHashMap<>();

    @Override
    public GatewayFilter apply(Config config) {
        if (!StringUtils.hasLength(config.getUrl())) {
            throw new IllegalArgumentException(String.format("CustomRedirectTo路由-url模板参数不正确"));
        }

        if (!config.getUrl().contains("{")) {
            return super.apply(config.getStatus(), config.getUrl());
        }

        GatewayFilter selectedFilter = new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                /**
                 * 针对rootPath是obpm
                 */
                final String directlyPathVariable = "cdn-upload-path";

                if(!isAllowedAccess(exchange) &&
                        /**
                         * 针对直接访问obpm/uploads路径的方式跳过验证
                         */
                        !(StringUtils.hasLength(config.getUrl()) &&
                        config.getUrl().contains(String.format("{%s}",directlyPathVariable)))
                ) {
                    HttpStatus httpStatus = HttpStatus.UNAUTHORIZED;
                    setResponseStatus(exchange, httpStatus);
                    ServerHttpResponse httpResponse = exchange.getResponse();
                    return httpResponse.setComplete();
                }

                UriTemplate uriTemplate = urlTemplateContainer.get(config.getUrl());
                if (uriTemplate == null) {
                    uriTemplate = new UriTemplate(config.getUrl());
                }

                ServerHttpRequest httpRequest = exchange.getRequest();
                Map<String, String> params = new HashMap<>();
                if (!CollectionUtils.isEmpty(httpRequest.getQueryParams())) {
                    for (String key : httpRequest.getQueryParams().keySet()) {
                        Optional<String> matchedRequestValueOptional =
                                httpRequest.getQueryParams().get(key).stream().filter(ii -> StringUtils.hasLength(ii)).findFirst();
                        if (!matchedRequestValueOptional.isPresent()) {
                            continue;
                        }

                        String value = matchedRequestValueOptional.get();
                        //针对正式环境obs的配置
                        if(StringUtils.hasLength(value) && !value.startsWith("http") &&
                                value.startsWith("/uploads") && config.getUrl().contains(".obs.")) {
                            //移除掉/uploads/
                            value = value.substring(9);
                        }

                        params.put(key, value);
                    }
                }
                if(httpRequest.getPath()!=null) {
                    String rootPath = URLDecoder.decode(httpRequest.getPath().value());
                    if (rootPath.startsWith("/")) {
                        rootPath = rootPath.substring(1);
                    }

                    params.put(directlyPathVariable, rootPath);
                }


                URI redirectUri = uriTemplate.expand(params);

                //针对/obpm/v2/sync/file/download?f=/uploads这种的请求
                if (redirectUri.toString().startsWith("/")) {
                    String originalRequestUrl = httpRequest.getURI().toString();
                    GatewayFilter superExtendedFilter = CustomRedirectToGatewayFilterFactory.super.apply(config.getStatus(), originalRequestUrl);
                    return superExtendedFilter.filter(exchange, chain);
                }

                if (!exchange.getResponse().isCommitted()) {
                    HttpStatus httpStatus =
                            HttpStatus.valueOf(Integer.parseInt(config.getStatus()));
                    setResponseStatus(exchange, httpStatus);

                    final ServerHttpResponse response = exchange.getResponse();
                    response.getHeaders().set(HttpHeaders.LOCATION, redirectUri.toString());
                    return response.setComplete();
                }

                return Mono.empty();
            }
        };

        return selectedFilter;
    }

    public static boolean isAllowedAccess(ServerWebExchange exchange) {
        try {
            ServerHttpRequest httpRequest = exchange.getRequest();
            String accessToken = extractFromRequest(httpRequest, ACCESS_TOKEN);
            if (StringUtils.hasLength(accessToken)) {
                try {
                    //不抛出异常, 则说明该地址是有效
                    String data =
                            JWT.decode(accessToken).getAlgorithm();
                    return true;
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }

            return false;
        } catch (Exception ex) {
            //added by lhc: 确保即使发生异常, 也不应该阻碍功能的执行
            ex.printStackTrace();
        }

        return true;
    }

    private static String extractFromRequest(ServerHttpRequest httpRequest,String key) {
        if (httpRequest.getHeaders().get(key) != null) {
            return httpRequest.getHeaders().get(key).get(0);
        }

        if (httpRequest.getCookies().get(key) != null && httpRequest.getCookies().get(key).size() > 0) {
            return httpRequest.getCookies().get(key).get(0).getValue();
        }

        return null;
    }
}
