package com.bocloud.ims.service.resource;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.CollectionUtils;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.network.NatGatewayDnatRuleDriver;
import com.bocloud.cmp.entity.enums.CloudProvider;
import com.bocloud.cmp.model.NatGatewayDnatRuleModel;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.GridBean;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.Pager;
import com.bocloud.common.model.Param;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.JSONTools;
import com.bocloud.ims.entity.resource.CloudServer;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.ElasticIp;
import com.bocloud.ims.entity.resource.NatGateway;
import com.bocloud.ims.entity.resource.NatGatewayDnatRule;
import com.bocloud.ims.entity.resource.Port;
import com.bocloud.ims.interfaces.resource.NatGatewayDnatRuleService;
import com.bocloud.ims.repository.resource.CloudServerRepository;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.ElasticIpRepository;
import com.bocloud.ims.repository.resource.NatGatewayDnatRuleRepository;
import com.bocloud.ims.repository.resource.NatGatewayRepository;
import com.bocloud.ims.repository.resource.PortRepository;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Maps;

@Service("natGatewayDnatRuleService")
public class NatGatewayDnatRuleServiceImpl implements NatGatewayDnatRuleService {
	private static Logger logger = LoggerFactory.getLogger(NatGatewayDnatRuleServiceImpl.class);
	@Autowired
	private NatGatewayDnatRuleRepository dnatRuleRepository;
	@Autowired
	private NatGatewayRepository natGatewayRepository;
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private PortRepository portRepository;
	@Autowired
	private ElasticIpRepository elasticIpRepository;
	@Autowired
	private CloudServerRepository cloudServerRepository;
	@Autowired
	private NatGatewayDnatRuleDriver dnatRuleDriver;
	@Autowired
	private LockFactory lockFactory;
	@Autowired
	private ThreadPoolTaskExecutor taskExecutor;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;

	@Override
	public BocloudResult<GridBean<NatGatewayDnatRule>> 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);

			int total = dnatRuleRepository.count(params);
			List<NatGatewayDnatRule> list = dnatRuleRepository.list(page, rows, params, sorter);
			for (NatGatewayDnatRule dnatRule : list) {
				if (dnatRule.getPrivateIp() == null) {
					Port port = portRepository.query(dnatRule.getPortId());
					dnatRule.setPrivateIp(port.getIpAddress());
				}
			}
			GridBean<NatGatewayDnatRule> gridBean = GridHelper.getBean(page, rows, total, list);
			return new BocloudResult<GridBean<NatGatewayDnatRule>>(true, gridBean, "查询成功");
		} catch (Exception e) {
			logger.error("list natGateway failure:", e);
			return new BocloudResult<GridBean<NatGatewayDnatRule>>(false, "查询失败", null, null);
		}
	}

	@Override
	public BocloudResult create(NatGatewayDnatRule dnatRule, RequestContext context) {
		try {
			if (dnatRule.getVendorId() == null) {
				return new BocloudResult<>(false, "参数格式不正确");
			}
			CloudVendor vendor = cloudVendorRepository.query(dnatRule.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "云平台不存在");
			}

			if (CloudProvider.ALIYUN.name().equals(vendor.getType())) {
				return aliyunCreate(dnatRule, context, vendor);
			}

			if (dnatRule.getProtocol() == null || dnatRule.getRegionId() == null || dnatRule.getElasticIpId() == null
					|| dnatRule.getNatGatewayId() == null) {
				return new BocloudResult<>(false, "参数格式不正确");
			}

			if (dnatRule.getPortId() == null && dnatRule.getPrivateIp() == null) {
				return new BocloudResult<>(false, "参数格式不正确");
			}

			NatGateway natGateway = natGatewayRepository.query(dnatRule.getNatGatewayId());
			if (null == natGateway) {
				return new BocloudResult<>(false, "未找到nat网关");
			}
			Port port = null;
			if (dnatRule.getPortId() != null) {
				port = portRepository.query(dnatRule.getPortId());
				if (null == port) {
					return new BocloudResult<>(false, "未找到端口");
				}
			}

			ElasticIp elasticIp = elasticIpRepository.query(dnatRule.getElasticIpId());
			if (null == elasticIp) {
				return new BocloudResult<>(false, "未找到弹性公网ip");
			}
			NatGatewayDnatRuleModel dnatRuleModel = new NatGatewayDnatRuleModel();
			BeanUtils.copyProperties(dnatRule, dnatRuleModel);
			if (null != port) {
				dnatRuleModel.setPortUuid(port.getPortUuid());
			}

			dnatRuleModel.setFloatingIpId(elasticIp.getElasticIpUuid());
			dnatRuleModel.setNatGatewayId(natGateway.getUuid());
			dnatRuleModel.setInternalServicePort(Optional.ofNullable(dnatRule.getInternalServicePort()).orElse(0));
			dnatRuleModel.setExternalServicePort(Optional.ofNullable(dnatRule.getExternalServicePort()).orElse(0));
			Port finalPort = port;
			taskExecutor.submit(() -> {
				OperateResult result = new OperateResult(true, "", "huawei.createNatGatewayDnatRule", null,
						OperateResult.OperateCategory.ResourceEvent, context);
				BocloudResult createResult = dnatRuleDriver.create(dnatRuleModel, vendor.getUuid(),
						dnatRule.getRegionId());
				if (createResult.isFailed()) {
					result.setSuccess(false);
					result.setMessage("创建nat网关dnat规则失败：" + createResult.getMessage());
					resourceEventPublisher.send(result);
					return;
				}

				if (CloudProvider.ALIYUN.name().equals(vendor.getType())) {
					NatGatewayDnatRuleModel dnatRuleModelresult = JSONObject.parseObject(
							JSONObject.toJSONString(createResult.getData()), NatGatewayDnatRuleModel.class);
					BeanUtils.copyProperties(dnatRuleModelresult, dnatRule);
					dnatRule.setGmtCreate(dnatRuleModelresult.getGmtCreate());
				} else if (CloudProvider.HUAWEI.name().equals(vendor.getType()) || CloudProvider.HCSO.name().equals(vendor.getType())) {
					NatGatewayDnatRuleModel dnatRuleModelresult = JSONObject.parseObject(
							JSONObject.toJSONString(createResult.getData()), NatGatewayDnatRuleModel.class);
					BeanUtils.copyProperties(dnatRuleModelresult, dnatRule);
					dnatRule.setPrivateIp(dnatRuleModel.getPrivateIp());
					dnatRule.setGmtCreate(dnatRuleModelresult.getGmtCreate());
				} else {
					BeanUtils.copyProperties(dnatRuleModel, dnatRule);
					dnatRule.setGmtCreate(new Date());
				}

				dnatRule.setCreatorId(context.getTarget());
				dnatRule.setVendorId(vendor.getId());
				if (null != finalPort) {
					dnatRule.setPortId(finalPort.getId());
				}
				dnatRule.setElasticIpId(elasticIp.getId());
				dnatRule.setNatGatewayId(natGateway.getId());
				dnatRuleRepository.save(dnatRule);
				result.setSuccess(true);
				result.setMessage("创建nat网关[" + natGateway.getName() + "]的dnat规则成功");
				resourceEventPublisher.send(result);
				return;
			});

			return new BocloudResult<>(true, "任务已经下发，正在执行...");
		} catch (Exception e) {
			logger.error("create nat gateway dnat rule failure:" + e.getMessage());
			return new BocloudResult<>(false, "创建失败", null, null);
		}
	}

	private BocloudResult aliyunCreate(NatGatewayDnatRule dnatRule, RequestContext context, CloudVendor vendor) {
		try {
			if (dnatRule.getProtocol() == null || dnatRule.getVendorId() == null || dnatRule.getRegionId() == null
					|| dnatRule.getNatGatewayId() == null) {
				return new BocloudResult<>(false, "参数格式不正确");
			}

			NatGateway natGateway = natGatewayRepository.query(dnatRule.getNatGatewayId());
			if (null == natGateway) {
				return new BocloudResult<>(false, "未找到nat网关");
			}
			if (StringUtils.isEmpty(natGateway.getForwardTableIds())) {
				return new BocloudResult<>(false, "nat网关缺少ForwardTableId");
			}

			NatGatewayDnatRuleModel dnatRuleModel = new NatGatewayDnatRuleModel();
			BeanUtils.copyProperties(dnatRule, dnatRuleModel);
			dnatRuleModel.setForwardTableId(natGateway.getForwardTableIds().split(",")[0]);
			dnatRuleModel.setNatGatewayId(natGateway.getUuid());
			dnatRuleModel.setInternalServicePort(Optional.ofNullable(dnatRule.getInternalServicePort()).orElse(0));
			dnatRuleModel.setExternalServicePort(Optional.ofNullable(dnatRule.getExternalServicePort()).orElse(0));
			taskExecutor.submit(() -> {
				OperateResult result = new OperateResult(true, "", "huawei.createNatGatewayDnatRule", null,
						OperateResult.OperateCategory.ResourceEvent, context);
				BocloudResult createResult = dnatRuleDriver.create(dnatRuleModel, vendor.getUuid(),
						dnatRule.getRegionId());
				if (createResult.isFailed()) {
					result.setSuccess(false);
					result.setMessage("创建nat网关dnat规则失败：" + createResult.getMessage());
					resourceEventPublisher.send(result);
					return;
				}
				NatGatewayDnatRuleModel dnatRuleModelresult = JSONObject
						.parseObject(JSONObject.toJSONString(createResult.getData()), NatGatewayDnatRuleModel.class);
				if (!CloudProvider.ALIYUN.name().equals(vendor.getType())) {
					BeanUtils.copyProperties(dnatRuleModel, dnatRule);
					dnatRule.setGmtCreate(new Date());
				} else {
					BeanUtils.copyProperties(dnatRuleModelresult, dnatRule);
					dnatRule.setGmtCreate(dnatRuleModelresult.getGmtCreate());
				}

				dnatRule.setCreatorId(context.getTarget());
				dnatRule.setVendorId(vendor.getId());
				dnatRule.setNatGatewayId(natGateway.getId());
				dnatRule.setRegionId(natGateway.getRegionId());
				dnatRuleRepository.save(dnatRule);
				result.setSuccess(true);
				result.setMessage("创建nat网关[" + natGateway.getName() + "]的dnat规则成功");
				resourceEventPublisher.send(result);
				return;
			});

			return new BocloudResult<>(true, "任务已经下发，正在执行...");
		} catch (Exception e) {
			logger.error("create nat gateway dnat rule failure:" + e.getMessage());
			return new BocloudResult<>(false, "创建失败", null, null);
		}
	}

	@Override
	public BocloudResult remove(Long id, RequestContext context) {
		try {
			NatGatewayDnatRule dnatRule = dnatRuleRepository.query(id);
			if (null == dnatRule) {
				return new BocloudResult<>(false, "该dnat规则不存在");
			}
			CloudVendor vendor = cloudVendorRepository.query(dnatRule.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "云平台不存在");
			}
			NatGateway natGateway = natGatewayRepository.query(dnatRule.getNatGatewayId());
			if (null == natGateway) {
				return new BocloudResult<>(false, "未找到nat网关");
			}

			taskExecutor.submit(() -> {
				OperateResult result = new OperateResult(true, "", "huawei.removeNatGatewayDnatRule", null,
						OperateResult.OperateCategory.ResourceEvent, context);
				try (BocloudACLock lock = lockFactory.getACLock("huawei_removeNatGatewayDnatRule" + id)) {
					if (!lock.acquire(10, TimeUnit.SECONDS)) {
						result.setSuccess(false);
						result.setMessage("删除nat网关Dnat规则失败，当前存在其他互斥任务正在执行，请稍后再试");
						resourceEventPublisher.send(result);
						return;
					}
					BocloudResult removeResult = null;
					if (CloudProvider.ALIYUN.name().equals(vendor.getType())) {
						removeResult = dnatRuleDriver.remove(vendor.getUuid(), dnatRule.getForwardTableId(),
								dnatRule.getUuid(), dnatRule.getRegionId());
					} else {
						removeResult = dnatRuleDriver.remove(vendor.getUuid(), natGateway.getUuid(), dnatRule.getUuid(),
								dnatRule.getRegionId());
					}
					if (removeResult.isFailed()) {
						result.setSuccess(false);
						result.setMessage("删除nat网关Dnat规则失败：" + removeResult.getMessage());
						resourceEventPublisher.send(result);
						return;
					}

					dnatRuleRepository.delete(id, context.getTarget());

					result.setSuccess(true);
					result.setMessage("删除nat网关[" + natGateway.getName() + "]的dnat规则成功");
					resourceEventPublisher.send(result);
					return;
				} catch (Exception e) {
					logger.error("删除nat网关dnat规则失败：", e);
					result.setSuccess(false);
					result.setMessage("删除nat网关dnat规则失败：" + e.getMessage());
					resourceEventPublisher.send(result);
					return;
				}
			});

			return new BocloudResult<>(true, "任务已经下发，正在执行...");
		} catch (Exception e) {
			logger.error("remove nat gateway failure:" + e.getMessage());
			return new BocloudResult<>(false, "删除失败", null, null);
		}
	}

	@Override
	public BocloudResult modify(NatGatewayDnatRule dnatRule, RequestContext context) {
		try {
			CloudVendor vendor = cloudVendorRepository.query(dnatRule.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "云平台不存在");
			}
			NatGatewayDnatRule dbDnatRule = dnatRuleRepository.query(dnatRule.getId());
			if (null == dbDnatRule) {
				return new BocloudResult<>(false, "该dnat规则不存在");
			}

			NatGateway natGateway = natGatewayRepository.query(dnatRule.getNatGatewayId());
			if (null == natGateway) {
				return new BocloudResult<>(false, "未找到nat网关");
			}

			NatGatewayDnatRuleModel natGatewayModel = new NatGatewayDnatRuleModel();
			BeanUtils.copyProperties(dnatRule, natGatewayModel);
			natGatewayModel.setUuid(dbDnatRule.getUuid());
			natGatewayModel.setStatus(dbDnatRule.getStatus());
			if (!CloudProvider.ALIYUN.name().equals(vendor.getType())) {
				ElasticIp elasticIp = elasticIpRepository.query(dnatRule.getElasticIpId());
				if (null == elasticIp) {
					return new BocloudResult<>(false, "未找到弹性公网ip");
				}

				if (null != dbDnatRule.getPortId()) {
					NatGatewayDnatRule natGatewayDnatRule = dnatRuleRepository.getByPortId(dbDnatRule.getPortId());
					if (null != natGatewayDnatRule && !natGatewayDnatRule.getId().equals(dnatRule.getId())) {
						return new BocloudResult<>(false, "所选择的云主机端口已添加到其他dnat规则中，请重新选择");
					}
				}

				if (null != dnatRule.getPortId()) {
					Port port = portRepository.query(dnatRule.getPortId());
					if (null == port) {
						return new BocloudResult<>(false, "未找到端口");
					}
					natGatewayModel.setPortUuid(port.getPortUuid());
				}

				natGatewayModel.setFloatingIpId(elasticIp.getElasticIpUuid());
			} else {
				if (StringUtils.isEmpty(natGateway.getForwardTableIds())) {
					return new BocloudResult<>(false, "nat网关缺少ForwardTableId");
				}
				natGatewayModel.setForwardTableId(natGateway.getForwardTableIds().split(",")[0]);
			}

			natGatewayModel.setNatGatewayId(natGateway.getUuid());

			if (NatGatewayDnatRule.Protocol.Any.name().equalsIgnoreCase(dnatRule.getProtocol())) {
				natGatewayModel.setInternalServicePortRange(null);
				natGatewayModel.setExternalServicePortRange(null);
				dnatRule.setProtocol(NatGatewayDnatRule.Protocol.Any.name());
				natGatewayModel.setProtocol(NatGatewayDnatRule.Protocol.Any.name());
			}
			natGatewayModel.setInternalServicePort(Optional.ofNullable(dnatRule.getInternalServicePort()).orElse(0));
			natGatewayModel.setExternalServicePort(Optional.ofNullable(dnatRule.getExternalServicePort()).orElse(0));
			taskExecutor.submit(() -> {
				OperateResult result = new OperateResult(true, "", "huawei.modifyNatGatewayDnatRule", null,
						OperateResult.OperateCategory.ResourceEvent, context);
				try (BocloudACLock lock = lockFactory
						.getACLock("huawei_modifyNatGatewayDnatRule" + dbDnatRule.getId())) {
					if (!lock.acquire(10, TimeUnit.SECONDS)) {
						result.setSuccess(false);
						result.setMessage("修改nat网关Dnat规则失败，当前存在其他互斥任务正在执行，请稍后再试");
						resourceEventPublisher.send(result);
						return;
					}
					BocloudResult modifyResult = dnatRuleDriver.update(natGatewayModel, vendor.getUuid(),
							dbDnatRule.getRegionId());
					if (modifyResult.isFailed()) {
						result.setSuccess(false);
						result.setMessage("修改nat网关Dnat规则失败：" + modifyResult.getMessage());
						resourceEventPublisher.send(result);
						return;
					}

					if (CloudProvider.ALIYUN.name().equals(vendor.getType())) {
						BeanUtils.copyProperties(natGatewayModel, dbDnatRule);
					} else {
						NatGatewayDnatRuleModel dnatRuleModel = JSONObject.parseObject(
								JSONObject.toJSONString(modifyResult.getData()), NatGatewayDnatRuleModel.class);
						BeanUtils.copyProperties(dnatRuleModel, dbDnatRule);
						Optional.ofNullable(portRepository.getByPortUuid(dnatRuleModel.getPortUuid(), vendor.getId()))
								.map(Port::getId).ifPresent(dbDnatRule::setPortId);
						Optional.ofNullable(elasticIpRepository.query(dnatRuleModel.getFloatingIpId()))
								.map(ElasticIp::getId).ifPresent(dbDnatRule::setElasticIpId);
					}

					dbDnatRule.setMenderId(context.getTarget());
					dbDnatRule.setGmtModify(new Date());
					dbDnatRule.setVendorId(vendor.getId());
					dbDnatRule.setRegionId(natGateway.getRegionId());
					dbDnatRule.setNatGatewayId(dbDnatRule.getNatGatewayId());
					dnatRuleRepository.update(dbDnatRule);
					result.setSuccess(true);
					result.setMessage("修改nat网关Dnat规则成功");
					resourceEventPublisher.send(result);
					return;
				} catch (Exception e) {
					logger.error("修改nat网关Dnat规则失败：", e);
					result.setSuccess(false);
					result.setMessage("修改nat网关Dnat规则失败：" + e.getMessage());
					resourceEventPublisher.send(result);
					return;
				}
			});

			return new BocloudResult<>(true, "任务已经下发，正在执行...");
		} catch (Exception e) {
			logger.error("modify nat gateway dnat rule failure:" + e.getMessage());
			return new BocloudResult<>(false, "修改失败", null, null);
		}
	}

	@Override
	public BocloudResult<NatGatewayDnatRule> detail(Long id) {
		try {
			NatGatewayDnatRule dnatRule = dnatRuleRepository.query(id);
			return new BocloudResult<>(true, dnatRule, "查询成功");
		} catch (Exception e) {
			logger.error("query natGateway failure:", e);
			return new BocloudResult<>(false, "查询失败", null, null);
		}
	}

	@Override
	public BocloudResult<List<ElasticIp>> listAvailableEipsForDnat(JSONObject object) {
		try {
			Long vendorId = object.getLong(Common.VENDORID);
			CloudVendor vendor = cloudVendorRepository.query(vendorId);

			switch (CloudProvider.valueOf(vendor.getType())) {
			case HUAWEI:
			case HCSO:
				return listAvailableEipsForHuaweiDnat(object.toJSONString());
			default:
				List<ElasticIp> elasticIps = elasticIpRepository.listAvailableEipsForDnat(vendorId);
				return new BocloudResult<>(true, elasticIps, "查询成功");
			}

		} catch (Exception e) {
			logger.error("query elasticIps failure:", e);
			return new BocloudResult<>(false, "查询失败", null, null);
		}
	}

    @Override
    public BocloudResult<List<Port>> listAvailablePortByVmId(Long vmId) {
        try {
            List<Port> ports = new ArrayList<>();
            CloudServer cloudServer = cloudServerRepository.query(vmId);
            if (null != cloudServer) {
                ports =
                    portRepository.listAvailablePortByDeviceId(cloudServer.getInstanceId(), cloudServer.getVendorId());
            }
            return new BocloudResult<>(true, ports, "查询成功");
        } catch (Exception e) {
            logger.error("query elasticIps failure:", e);
            return new BocloudResult<>(false, "查询失败", null, null);
        }
    }

	@Override
	public BocloudResult<List<String>> listBindPrivateIps(Long natGatewayId) {
		try {
			List<String> privateIps = dnatRuleRepository.listBindPrivateIps(natGatewayId);
			return new BocloudResult<>(true, privateIps, "查询成功");
		} catch (Exception e) {
			logger.error("query  failure:", e);
			return new BocloudResult<>(false, "查询失败", null, null);
		}
	}

	@Override
	public BocloudResult<List<ElasticIp>> listAvailableEipsForHuaweiDnat(String params) {
		try {
			JSONObject object = JSONTools.isJSONObj(params);
			Long vendorId = object.getLong(Common.VENDORID);
			String regionId = object.getString("regionId");
			// DNAT规则Id
			Long dnatRuleId = object.getLong("dnatRuleId");
			List<ElasticIp> list = elasticIpRepository.listAvailableEipsForDnat(vendorId, regionId);
			// 查询DNAT绑定的弹性公网IP
			if (dnatRuleId != null) {
				List<ElasticIp> bindList = elasticIpRepository.listBindNatGatewayDnat(vendorId, dnatRuleId);
				if (!CollectionUtils.isEmpty(bindList)) {
					for (ElasticIp eip : bindList) {
						eip.setBindFlag(true);
						list.add(eip);
					}
				}
			}

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