package com.winfred.gatewayserver;

import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;

import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.discovery.ServiceRouteMapper;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CustomRouteLocator extends DiscoveryClientRouteLocator {

	private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();
	private PathMatcher pathMatcher = new AntPathMatcher();
	private ZuulProperties properties;

	public CustomRouteLocator(String servletPath, DiscoveryClient discovery, ZuulProperties properties,
			ServiceRouteMapper serviceRouteMapper) {
		super(servletPath, discovery, properties, serviceRouteMapper);
		this.properties = properties;
	}

	@Override
	public Route getMatchingRoute(String path) {
		log.info("Finding route for path: " + path);
		if (this.routes.get() == null) {
			this.routes.set(locateRoutes());
		}

		String adjustedPath = path;
		ZuulRoute priorityRoute = null;
		int matchPatternLength = 0;
		if (!matchesIgnoredPatterns(adjustedPath)) {
			for (Entry<String, ZuulRoute> entry : this.routes.get().entrySet()) {
				String pattern = entry.getKey();
				if (this.pathMatcher.match(pattern, adjustedPath)) {
					// 当有多个路由匹配时,取更精确的路由匹配
					if (pattern.length() > matchPatternLength) {
						matchPatternLength = pattern.length();
						priorityRoute = entry.getValue();
					}
				}
			}
		}

		log.info("priorityRoute = " + priorityRoute);
		if (null != priorityRoute) {
			return getRoute(priorityRoute, adjustedPath);
		}
		return null;
	}

	public Route getRoute(ZuulRoute route, String path) {
		if (route == null) {
			return null;
		}
		String targetPath = path;
		String prefix = this.properties.getPrefix();
		if (path.startsWith(prefix) && this.properties.isStripPrefix()) {
			targetPath = path.substring(prefix.length());
		}
		if (route.isStripPrefix()) {
			int index = route.getPath().indexOf('*') - 1;
			if (index > 0) {
				String routePrefix = route.getPath().substring(0, index);
				targetPath = targetPath.replaceFirst(routePrefix, "");
				prefix = prefix + routePrefix;
			}
		}
		Boolean retryable = this.properties.getRetryable();
		if (route.getRetryable() != null) {
			retryable = route.getRetryable();
		}
		return new Route(route.getId(), targetPath, route.getLocation(), prefix, retryable,
				route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null);
	}

	@Override
	protected void doRefresh() {
		super.doRefresh();
		this.routes.set(locateRoutes());
	}

}
