package com.bocloud.ims.service.resource.f5;

import com.bocloud.cmp.driver.f5.F5NodeDriver;
import com.bocloud.cmp.driver.f5.F5PoolDriver;
import com.bocloud.cmp.entity.enums.F5BasicObject;
import com.bocloud.cmp.model.f5.PoolMemberModel;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.ims.entity.middleware.Kvs;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.f5.BiNode;
import com.bocloud.ims.entity.resource.f5.BiPartition;
import com.bocloud.ims.entity.resource.f5.BiPool;
import com.bocloud.ims.entity.resource.f5.BiPoolMember;
import com.bocloud.ims.interfaces.resource.f5.F5BiPoolMemberService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.f5.F5BiNodeRepository;
import com.bocloud.ims.repository.resource.f5.F5BiPartitionRepository;
import com.bocloud.ims.repository.resource.f5.F5BiPoolMemberRepository;
import com.bocloud.ims.repository.resource.f5.F5BiPoolRepository;
import com.bocloud.ims.service.utils.BigIpHelper;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Service("f5BiPoolMemberService")
public class F5BiPoolMemberServiceImpl implements F5BiPoolMemberService {
	private static final Logger logger = LoggerFactory.getLogger(F5BiVirtualServerServiceImpl.class);
	@Autowired
	private LockFactory lockFactory;
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private F5PoolDriver f5PoolDriver;
	@Autowired
	private F5BiPoolMemberRepository f5BiPoolMemberRepository;
	@Autowired
	private F5BiPoolRepository f5BiPoolRepository;
	@Autowired
	private F5BiNodeRepository f5BiNodeRepository;
	@Autowired
	private F5NodeDriver f5NodeDriver;
	@Autowired
	private F5BiPartitionRepository f5BiPartitionRepository;

	@Override
	public BocloudResult<GridBean<BiPoolMember>> list(Pager pager) {
		GridBean gridBean = null;
		try {
			int page = pager.getPage();
			int rows = pager.getRows();
			List<Param> params = pager.getParams();
			Map<String, String> sorter = pager.getSorter();
			boolean simple = pager.getSimple();

			if (null == params) {
				params = Lists.newArrayList();
			}
			if (null == sorter) {
				sorter = Maps.newHashMap();
			}
			sorter.put("gmtCreate", Common.ONE);
			for (Param param : params) {
				Map<String, Object> dataMap = param.getParam();
				if (dataMap.containsKey("partitionId")) {
					Long partitionId = Long.valueOf(dataMap.get("partitionId").toString());
					BiPartition partition = f5BiPartitionRepository.query(partitionId);
					if (0 == partitionId) {
						dataMap.remove("partitionId");
					} else if (!partition.getName().equals(BigIpHelper.EnumHelper.Common.name())) {
						BiPartition commonPartition = f5BiPartitionRepository
								.queryByName(BigIpHelper.EnumHelper.Common.name(), partition.getVendorId());
						dataMap.remove("partitionId");
						HashMap<String, Object> queryDataMap = Maps.newHashMap();
						queryDataMap.put("partitionId",
								new StringBuilder().append(partitionId).append(",").append(commonPartition.getId()));
						Param paramData = new Param();
						paramData.setParam(queryDataMap);
						paramData.setSign(Sign.IN);
						params.add(paramData);
					}
					break;
				}
			}
			int total = this.f5BiPoolMemberRepository.count(params);
			if (simple) {
				List<SimpleBean> beans = this.f5BiPoolMemberRepository.list(params, sorter);
				gridBean = new GridBean(1, 1, total, beans);
			} else {
				List<BiPoolMember> biPoolMembers = this.f5BiPoolMemberRepository.list(page, rows, params, sorter);
				gridBean = GridHelper.getBean(page, rows, total, biPoolMembers);
			}
			return new BocloudResult(true, gridBean, "查询成功");
		} catch (Exception e) {
			logger.error("Query biPoolMember list fail:", e);
			return new BocloudResult(false, "查询失败");
		}
	}

	@Override
	public BocloudResult create(BiPoolMember biPoolMember, Long userId) {
		// 云平台id
		Long vendorId = biPoolMember.getVendorId();
		try {
			CloudVendor vendor = cloudVendorRepository.query(vendorId);
			if (null == vendor) {
				logger.error("get cloudVendor failure!");
				return new BocloudResult(false, "云平台不存在!");
			}
			String version = vendor.getVersion();
			biPoolMember.setCreatorId(userId);
			biPoolMember.setMenderId(userId);
			// 查询当前pool信息
			BiPool biPool = f5BiPoolRepository.query(biPoolMember.getPoolId());
			if (null == biPool) {
				logger.error("get Pool failure!");
				return new BocloudResult(false, "pool资源不存在!");
			}
			BiPartition poolPartition = f5BiPartitionRepository.query(biPool.getPartitionId());
			logger.info("create pool member start...");
			if (null == biPoolMember.getName() || StringUtils.isEmpty(biPoolMember.getName())) {
				biPoolMember.setName(biPoolMember.getPoolMemberAddress());
			}
			boolean existName = f5BiPoolMemberRepository.existName(
					biPoolMember.getName() + ":" + biPoolMember.getPoolMemberPort(), vendorId, biPool.getId(),
					biPoolMember.getPartitionId());
			if (!existName) {
				// 检验ip加端口的组合是否存在
				List<BiPoolMember> poolMembers = f5BiPoolMemberRepository.queryByPoolId(biPool.getId());
				if (!checkData(biPoolMember, poolMembers)) {
					logger.error("create poolmember error!");
					return new BocloudResult(false, "poolmember已存在!");
				}
			} else {
				logger.error("create poolmember error!");
				return new BocloudResult(false, "poolmember已存在!");
			}
			BiPartition poolMemberPartition = f5BiPartitionRepository.query(biPoolMember.getPartitionId());
			// 数据转换
			BocloudResult<PoolMemberModel> BocloudResult = convertData(biPoolMember, poolMemberPartition);
			if (!BocloudResult.isSuccess()) {
				logger.error(BocloudResult.getMessage());
				return new BocloudResult(false, null, BocloudResult.getMessage());
			}
			PoolMemberModel memberModel = BocloudResult.getData();
			String url = "/mgmt/tm/ltm/pool/" + BigIpHelper.partitionCode(poolPartition.getName(), biPool.getName())
					+ "/members/";
			// 调用为一个pool新增poolmember的接口返回的信息异常，但实际操作却是成功，所有调用后不去获取执行状态，通过后面查询新增的poolmumber是否存在来判定是否执行成功
			f5PoolDriver.createPoolMember(vendor.getUuid(), memberModel, url);
			// 拼接请求获取poolmumber的路径
			url += BigIpHelper.partitionCode(memberModel.getPartition(), memberModel.getName());
			BocloudResult bocloudResult = f5PoolDriver.detail(vendor.getUuid(), url);
			if (bocloudResult.isSuccess()) {
				try {
					// 获取poolmember的状态
					url += "/stats";
					Thread.sleep(5000);
					BocloudResult result = f5PoolDriver.stats(vendor.getUuid(), url);
					if (result.isFailed()) {
						logger.error(BocloudResult.getMessage());
					}
					String description = BigIpHelper.getResourceStatus(version, result, F5BasicObject.POOL,
							F5BasicObject.POOL_MEMBER,
							BigIpHelper.partitionCode(poolPartition.getName(), biPool.getName()),
							BigIpHelper.partitionCode(memberModel.getPartition(), memberModel.getName()));
					biPoolMember.setStatus(description);
					f5BiPoolMemberRepository.save(biPoolMember);
					// 保存poolmember的ip地址保存为node节点
					BiNode biNode = new BiNode();
					BiNode node = f5BiNodeRepository.queryByAddress(biPoolMember.getPoolMemberAddress(), vendorId,
							biPoolMember.getPartitionId());
					if (null == node) {
						biNode.setName(StringUtils.substringBefore(biPoolMember.getName(), ":"));
						biNode.setPartitionId(biPoolMember.getPartitionId());
						biNode.setAddress(biPoolMember.getPoolMemberAddress());
						biNode.setCreatorId(userId);
						biNode.setGmtCreate(new Date());
						biNode.setVendorId(vendorId);
						biNode.setRatio(1L);
						biNode.setConnectionLimit(0L);
						biNode.setConnectionRateLimit(0L);
						biNode.setHealthMonitors("default");
						biNode.setSelectMonitors(String.valueOf(new ArrayList<>()));
						if ("node-list".equals(biPoolMember.getNodeType())) {
							BiNode queryNode = f5BiNodeRepository.queryByAddress(biPoolMember.getPoolMemberAddress(),
									vendorId, biPoolMember.getPartitionId());
							biPoolMember.setNodeId(queryNode.getId());
						}
						String nodeStatus = BigIpHelper.getResourceStatus(version,
								f5NodeDriver.stats(vendor.getUuid(),
										BigIpHelper.partitionCode(memberModel.getPartition(), biNode.getName())),
								null, F5BasicObject.NODE,
								BigIpHelper.partitionCode(poolMemberPartition.getName(), biNode.getName()), null);
						biNode.setStatus(nodeStatus);
						f5BiNodeRepository.save(biNode);
					}
				} catch (Exception e) {
					logger.error(e.getMessage());
					return new BocloudResult(false, null, "创建失败");
				}
				return new BocloudResult(true, "创建成功");
			} else {
				logger.error(bocloudResult.getMessage());
				return new BocloudResult(false, null, bocloudResult.getMessage());
			}
		} catch (Exception e) {
			logger.error("create pool member error", e);
			return new BocloudResult(false, "添加失败");
		}
	}

	/**
	 * 检查当前pool中的poolmember数据
	 *
	 * @param poolMember
	 * @param poolMembers
	 */
	private Boolean checkData(BiPoolMember poolMember, List<BiPoolMember> poolMembers) {
		// 将port为*的进行处理调整为0
		if ("*".equals(poolMember.getPoolMemberPort())) {
			poolMember.setPoolMemberPort("0");
		}
		StringBuilder newIpAndPort = new StringBuilder();
		newIpAndPort.append(poolMember.getPoolMemberAddress());
		newIpAndPort.append(":");
		newIpAndPort.append(poolMember.getPoolMemberPort());
		for (BiPoolMember biPoolMember : poolMembers) {
			StringBuilder ipAndPort = new StringBuilder();
			ipAndPort.append(biPoolMember.getPoolMemberAddress());
			ipAndPort.append(":");
			ipAndPort.append(biPoolMember.getPoolMemberPort());
			if (ipAndPort.toString().equals(newIpAndPort.toString())) {
				return false;
			}
		}
		return true;
	}

	private BocloudResult<PoolMemberModel> convertData(BiPoolMember biPoolMember, BiPartition partition) {
		PoolMemberModel poolMemberModel = new PoolMemberModel();
		// 处理port为*的数据
		if ("*".equals(biPoolMember.getPoolMemberPort())) {
			biPoolMember.setPoolMemberPort("0");
		}
		poolMemberModel.setName(biPoolMember.getName() + ":" + biPoolMember.getPoolMemberPort());
		poolMemberModel.setPartition(partition.getName());
		biPoolMember.setName(biPoolMember.getName() + ":" + biPoolMember.getPoolMemberPort());
		poolMemberModel.setAddress(biPoolMember.getPoolMemberAddress());
		poolMemberModel.setDescription(biPoolMember.getRemark());
		poolMemberModel.setRatio(biPoolMember.getRatio());
		poolMemberModel.setConnectionLimit(biPoolMember.getConnectionLimit());
		poolMemberModel.setPriorityGroup(biPoolMember.getPriority());
		if (0L == biPoolMember.getConnectionRateLimit()) {
			poolMemberModel.setRateLimit("disabled");
		} else {
			poolMemberModel.setRateLimit(String.valueOf(biPoolMember.getConnectionRateLimit()));
		}
		return new BocloudResult<PoolMemberModel>(true, poolMemberModel, "");
	}

	@Override
	public BocloudResult modify(BiPoolMember biPoolMember, Long userId) {
		String path = Kvs.class.getSimpleName() + "_" + biPoolMember.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			BiPoolMember poolMember = this.f5BiPoolMemberRepository.query(biPoolMember.getId());
			if (null == poolMember) {
				logger.warn("data does not exist!");
				return new BocloudResult(false, "数据不存在");
			}
			BiPartition poolMemberPartition = f5BiPartitionRepository.query(poolMember.getPartitionId());
			// 查询所在的pool
			BiPool biPool = f5BiPoolRepository.query(biPoolMember.getPoolId());
			if (null == biPool) {
				logger.warn("data does not exist!");
				return new BocloudResult(false, "数据不存在");
			}
			BiPartition poolPartition = f5BiPartitionRepository.query(biPool.getPartitionId());
			CloudVendor vendor = cloudVendorRepository.query(biPoolMember.getVendorId());
			if (null == vendor) {
				logger.error("get cloudVendor failure");
				return new BocloudResult(false, "云平台不存在，修改失败");
			}
			String version = vendor.getVersion();
			biPoolMember.setMenderId(userId);
			biPoolMember.setGmtModify(new Date());
			// 处理poolmember的名称去除端口号,更新和新增的name的传参格式不同
			biPoolMember.setName(StringUtils.substringBefore(biPoolMember.getName(), ":"));
			logger.info("update poolMenber start...");
			// 数据转换
			BocloudResult<PoolMemberModel> BocloudResult = convertData(biPoolMember, poolMemberPartition);
			if (BocloudResult.isFailed()) {
				logger.error(BocloudResult.getMessage());
				return new BocloudResult(false, null, "更新失败：" + BocloudResult.getMessage());
			}
			PoolMemberModel poolMemberModel = BocloudResult.getData();
			String name = poolMemberModel.getName();
			// 拼接路径
			String url = "/mgmt/tm/ltm/pool/" + BigIpHelper.partitionCode(poolPartition.getName(), biPool.getName())
					+ "/members/"
					+ BigIpHelper.partitionCode(poolMemberModel.getPartition(), poolMemberModel.getName());
			BocloudResult bocloudResult = f5PoolDriver.updatePoolMember(vendor.getUuid(), url, poolMemberModel);
			if (bocloudResult.isSuccess()) {
				try {
					// 获取poolmember的状态
					url += "/stats";
					Thread.sleep(5000);
					BocloudResult result = f5PoolDriver.stats(vendor.getUuid(), url);
					if (result.isFailed()) {
						logger.error(BocloudResult.getMessage());
					}
					String description = BigIpHelper.getResourceStatus(version, result, F5BasicObject.POOL,
							F5BasicObject.POOL_MEMBER,
							BigIpHelper.partitionCode(poolPartition.getName(), biPool.getName()),
							BigIpHelper.partitionCode(poolMemberModel.getPartition(), name));
					biPoolMember.setStatus(description);
					f5BiPoolMemberRepository.update(biPoolMember);
				} catch (Exception e) {
					logger.error(e.getMessage());
					return new BocloudResult(false, null, e.getMessage());
				}
				return new BocloudResult(true, "更新成功");
			} else {
				logger.error(bocloudResult.getMessage());
				return new BocloudResult(false, null, bocloudResult.getMessage());
			}
		} catch (Exception e) {
			logger.error("Modify poolMenber fail:", e);
			return new BocloudResult(false, "修改失败");
		}
	}

	@Override
	public BocloudResult remove(Long id, Long userId) {
		String path = Kvs.class.getSimpleName() + "_" + id;
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			BiPoolMember biPoolMember = this.f5BiPoolMemberRepository.query(id);
			if (null == biPoolMember) {
				return new BocloudResult(false, "请求内容不存在");
			}
			BiPartition biPoolMemberPartition = f5BiPartitionRepository.query(biPoolMember.getPartitionId());
			CloudVendor vendor = cloudVendorRepository.query(biPoolMember.getVendorId());
			if (null == vendor) {
				logger.error("get poolMember failure!");
				return new BocloudResult(false, "删除失败!");
			}
			BiPool biPool = f5BiPoolRepository.query(biPoolMember.getPoolId());
			if (null == biPool) {
				logger.error("get pool failure!");
				return new BocloudResult(false, "删除失败!");
			}
			BiPartition poolPartition = f5BiPartitionRepository.query(biPool.getPartitionId());
			logger.info("remove poolMenber start...");
			// 拼接url
			String url = "/mgmt/tm/ltm/pool/" + BigIpHelper.partitionCode(poolPartition.getName(), biPool.getName())
					+ "/members/" + BigIpHelper.partitionCode(biPoolMemberPartition.getName(), biPoolMember.getName());
			BocloudResult result = f5PoolDriver.removePoolMember(vendor.getUuid(), url);
			if (result.isSuccess()) {
				try {
					f5BiPoolMemberRepository.remove(id, userId);
				} catch (Exception e) {
					logger.error(e.getMessage());
					return new BocloudResult(false, null, e.getMessage());
				}
				return new BocloudResult(true, "删除成功");
			} else {
				logger.error(result.getMessage());
				return new BocloudResult(false, null, result.getMessage());
			}
		} catch (Exception e) {
			logger.error("Remove PoolMember error:", e);
			return new BocloudResult(false, "删除失败");
		}
	}

	@Override
	public BocloudResult<BiPoolMember> detail(Long id) {
		try {
			BiPoolMember biPoolMember = f5BiPoolMemberRepository.query(id);
			if (null == biPoolMember) {
				return new BocloudResult(false, "数据不存在");
			}
			return new BocloudResult(true, biPoolMember, "查询数据成功");
		} catch (Exception e) {
			logger.error("Get biPoolMember error：", e);
			return new BocloudResult(false, "查询详情失败");
		}
	}

}
