package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.network.ElasticIpDriver;
import com.bocloud.cmp.entity.enums.CloudProvider;
import com.bocloud.cmp.entity.enums.InstanceType;
import com.bocloud.cmp.entity.enums.ResourceCategory;
import com.bocloud.cmp.model.ElasticIpGenericParam;
import com.bocloud.cmp.model.ElasticIpModel;
import com.bocloud.cmp.model.common.*;
import com.bocloud.cmp.model.common.OrderMessage.Operation;
import com.bocloud.cmp.provider.aliyunconvertor.AccessControlConvertor;
import com.bocloud.common.model.*;
import com.bocloud.common.model.RequestContext.Catalog;
import com.bocloud.common.utils.*;
import com.bocloud.ims.entity.common.Business;
import com.bocloud.ims.entity.common.Project;
import com.bocloud.ims.entity.common.Tenant;
import com.bocloud.ims.entity.common.User;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.interfaces.resource.ElasticIpService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.internal.SmsBusinessService;
import com.bocloud.ims.service.internal.SmsProjectService;
import com.bocloud.ims.service.internal.SmsTenantService;
import com.bocloud.ims.service.internal.SmsUserService;
import com.bocloud.nms.enums.OrderItemStatus;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service("elasticIpService")
public class ElasticIpServiceImpl implements ElasticIpService {
	private static Logger logger = LoggerFactory.getLogger(ElasticIpServiceImpl.class);
	@Autowired
	private ElasticIpRepository elasticIpRepository;
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private ElasticIpDriver elasticIpDriver;
	@Autowired
	private LockFactory lockFactory;
	@Autowired
	private CloudServerRepository cloudServerRepository;
	@Autowired
	private NatGatewayRepository gatewayRepository;
	@Autowired
	private NetworkInterfaceRepository networkInterfaceRepository;
	@Autowired
	private LoadBalancerRepository loadBalancerRepository;
	@Autowired
	private NatGatewaySnatRuleRepository natGatewaySnatRuleRepository;
	@Autowired
	private NatGatewayDnatRuleRepository natGatewayDnatRuleRepository;
	@Autowired
	private NatGatewayRepository natGatewayRepository;
	@Autowired
	private PortRepository portRepository;
	@Autowired
	private ThreadPoolTaskExecutor taskExecutor;
	@Autowired
	private ResourceEventRepository resourceEventRepository;
	@Autowired
	private AmqpTemplate amqpTemplate;
	@Autowired
	private SmsProjectService smsProjectService;
	@Autowired
	private SmsUserService smsUserService;
	@Autowired
	private SmsBusinessService smsBusinessService;
	@Autowired
	private SmsTenantService smsTenantService;

	@Override
	public BocloudResult<GridBean<ElasticIp>> list(Pager pager, RequestContext context) {
		try {
			List<Param> params = Optional.ofNullable(pager.getParams()).orElse(new ArrayList<>());
			Map<String, String> sorter = Optional.ofNullable(pager.getSorter()).orElse(Maps.newHashMap());
			sorter.put("gmtCreate", Common.ONE);
			Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
			Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);

			String vendorType = null;
			for (Param param : params) {
				Map<String, Object> paramMap = param.getParam();
				if (paramMap.containsKey("vendorType")) {
					vendorType = String.valueOf(paramMap.get("vendorType"));
					paramMap.remove("vendorType");
				}
			}
			int total = 0;
			List<ElasticIp> list = new ArrayList<>();
			if (context.getCatalog().equals(Catalog.Tenant)) {
				// 租户能看到 1.属于当前租户的资源 2.租户下所有项目的资源
				List<Project> tenantProjects = smsProjectService.listTenantProjects(context.getTenant());
				List<Long> projectIds = tenantProjects.stream().map(Project::getId).collect(Collectors.toList());
				total = this.elasticIpRepository.count(null, context.getTenant(), projectIds, params, vendorType);
				list = this.elasticIpRepository.list(page, rows, null, context.getTenant(), projectIds, params, sorter,
						vendorType);
				list = convertor(list);
			} else if (context.getCatalog().equals(Catalog.User)) {
				// 用户能看到 1.自己申请的资源 2.自己所在的资源可见项目（资源对项目下所有成员可见）下创建的资源 3.自己管理的项目下的资源
				List<Project> userManagerProjects = smsProjectService.listUserManagerProjects(context.getTarget());
				List<Project> visibleProjects = smsProjectService.listUserVisibleProjects(context.getTarget());
				List<Long> projectIds = new ArrayList<>();
				List<Long> userManagerProjectsIds = userManagerProjects.stream().map(Project::getId)
						.collect(Collectors.toList());
				if (!userManagerProjectsIds.isEmpty()) {
					projectIds.addAll(userManagerProjectsIds);
				}
				List<Long> visibleProjectsIds = visibleProjects.stream().map(Project::getId)
						.collect(Collectors.toList());
				if (!visibleProjectsIds.isEmpty()) {
					projectIds.addAll(visibleProjectsIds);
				}
				total = this.elasticIpRepository.count(context.getTarget(), null, projectIds, params, vendorType);
				list = this.elasticIpRepository.list(page, rows, context.getTarget(), null, projectIds, params, sorter,
						vendorType);
				list = convertor(list);
			} else {
				total = elasticIpRepository.count(params);
				list = elasticIpRepository.list(page, rows, params, sorter);
			}
			for (ElasticIp eip : list) {
				CloudProvider provider = CloudProvider.valueOf(eip.getVendorType());
				switch (provider) {
				case AWS:
					if (StringUtils.isEmpty(eip.getInstanceType())) {
						if (!StringUtils.isEmpty(eip.getInstanceId())) {
							CloudServer server = cloudServerRepository.getByInstanceId(eip.getInstanceId(),
									eip.getVendorId());
							if (null != server) {
								eip.setVmName(server.getName());
								eip.setPrivateIps(server.getPrivateIps());
							}
						}
						if (!StringUtils.isEmpty(eip.getNetworkInterfaceId())) {
							NetworkInterface networkInterface = networkInterfaceRepository
									.getByNetworkInterfaceId(eip.getNetworkInterfaceId(), eip.getVendorId());
							if (null != networkInterface) {
								eip.setPrivateIps(networkInterface.getPrimaryPrivateIPAddress());
							}
						}
					}
					break;
				case ALIYUN:
					if (InstanceType.EcsInstance.name().equalsIgnoreCase(eip.getInstanceType())) {
						CloudServer server = cloudServerRepository.getByInstanceId(eip.getInstanceId(),
								eip.getVendorId());
						if (null != server) {
							eip.setInstanceName(server.getName());
							String privateIpStr = "";
							List<String> parseArray = JSON.parseArray(server.getPrivateIps(), String.class);
							if (!ListTool.isEmpty(parseArray)) {
								for (String ip : parseArray) {
									privateIpStr += ip + ",";
								}
								if (privateIpStr.length() > 0) {
									privateIpStr = privateIpStr.substring(0, privateIpStr.length() - 1);
								}
							}
							eip.setPrivateIps(privateIpStr);
						}
					}
					if (InstanceType.SlbInstance.name().equalsIgnoreCase(eip.getInstanceType())) {
						LoadBalancer loadBalancer = loadBalancerRepository.queryByUuid(eip.getInstanceId(),
								eip.getVendorId());
						if (null != loadBalancer) {
							eip.setInstanceName(loadBalancer.getName());
							eip.setPrivateIps(loadBalancer.getVipAddress());
						}
					}
					if (InstanceType.NetworkInterface.name().equalsIgnoreCase(eip.getInstanceType())) {
						NetworkInterface nic = networkInterfaceRepository.query(eip.getInstanceId(), eip.getVendorId());
						if (null != nic) {
							eip.setInstanceName(nic.getName());
							eip.setPrivateIps(nic.getPrimaryPrivateIPAddress());
						}
					}
					if (InstanceType.Nat.name().equalsIgnoreCase(eip.getInstanceType())) {
						NatGateway gateway = gatewayRepository.queryByUuid(eip.getInstanceId(), eip.getVendorId());
						if (null != gateway) {
							eip.setInstanceName(gateway.getName());
							eip.setPrivateIpAddress(gateway.getPrivateIpAddress());
							eip.setPrivateIps(gateway.getPrivateIpAddress());
						}
					}
					break;
				case HUAWEI:
					if (!StringUtils.isEmpty(eip.getInstanceId())) {
						Port port = portRepository.getByPortUuid(eip.getInstanceId(), eip.getVendorId());
						if (null != port) {
							if (port.getDeviceOwner().contains("compute")) {
								CloudServer cloudServer = cloudServerRepository.get(port.getDeviceUuid());
								eip.setInstanceName("云服务器：" + cloudServer.getName());
							} else if (port.getDeviceOwner().contains("LOADBALANCERV2")) {
								LoadBalancer loadBalancer = loadBalancerRepository.queryByUuid(port.getDeviceUuid());
								eip.setInstanceName("负载均衡器：" + loadBalancer.getName());
							} else if (port.getDeviceOwner().contains("nat_gateway")) {
								// network:nat_gateway
								NatGateway gateway = gatewayRepository.queryByUuid(port.getDeviceUuid(),
										eip.getVendorId());
								if (null != gateway) {
									eip.setInstanceName("NAT网关：" + gateway.getName());
								}
							}
						}
					}
					break;
				default:
					break;
				}
			}
			GridBean<ElasticIp> gridBean = GridHelper.getBean(page, rows, total, list);
			gridBean.setRows(list);
			return new BocloudResult<GridBean<ElasticIp>>(true, gridBean, "查询成功");
		} catch (Exception e) {
			logger.error("list elastic ip failure:", e);
			return new BocloudResult<GridBean<ElasticIp>>(false, "查询失败", null, null);
		}
	}
	
	public List<ElasticIp> convertor(List<ElasticIp> list) {
		try {
			if (!ListTool.isEmpty(list)) {
				List<Long> tenantIds = new ArrayList<>();
				List<Long> userIds = new ArrayList<>();
				List<Long> businessIds = new ArrayList<>();
				List<Long> projectIds = new ArrayList<>();
				for (ElasticIp clasticIp : list) {
					if (clasticIp.getTenantId() != null) {
						tenantIds.add(clasticIp.getTenantId());
					}
					if (clasticIp.getBusinessId() != null) {
						businessIds.add(clasticIp.getBusinessId());
					}
					if (clasticIp.getProjectId() != null) {
						projectIds.add(clasticIp.getProjectId());
					}
					if (clasticIp.getCreatorId() != null) {
						userIds.add(clasticIp.getCreatorId());
					}
					if (clasticIp.getOwnerId() != null) {
						userIds.add(clasticIp.getOwnerId());
					}
					if (clasticIp.getMenderId() != null) {
						userIds.add(clasticIp.getMenderId());
					}
				}
				Map<Long, String> userMap = smsUserService.listUsersByIds(userIds).stream()
						.collect(Collectors.toMap(User::getId, User::getName));
				Map<Long, String> projectMap = smsProjectService.listProjectsByIds(projectIds).stream()
						.collect(Collectors.toMap(Project::getId, Project::getName));
				Map<Long, String> businessMap = smsBusinessService.listBusinesssByIds(businessIds).stream()
						.collect(Collectors.toMap(Business::getId, Business::getName));
				Map<Long, String> tenantMap = smsTenantService.listTenantsByIds(tenantIds).stream()
						.collect(Collectors.toMap(Tenant::getId, Tenant::getName));
				for (ElasticIp clasticIp : list) {
					// 设置虚拟机归属信息
					if (clasticIp.getTenantId() != null) {
						clasticIp.setTenantName(tenantMap.get(clasticIp.getTenantId()));
					}
					if (clasticIp.getBusinessId() != null) {
						clasticIp.setBusinessName(businessMap.get(clasticIp.getBusinessId()));
					}
					if (clasticIp.getProjectId() != null) {
						clasticIp.setProjectName(projectMap.get(clasticIp.getProjectId()));
					}
					if (clasticIp.getCreatorId() != null) {
						clasticIp.setCreatorName(userMap.get(clasticIp.getCreatorId()));
					}
					if (clasticIp.getOwnerId() != null) {
						clasticIp.setOwnerName(userMap.get(clasticIp.getOwnerId()));
					}
					if (clasticIp.getMenderId() != null) {
						clasticIp.setMenderName(userMap.get(clasticIp.getMenderId()));
					}
				}
			}
		} catch (Exception e) {
			logger.error("获取浮动IP基本信息异常：" + e.getMessage());
		}
		return list;
	}

	@Override
	public BocloudResult<ElasticIp> allocate(ElasticIp elasticIp, RequestContext context) {
		try {
			CloudVendor vendor = cloudVendorRepository.query(elasticIp.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "指定的地域下云平台不存在");
			}
			CloudProvider provider = CloudProvider.valueOf(vendor.getType());
			BocloudResult result = null;
			ElasticIpModel elasticIpModel = JSONObject.parseObject(JSON.toJSONString(elasticIp), ElasticIpModel.class);
			switch (provider) {
			case ALIYUN:
				result = elasticIpDriver.allocate(elasticIpModel, vendor.getUuid(), elasticIp.getRegionId());
				break;
			case AWS:
				result = elasticIpDriver.allocate(vendor.getUuid(), elasticIp.getName(), elasticIp.getRegionId());
				break;
			case HUAWEI:
				if (StringUtils.isEmpty(elasticIpModel.getBandwidthShareType())) {
					elasticIpModel.setBandwidthShareType("PER");
				}
				if (StringUtils.isEmpty(elasticIpModel.getBandwidthChargeMode())) {
					elasticIpModel.setBandwidthChargeMode("bandwidth");
				}
				if (StringUtils.isEmpty(elasticIpModel.getBandwidthName())) {
					elasticIpModel.setBandwidthName(UUID.randomUUID().toString());
				}
				if (StringUtils.isEmpty(elasticIpModel.getIpVersion())) {
					elasticIpModel.setIpVersion("4");
				}
				result = elasticIpDriver.allocate(elasticIpModel, vendor.getUuid(), elasticIp.getRegionId());
				break;
			default:
				break;
			}
			if (result.isFailed()) {
				return new BocloudResult<>(false, "浮动IP创建失败:" + result.getMessage(), null, null);
			}
			Long creatorId = elasticIp.getCreatorId();
			elasticIp = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), ElasticIp.class);
			elasticIp.setVendorId(vendor.getId());
			elasticIp.setRegionId(elasticIpModel.getRegionId());
			elasticIp.setCreatorId(creatorId);
			elasticIpRepository.save(elasticIp);
			return new BocloudResult<>(true, elasticIp, "分配成功");
		} catch (Exception e) {
			logger.error("allocate elastic ip failure:", e);
			return new BocloudResult<>(false, "分配失败", null, null);
		}
	}

	@Override
	public BocloudResult release(Long id, RequestContext context) {
		try {
			ElasticIp elasticIp = elasticIpRepository.query(id);
			if (elasticIp == null) {
				return new BocloudResult<>(false, "弹性IP不存在", null, null);
			}
			CloudVendor vendor = cloudVendorRepository.query(elasticIp.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "指定的地域下云平台不存在");
			}
			BocloudResult result = elasticIpDriver.release(vendor.getUuid(), elasticIp.getElasticIpUuid(),
					elasticIp.getRegionId());
			if (result.isFailed()) {
				return new BocloudResult<>(false, "释放失败:" + result.getMessage(), null, null);
			}
			elasticIpRepository.remove(elasticIp.getId(), context.getTarget());
			return new BocloudResult<>(true, "释放成功");
		} catch (Exception e) {
			logger.error("release elastic ip failure:", e);
			return new BocloudResult<>(false, "释放失败", null, null);
		}
	}

	@Override
	public BocloudResult associate(ElasticIpGenericParam elasticIpGenericParam, RequestContext context) {
		try {
			ElasticIp elasticIp = elasticIpRepository.query(elasticIpGenericParam.getId());
			if (elasticIp == null) {
				return new BocloudResult<>(false, "弹性IP不存在", null, null);
			}
			CloudVendor vendor = cloudVendorRepository.query(elasticIp.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "云平台不存在");
			}
			CloudProvider provider = CloudProvider.valueOf(vendor.getType());
			BocloudResult result = null;
			String instanceId = elasticIpGenericParam.getInstanceId();
			String instanceType = elasticIpGenericParam.getInstanceType();
			String networkInterfaceId = elasticIpGenericParam.getNetworkInterfaceId();
			String action = elasticIpGenericParam.getAction();

			switch (provider) {
			case ALIYUN:
				ElasticIpModel model = new ElasticIpModel();
				model.setElasticIpUuid(elasticIp.getElasticIpUuid());
				model.setInstanceId(instanceId);
				model.setInstanceType(instanceType);// Nat、SlbInstance、EcsInstance、NetworkInterface
				result = elasticIpDriver.associate(model, vendor.getUuid(), elasticIp.getRegionId());
				elasticIp.setInstanceId(instanceId);
				elasticIp.setInstanceType(instanceType);
				if ("NetworkInterface".equalsIgnoreCase(instanceType)) {
					NetworkInterface networkInterface = networkInterfaceRepository.query(instanceId, vendor.getId());
					if (null != networkInterface) {
						networkInterface.setPublicIPAddress(elasticIp.getPublicIp());
						networkInterfaceRepository.update(networkInterface);
					}
				} else {
					CloudServer cloudserver = cloudServerRepository.getByInstanceId(instanceId, vendor.getId());
					if (null != cloudserver) {
						cloudserver.setPublicIps(JSON.toJSONString(new String[] { elasticIp.getPublicIp() }));
						cloudServerRepository.update(cloudserver);
					}
				}
				break;
			case AWS:
				String privateIpAddress = elasticIpGenericParam.getPrivateIpAddress();
				Boolean allowReassociation = elasticIpGenericParam.getAllowReassociation();
				String associatedId = null;
				if (!StringUtils.isEmpty(instanceId)) {
					associatedId = instanceId;
					result = elasticIpDriver.associateInstance(vendor.getUuid(), elasticIp.getElasticIpUuid(),
							instanceId, privateIpAddress, allowReassociation, elasticIp.getRegionId());
				} else if (!StringUtils.isEmpty(networkInterfaceId)) {
					associatedId = networkInterfaceId;
					result = elasticIpDriver.associateNetworkInterface(vendor.getUuid(), elasticIp.getElasticIpUuid(),
							networkInterfaceId, privateIpAddress, allowReassociation, elasticIp.getRegionId());
				}
				if (result.isSuccess()) {
					ElasticIp assElasticIp = elasticIpRepository.queryByAssociatedId(associatedId, vendor.getId());
					if (assElasticIp != null) {
						assElasticIp.setAssociationId(null);
						assElasticIp.setInstanceId(null);
						assElasticIp.setNetworkInterfaceId(null);
						assElasticIp.setGmtModify(new Date());
						elasticIpRepository.update(assElasticIp);
					}

					CloudServer cloudserver = cloudServerRepository.getByInstanceId(instanceId, vendor.getId());
					if (null != cloudserver) {
						cloudserver.setPublicIps(JSON.toJSONString(new String[]{elasticIp.getPublicIp()}));
						cloudServerRepository.update(cloudserver);
					}
				}
				break;
			case HUAWEI:
				if ("bindLoadBalancer".equals(action)) {
					ElasticIpModel hwEipModel = new ElasticIpModel();
					hwEipModel.setElasticIpUuid(elasticIp.getElasticIpUuid());
					hwEipModel.setPortId(instanceId);
					result = elasticIpDriver.associate(hwEipModel, vendor.getUuid(), elasticIp.getRegionId());
					if (result.isFailed()) {
						return new BocloudResult<>(false, "关联失败:" + result.getMessage(), null, null);
					}
				} else {
					CloudServer hwServer = null;
					if (StringUtils.isEmpty(instanceId)) {
						Long serverId = elasticIpGenericParam.getServerId();
						hwServer = cloudServerRepository.query(serverId);
					} else {
						Port port = portRepository.getByPortUuid(instanceId, elasticIp.getVendorId());
						Assert.notNull(port, "当前虚机不存在");
						if (port.getDeviceOwner().contains("compute")) {
							hwServer = cloudServerRepository.get(port.getDeviceUuid());
						}
					}
					Assert.notNull(hwServer, "当前虚机不存在");
					List<JSONObject> privateArrays = JSONObject.parseArray(hwServer.getPrivateIps(), JSONObject.class);
					if (ListTool.isEmpty(privateArrays)) {
						return new BocloudResult<>(false, "当前虚机网卡信息错误");
					}
					Boolean flag = false;
					for (JSONObject jsonObject : privateArrays) {
						if ("floating".equalsIgnoreCase(jsonObject.getString("type"))) {
							flag = true;
							break;
						}
					}
					if (flag) {
						return new BocloudResult<>(false, "一台虚拟机只允许绑定一个浮动IP");
					}
					if (StringUtils.isEmpty(instanceId)) {
						instanceId = privateArrays.get(0).getString("portId");
					}
					ElasticIpModel hwEipModel = new ElasticIpModel();
					hwEipModel.setElasticIpUuid(elasticIp.getElasticIpUuid());
					hwEipModel.setPortId(instanceId);
					result = elasticIpDriver.associate(hwEipModel, vendor.getUuid(), elasticIp.getRegionId());
					if (result.isFailed()) {
						return new BocloudResult<>(false, "关联失败:" + result.getMessage(), null, null);
					}
					// 保存虚机信息
					JSONObject eipObject = new JSONObject();
					eipObject.put("address", elasticIp.getPublicIp());
					eipObject.put("portId", instanceId);
					eipObject.put("type", "floating");
					privateArrays.add(eipObject);
					hwServer.setPrivateIps(JSON.toJSONString(privateArrays));
					cloudServerRepository.update(hwServer);
					elasticIp.setAssociationId(hwServer.getInstanceId());
					elasticIp.setInstanceType("EcsInstance");
				}
				// ACTIVE绑定
				elasticIp.setStatus("ACTIVE");
				elasticIp.setInstanceId(instanceId);
				elasticIp.setNetworkInterfaceId(networkInterfaceId);
				elasticIp.setGmtModify(new Date());
				elasticIpRepository.update(elasticIp);
				return new BocloudResult<>(true, "关联成功");
			default:
				break;
			}
			if (result.isFailed()) {
				return new BocloudResult<>(false, "关联失败:" + result.getMessage(), null, null);
			}
			elasticIp.setAssociationId(String.valueOf(result.getData()));
			elasticIp.setInstanceId(instanceId);
			elasticIp.setNetworkInterfaceId(networkInterfaceId);
			elasticIp.setGmtModify(new Date());
			elasticIpRepository.update(elasticIp);
			return new BocloudResult<>(true, "关联成功");
		} catch (Exception e) {
			logger.error("associate elastic ip failure:", e);
			return new BocloudResult<>(false, "关联失败:" + e.getMessage());
		}
	}

	@Override
	public BocloudResult disassociate(Long id, RequestContext context) {
		try {
			ElasticIp elasticIp = elasticIpRepository.query(id);
			if (elasticIp == null) {
				return new BocloudResult<>(false, "弹性IP不存在", null, null);
			}
			CloudVendor vendor = cloudVendorRepository.query(elasticIp.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "云平台不存在");
			}
			CloudProvider provider = CloudProvider.valueOf(vendor.getType());
			BocloudResult result = null;
			switch (provider) {
			case ALIYUN:
				ElasticIpModel model = new ElasticIpModel();
				model.setElasticIpUuid(elasticIp.getElasticIpUuid());
				model.setInstanceId(elasticIp.getInstanceId());
				model.setInstanceType(elasticIp.getInstanceType());// Nat、SlbInstance、EcsInstance、NetworkInterface
				result = elasticIpDriver.disassociate(model, vendor.getUuid(), elasticIp.getRegionId());
				CloudServer cloudserver = cloudServerRepository.getByInstanceId(elasticIp.getInstanceId(),
						vendor.getId());
				if (null != cloudserver) {
					cloudserver.setPublicIps(null);
					cloudServerRepository.update(cloudserver);
				}
				break;
			case AWS:
				result = elasticIpDriver.disassociate(vendor.getUuid(), elasticIp.getAssociationId(),
						elasticIp.getRegionId());
				cloudserver = cloudServerRepository.getByInstanceId(elasticIp.getInstanceId(),
						vendor.getId());
				if (null != cloudserver) {
					cloudserver.setPublicIps(null);
					cloudServerRepository.update(cloudserver);
				}
				break;
			case HUAWEI:
				ElasticIpModel hwEipModel = new ElasticIpModel();
				hwEipModel.setElasticIpUuid(elasticIp.getElasticIpUuid());
				result = elasticIpDriver.disassociate(hwEipModel, vendor.getUuid(), elasticIp.getRegionId());
				// DOWN未绑定
				elasticIp.setStatus("DOWN");
				if (!StringUtils.isEmpty(elasticIp.getInstanceId())) {
					Port port = portRepository.getByPortUuid(elasticIp.getInstanceId(), elasticIp.getVendorId());
					if (null != port) {
						if (port.getDeviceOwner().contains("compute")) {
							CloudServer cloudServer = cloudServerRepository.get(port.getDeviceUuid());
							String privateIps = cloudServer.getPrivateIps();
							List<JSONObject> publicArrays = JSONObject.parseArray(privateIps, JSONObject.class);
							List<JSONObject> privateArrays = new ArrayList<JSONObject>();
							if (!ListTool.isEmpty(publicArrays)) {
								for (JSONObject poblicIpObject : publicArrays) {
									String type = poblicIpObject.getString("type");
									if ("floating".equalsIgnoreCase(type) && poblicIpObject.getString("portId")
											.equalsIgnoreCase(port.getPortUuid())) {
										continue;
									} else {
										privateArrays.add(poblicIpObject);
									}
								}
							}
							cloudServer.setPrivateIps(JSON.toJSONString(privateArrays));
							cloudServerRepository.update(cloudServer);
						}
					}
				}
				break;
			default:
				break;
			}
			if (result.isFailed()) {
				return new BocloudResult<>(false, "取消关联失败:" + result.getMessage(), null, null);
			}
			elasticIp.setAssociationId(null);
			elasticIp.setInstanceType(null);
			elasticIp.setInstanceId(null);
			elasticIp.setNetworkInterfaceId(null);
			elasticIp.setGmtModify(new Date());
			elasticIpRepository.update(elasticIp);
			return new BocloudResult<>(true, "取消关联成功");
		} catch (Exception e) {
			logger.error("disassociate elastic ip failure:", e);
			return new BocloudResult<>(false, "取消关联失败", null, null);
		}
	}

	@Override
	public BocloudResult<ElasticIp> modify(ElasticIp elasticIp, RequestContext context) {
		String path = ElasticIp.class.getSimpleName() + "_" + elasticIp.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			ElasticIp oldElasticIp = elasticIpRepository.query(elasticIp.getId());
			Assert.notNull(oldElasticIp, "ElasticIp不存在!");
			CloudVendor vendor = cloudVendorRepository.query(oldElasticIp.getVendorId());
			Assert.notNull(vendor, "云平台不存在!");
			ElasticIpModel model = JSONObject.parseObject(JSONObject.toJSONString(elasticIp), ElasticIpModel.class);
			BocloudResult bocloudResult = elasticIpDriver.update(vendor.getUuid(), model, elasticIp.getRegionId());
			if (bocloudResult.isFailed()) {
				return bocloudResult;
			}
			switch (CloudProvider.valueOf(vendor.getType())) {
			case HUAWEI:
				ElasticIpModel newModel = JSONObject.parseObject(JSONObject.toJSONString(bocloudResult.getData()),
						ElasticIpModel.class);
				oldElasticIp.setIpVersion(newModel.getIpVersion());
				oldElasticIp.setPublicIpv6Address(newModel.getPublicIpv6Address());
				break;
			default:
				BeanUtils.copyProperties(model, oldElasticIp);
				break;
			}
			elasticIpRepository.update(oldElasticIp);
			return new BocloudResult<ElasticIp>(true, oldElasticIp, "修改成功");
		} catch (Exception e) {
			logger.error("Modify eip fail:", e);
			return new BocloudResult<ElasticIp>(false, "修改失败:" + e.getMessage());
		}
	}

	@Override
	public BocloudResult<ElasticIp> detail(Long id) {
		try {
			ElasticIp elasticIp = elasticIpRepository.query(id);
			if(null == elasticIp) {
				return new BocloudResult<>(false, "资源不存在");
			}
			// 设置虚拟机归属
			if (null != elasticIp.getOwnerId() && 0L != elasticIp.getOwnerId()) {
				BocloudResult<User> ownerDetail = smsUserService.detail(elasticIp.getOwnerId());
				User owner = ownerDetail.getData();
				if (owner != null) {
					elasticIp.setOwnerName(owner.getName());
				}
			}
			if (null != elasticIp.getMenderId() && 0L != elasticIp.getMenderId()) {
				BocloudResult<User> menderDetail = smsUserService.detail(elasticIp.getMenderId());
				User mender = menderDetail.getData();
				if (mender != null) {
					elasticIp.setMenderName(mender.getName());
				}
			}
			if (null != elasticIp.getCreatorId() && 0L != elasticIp.getCreatorId()) {
				BocloudResult<User> creatorDetail = smsUserService.detail(elasticIp.getCreatorId());
				User creator = creatorDetail.getData();
				if (creator != null) {
					elasticIp.setCreatorName(creator.getName());
				}
			}
			if (null != elasticIp.getProjectId() && 0L != elasticIp.getProjectId()) {
				BocloudResult<Project> projectResult = smsProjectService.detail(elasticIp.getProjectId());
				if (projectResult.isFailed() || null == projectResult.getData()) {
					return new BocloudResult(false, projectResult.getMessage());
				}
				Project project = projectResult.getData();
				if (null != project) {
					elasticIp.setProjectName(project.getName());
				}
			}
			if (null != elasticIp.getBusinessId() && 0L != elasticIp.getBusinessId()) {
				BocloudResult<Business> businessDetail = smsBusinessService.detail(elasticIp.getBusinessId());
				Business business = businessDetail.getData();
				if (business != null) {
					elasticIp.setBusinessName(business.getName());
				}
			}
			if (null != elasticIp.getTenantId() && 0L != elasticIp.getTenantId()) {
				BocloudResult<Tenant> tenantDetail = smsTenantService.detail(elasticIp.getTenantId());
				Tenant tenant = tenantDetail.getData();
				if (tenant != null) {
					elasticIp.setTenantName(tenant.getName());
				}
			}
			return new BocloudResult<ElasticIp>(true, elasticIp, "查询成功");
		} catch (Exception e) {
			logger.error("detail elastic ip failure:", e);
			return new BocloudResult<ElasticIp>(false, "查询失败", null, null);
		}
	}

	@Override
	public BocloudResult<List<ElasticIp>> listFrees(Long vendorId, String regionId, RequestContext context) {
		try {
			List<ElasticIp> list = new ArrayList<>();
			if (context.getCatalog().equals(Catalog.Tenant)) {
				// 租户能看到 1.属于当前租户的资源 2.租户下所有项目的资源
				List<Project> tenantProjects = smsProjectService.listTenantProjects(context.getTenant());
				List<Long> projectIds = tenantProjects.stream().map(Project::getId).collect(Collectors.toList());
				list = elasticIpRepository.listEips(null, context.getTenant(), projectIds, regionId, vendorId);
			} else if (context.getCatalog().equals(Catalog.User)) {
				// 用户能看到 1.自己申请的资源 2.自己所在的资源可见项目（资源对项目下所有成员可见）下创建的资源 3.自己管理的项目下的资源
				List<Project> userManagerProjects = smsProjectService.listUserManagerProjects(context.getTarget());
				List<Project> visibleProjects = smsProjectService.listUserVisibleProjects(context.getTarget());
				List<Long> projectIds = new ArrayList<>();
				List<Long> userManagerProjectsIds = userManagerProjects.stream().map(Project::getId)
						.collect(Collectors.toList());
				if (!userManagerProjectsIds.isEmpty()) {
					projectIds.addAll(userManagerProjectsIds);
				}
				List<Long> visibleProjectsIds = visibleProjects.stream().map(Project::getId)
						.collect(Collectors.toList());
				if (!visibleProjectsIds.isEmpty()) {
					projectIds.addAll(visibleProjectsIds);
				}
				list = elasticIpRepository.listEips(context.getTarget(), null, projectIds, regionId, vendorId);
			} else {
				list = elasticIpRepository.listEips(null, null, null, regionId, vendorId);
			}

			return new BocloudResult<List<ElasticIp>>(true, list, "查询成功!");
		} catch (Exception e) {
			logger.error("list elasticIp failure:", e);
			return new BocloudResult<List<ElasticIp>>(false, "查询失败!");
		}
	}

	@Override
	public BocloudResult listAvailableEipsForNatGateway(String params) {
		try {
			JSONObject object = JSONTools.isJSONObj(params);
			Long vendorId = object.getLong(Common.VENDORID);
			String ip = object.getString("ip");
			String regionId = object.getString("regionId");
			String instanceUuid = object.getString("instanceUuid");
			// SNAT规则Id
			Long snatRuleId = object.getLong("snatRuleId");
			// DNAT规则Id
			Long dnatRuleId = object.getLong("dnatRuleId");
			List<ElasticIp> list = new ArrayList<>();
			if (StringUtils.isEmpty(instanceUuid)) {
				list = elasticIpRepository.listAvailableEipsForNatGateway(vendorId, ip, regionId);
				// 查询网关绑定的弹性公网IP
                if (snatRuleId != null) {
                    // elasticIpRepository.listBindNat(vendorId,natGatewayId);
                    List<ElasticIp> bindList = elasticIpRepository.listBindNatGatewaySnat(vendorId, snatRuleId);
                    if (!CollectionUtils.isEmpty(bindList)) {
                        for (ElasticIp eip : bindList) {
                            if (list.contains(eip)) {
                                list.remove(eip);
                            }
                            eip.setBindFlag(true);
                            list.add(eip);
                        }
                    }
                }

				if (dnatRuleId != null) {
					// elasticIpRepository.listBindNat(vendorId,natGatewayId);
					List<ElasticIp> bindList = elasticIpRepository.listBindNatGatewayDnat(vendorId, dnatRuleId);
					if (!CollectionUtils.isEmpty(bindList)) {
						for (ElasticIp eip : bindList) {
						    if (list.contains(eip)) {
                                list.remove(eip);
                            }
						    eip.setBindFlag(true);
                            list.add(eip);
						}
					}
				}
			} else {
				String instanceType = object.getString("instanceType");
				list = elasticIpRepository.listByIid(instanceUuid, vendorId, instanceType);
				NatGateway gateway = natGatewayRepository.query(instanceUuid);
				if (null == gateway) {
					return new BocloudResult<>(true, list, "查询成功!");
				}
				// 剔除已使用的ip
				List<String> ips = new ArrayList<>();
				ips.addAll(natGatewaySnatRuleRepository.listIpsByNatGatewayId(gateway.getId()));
				ips.addAll(natGatewayDnatRuleRepository.listIpsByNatGatewayId(gateway.getId()));
				list = list.stream().filter(elasticIp -> !ips.contains(elasticIp.getPublicIp()))
						.collect(Collectors.toList());
			}
			return new BocloudResult<>(true, list, "查询成功!");
		} catch (Exception e) {
			logger.error("list elasticIp failure:", e);
			return new BocloudResult<>(false, "查询失败!");
		}
	}

	@Override
	public BocloudResult<ElasticIp> modifyBandwidth(ElasticIp elasticIp, RequestContext context) {
		String path = ElasticIp.class.getSimpleName() + "_" + elasticIp.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			ElasticIp oldElasticIp = elasticIpRepository.query(elasticIp.getId());
			Assert.notNull(oldElasticIp, "ElasticIp不存在!");
			CloudVendor vendor = cloudVendorRepository.query(oldElasticIp.getVendorId());
			Assert.notNull(vendor, "云平台不存在!");
			ElasticIpModel model = JSONObject.parseObject(JSONObject.toJSONString(elasticIp), ElasticIpModel.class);
			BocloudResult bocloudResult = elasticIpDriver.updateBandwidth(vendor.getUuid(), model,
					elasticIp.getRegionId());
			if (bocloudResult.isFailed()) {
				return bocloudResult;
			}

			ElasticIpModel newModel = JSONObject.parseObject(JSONObject.toJSONString(bocloudResult.getData()),
					ElasticIpModel.class);
			oldElasticIp.setBandwidth(newModel.getBandwidth());
			oldElasticIp.setBandwidthName(newModel.getBandwidthName());
			oldElasticIp.setBandwidthChargeMode(newModel.getBandwidthChargeMode());

			elasticIpRepository.update(oldElasticIp);
			return new BocloudResult<>(true, "修改成功");
		} catch (Exception e) {
			logger.error("Modify eip fail:", e);
			return new BocloudResult<>(false, "修改失败", null, null);
		}
	}

	@Override
	public BocloudResult apply(ApplicationParam param) {
		try {
			Assert.notNull(param, "参数格式错误");
			ServiceOrder order = param.getOrder();
			Assert.notNull(order, "订单不存在");
			List<OrderItem> items = order.getItems();
			Assert.notEmpty(items, "订单明细不存在");
			// 申请人
			RequestContext context = param.getContext();
			Assert.notNull(context, "申请人不存在");
			// 位置
			LocationModel location = param.getLocation();
			// 订购信息
			EmptionModel emption = param.getEmption();
			// 使用时长信息
			DurationModel duration = emption.getDuration();
			// 使用时长单位Hour/Month
			DurationModel.Mode mode = duration.getMode();
			// 时长
			Integer month = duration.getAmount();
			// 申请个数
			Integer count = null == emption.getCount() ? 1 : emption.getCount();
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(new Date());
			if (DurationModel.Mode.Hour == mode) {
				calendar.setTime(DateTools.stringToDateTime("2099-12-31 00:00:00"));
			} else {
				calendar.add(Calendar.MONTH, month);
			}
			CloudVendor vendor = cloudVendorRepository.query(location.getVendorId());
			Assert.notNull(vendor, "当前可用域未绑定云平台");
			CloudProvider cloudProvider = CloudProvider.valueOf(vendor.getType());
			// 配置信息
			JSONObject configs = param.getConfigs();
			// networkId
			Long networkId = configs.getLong("networkId");
			// 创建成功的IP
			List<BocloudResult> succList = Lists.newArrayList();
			// 创建失败的IP
			List<Future<BocloudResult>> futures = new ArrayList<>();
			for (int i = 0; i < count; i++) {
				OrderItem item = items.get(i);
				ElasticIp elasticIp = new ElasticIp();
				elasticIp = JSONObject.parseObject(configs.toJSONString(), ElasticIp.class);
				elasticIp.setVendorId(vendor.getId());
				elasticIp.setUuid(item.getSn());
				elasticIp.setCreatorId(location.getCreatorId() == null ? 0 : location.getCreatorId());
				elasticIp.setOwnerId(location.getOwnerId() == null ? 0 : location.getOwnerId());
				elasticIp.setMenderId(location.getMenderId() == null ? 0 : location.getMenderId());
				elasticIp.setCatalog(context.getCatalog());
				elasticIp.setTenantId(context.getTenant() == null ? 0 : context.getTenant());
				elasticIp.setBusinessId(location.getBusinessId() == null ? 0 : location.getBusinessId());
				elasticIp.setProjectId(location.getProjectId() == null ? 0 : location.getProjectId());
				// 修改资源过期时间
				elasticIp.setExpiredTime(calendar.getTime());
				final ElasticIp eip = elasticIp;
				futures.add(taskExecutor.submit(() -> allocate(eip, context)));
			}
			try {
				for (int i = 0; i < futures.size(); i++) {
					List<ResourceModel> resources = new ArrayList<>();
					OrderItem item = items.get(i);
					// 通知订单
					OrderMessage message = null;
					BocloudResult result = futures.get(i).get(10, TimeUnit.MINUTES);
					if (result.isSuccess()) {
						ElasticIp elasticIp = (ElasticIp) result.getData();
						elasticIp.setUuid(item.getSn());
						// 自服务需要用到
						elasticIp.setCreatorId(location.getCreatorId() == null ? 0 : location.getCreatorId());
						elasticIp.setOwnerId(location.getOwnerId() == null ? 0 : location.getOwnerId());
						elasticIp.setMenderId(location.getMenderId() == null ? 0 : location.getMenderId());
						elasticIp.setCatalog(context.getCatalog());
						elasticIp.setTenantId(context.getTenant() == null ? 0 : context.getTenant());
						elasticIp.setBusinessId(location.getBusinessId() == null ? 0 : location.getBusinessId());
						elasticIp.setProjectId(location.getProjectId() == null ? 0 : location.getProjectId());
						// 修改资源过期时间
						elasticIp.setExpiredTime(calendar.getTime());
						elasticIpRepository.update(elasticIp);

						ResourceEvent resourceEvent = new ResourceEvent(elasticIp.getId(), "浮动IP申请",
								"浮动IP 【" + elasticIp.getPublicIp() + "】申请成功！", context.getTarget(), new Date(),
								ResourceCategory.FloatingIp.name(), vendor.getId(), context.getCatalog());
						resourceEvent.setResourceName(elasticIp.getName());
						resourceEventRepository.save(resourceEvent);

						BocloudResult bocloudResult = new BocloudResult<>(true,
								"浮动IP【" + elasticIp.getPublicIp() + "】申请成功【" + result.getMessage() + "】");
						succList.add(bocloudResult);

						resources.add(new ResourceModel(elasticIp.getPublicIp(), elasticIp.getElasticIpUuid(),
								param.getService(), elasticIp.getInstanceId(), elasticIp.getUuid()));
						message = new OrderMessage(item.getSn(), OrderMessage.Operation.Application,
								OrderItemStatus.OpenedSuccess, null, bocloudResult.getMessage(),
								elasticIp.getGmtCreate(), calendar.getTime(), resources,
								ResourceCategory.FloatingIp.name(), context);
						message.setCategory(ResourceCategory.FloatingIp.name());
						// 通知订单
						amqpTemplate.convertAndSend("resource.result.queue", message);
					} else {
						BocloudResult bocloudResult = new BocloudResult<>(false,
								"浮动IP申请失败：【" + result.getMessage() + "】");
						message = new OrderMessage(item.getSn(), OrderMessage.Operation.Application,
								OrderItemStatus.OpenedFailure, null, result.getMessage(), null, null, resources,
								ResourceCategory.FloatingIp.name(), context);
						List<QuotaBean> quotas = new ArrayList<>();
						quotas.add(new QuotaBean(param.getService(), 1));
						quotas.add(new QuotaBean("meta.fip", 1));
						message.setQuotas(quotas);
						message.setCategory(ResourceCategory.FloatingIp.name());
						// 通知订单
						amqpTemplate.convertAndSend("resource.result.queue", message);
					}
				}
			} catch (Exception e) {
				logger.error("apply elasticIp fail:", e);
			}
			logger.info("apply floatingip  end...");
			return new BocloudResult<>(true, "浮动IP申请成功");
		} catch (Exception e) {
			logger.error("Apply floatingip error：{}", e.getMessage());
			return new BocloudResult<>(false, e.getMessage());
		}

	}

	@Override
	public BocloudResult cancelation(CancelationParam param) {
		try {
			ElasticIp elasticIp = elasticIpRepository.getByUuid(param.getInstance());
			Assert.notNull(elasticIp, "浮动IP不存在!");
			CloudVendor vendor = cloudVendorRepository.query(elasticIp.getVendorId());
			Assert.notNull(vendor, "云平台不存在！");

			RequestContext context = param.getContext();
			BocloudResult result = this.release(elasticIp.getId(), context);
			Assert.isTrue(result.isSuccess(), result.getMessage());

			OrderMessage orderMessage = new OrderMessage(param.getInstance(), OrderMessage.Operation.Cancelation,
					OrderItemStatus.UnsubscribeSuccess, null, "浮动IP退订成功", null, null, new ArrayList<>(),
					ResourceCategory.FloatingIp.name(), context);
			orderMessage.setCategory(ResourceCategory.FloatingIp.name());
			List<QuotaBean> quotas = new ArrayList<>();
			CloudProvider provider = CloudProvider.valueOf(vendor.getType());
			switch (provider) {
				case ALIYUN:
				case HUAWEI:
				case AWS:
					quotas.add(new QuotaBean("network." + vendor.getType().toLowerCase() + ".eip", 1));
					break;
				default:
					break;
			}
			quotas.add(new QuotaBean("meta.fip", 1));
			orderMessage.setQuotas(quotas);
			amqpTemplate.convertAndSend("resource.result.queue", orderMessage);
			return new BocloudResult<>(true, "浮动IP退订成功");
		} catch (Exception e) {
			logger.error("Get error message：{}", e.getMessage());
			return new BocloudResult<>(false, "浮动IP退订失败: " + e.getMessage());
		}

	}

	@Override
	public BocloudResult extension(ExtensionParam param) {
		try {
			ElasticIp elasticIp = elasticIpRepository.getByUuid(param.getInstance());
			Assert.notNull(elasticIp, "浮动IP不存在!");
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(elasticIp.getExpiredTime());
			calendar.add(Calendar.MONTH, param.getExtension());
			elasticIp.setExpiredTime(calendar.getTime());
			elasticIpRepository.update(elasticIp);

			OrderMessage message = new OrderMessage();
			message.setSn(param.getInstance());
			message.setOperation(Operation.Extension);
			message.setStatus(OrderItemStatus.ExtensionSuccess);
			message.setExtension(param.getExtension());
			message.setExpiredTime(calendar.getTime());
			message.setCategory(ResourceCategory.FloatingIp.name());
			message.setMessage("浮动IP延期成功");
			amqpTemplate.convertAndSend("resource.result.queue", message);
			return new BocloudResult<>(false, "浮动IP延期成功");
		} catch (Exception e) {
			logger.error("Get error message：{}", e);
			return new BocloudResult<>(false, e.getMessage());
		}

	}
}
