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

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.compute.ServerDriver;
import com.bocloud.cmp.entity.enums.ResourceCategory;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.cmp.model.ServerModel;
import com.bocloud.cmp.provider.utils.IPAddress;
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.enums.ResourceCatalog;
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.Common;
import com.bocloud.common.utils.IDFactory;
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.entity.resource.Ip;
import com.bocloud.ims.entity.resource.Ip.IpStatus;
import com.bocloud.ims.entity.resource.IpPool;
import com.bocloud.ims.entity.resource.Resource;
import com.bocloud.ims.repository.resource.CloudServerRepository;
import com.bocloud.ims.repository.resource.IpPoolRepository;
import com.bocloud.ims.repository.resource.IpRepository;
import com.bocloud.ims.repository.resource.ResourceRepository;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.http.core.ServiceFactory;
import com.bocloud.service.http.model.RemoteService;
import org.apache.curator.shaded.com.google.common.collect.Lists;
import org.apache.curator.shaded.com.google.common.collect.Maps;
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 org.apache.commons.lang3.StringUtils;

import java.util.Date;
import java.util.HashMap;
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;

/**
 * vmware tools安装远程调用worker类
 *
 * @author wangyu
 * @version 1.0
 * @since 2018年10月24日
 */
@Component("VmToolsInstallService")
public class VmToolsInstallService {

	private static final String QUEUE = "queue";
	private static final String TARGETS = "targets";
	private static final String MODULE = "module";
	private static OperateCategory operateCategory = OperateCategory.ResourceEvent;
	private static Logger logger = LoggerFactory.getLogger(VmToolsInstallService.class);
	@Autowired
	private ServiceFactory serviceFactory;
	@Autowired
	private AmqpAdmin amqpAdmin;
	@Autowired
	private AmqpTemplate amqpTemplate;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;
	@Autowired
	private ServerDriver serverDriver;
	@Autowired
	private CloudServerRepository cloudServerRepository;
	@Autowired
	private ResourceRepository resourceRepository;
	@Autowired
	private IpRepository ipRepository;
	@Autowired
	private IpPoolRepository ipPoolRepository;
	@Autowired
	private ThreadPoolTaskExecutor taskExecutor;

	public BocloudResult install(CloudVendor vendor, CloudServer server, Map<String, Object> paramMap,
			RequestContext context) {
		Map<String, Object> headers = MapTools.simpleMap("Content-Type", "application/json");
		Future<BocloudResult> future = invokeWorker(taskExecutor, vendor, server, headers, 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执行vmware tools安装
	 *
	 * @param taskExecutor
	 * @param vendor
	 * @param server
	 * @param headers
	 * @param paramMap
	 * @param user
	 */
	private Future<BocloudResult> invokeWorker(ThreadPoolTaskExecutor taskExecutor, CloudVendor vendor,
			CloudServer server, Map<String, Object> headers, Map<String, Object> paramMap, RequestContext context) {
		RemoteService remoteService = serviceFactory.build(BoCloudService.WORKER, "/api/worker/v1/job/submit",
				BoCloudMethod.OPERATE, headers, paramMap);
		return taskExecutor.submit(() -> {
			@SuppressWarnings("unchecked")
			List<Map<String, Object>> failedTargets = (List<Map<String, Object>>) paramMap.get(TARGETS);
			List<String> hosts = Lists.newArrayList();
			for (Map<String, Object> failedTarget : failedTargets) {
				hosts.add((String) failedTarget.get("host"));
			}
			BocloudResult result = null;
			if (!remoteService.isExist()) {
				logger.error("远程Worker不存在!");
				return new BocloudResult(false, hosts, "远程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));
			
			Map<String, Object> paramsMap = new HashMap<>();
			paramsMap.put(QUEUE, resultQueue);
			Map<String, Object> moduleMap = Maps.newHashMap();
			moduleMap.put(Common.NAME, "install_vmwaretools");
			moduleMap.put("args", new HashMap<>());
			paramsMap.put(MODULE, moduleMap);
			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) {
				paramsMap.putAll(oldParams);
			}
			remoteService.getParam().setParams(paramsMap );
			
			remoteService.setDataFormat(PostDataFormat.RAW);
			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, hosts, "安装成功！");
							// 此处等待15秒为了让vcenter获取到虚拟机安装vmware tools的状态
							// Thread.sleep(15 * 1000);
							waitVmToolStatus(server.getInstanceId(), vendor.getUuid());
							// 更新虚拟机
							updateServer(server, vendor, context);
						} 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, hosts, msg);
						}
					} else {
						logger.error("获取任务执行结果消息超时！");
						result = new BocloudResult(false, hosts, "安装失败！获取worker执行结果为空");
					}
				} catch (Exception e) {
					logger.error("等待worker返回任务结果消息异常:", e);
					result = new BocloudResult(false, hosts, "安装失败！" + bocloudResult.getMessage());
				} finally {
					amqpAdmin.deleteQueue(resultQueue);
				}
			} else {
				logger.error("请求发送到worker失败：{}", bocloudResult.getMessage());
				result = new BocloudResult(false, hosts, "安装失败！" + bocloudResult.getMessage());
			}
			return result;
		});
	}

	/**
	 * 等待vmware tools安装状态
	 *
	 * @param instanceId
	 * @param vendor
	 * @param region
	 */
	private void waitVmToolStatus(String instanceId, String vendor) {
		BocloudResult result = serverDriver.detail(instanceId, vendor, null);
		if (result.isFailed()) {
			return;
		}
		ServerModel model = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), ServerModel.class);
		if (null == model) {
			return;
		}
		Boolean flag = true;
		Long startTime = System.currentTimeMillis();
		while (flag) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				logger.error("Get error message:", e);
				Thread.currentThread().interrupt();
			}
			result = serverDriver.detail(instanceId, vendor, null);
			if (result.isFailed()) {
				flag = false;
				break;
			}
			model = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), ServerModel.class);
			if (null == model) {
				flag = false;
				break;
			}
			if (null != model.getToolsInstalled() && model.getToolsInstalled()) {
				flag = false;
				break;
			}
			Long endTime = System.currentTimeMillis();
			// 等待60s
			if (endTime - startTime > 60 * 1000) {
				flag = false;
				break;
			}
		}
	}

	/**
	 * 更新虚拟机vmware tools运行状态以及IP地址
	 *
	 * @param cloudServer
	 * @param vendor
	 * @param user
	 * @throws Exception
	 */
	private void updateServer(CloudServer cloudServer, CloudVendor vendor, RequestContext context) throws Exception {
		// 开启一个线程去获取IP地址(已安装vmware tools的虚拟机)
		new Thread(new Runnable() {
			@Override
			public void run() {
				String operate = vendor.getType().toLowerCase() + ".vm";
				String message = "虚拟机【" + cloudServer.getName() + "】获取IP地址失败";
				try {
					BocloudResult result = serverDriver.getVmIps(cloudServer.getInstanceId(), vendor.getUuid(), null);
					if (result.isSuccess()) {
						ServerModel model = JSONObject.parseObject(JSONObject.toJSONString(result.getData()),
								ServerModel.class);
						message = "虚拟机【" + cloudServer.getName() + "】获取IP地址成功";
						List<String> ips = Lists.newArrayList();
						if (!StringUtils.isEmpty(model.getPrivateIps())) {
							List<IpPool> pools = ipPoolRepository.listByIpVersion(null);
							List<JSONObject> objects = JSONObject.parseArray(model.getPrivateIps(), JSONObject.class);
							for (JSONObject object : objects) {
								String address = object.getString("address");
								if (StringUtils.isEmpty(address)) {
									continue;
								}
								ips.add(object.getString("address"));
								Ip ip = ipRepository.checkExist(address, null, vendor.getId());
								if (null == ip) {
									ip = new Ip(address, null, null, null, vendor.getId(), IDFactory.instance().uuid(),
											null, IpStatus.used);
									ip.setCatalog("VM");
									ip.setTarget(cloudServer.getId());
									for (IpPool pool : pools) {
										String dns = null;
										if (!StringUtils.isEmpty(pool.getDnsNames())) {
											List<String> dnss = JSONArray.parseArray(pool.getDnsNames(), String.class);
											if (!ListTool.isEmpty(dnss)) {
												dns = dnss.get(0);
											}
										}
										List<String> poolList = JSONArray.parseArray(pool.getPools(), String.class);
										IPAddress ipAddress = IPAddress.getIPAddress(address);
										boolean flag = ipAddress.isInPools(address, poolList);
										if (flag) {
											ip.setMask(ipAddress.getMask(pool.getCidr()));
											ip.setGateway(pool.getGateway());
											ip.setDns(dns);
											ip.setPoolId(pool.getId());
											break;
										}
									}
									ipRepository.save(ip);
								} else {
									ip.setTarget(cloudServer.getId());
									ip.setCatalog("VM");
									ip.setVendorId(vendor.getId());
									ip.setStatus(IpStatus.used);
									ipRepository.update(ip);
								}
							}
						} else {
							ipRepository.delete(cloudServer.getId(), "VM");
						}
						if (!ListTool.isEmpty(ips)) {
							cloudServer.setManagerIp(ips.get(0));
						}
						cloudServer.setPrivateIps(model.getPrivateIps());
						cloudServer.setToolsInstalled(true);
						cloudServer.setToolsRunning(true);
						cloudServerRepository.update(cloudServer);
						Resource resource = resourceRepository.query(ResourceCategory.Computer.name(),
								ResourceCatalog.LOGICAL.name(), cloudServer.getId());
						if (null == resource) {
							resource = new Resource(ResourceCategory.Computer.name(), ResourceCatalog.LOGICAL.name(),
									cloudServer.getId(), JSONObject.toJSONString(ips), cloudServer.getOsCategory(),
									cloudServer.getVendorId());
							resource.preSave(cloudServer.getName(), cloudServer.getStatus(), cloudServer.getCreatorId(),
									cloudServer.getTenantId());
							resource.setInventory(JSONObject.toJSONString(cloudServer));
							resourceRepository.save(resource);
						} else {
							resource.setMenderId(context.getTarget());
							resource.setOwnerId(context.getTarget());
							resource.setGmtModify(new Date());
							resourceRepository.update(resource);
						}
						resourceEventPublisher.send(new OperateResult(true, message, operate,
								MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
					} else {
						resourceEventPublisher.send(new OperateResult(false, message, operate,
								MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
					}
				} catch (Exception e) {
					logger.error("Get error message:", e);
					resourceEventPublisher.send(new OperateResult(false, message, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				}
			}
		}).start();
	}

	/**
	 * 处理请求结果
	 *
	 * @param vendor
	 * @param id
	 * @param result
	 * @param user
	 */
	private void handleResult(CloudVendor vendor, Long id, BocloudResult result, RequestContext context) {
		String operate = vendor.getType().toLowerCase() + ".vm";
		String content = "IP地址为：" + result.getData() + "虚拟机安装Vmware Tools成功";
		if (!result.isSuccess()) {
			String message = result.getMessage();
			if (StringUtils.isEmpty(message)) {
				message = "ansible-role-vmwaretools : Check vmware-tools directory exists execute fail";
			}
			content = "IP地址为：" + result.getData() + "虚拟机安装Vmware Tools失败：" + message;
		}
		sendMsg(result.isSuccess(), operate, content, vendor, id, context);
	}

	/**
	 * 发送安装结果消息给用户
	 *
	 * @param success
	 * @param operate
	 * @param content
	 * @param vendor
	 * @param id
	 * @param user
	 */
	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, "虚拟机安装Vmware Tools", content, true, false);
		amqpTemplate.convertAndSend(BoCloudExchange.Default.DIRECT.getExchange(), "event.notice.queue", message);
	}
}
