package com.bocloud.ims.service.internal.worker;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.common.enums.BoCloudExchange;
import com.bocloud.common.enums.PostDataFormat;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.utils.IDFactory;
import com.bocloud.common.utils.MapTools;
import com.bocloud.service.http.model.RemoteService;
import org.apache.commons.lang.IllegalClassException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Callable;

/**
 * @author wangyu
 * @version 5.0.0-SNAPSHOT
 * @since 2021/3/12
 */
public class RemoteWorker implements Callable<BocloudResult> {
	private static final String EXCHANGE;
	private static final Map<String, Object> arguments;
	private static final Integer DEFAULT_TIMEOUT;
	private static final String QUEUE = "queue";
	private static final String TIMEOUT = "timeout";
	private static Logger logger;
	private List<WorkerHost> targets;
	private Integer timeout;
	private String callback;
	private boolean sync = false;
	private RemoteService service;
	private RabbitAdmin rabbitAdmin;

	static {
		EXCHANGE = BoCloudExchange.Default.DIRECT.getExchange();
		arguments = MapTools.simpleMap("x-ha-policy", "all");
		DEFAULT_TIMEOUT = 600000;
		logger = LoggerFactory.getLogger(RemoteWorker.class);
	}

	public RemoteWorker(RemoteService service, RabbitAdmin rabbitAdmin, Integer timeout, String callback, boolean sync) throws Exception {
		this.service = service;
		this.rabbitAdmin = rabbitAdmin;
		if (timeout == null) {
			this.timeout = DEFAULT_TIMEOUT;
		} else {
			this.timeout = timeout * 1000;
		}

		this.callback = callback;
		this.sync = sync;
		this.init();
	}

	@Override
	public BocloudResult call() throws Exception {
		if (this.service != null && this.service.isExist()) {
			return this.sync ? this.handleSync() : this.handleAsync();
		} else {
			logger.error("remote worker does not exist!");
			return this.handleError("远程Worker服务不存在");
		}
	}

	private BocloudResult handleSync() {
		logger.info("start to sync call remote worker [{}] and timeout is :{} ms ", this.service.getUrl(), this.timeout);
		Object params = this.service.getParam().getParams();
		if (params == null) {
			params = new HashMap<String, Object>();
			this.service.getParam().setParams((Map<String, Object>) params);
		}
		if (!(params instanceof Map)) {
			return new BocloudResult(false, "params not a map");
		}
		((Map) params).put("timeout", this.timeout / 1000);
		BocloudResult invoke = this.service.invoke();
		if (logger.isDebugEnabled()) {
			logger.debug("remote worker response is {}", invoke);
		}

		return this.handleResult(invoke);
	}

	private BocloudResult handleAsync() {
		if (StringUtils.isEmpty(this.callback)) {
			this.callback = IDFactory.instance().uuid();
		}
		Object params = this.service.getParam().getParams();
		if (params == null) {
			params = new HashMap<String, Object>();
			this.service.getParam().setParams((Map<String, Object>) params);
		}
		if (!(params instanceof Map)) {
			return new BocloudResult(false, "params not a map");
		}
		Map paramMap = ((Map) params);
		Object object = paramMap.get("module");
		if (object == null) {
			logger.info("start to async call remote worker [{}] with callback queue is [{}] and timeout is :{}ms", new Object[]{this.service.getUrl(), this.callback, this.timeout});
		} else {
			Module<?> module = (Module) object;
			logger.info("start to async call remote worker [{}] with callback queue is [{}] and task module is [{}] and timeout is :{}ms", new Object[]{this.service.getUrl(), this.callback, module.getName(), this.timeout});
		}

		BocloudResult result;
		label95:
		{
			BocloudResult var8;
			try {
				paramMap.put("queue", this.callback);
				this.rabbitAdmin.declareQueue(new Queue(this.callback, false, false, true, arguments));
				this.rabbitAdmin.declareBinding(new Binding(this.callback, Binding.DestinationType.QUEUE, EXCHANGE, this.callback, arguments));
				logger.info("request params is {}", JSONObject.toJSON(this.service.getParam().getParams()));
				BocloudResult invoke = this.service.invoke();
				if (!invoke.isFailed()) {
					Message message = this.rabbitAdmin.getRabbitTemplate().receive(this.callback, (long) (this.timeout + '\uea60'));
					if (message == null) {
						logger.error("get callback from remote worker by queue [{}] timeout", this.callback);
						result = this.handleError("获取任务结果消息超时！");
					} else {
						String content = new String(message.getBody(), StandardCharsets.UTF_8.name());
						logger.info("get result [{}] from remote worker by queue [{}]", content, this.callback);
						if (logger.isDebugEnabled()) {
							logger.debug("callback from remote worker is :\n{}", content);
						}

						try {
							BocloudResult bocloudResult = JSONObject.parseObject(content, BocloudResult.class);
							result = this.handleResult(bocloudResult);
						} catch (Exception var12) {
							logger.error("callback data transfer exception!", var12);
							result = this.handleError("返回数据格式异常!");
						}
					}
					break label95;
				}

				logger.error("invoke remote worker [{}] failed for : {}", this.service.getUrl(), invoke.getMessage());
				var8 = this.handleError(invoke.getMessage());
			} catch (Exception var13) {
				logger.error("等待Worker 【{}】返回任务结果消息异常:", this.service.getUrl(), var13);
				result = this.handleError(var13.getMessage());
				break label95;
			} finally {
				this.rabbitAdmin.deleteQueue(this.callback);
			}

			return var8;
		}

		if (logger.isDebugEnabled()) {
			logger.debug("执行结果: {}", result);
		}

		return result;
	}

	private BocloudResult handleResult(BocloudResult invoke) {
		if (invoke.isSuccess()) {
			return invoke;
		} else {
			return invoke.getData() == null ? this.handleError(invoke.getMessage()) : invoke;
		}
	}

	private BocloudResult handleError(String message) {
		List<HostResult> hostResults = new ArrayList();
		Iterator var4 = this.targets.iterator();

		while (var4.hasNext()) {
			WorkerHost target = (WorkerHost) var4.next();
			hostResults.add(new HostResult(false, message, target.getHost(), target.getRawName(), target.getPort(), target.getUser()));
		}

		return new BocloudResult(false, hostResults, message);
	}

	private void init() throws Exception {
		Object params = this.service.getParam().getParams();
		if (params == null) {
			params = new HashMap<String, Object>();
			this.service.getParam().setParams((Map<String, Object>) params);
		}
		if (!(params instanceof Map)) {
			throw new IllegalClassException("params not a map");
		}
		Map paramMap = ((Map) params);
		paramMap.put("timeout", this.timeout / 1000);
		paramMap.put("queue", this.callback);
		this.service.setDataFormat(PostDataFormat.RAW);
		String targetParam = JSONObject.toJSONString(paramMap.get("targets"));
		this.targets = WorkerHost.parseTargets(targetParam);
		if (this.targets.isEmpty()) {
			throw new Exception("目标机器参数信息格式错误");
		}
	}
}
