package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.compute.ResourcePoolDriver;
import com.bocloud.cmp.model.ResourcePoolBean;
import com.bocloud.cmp.model.ResourcePoolModel;
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.*;
import com.bocloud.ims.interfaces.resource.VmResourcePoolService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 资源池service实现
 *
 * @author wangyu
 * @version 1.0
 * @since 2017年11月24日
 */
@Service("resourcePoolService")
public class VmResourcePoolServiceImpl implements VmResourcePoolService {

	private static OperateCategory operateCategory = OperateResult.OperateCategory.ResourceEvent;
	private Logger logger = LoggerFactory.getLogger(VmResourcePoolServiceImpl.class);
	@Autowired
	private ResourcePoolRepository resourcePoolRepository;
	@Autowired
	private ServerRepository serverRepository;
	@Autowired
	private ClusterRepository clusterRepository;
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private CloudServerRepository cloudServerRepository;
	@Autowired
	private ResourcePoolDriver resourcePoolDriver;
	@Autowired
	private VdcRepository vdcRepository;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;
	@Autowired
	private LockFactory lockFactory;

	@Override
	public BocloudResult<List<VmResourcePool>> list(Long parentId, Long vendorId) {
		try {
			List<VmResourcePool> vmResourcePools = resourcePoolRepository.list(parentId, vendorId);
			for (VmResourcePool vmResourcePool : vmResourcePools) {
				if ("CLUSTER".equals(vmResourcePool.getOwnerCategory())) {
					Cluster cluster = clusterRepository.query(vmResourcePool.getOwner());
					if (null != cluster) {
						vmResourcePool.setOwnerName(cluster.getName());
					}
				} else if ("HOST".equals(vmResourcePool.getOwnerCategory())) {
					Server server = serverRepository.query(vmResourcePool.getOwner());
					if (null != server) {
						vmResourcePool.setOwnerName(server.getName());
					}
				}
				List<VmResourcePool> children = resourcePoolRepository.list(vmResourcePool.getId(), vendorId);
				if (!children.isEmpty()) {
					vmResourcePool.setChildren("[]");
				} else {
					for (VmResourcePool child : children) {
						if ("CLUSTER".equals(child.getOwnerCategory())) {
							Cluster cluster = clusterRepository.query(child.getOwner());
							if (null != cluster) {
								child.setOwnerName(cluster.getName());
							}
						} else if ("HOST".equals(child.getOwnerCategory())) {
							Server server = serverRepository.query(child.getOwner());
							if (null != server) {
								child.setOwnerName(server.getName());
							}
						}
					}
				}
			}
			return new BocloudResult<>(true, vmResourcePools, "查询资源池成功");
		} catch (Exception e) {
			logger.error("list resource pool fail:", e);
			return new BocloudResult<>(false, "查询资源池失败");
		}
	}

	@Override
	public BocloudResult<String> create(ResourcePoolModel model, RequestContext context) {
		try {
			String ownerName = null;
			Long vendorId = null;
			if ("HOST".equals(model.getOwnerCategory())) {
				Server server = serverRepository.query(model.getOwner());
				if (null == server) {
					logger.warn("Server not exists");
					return new BocloudResult<>(false, "创建失败：宿主机不存在");
				}
				if (!server.getConnected()) {
					return new BocloudResult<>(false, "创建失败：宿主机处于断开状态");
				}
				ownerName = server.getName();
				vendorId = server.getVendorId();
			} else {
				Cluster cluster = clusterRepository.query(model.getOwner());
				if (null == cluster) {
					logger.warn("Cluster not exists");
					return new BocloudResult<>(false, "创建失败：集群不存在");
				}
				ownerName = cluster.getName();
				vendorId = cluster.getVendorId();
			}
			model.setOwnerName(ownerName);
			CloudVendor vendor = cloudVendorRepository.query(vendorId);
			if (null == vendor) {
				logger.warn("Cloud vendor not exists");
				return new BocloudResult<>(false, "云平台不存在");
			}
			Vdc vdc = vdcRepository.query(model.getVdcId());
			if (null == vdc) {
				logger.warn("Date center not exists");
				return new BocloudResult<>(false, "数据中心不存在");
			}
			model.setVdcName(vdc.getName());
			VmResourcePool parent = resourcePoolRepository.query(model.getParentId());
			if (null == parent) {
				logger.warn("Parent resource pool does not exist");
				return new BocloudResult<>(false, "父资源池不存在");
			}
			model.setParentName(parent.getName());
			new Thread(() -> {
				String operate = vendor.getType().toLowerCase() + ".pool";
				String content = "资源池创建失败";
				Long owner = model.getOwner();
				String ownerCategory = model.getOwnerCategory();
				BocloudResult result = resourcePoolDriver.create(vendor.getUuid(), model);
				try {
					if (result.isSuccess()) {
						content = "资源池创建成功";
						ResourcePoolModel poolModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()),
								ResourcePoolModel.class);
						VmResourcePool vmResourcePool = new VmResourcePool();
						BeanUtils.copyProperties(poolModel, vmResourcePool);
						vmResourcePool.setParentId(parent.getId());
						vmResourcePool.setCreatorId(context.getTarget());
						vmResourcePool.setVendorId(vendor.getId());
						vmResourcePool.setTenantId(0L);
						vmResourcePool.setVdcId(vdc.getId());
						vmResourcePool.setOwner(owner);
						vmResourcePool.setOwnerCategory(ownerCategory);
						resourcePoolRepository.save(vmResourcePool);
					} else {
						content = result.getMessage();
					}
					resourceEventPublisher.send(new OperateResult(result.isSuccess(), content, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				} catch (Exception e) {
					logger.error("Get error message:", e);
					resourceEventPublisher.send(new OperateResult(false, content, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				}
			}).start();
			return new BocloudResult<>(true, "资源池创建任务已下发，正在执行");
		} catch (Exception e) {
			logger.error("Get error message:", e);
			return new BocloudResult<>(false, "资源池创建失败");
		}
	}

	@Override
	public BocloudResult<String> modify(ResourcePoolBean bean, RequestContext context) {
		String path = VmResourcePool.class.getSimpleName() + "_" + bean.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			VmResourcePool vmResourcePool = resourcePoolRepository.query(bean.getId());
			if (null == vmResourcePool) {
				logger.warn("Resource pool not exists");
				return new BocloudResult<>(false, "资源池不存在");
			}
			if (!vmResourcePool.getName().equals(bean.getName())) {
				boolean flag = resourcePoolRepository.exists(bean.getName(), vmResourcePool.getParentId());
				if (flag) {
					return new BocloudResult<>(false, "此名称已存在");
				}
			}
			bean.setOldName(vmResourcePool.getName());
			CloudVendor vendor = cloudVendorRepository.query(vmResourcePool.getVendorId());
			if (null == vendor) {
				logger.warn("Cloud vendor not exists");
				return new BocloudResult<>(false, "云平台不存在");
			}
			Vdc vdc = vdcRepository.query(vmResourcePool.getVdcId());
			if (null == vdc) {
				logger.warn("Date center not exists");
				return new BocloudResult<>(false, "数据中心不存在");
			}
			bean.setVdcName(vdc.getName());
			new Thread(() -> {
				String operate = vendor.getType().toLowerCase() + ".pool";
				String content = "资源池编辑失败";
				BocloudResult result = resourcePoolDriver.modify(vendor.getUuid(), bean);
				try {
					if (result.isSuccess()) {
						content = "资源池编辑成功";
						ResourcePoolBean poolBean = JSONObject.parseObject(JSONObject.toJSONString(result.getData()),
								ResourcePoolBean.class);
						BeanUtils.copyProperties(poolBean, vmResourcePool);
						vmResourcePool.setMenderId(context.getTarget());
						resourcePoolRepository.update(vmResourcePool);
					} else {
						content = result.getMessage();
					}
					resourceEventPublisher.send(new OperateResult(result.isSuccess(), content, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				} catch (Exception e) {
					logger.error("Get error message:", e);
					resourceEventPublisher.send(new OperateResult(false, "资源池编辑失败", operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				}
			}).start();
			return new BocloudResult<>(true, "资源池更新任务已下发，正在执行");
		} catch (Exception e) {
			logger.error("Update resource pool fail:", e);
			return new BocloudResult<>(false, "修改资源池失败");
		}
	}

	@Override
	public BocloudResult<String> remove(Long id, Long userId) {
		String path = VmResourcePool.class.getSimpleName() + "_" + id;
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			VmResourcePool vmResourcePool = resourcePoolRepository.query(id);
			if (null == vmResourcePool) {
				logger.warn("Resource pool not exists");
				return new BocloudResult<>(false, "资源池不存在");
			}
			List<VmResourcePool> children = resourcePoolRepository.listByParentId(id);
			if (!ListTool.isEmpty(children)) {
				return new BocloudResult<>(false, "该资源池下包含子资源池，请先删除子资源池");
			}
			List<CloudServer> cloudServers = cloudServerRepository.listByResPoolId(id);
			if (!ListTool.isEmpty(cloudServers)) {
				return new BocloudResult<>(false, "该资源池下存在虚拟机，删除失败");
			}
			CloudVendor vendor = cloudVendorRepository.query(vmResourcePool.getVendorId());
			if (null == vendor) {
				logger.warn("Cloud vendor not exists");
				return new BocloudResult<>(false, "云平台不存在");
			}
			BocloudResult result = resourcePoolDriver.remove(vendor.getUuid(), vmResourcePool.getName());
			if (result.isFailed()) {
				return result;
			}
			resourcePoolRepository.remove(id, userId);
			return new BocloudResult<>(true, "资源池删除成功");
		} catch (Exception e) {
			logger.error("Remove resource pool fail:", e);
			return new BocloudResult<>(false, "资源池删除失败");
		}
	}

	@Override
	public BocloudResult<VmResourcePool> detail(Long id) {
		try {
			VmResourcePool vmResourcePool = resourcePoolRepository.query(id);
			if ("CLUSTER".equals(vmResourcePool.getOwnerCategory())) {
				Cluster cluster = clusterRepository.query(vmResourcePool.getOwner());
				if (null != cluster) {
					vmResourcePool.setOwnerName(cluster.getName());
				}
			} else if ("HOST".equals(vmResourcePool.getOwnerCategory())) {
				Server server = serverRepository.query(vmResourcePool.getOwner());
				if (null != server) {
					vmResourcePool.setOwnerName(server.getName());
				}
			}
			return new BocloudResult<>(true, vmResourcePool, "查询成功");
		} catch (Exception e) {
			logger.error("Query resource pool fail:", e);
			return new BocloudResult<>(false, "查询失败");
		}
	}

	@Override
	public BocloudResult<String> exists(String name, Long parentId) {
		try {
			boolean flag = resourcePoolRepository.exists(name, parentId);
			if (flag) {
				return new BocloudResult<>(false, "资源池名称已存在");
			}
			return new BocloudResult<>(true, "查询成功");
		} catch (Exception e) {
			logger.error("Get error message:", e);
			return new BocloudResult<>(false, "查询失败");
		}
	}

	@Override
	public BocloudResult vrps(String condition) {
		JSONObject object = JSONObject.parseObject(condition);
		String name = null;
		Long parentId = null;
		Long vendorId = null;
		if (null != object) {
			name = object.getString("name");
			parentId = object.getLong("parentId");
			vendorId = object.getLong("vendorId");
		}
		if (null == name || "".equals(name)) {
			return this.list(parentId, vendorId);
		} else {
			return this.exists(name, parentId);
		}
	}

}
