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

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.common.enums.BoCloudExchange;
import com.bocloud.common.enums.BoCloudMethod;
import com.bocloud.common.enums.BoCloudService;
import com.bocloud.common.enums.PostDataFormat;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.OperateResult.OperateCategory;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.CloudServer;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.http.core.ServiceFactory;
import com.bocloud.service.http.model.RemoteService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 磁盘格式化任务
 * 
 * @author wangyu
 * @version 1.0
 * @since 2020年4月28日
 *
 */
@Component
public class DiskFormatService {

	private static OperateCategory operateCategory = OperateCategory.ResourceEvent;
	private static Logger logger = LoggerFactory.getLogger(DiskFormatService.class);
	@Autowired
	private ServiceFactory serviceFactory;
	@Autowired
	private AmqpAdmin amqpAdmin;
	@Autowired
	private AmqpTemplate amqpTemplate;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;
	@Autowired
	private ThreadPoolTaskExecutor taskExecutor;

	/**
	 * 执行格式化磁盘任务
	 * 
	 * @param vendor
	 * @param server
	 * @param paramMap
	 * @param context
	 * @return
	 */
	public BocloudResult format(CloudVendor vendor, CloudServer server, Map<String, Object> paramMap,
			RequestContext context) {
		Future<BocloudResult> future = invokeWorker(vendor, server, taskExecutor, paramMap, context);
		// 获取处理结果
		taskExecutor.execute(() -> {
			// 处理任务执行的结果
			while (true) {
				try {
					// 查看每个worker执行结果
					BocloudResult result = future.get(1, TimeUnit.HOURS);
					handleResult(vendor, server.getId(), result, context);
				} catch (InterruptedException | ExecutionException | TimeoutException e) {
					logger.error("worker任务执行任务异常", e);
					Thread.currentThread().interrupt();
				}
				break;
			}
		});
		return new BocloudResult(true, "任务提交成功");
	}

	/**
	 * 调用worker执行磁盘格式化任务
	 * 
	 * @param vendor
	 * @param server
	 * @param taskExecutor
	 * @param paramMap
	 * @param context
	 */
	private Future<BocloudResult> invokeWorker(CloudVendor vendor, CloudServer server,
			ThreadPoolTaskExecutor taskExecutor, Map<String, Object> paramMap, RequestContext context) {
		Map<String, Object> headers = MapTools.simpleMap("Content-Type", "application/json");
		RemoteService remoteService = serviceFactory.build(BoCloudService.WORKER, "/api/worker/v1/job/submit",
				BoCloudMethod.OPERATE, headers, paramMap);
		Future<BocloudResult> future = taskExecutor.submit(() -> {
			BocloudResult result = null;
			if (!remoteService.isExist()) {
				logger.error("远程Worker不存在!");
				return new BocloudResult(false, "远程Worker服务不存在！");
			}
			// 声明临时队列，用于获取worker执行结果
			String resultQueue = UUID.randomUUID().toString();
			amqpAdmin.declareQueue(new Queue(resultQueue, false, false, true));
			amqpAdmin.declareBinding(new Binding(resultQueue, Binding.DestinationType.QUEUE,
					BoCloudExchange.Default.DIRECT.getExchange(), resultQueue, null));
			
			JSONObject paramObject = new JSONObject();
			paramObject.put("queue", resultQueue);
			JSONObject oldParams = null;
	        if(null == remoteService.getParam().getParams()) {
	        	oldParams = new JSONObject();
	        }else {
	        	oldParams = JSONObject.parseObject(JSONObject.toJSONString(remoteService.getParam().getParams()));
	        }
			if(oldParams.keySet().size() > 0) {
				paramObject.putAll(oldParams);
			}
			remoteService.getParam().setParams(paramObject);
			
			remoteService.setDataFormat(PostDataFormat.RAW);
			logger.info("格式化磁盘调用worker参数：{}", JSONObject.toJSON(paramMap));
			BocloudResult bocloudResult = remoteService.invoke();
			// 这里说明请求成功发送到worker上，接下来可以等待结果消息了
			if (bocloudResult.isSuccess()) {
				try {
					Message message = amqpTemplate.receive(resultQueue, 1000L * 60 * 30);
					if (null != message) {
						String content = new String(message.getBody());
						logger.info("Received Job result!,content {}", content);
						JSONObject jsonObject = JSONObject.parseObject(content);
						Boolean isSuccess = jsonObject.getBoolean("success");
						if (isSuccess) {
							result = new BocloudResult(true, "格式化成功！");
						} else {
							List<JSONObject> results = JSONArray.parseArray(jsonObject.getString("data"),
									JSONObject.class);
							String msg = "安装失败！获取worker执行结果为空";
							if (!ListTool.isEmpty(results)) {
								JSONObject resultObj = results.get(0);
								JSONObject messageObj = resultObj.getJSONObject("message");
								if (null != messageObj) {
									msg = messageObj.getString("msg");
								}
							}
							result = new BocloudResult(false, msg);
						}
					} else {
						logger.error("获取任务执行结果消息超时！");
						result = new BocloudResult(false, "执行失败！获取worker执行结果为空");
					}
				} catch (Exception e) {
					logger.error("等待worker返回任务结果消息异常:", e);
					result = new BocloudResult(false, "执行失败！" + bocloudResult.getMessage());
				} finally {
					amqpAdmin.deleteQueue(resultQueue);
				}
			} else {
				logger.error("请求发送到worker失败：{}", bocloudResult.getMessage());
				result = new BocloudResult(false, "安装失败！" + bocloudResult.getMessage());
			}
			return result;
		});
		return future;
	}

	/**
	 * 处理请求结果
	 * 
	 * @param vendor
	 * @param id
	 * @param result
	 * @param context
	 */
	private void handleResult(CloudVendor vendor, Long id, BocloudResult result, RequestContext context) {
		String operate = vendor.getType().toLowerCase() + ".vm";
		String content = "虚拟机安装格式化磁盘成功";
		if (result.isFailed()) {
			content = "虚拟机安装格式化磁盘失败：" + result.getMessage();
		}
		sendMsg(result.isSuccess(), operate, content, vendor, id, context);
	}

	/**
	 * 发送执行结果给用户
	 * 
	 * @param success
	 * @param operate
	 * @param content
	 * @param vendor
	 * @param id
	 * @param context
	 */
	private void sendMsg(boolean success, String operate, String content, CloudVendor vendor, Long id,
			RequestContext context) {
		resourceEventPublisher.send(new OperateResult(success, content, operate,
				MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
		NotifyMessage message = new NotifyMessage(context, "虚拟机格式化磁盘", content, true, false);
		amqpTemplate.convertAndSend(BoCloudExchange.Default.DIRECT.getExchange(), "event.notice.queue", message);
	}
}
