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

import com.alibaba.fastjson.JSONArray;
import com.bocloud.cmp.driver.f5.F5NodeDriver;
import com.bocloud.cmp.entity.enums.F5BasicObject;
import com.bocloud.cmp.model.f5.NodeModel;
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.BiMonitor;
import com.bocloud.ims.entity.resource.f5.BiNode;
import com.bocloud.ims.entity.resource.f5.BiPartition;
import com.bocloud.ims.entity.resource.f5.BiPoolMember;
import com.bocloud.ims.interfaces.resource.f5.F5BiNodeService;
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.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("f5BiNodeService")
public class F5BiNodeServiceImpl implements F5BiNodeService {

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

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

	@Override
	public BocloudResult create(BiNode biNode, Long userId) {
		String path = Kvs.class.getSimpleName() + "_" + biNode.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			// 云平台id
			Long vendorId = biNode.getVendorId();
			boolean isExists = f5BiNodeRepository.existName(biNode.getName(), vendorId, biNode.getPartitionId());
			if (isExists) {
				return new BocloudResult(false, "添加失败：该名称已存在");
			}
			CloudVendor vendor = cloudVendorRepository.query(vendorId);
			if (null == vendor) {
				logger.error("get cloudVendor failure!");
				return new BocloudResult(false, "云平台不存在!");
			}
			String version = vendor.getVersion();
			biNode.setCreatorId(userId);
			biNode.setMenderId(userId);
			biNode.setGmtCreate(new Date());
			BiPartition partition = f5BiPartitionRepository.query(biNode.getPartitionId());
			logger.info("create node start...");
			// 数据转换
			BocloudResult<NodeModel> BocloudResult = convertData(biNode, partition);
			if (BocloudResult.isFailed()) {
				logger.error(BocloudResult.getMessage());
				return new BocloudResult(false, null, BocloudResult.getMessage());
			}
			NodeModel nodeModel = BocloudResult.getData();
			String nodeName = nodeModel.getName();
			BocloudResult bocloudResult = f5NodeDriver.create(vendor.getUuid(), nodeModel);
			if (bocloudResult.isSuccess()) {
				try {
					// 获取node的状态
					Thread.sleep(5000);
					BocloudResult result = f5NodeDriver.stats(vendor.getUuid(),
							BigIpHelper.partitionCode(nodeModel.getPartition(), nodeName));
					if (result.isFailed()) {
						logger.error(BocloudResult.getMessage());
					}
					String description = BigIpHelper.getResourceStatus(version, result, null, F5BasicObject.NODE,
							BigIpHelper.partitionCode(nodeModel.getPartition(), nodeName), null);
					biNode.setStatus(description);
					f5BiNodeRepository.save(biNode);
				} 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("create node error", e);
			return new BocloudResult(false, e.getMessage());
		}
	}

	private BocloudResult<NodeModel> convertData(BiNode biNode, BiPartition partition) {
		NodeModel nodeModel = new NodeModel();
		// profile名称
		nodeModel.setName(biNode.getName());
		// 分区
		nodeModel.setPartition(partition.getName());
		// 描述
		nodeModel.setDescription(biNode.getRemark());
		// 地址
		nodeModel.setAddress(biNode.getAddress());
		// 设置health monitors
		StringBuilder monitorModel = new StringBuilder();
		List<Long> monitorIds = Lists.newArrayList();
		switch (biNode.getHealthMonitors()) {
		case "default":
			nodeModel.setMonitor("default");
			biNode.setSelectMonitors(String.valueOf(monitorIds));
			break;
		case "none":
			nodeModel.setMonitor(biNode.getHealthMonitors());
			biNode.setSelectMonitors(String.valueOf(monitorIds));
			break;
		case "node-specific":
			List<Long> monitors = JSONArray.parseArray(biNode.getSelectMonitors()).toJavaList(Long.class);
			for (int i = 0; i < monitors.size(); i++) {
				try {
					BiMonitor monitor = f5BiMonitorRepository.query(monitors.get(i));
					BiPartition monitorPartition = f5BiPartitionRepository.query(monitor.getPartitionId());
					String monitorFullPath = BigIpHelper.jointPartition(monitorPartition.getName(), monitor.getName());
					if (0 >= i) {
						if (!"all".equals(biNode.getAvailabilityRequirement())) {
							monitorModel.append("min " + biNode.getAvailableCount() + " of " + monitorFullPath);
						} else {
							monitorModel.append(monitorFullPath);
						}
					} else {
						if (!"all".equals(biNode.getAvailabilityRequirement())) {
							monitorModel.append(" " + monitorFullPath);
						} else {
							monitorModel.append(" and " + monitorFullPath);
						}
					}
				} catch (Exception e) {
					logger.error(e.getMessage());
					return new BocloudResult<NodeModel>(false, nodeModel, e.getMessage());
				}
			}
			nodeModel.setMonitor(monitorModel.toString());
			break;
		default:
			break;
		}
		nodeModel.setRatio(biNode.getRatio());
		nodeModel.setConnectionLimit(biNode.getConnectionLimit());
		if (0 == biNode.getConnectionRateLimit()) {
			nodeModel.setRateLimit("disabled");
		} else {
			nodeModel.setRateLimit(String.valueOf(biNode.getConnectionLimit()));
		}
		return new BocloudResult<NodeModel>(true, nodeModel, "");
	}

	@Override
	public BocloudResult modify(BiNode biNode, Long userId) {
		String path = Kvs.class.getSimpleName() + "_" + biNode.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			CloudVendor vendor = cloudVendorRepository.query(biNode.getVendorId());
			if (null == vendor) {
				logger.error("get cloudVendor failure");
				return new BocloudResult(false, "云平台不存在，修改失败");
			}
			String version = vendor.getVersion();
			BiNode object = this.f5BiNodeRepository.query(biNode.getId());
			if (null == object) {
				logger.warn("data does not exist!");
				return new BocloudResult(false, "数据不存在");
			}
			biNode.setMenderId(userId);
			biNode.setGmtModify(new Date());
			BiPartition partition = f5BiPartitionRepository.query(biNode.getPartitionId());
			logger.info("update node start...");
			// 数据转换
			BocloudResult<NodeModel> BocloudResult = convertData(biNode, partition);
			if (BocloudResult.isFailed()) {
				logger.error(BocloudResult.getMessage());
				return new BocloudResult(false, null, "更新失败：" + BocloudResult.getMessage());
			}
			NodeModel nodeModel = BocloudResult.getData();
			String nodeName = nodeModel.getName();
			BocloudResult bocloudResult = f5NodeDriver.modify(vendor.getUuid(), nodeModel);
			if (bocloudResult.isSuccess()) {
				try {
					// 获取node的状态
					Thread.sleep(5000);
					BocloudResult result = f5NodeDriver.stats(vendor.getUuid(),
							BigIpHelper.partitionCode(nodeModel.getPartition(), nodeName));
					if (result.isFailed()) {
						logger.error(BocloudResult.getMessage());
					}
					String description = BigIpHelper.getResourceStatus(version, result, null, F5BasicObject.NODE,
							BigIpHelper.partitionCode(nodeModel.getPartition(), nodeName), null);
					biNode.setStatus(description);
					f5BiNodeRepository.update(biNode);
				} 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 BiNode 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), "请求超时");
			BiNode biNode = this.f5BiNodeRepository.query(id);
			if (null == biNode) {
				return new BocloudResult(false, "请求内容不存在");
			}
			CloudVendor vendor = cloudVendorRepository.query(biNode.getVendorId());
			if (null == vendor) {
				logger.error("get cloudVendor failure!");
				return new BocloudResult(false, "删除失败!");
			}
			// 检查是否被引用
			BiPoolMember member = f5BiPoolMemberRepository.queryByAddress(biNode.getAddress(), biNode.getVendorId(),
					biNode.getPartitionId());
			if (null != member) {
				String poolName = f5BiPoolRepository.query(member.getPoolId()).getName();
				logger.error("remove node failure!");
				return new BocloudResult(false, "node address:" + biNode.getAddress() + "已被pool:" + poolName + "的成员引用");
			}
			BiPartition partition = f5BiPartitionRepository.query(biNode.getPartitionId());
			logger.info("remove Node start...");
			BocloudResult result = f5NodeDriver.remove(vendor.getUuid(),
					BigIpHelper.partitionCode(partition.getName(), biNode.getName()));
			if (result.isSuccess()) {
				try {
					f5BiNodeRepository.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(e.getMessage());
			return new BocloudResult(false, "删除失败");
		}
	}

	@Override
	public BocloudResult<BiNode> detail(Long id) {
		try {
			BiNode biNode = this.f5BiNodeRepository.query(id);
			if (null == biNode) {
				return new BocloudResult(false, "数据不存在");
			}
			// 汉化关联的monitor
			String selectMonitors = biNode.getSelectMonitors();
			List<Long> selectMonitorIds = JSONArray.parseArray(selectMonitors, Long.class);
			ArrayList<String> selectMonitorNames = Lists.newArrayList();
			for (Long monitorId : selectMonitorIds) {
				BiMonitor monitor = f5BiMonitorRepository.query(monitorId);
				BiPartition partition = f5BiPartitionRepository.query(monitor.getPartitionId());
				selectMonitorNames.add(BigIpHelper.jointPartition(partition.getName(), monitor.getName()));
			}
			biNode.setSelectMonitors(String.valueOf(selectMonitorNames));
			return new BocloudResult(true, biNode, "查询数据成功");
		} catch (Exception e) {
			logger.error("Get BiNode error：", e);
			return new BocloudResult(false, "查询详情失败");
		}
	}

}
