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

import com.alibaba.fastjson.JSONArray;
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.cmp.model.f5.PoolModel;
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.*;
import com.bocloud.ims.interfaces.resource.f5.F5BiPoolService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.f5.*;
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.BeanUtils;
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("f5BiPoolService")
public class F5BiPoolServiceImpl implements F5BiPoolService {

	private static Logger logger = LoggerFactory.getLogger(F5BiPoolServiceImpl.class);
	@Autowired
	private LockFactory lockFactory;
	@Autowired
	private F5BiPoolRepository f5BiPoolRepository;
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private F5BiMonitorRepository f5BiMonitorRepository;
	@Autowired
	private F5PoolDriver f5PoolDriver;
	@Autowired
	private F5BiPoolMemberRepository f5BiPoolMemberRepository;
	@Autowired
	private F5BiNodeRepository f5BiNodeRepository;
	@Autowired
	private F5NodeDriver f5NodeDriver;
	@Autowired
	private F5BiPartitionRepository f5BiPartitionRepository;

	@Override
	public BocloudResult<GridBean<BiPool>> 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.f5BiPoolRepository.count(params);
			if (simple) {
				List<SimpleBean> beans = this.f5BiPoolRepository.list(params, sorter);
				gridBean = new GridBean(1, 1, total, beans);
			} else {
				List<BiPool> list = this.f5BiPoolRepository.list(page, rows, params, sorter);
				gridBean = GridHelper.getBean(page, rows, total, list);
			}
			return new BocloudResult(true, gridBean, "查询成功");
		} catch (Exception e) {
			logger.error("Query bipool list fail:", e);
			return new BocloudResult(false, "查询失败");
		}

	}

	@Override
	public BocloudResult create(BiPool biPool, Long userId) {
		String path = Kvs.class.getSimpleName() + "_" + biPool.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			// 设置属性
			biPool.setCreatorId(userId);
			biPool.setMenderId(userId);
			Long vendorId = biPool.getVendorId();
			// 云平台id
			CloudVendor vendor = cloudVendorRepository.query(vendorId);
			if (null == vendor) {
				logger.error("get cloudVendor failure!");
				return new BocloudResult(false, "云平台不存在!");
			}
			String version = vendor.getVersion();
			StringBuilder monitorName = new StringBuilder();
			if (null != biPool.getMonitor()) {
				List<Long> monitor = JSONArray.parseArray(biPool.getMonitor()).toJavaList(Long.class);
				biPool.setMonitor(StringUtils.join(monitor, ","));
				for (int i = 0; i < monitor.size(); i++) {
					Long monitorId = monitor.get(i);
					BiMonitor biMonitor = f5BiMonitorRepository.query(monitorId);
					if (null == biMonitor) {
						logger.error("get biMonitor failure!");
						return new BocloudResult(false, "健康检查不存在!");
					}
					BiPartition monitorPartition = f5BiPartitionRepository.query(biMonitor.getPartitionId());
					if (0 == i) {
						monitorName.append(BigIpHelper.jointPartition(monitorPartition.getName(), biMonitor.getName()));
						continue;
					}
					// 多个monitor需要进行拼接
					if (i > 0) {
						monitorName.append(
								" and " + BigIpHelper.jointPartition(monitorPartition.getName(), biMonitor.getName()));
					}
				}
			}
			BiPartition poolPartition = f5BiPartitionRepository.query(biPool.getPartitionId());
			logger.info("create pool start...");
			// 数据转换
			BocloudResult<PoolModel> BocloudResult = convertData(biPool, monitorName.toString(), poolPartition);
			if (BocloudResult.isFailed()) {
				logger.error(BocloudResult.getMessage());
				return new BocloudResult(false, null, BocloudResult.getMessage());
			}
			PoolModel poolModel = BocloudResult.getData();
			BocloudResult bocloudResult = f5PoolDriver.create(vendor.getUuid(), poolModel);
			if (bocloudResult.isSuccess()) {
				try {
					// 获取pool的状态
					String url = "/mgmt/tm/ltm/pool/"
							+ BigIpHelper.partitionCode(poolModel.getPartition(), poolModel.getName()) + "/stats";
					Thread.sleep(5000);
					String poolStatus = BigIpHelper.getResourceStatus(version,
							f5PoolDriver.stats(vendor.getUuid(), url), null, F5BasicObject.POOL,
							BigIpHelper.partitionCode(poolModel.getPartition(), poolModel.getName()), null);
					biPool.setStatus(poolStatus);
					// 数据库保存pool
					f5BiPoolRepository.save(biPool);
					BiPool pool = f5BiPoolRepository.queryByName(biPool.getName(), vendorId, biPool.getPartitionId());
					if (null == pool) {
						logger.error(BocloudResult.getMessage());
						return new BocloudResult(false, null, "资源创建成功，但服务同步数据失败。");
					}
					// 新增poolMember
					List<BiPoolMember> members = biPool.getMembers();
					ArrayList<BiNode> nodes = Lists.newArrayList();
					// 处理新增的poolMumber,不传名称的poolmember用address的值
					for (BiPoolMember biPoolMember : members) {
						BiPartition poolMemberPartition = f5BiPartitionRepository.query(biPoolMember.getPartitionId());
						biPoolMember.setCreatorId(userId);
						biPoolMember.setGmtCreate(new Date());
						biPoolMember.setPoolId(pool.getId());
						biPoolMember.setVendorId(vendorId);
						// 为新增的poolmember加入默认值
						if (null == biPoolMember.getRatio()) {
							biPoolMember.setRatio(1L);
						}
						if ("node-list".equals(biPoolMember.getNodeType())) {
							BiNode biNode = f5BiNodeRepository.queryByAddress(biPoolMember.getPoolMemberAddress(),
									vendorId, biPoolMember.getPartitionId());
							biPoolMember.setNodeId(biNode.getId());
						}
						biPoolMember.setConnectionLimit(0L);
						biPoolMember.setConnectionRateLimit(0L);
						biPoolMember.setPriority(0L);
						// 获取poolmember的状态
						String poolMemberUrl = "/mgmt/tm/ltm/pool/"
								+ BigIpHelper.partitionCode(poolModel.getPartition(), poolModel.getName()) + "/members/"
								+ BigIpHelper.partitionCode(poolMemberPartition.getName(), biPoolMember.getName())
								+ "/stats";
						String poolMemberStatus = BigIpHelper.getResourceStatus(version,
								f5PoolDriver.stats(vendor.getUuid(), poolMemberUrl), F5BasicObject.POOL,
								F5BasicObject.POOL_MEMBER,
								BigIpHelper.partitionCode(poolModel.getPartition(), poolModel.getName()),
								BigIpHelper.partitionCode(poolMemberPartition.getName(), biPoolMember.getName()));
						biPoolMember.setStatus(poolMemberStatus);
						// 将创建的member存储为到node中
						BiNode biNode = new BiNode();
						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 (nodes.isEmpty()) {
							// 获取node的状态
							String nodeStatus = BigIpHelper.getResourceStatus(version,
									f5NodeDriver.stats(vendor.getUuid(),
											BigIpHelper.partitionCode(poolMemberPartition.getName(), biNode.getName())),
									null, F5BasicObject.NODE,
									BigIpHelper.partitionCode(poolMemberPartition.getName(), biNode.getName()), null);
							biNode.setStatus(nodeStatus);
							BiNode node = f5BiNodeRepository.queryByAddress(biNode.getAddress(), vendor.getId(),
									biNode.getPartitionId());
							if (null == node) {
								nodes.add(biNode);
							}
						} else {
							List<BiNode> nodeDatas = Lists.newArrayList();
							for (BiNode node : nodes) {
								if (!node.getAddress().equals(biNode.getAddress())) {
									// 获取node的状态
									String nodeStatus = BigIpHelper.getResourceStatus(version,
											f5NodeDriver.stats(vendor.getUuid(),
													BigIpHelper.partitionCode(poolMemberPartition.getName(),
															biNode.getName())),
											null, F5BasicObject.NODE,
											BigIpHelper.partitionCode(poolMemberPartition.getName(), biNode.getName()),
											null);
									biNode.setStatus(nodeStatus);
									BiNode nodeData = f5BiNodeRepository.queryByAddress(biNode.getAddress(),
											vendor.getId(), biNode.getPartitionId());
									if (null == nodeData) {
										nodeDatas.add(biNode);
									}
								}
							}
							nodes.addAll(nodeDatas);
						}
					}
					// 新增node
					if (!nodes.isEmpty()) {
						f5BiNodeRepository.batchSave(nodes, nodes.size());
					}
					// 新增pool member
					if (!members.isEmpty()) {
						f5BiPoolMemberRepository.batchSave(members, members.size());
					}
				} 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 bipool fail:", e);
			return new BocloudResult(false, "添加BiPool失败");
		}
	}

	private BocloudResult<PoolModel> convertData(BiPool biPool, String monitorName, BiPartition poolPartition)
			throws Exception {
		PoolModel poolModel = new PoolModel();
		poolModel.setName(biPool.getName());
		poolModel.setPartition(poolPartition.getName());
		poolModel.setMonitor(monitorName);
		poolModel.setDescription(biPool.getRemark());
		poolModel.setLoadBalancingMode(biPool.getLoadBalancingMethod());
		if ("less-than".equals(biPool.getPriorityGroupActivation())) {
			poolModel.setMinActiveMembers(Long.valueOf(biPool.getAvailableMember()));
		}
		List<PoolMemberModel> membersModel = Lists.newArrayList();
		List<BiPoolMember> members = biPool.getMembers();
		if (null != members) {
			for (BiPoolMember poolMember : members) {
				BiPartition poolMemberPartition = f5BiPartitionRepository.query(poolMember.getPartitionId());
				// 处理port为*的数据
				if ("*".equals(poolMember.getPoolMemberPort())) {
					poolMember.setPoolMemberPort("0");
				}
				// 处理拼接poolMumber
				PoolMemberModel poolMemberModel = new PoolMemberModel();
				if (null == poolMember.getPartitionId()) {
					poolMemberModel.setPartition(poolPartition.getName());
					poolMember.setPartitionId(poolPartition.getId());
				} else {
					poolMemberModel.setPartition(poolMemberPartition.getName());
				}
				if ("".equals(poolMember.getName()) || null == poolMember.getName()) {
					poolMemberModel.setName(poolMember.getPoolMemberAddress() + ":" + poolMember.getPoolMemberPort());
					poolMember.setName(poolMember.getPoolMemberAddress() + ":" + poolMember.getPoolMemberPort());
				} else {
					poolMemberModel.setName(poolMember.getName() + ":" + poolMember.getPoolMemberPort());
					poolMember.setName(poolMember.getName() + ":" + poolMember.getPoolMemberPort());
				}
				poolMemberModel.setAddress(poolMember.getPoolMemberAddress());
				if ("less-than".equals(biPool.getPriorityGroupActivation())) {
					poolMemberModel.setPriorityGroup(poolMember.getPriority());
					if (null != poolMember.getRatio()) {
						poolMemberModel.setRatio(poolMember.getRatio());
					}
				}
				membersModel.add(poolMemberModel);
			}
		}
		poolModel.setMembers(membersModel);
		return new BocloudResult<PoolModel>(true, poolModel, "");
	}

	@Override
	public BocloudResult modify(BiPool biPool, Long id) {
		String path = Kvs.class.getSimpleName() + "_" + biPool.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			BiPool pool = f5BiPoolRepository.query(biPool.getId());
			if (null == pool) {
				logger.warn("bipool data not exist");
				return new BocloudResult(false, "数据不存在");
			}
			BiPartition poolPartition = f5BiPartitionRepository.query(pool.getPartitionId());
			CloudVendor vendor = cloudVendorRepository.query(biPool.getVendorId());
			if (null == vendor) {
				logger.error("get cloudVendor failure");
				return new BocloudResult(false, "云平台不存在，修改失败");
			}
			String version = vendor.getVersion();
			List<Long> monitor = null;
			StringBuilder monitorName = new StringBuilder();
			if (null != biPool.getMonitor()) {
				monitor = JSONArray.parseArray(biPool.getMonitor()).toJavaList(Long.class);
				biPool.setMonitor(StringUtils.join(monitor, ","));
				for (int i = 0; i < monitor.size(); i++) {
					Long monitorId = monitor.get(i);
					BiMonitor biMonitor = f5BiMonitorRepository.query(monitorId);
					if (null == biMonitor) {
						logger.error("get biMonitor failure!");
						return new BocloudResult(false, "健康检查不存在!");
					}
					BiPartition monitorPartition = f5BiPartitionRepository.query(biMonitor.getPartitionId());
					if (0 == i) {
						monitorName.append(BigIpHelper.jointPartition(monitorPartition.getName(), biMonitor.getName()));
						continue;
					}
					// 多个monitor需要进行拼接
					if (i > 0) {
						monitorName.append(
								" and " + BigIpHelper.jointPartition(monitorPartition.getName(), biMonitor.getName()));
					}
				}
			}
			BeanUtils.copyProperties(biPool, pool);
			pool.setMenderId(id);
			logger.info("update pool start...");
			// 数据转换
			biPool.setMembers(null);
			BocloudResult<PoolModel> BocloudResult = convertData(pool, monitorName.toString(), poolPartition);
			if (BocloudResult.isFailed()) {
				logger.error(BocloudResult.getMessage());
				return new BocloudResult(false, null, "更新失败：" + BocloudResult.getMessage());
			}
			PoolModel poolModel = BocloudResult.getData();
			// 执行修改时，不允许修改name，后面会做置空处理，所有提前拿到名称用来做查询pool的状态
			String poolName = poolModel.getName();
			// 更新操作时，member单独操作处理
			BocloudResult bocloudResult = f5PoolDriver.modify(vendor.getUuid(), poolModel);
			if (bocloudResult.isSuccess()) {
				try {
					// 获取pool的状态
					String url = "/mgmt/tm/ltm/pool/" + BigIpHelper.partitionCode(poolModel.getPartition(), poolName)
							+ "/stats";
					Thread.sleep(5000);
					String poolStatus = BigIpHelper.getResourceStatus(version,
							f5PoolDriver.stats(vendor.getUuid(), url), null, F5BasicObject.POOL,
							BigIpHelper.partitionCode(poolModel.getPartition(), poolName), null);
					pool.setStatus(poolStatus);
					f5BiPoolRepository.update(pool);
				} catch (Exception e) {
					logger.error(e.getMessage());
					return new BocloudResult(false, null, "更新失败：" + bocloudResult.getMessage());
				}
				return new BocloudResult(true, "更新成功");
			} else {
				logger.error(bocloudResult.getMessage());
				return new BocloudResult(false, null, bocloudResult.getMessage());
			}
		} catch (Exception e) {
			logger.error("modify bipool 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), "请求超时");
			BiPool pool = f5BiPoolRepository.query(id);
			if (null == pool) {
				logger.warn("bipool data not exist");
				return new BocloudResult(false, "数据不存在");
			}
			BiPartition poolPartition = f5BiPartitionRepository.query(pool.getPartitionId());
			CloudVendor vendor = cloudVendorRepository.query(pool.getVendorId());
			if (null == vendor) {
				logger.error("get cloudVendor failure!");
				return new BocloudResult(false, "删除失败!");
			}
			logger.info("remove pool start...");
			BocloudResult bocloudResult = f5PoolDriver.remove(vendor.getUuid(),
					BigIpHelper.partitionCode(poolPartition.getName(), pool.getName()));
			if (bocloudResult.isSuccess()) {
				try {
					f5BiPoolMemberRepository.removeByPoolId(id, userId);
					f5BiPoolRepository.remove(id, userId);
				} 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("remove bipool fail:", e);
			return new BocloudResult(false, "删除失败");
		}
	}

	@Override
	public BocloudResult<BiPool> detail(Long id) {
		try {
			BiPool biPool = this.f5BiPoolRepository.query(id);
			if (null == biPool) {
				return new BocloudResult(false, "数据不存在");
			}
			// 处理monitor的信息
			if (null != biPool.getMonitor() && !"".equals(biPool.getMonitor())) {
				String monitor = biPool.getMonitor();
				String[] monitors = monitor.split(",");
				List<String> monitorList = Lists.newArrayList();
				for (String string : monitors) {
					BiMonitor biMonitor = f5BiMonitorRepository.query(Long.valueOf(string));
					BiPartition monitorPartition = f5BiPartitionRepository.query(biMonitor.getPartitionId());
					String monitorName = BigIpHelper.jointPartition(monitorPartition.getName(), biMonitor.getName());
					monitorList.add(monitorName);
				}
				biPool.setMonitor(StringUtils.join(monitorList, ","));
			}
			return new BocloudResult(true, biPool, "查询详情成功");
		} catch (Exception e) {
			logger.error("Get bipool error：", e);
			return new BocloudResult(false, "查询详情失败");
		}
	}
}
