package cn.myapps.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter;
import org.springframework.cloud.gateway.filter.ratelimit.PrincipalNameKeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
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.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.*;

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class})
public class GatewayApp {
	public static void main(String[] args) {
		SpringApplication.run(GatewayApp.class, args);
	}

	@Primary
	@Bean
	public RateLimiter myRateLimiter() {
		System.err.println("启用限流功能，初始化对象" + new Date());
		return new RateLimiter() {
			private final AtomicLong requestCount = new AtomicLong(1);
			private Timestamp lastWindowExecutedTime = Timestamp.from(Instant.now());
			private final int rateLimitPerWindow = 30;

			/**
			 * 默认5秒钟1次请求
			 * @param routeId
			 * @param id
			 * @return
			 */
			@Override
			public Mono<Response> isAllowed(String routeId, String id) {
				boolean isAllowed = true;
				Map<String, String> headers = new HashMap<>();

				/*
				if ("obpm-runtime-v2-sync".equals(routeId)) {
					Timestamp now = Timestamp.from(Instant.now());
					int windowScopeValue = 5;
					long windows = (now.getTime() - lastWindowExecutedTime.getTime()) / (1 * 1_000);
					Calendar calendar = Calendar.getInstance();
					calendar.setTime(new Date(now.getTime()));
					int hour = calendar.get(Calendar.HOUR_OF_DAY);
					if (windows <= windowScopeValue && hour > 7 && hour < 19) {
						if (requestCount.incrementAndGet() > rateLimitPerWindow) {
							isAllowed = false;
						}
					} else {
						lastWindowExecutedTime = Timestamp.from(Instant.now());
						requestCount.set(1);
					}

					if (!isAllowed) {
						SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
						String message = String.format("【当前时间点=%s】达到限流拒绝条件(结果=%s), 当前窗口次数=%s; 上次执行时间=%s;",
								hour,
								isAllowed,
								requestCount.get(),
								simpleDateFormat.format(lastWindowExecutedTime));
						System.err.println(message);
						headers.put("rate-allowed-message", "Refused by the rate limited:" + requestCount.get());
					}

					headers.put("rate-access-hour", String.valueOf(hour));
				}
				if (isAllowed) {
					headers.put("rate-allowed-message", encoding(String.format("Not reach the rate limited condition:routeId=%s;id=%s", routeId, id)));
				}
				 */

				headers.put("rate-allowed-access", String.valueOf(isAllowed));
				headers.put("rate-allowed-request-count", String.valueOf(requestCount.get()));

				return Mono.just(new Response(isAllowed, headers));
			}

			@Override
			public Map getConfig() {
				return null;
			}

			@Override
			public Class getConfigClass() {
				return null;
			}

			@Override
			public Object newConfig() {
				return null;
			}

			private String encoding(String message) {
				try {
					return java.net.URLEncoder.encode(message, "UTF-8");
				} catch (Exception ex) {
					ex.printStackTrace();
					return message;
				}
			}
		};
	}

	@Bean("customPrincipalNameKeyResolver")
	public PrincipalNameKeyResolver customPrincipalNameKeyResolver() {
		System.err.println("启用限流功能的PrincipalNameKeyResolver对象" + new Date());

		return new PrincipalNameKeyResolver() {
			@Override
			public Mono<String> resolve(ServerWebExchange exchange) {
				/**
				 * 10分钟限制100次请求
				 */
        /*
        if(exchange.getRequest()!=null &&  exchange.getRequest().getURI()!=null) {
            String url = exchange.getRequest().getURI().toString();
            if (url.contains("/v2/sync")) {
                Timestamp now = Timestamp.from(Instant.now());
                long minutes = (now.getTime() - lastExecuteTime.getTime()) / (10 * 60_000);
                if (minutes < 0 && requestCount.get() > 100) {
                    return Mono.empty();
                }
            }
        }

        return super.resolve(exchange);

         */

				if (exchange.getRequest().getURI() == null || exchange.getRequest().getURI().getPath() == null) {
					return Mono.empty();
				}

				return Mono.just(String.valueOf(exchange.getRequest().getURI().getPath().hashCode()));
			}
		};
	}
}
