package com.sf.vertx.init;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.sf.vertx.api.pojo.VertxConfig;
import com.sf.vertx.constans.RedisKeyConfig;
import com.sf.vertx.handle.BodyHandler;
import com.sf.vertx.handle.ProxyHandler;
import com.sf.vertx.handle.RateLimitHandler;
import com.sf.vertx.handle.RestfulFailureHandler;
import com.sf.vertx.service.AppConfigService;
import com.sf.vertx.service.impl.AppConfigServiceImpl;
import com.sf.vertx.utils.Filter;
import com.sf.vertx.utils.ProxyTool;

import io.vertx.circuitbreaker.CircuitBreaker;
import io.vertx.circuitbreaker.CircuitBreakerOptions;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.SocketAddress;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.client.WebClient;
import io.vertx.httpproxy.Body;
import io.vertx.httpproxy.HttpProxy;
import io.vertx.httpproxy.ProxyContext;
import io.vertx.httpproxy.ProxyInterceptor;
import io.vertx.httpproxy.ProxyResponse;
import lombok.extern.slf4j.Slf4j;

/***
 * 动态构建vertx服务
 * 
 * @author xy
 *
 */
@Slf4j
@Order(value = 10)
@Component
public class DynamicBuildServer implements ApplicationRunner {
	@Value("${server.vertx.server.default.port}")
	private Integer serverDefaultPort;
	public static CircuitBreaker appCircuitBreaker;
	@Autowired
	private RedisTemplate<String, String> redisTemplate;

	@Autowired
	private AppConfigService appConfigService;

	@Autowired
	private RedisKeyConfig redisKeyConfig;

	@Override
	public void run(ApplicationArguments args) throws Exception {
		// 初始化redis key
		redisKeyConfig.init();
		// 从redis同步app配置
		appConfigService.initAllAppConfig();
		// 从redis同步vertx配置
		appConfigService.initVertxConfig();
		// 加载vertx、应用配置
		appStartLoadData();
	}

	/***
	 * 应用启动, 从redis读取配置,初始化vertx服务
	 */
	private void appStartLoadData() {
		VertxConfig vertxConfig = AppConfigServiceImpl.getVertxConfig();
		// TODO 编解码线程池,后面优化协程等方式
		VertxOptions vertxOptions = new VertxOptions();
		loadVertxOptions(vertxConfig, vertxOptions);

		Vertx VERTX = Vertx.vertx(vertxOptions);

//		CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", VERTX,
//				  new CircuitBreakerOptions().setMaxFailures(3).setMaxRetries(5).setTimeout(2000)
//				  .setFallbackOnFailure(true)
//				).openHandler(v -> {
//				  log.info("Circuit opened");
//				}).closeHandler(v -> {
//					log.info("Circuit closed");
//				});//.retryPolicy(retryCount -> retryCount * 100L);

		appCircuitBreaker = CircuitBreaker.create("my-circuit-breaker", VERTX, new CircuitBreakerOptions().setMaxFailures(3) // 最大失败数
				.setTimeout(2000) // 超时时间
				.setFallbackOnFailure(true) // 失败后是否调用回退函数（fallback）
				.setResetTimeout(10000) // 在开启状态下，尝试重试之前所需时间
		).openHandler(v -> {
			log.info("Circuit open");
		}).halfOpenHandler(v -> {
			log.info("Circuit halfOpen");
		}).closeHandler(v -> {
			log.info("Circuit close");
		});
		
		CircuitBreaker interfaceBreaker = CircuitBreaker.create("my-circuit-breaker", VERTX, new CircuitBreakerOptions().setMaxFailures(3) // 最大失败数
				.setTimeout(2000) // 超时时间
				.setFallbackOnFailure(true) // 失败后是否调用回退函数（fallback）
				.setResetTimeout(10000) // 在开启状态下，尝试重试之前所需时间
		).openHandler(v -> {
			log.info("Circuit open");
		}).halfOpenHandler(v -> {
			log.info("Circuit halfOpen");
		}).closeHandler(v -> {
			log.info("Circuit close");
		});

		// 创建HTTP监听
		// 所有ip都能访问
		HttpServerOptions httpServerOptions = new HttpServerOptions().setHost("0.0.0.0");
		HttpServer server = VERTX.createHttpServer(httpServerOptions);
		Router mainHttpRouter = Router.router(VERTX);
		Integer serverPort = (vertxConfig == null || vertxConfig.getPort() == null) ? serverDefaultPort
				: vertxConfig.getPort();
		log.info("serverPort:{}", serverPort);
		server.requestHandler(mainHttpRouter).listen(serverPort, h -> {
			if (h.succeeded()) {
				log.info("HTTP端口监听成功:{}", serverPort);
			} else {
				log.error("HTTP端口监听失败:{}", serverPort);
			}
		});

//		HttpClientOptions clientOptions = new HttpClientOptions();
//		clientOptions.setMaxPoolSize(20); // 最大连接池大小
//		clientOptions.setConnectTimeout(2000); // 连接超时 毫秒
//		clientOptions.setHttp2KeepAliveTimeout(1);
//		clientOptions.setIdleTimeout(1000); // 连接空闲超时 毫秒
//		HttpClient proxyClient = VERTX.createHttpClient(clientOptions);
		HttpClient proxyClient = VERTX.createHttpClient();
		HttpProxy proxy = HttpProxy.reverseProxy(proxyClient);
		proxy.originSelector(request -> Future.succeededFuture(ProxyTool.resolveOriginAddress(request)));
		proxy.addInterceptor(new ProxyInterceptor() {
			@Override
			public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
				// 修改uri context.request().setURI();
				String sacAppHeaderKey = context.request().headers().get(AppConfigServiceImpl.getSacAppHeaderKey());
				log.info("addInterceptor uri appCode:{}", sacAppHeaderKey);
				return context.sendRequest();
			}

			@Override
			public Future<Void> handleProxyResponse(ProxyContext context) {
				// 调试代码,获取reponse body
				Filter filter = new Filter();
				ProxyResponse proxyResponse = context.response();
				Body body = proxyResponse.getBody();
				proxyResponse.setBody(Body.body(filter.init(context.request().getURI(), body.stream(), false)));
				// 继续拦截链
				return context.sendResponse();
			}
		});
		WebClient mainWebClient = WebClient.create(VERTX);
		// mainHttpRouter.route().handler(ProxyHandler.create(proxy));

		// TODO 实例化方式 从 VertxConfig 读取
		String rateLimitModel = vertxConfig.getRateLimitModel() != null
				&& StringUtils.equals(vertxConfig.getRateLimitModel(), "redis") ? "redis" : "local";
		mainHttpRouter.route().handler(RateLimitHandler.create(rateLimitModel)).handler(BodyHandler.create())
				.handler(ProxyHandler.create(mainWebClient, proxy))
				.failureHandler(RestfulFailureHandler.create());
		// mainHttpRouter.route().handler(BodyHandler.create()).handler(ProxyHandler.create(mainWebClient,proxy));

		// 服务健康检测重试
//		Integer periodicTime = AppConfigServiceImpl.getVertxConfig().getAddressRetryStrategy() != null
//				&& AppConfigServiceImpl.getVertxConfig().getAddressRetryStrategy().getPeriodicTime() > 0
//						? AppConfigServiceImpl.getVertxConfig().getAddressRetryStrategy().getPeriodicTime()
//						: 3;
//
//		// TODO 是否开启健康检测
//		long timerID = VERTX.setPeriodic(periodicTime, id -> {
//			Set<String> set = redisTemplate.opsForZSet().range(RedisKeyConfig.APP_CONFIG_SET_KEY, 0, -1);
//			for (String appCode : set) {
//				Set<String> setAddressRetryStrategy = redisTemplate.opsForZSet()
//						.range(RedisKeyConfig.VERTX_ADDRESS_RETRY_STRATEGY_SET_KEY + ":" + appCode, 0, -1);
//				for (String address : setAddressRetryStrategy) {
//					// 发起请求,测试服务是否可用
//					// TODO 调用后端配置的健康检测地址
//				}
//
//			}
//		});
	}

	private void loadVertxOptions(VertxConfig vertxConfig, VertxOptions vertxOptions) {
		long blockedThreadCheckInterval = vertxConfig == null || vertxConfig.getVertxOptionsConfig() == null ? -1
				: vertxConfig.getVertxOptionsConfig().getBlockedThreadCheckInterval();
		int workerPoolSize = vertxConfig == null || vertxConfig.getVertxOptionsConfig() == null ? -1
				: vertxConfig.getVertxOptionsConfig().getWorkerPoolSize();
		if (workerPoolSize != -1) {
			vertxOptions.setWorkerPoolSize(workerPoolSize);
		}
		blockedThreadCheckInterval = 1000000L;
		if (blockedThreadCheckInterval != -1) {
			vertxOptions.setBlockedThreadCheckInterval(blockedThreadCheckInterval); // 不打印Thread blocked 阻塞日志
		}
	}
}
