package com.bocloud.ims.service.scheduler.scheduler;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.compute.HostDriver;
import com.bocloud.cmp.entity.enums.AlarmLevel;
import com.bocloud.cmp.entity.enums.AlarmTargetCatalog;
import com.bocloud.cmp.entity.enums.BocloudStatus;
import com.bocloud.cmp.model.HostModel;
import com.bocloud.cmp.model.HostOperateModel;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.utils.ListTool;
import com.bocloud.ims.entity.common.MathUtils;
import com.bocloud.ims.entity.resource.AlarmThreshold;
import com.bocloud.ims.entity.resource.CloudServer;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.Cluster;
import com.bocloud.ims.entity.resource.ResourceAlarm;
import com.bocloud.ims.entity.resource.Server;
import com.bocloud.ims.entity.resource.ServerConfig;
import com.bocloud.ims.entity.resource.Vdc;
import com.bocloud.ims.repository.resource.AlarmThresholdRepository;
import com.bocloud.ims.repository.resource.CloudServerRepository;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.ClusterRepository;
import com.bocloud.ims.repository.resource.ResourceAlarmRepository;
import com.bocloud.ims.repository.resource.ServerConfigRepository;
import com.bocloud.ims.repository.resource.ServerRepository;
import com.bocloud.ims.repository.resource.VdcRepository;
import com.bocloud.service.core.CurrentService;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;

/**
 * 告警阈值定时任务类
 *
 * @author wangyu
 * @version 1.0
 * @since 2018年2月20日
 */
@Component("alarmThresholdScheduler")
public class AlarmThresholdScheduler {

	/**
	 * 严重告警
	 */
	private static String DANGER = "DANGER";

	/**
	 * 警告告警
	 */
	private static String WARN = "WARN";

	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private ClusterRepository clusterRepository;
	@Autowired
	private AlarmThresholdRepository alarmThresholdRepository;
	@Autowired
	private CloudServerRepository cloudServerRepository;
	@Autowired
	private VdcRepository vdcRepository;
	@Autowired
	private ServerRepository serverRepository;
	@Autowired
	private ServerConfigRepository serverConfigRepository;
	@Autowired
	private ResourceAlarmRepository alarmRepository;
	@Autowired
	private HostDriver hostDriver;
	@Autowired
	private CurrentService currentService;

	/**
	 * @since 2018-04-25 modify by sxp 增加集群磁盘使用状态的计算
	 * @since 2018-04-26 modify by sxp 根据正在运行的虚拟机分配额来统计使用量
	 */
	@Scheduled(cron = "${alarm.threshold.scheduler:0 0/1 * * * ?}")
	protected void sync() throws Exception {
		if (currentService.isLeader()) {
			String runningStatus = BocloudStatus.Server.RUNNING.name();
			Long diskTotal, diskUsed;
			double diskUsage;
			BigDecimal diskRedVal, diskYellowVal;

			List<AlarmThreshold> alarmThresholds = alarmThresholdRepository.list();
			if (ListTool.isEmpty(alarmThresholds)) {
				return;
			}
			for (AlarmThreshold threshold : alarmThresholds) {
				String catalog = threshold.getCatalog();
				Integer cpuTotal = 0;
				Integer memTotal = 0;
				Integer cpuUsed = 0;
				Integer memUsed = 0;
				Double cpuUsage = 0.0;
				Double memUsage = 0.0;
				diskUsed = 0L;
				diskUsage = 0;
				BigDecimal cpuRedVal = threshold.getCpuRedVal();
				BigDecimal cpuYellowVal = threshold.getCpuYellowVal();
				BigDecimal memRedVal = threshold.getMemoryRedVal();
				BigDecimal memYellowVal = threshold.getMemoryYellowVal();
				diskRedVal = threshold.getDiskRedVal();
				diskYellowVal = threshold.getDiskYellowVal();

				List<Server> servers = Lists.newArrayList();
				switch (AlarmTargetCatalog.valueOf(catalog)) {
				case VENDOR:
					// 查询云平台下的所有虚拟机，汇总CPU和内存使用总量
					CloudVendor vendor = cloudVendorRepository.query(threshold.getTarget());
					if (null == vendor) {
						continue;
					}
					List<Vdc> vdcs = vdcRepository.listByVendor(vendor.getId());
					for (Vdc vdc : vdcs) {
						cpuTotal += vdc.getCpuTotal().intValue();
						memTotal += vdc.getMemTotal().intValue();
					}
					List<CloudServer> cloudServers = cloudServerRepository.listByVendorId(vendor.getId());
					for (CloudServer cloudServer : cloudServers) {
						// 根据正在运行的虚拟机分配额来统计使用量
						if (!runningStatus.equals(cloudServer.getStatus())) {
							continue;
						}
						ServerConfig serverConfig = serverConfigRepository.getBySid(cloudServer.getHostId());
						if (null != serverConfig) {
							cpuUsed += new BigDecimal(serverConfig.getCpuHz())
									.divide(new BigDecimal(serverConfig.getCpuCore()), 2, RoundingMode.HALF_UP)
									.multiply(new BigDecimal(cloudServer.getCpu())).intValue();
						}
						memUsed += null == cloudServer.getMemory() ? 0 : cloudServer.getMemory().intValue();
					}
					if (0 != cpuTotal) {
						// CPU使用率
						cpuUsage = new BigDecimal(cpuUsed).divide(new BigDecimal(cpuTotal), 2, RoundingMode.HALF_UP)
								.doubleValue();
					}
					if (0 != memTotal) {
						// 内存使用率
						memUsage = new BigDecimal(memUsed).divide(new BigDecimal(memTotal), 2, RoundingMode.HALF_UP)
								.doubleValue();
					}
					// 创建告警
					String vendorStatus = createAlarm(cpuUsage, cpuRedVal, cpuYellowVal, memUsage, memRedVal,
							memYellowVal, diskUsage, diskRedVal, diskYellowVal, catalog, vendor.getId());
					if (null != vendorStatus) {
						vendor.setStatus(vendorStatus);
						cloudVendorRepository.update(vendor);
					} else {
						vendor.setStatus("NORMAL");
						cloudVendorRepository.update(vendor);
					}
					break;
				case CLUSTER:
					Cluster cluster = clusterRepository.query(threshold.getTarget());
					if (null == cluster) {
						continue;
					}
					cpuTotal = cluster.getCpuTotal().intValue();
					memTotal = cluster.getMemTotal().intValue();
					diskTotal = cluster.getDiskTotal().longValue();

					servers = serverRepository.listByCluster(cluster.getId());
					for (Server server : servers) {
						List<CloudServer> vms = cloudServerRepository.listByHid(server.getId());
						for (CloudServer vm : vms) {
							// 根据正在运行的虚拟机分配额来统计使用量
							if (!runningStatus.equals(vm.getStatus())) {
								continue;
							}
							ServerConfig serverConfig = serverConfigRepository.getBySid(vm.getHostId());
							if (null != serverConfig) {
								cpuUsed += new BigDecimal(serverConfig.getCpuHz())
										.divide(new BigDecimal(serverConfig.getCpuCore()), 2, RoundingMode.HALF_UP)
										.multiply(new BigDecimal(vm.getCpu())).intValue();
							}
							memUsed += null == vm.getMemory() ? 0 : vm.getMemory().intValue();
							diskUsed += vm.getDisk();
						}
					}
					if (0 != cpuTotal) {
						// CPU使用率
						cpuUsage = new BigDecimal(cpuUsed).divide(new BigDecimal(cpuTotal), 2, RoundingMode.HALF_UP)
								.doubleValue();
					}
					if (0 != memTotal) {
						// 内存使用率
						memUsage = new BigDecimal(memUsed).divide(new BigDecimal(memTotal), 2, RoundingMode.HALF_UP)
								.doubleValue();
					}
					if (diskTotal != 0) {
						// 磁盘使用率
						diskUsage = MathUtils.round((double) diskUsed / diskTotal, 2);
					}
					// 创建告警
					String clusterStatus = createAlarm(cpuUsage, cpuRedVal, cpuYellowVal, memUsage, memRedVal,
							memYellowVal, diskUsage, diskRedVal, diskYellowVal, catalog, cluster.getId());
					cluster.setStatus(clusterStatus);
					clusterRepository.update(cluster);
					break;
				case HOST:
					Server server = serverRepository.query(threshold.getTarget());
					if (null == server) {
						continue;
					}
					ServerConfig serverConfig = serverConfigRepository.getBySid(threshold.getTarget());
					if (null == serverConfig) {
						continue;
					}
					cpuTotal = serverConfig.getCpuHz().intValue();
					memTotal = serverConfig.getMemoryCapacity().intValue();
					diskTotal = serverConfig.getDiskCapacity().longValue();
					List<CloudServer> vms = cloudServerRepository.listByHid(server.getId());
					for (CloudServer vm : vms) {
						// 根据正在运行的虚拟机分配额来统计使用量
						if (!runningStatus.equals(vm.getStatus())) {
							continue;
						}
						cpuUsed += vm.getCpu();
						memUsed += null == vm.getMemory() ? 0 : vm.getMemory().intValue();
						diskUsed += vm.getDisk();
					}
					if (0 != cpuTotal) {
						// CPU使用率
						cpuUsage = new BigDecimal(cpuUsed).divide(new BigDecimal(cpuTotal), 2, RoundingMode.HALF_UP)
								.doubleValue();
					}
					if (0 != memTotal) {
						// 内存使用率
						memUsage = new BigDecimal(memUsed).divide(new BigDecimal(memTotal), 2, RoundingMode.HALF_UP)
								.doubleValue();
					}
					if (diskTotal != 0) {
						// 磁盘使用率
						diskUsage = MathUtils.round((double) diskUsed / diskTotal, 2);
					}
					// 创建告警
					String hostStatus = createAlarm(cpuUsage, cpuRedVal, cpuYellowVal, memUsage, memRedVal,
							memYellowVal, diskUsage, diskRedVal, diskYellowVal, catalog, server.getId());
					if (null == hostStatus) {
						CloudVendor cloudVendor = cloudVendorRepository.query(server.getVendorId());
						HostOperateModel operateModel = new HostOperateModel();
						operateModel.setHostName(server.getName());
						BocloudResult result = hostDriver.detail(cloudVendor.getUuid(), null, operateModel);
						if (result.isSuccess()) {
							HostModel hostModel = JSONObject.parseObject(JSONObject.toJSONString(result.getData()),
									HostModel.class);
							if (null != hostModel) {
								server.setStatus(hostModel.getStatus());
							} else {
								server.setStatus(BocloudStatus.Server.EXCEPTION.name());
							}
						}
					} else {
						server.setStatus(hostStatus);
					}
					serverRepository.update(server);
					break;
				case TENANT:
					diskRedVal = threshold.getDiskRedVal();
					diskYellowVal = threshold.getDiskYellowVal();
					// BigDecimal instanceRedVal =
					// threshold.getInstanceRedVal();
					// BigDecimal instanceYellowVal =
					// threshold.getInstanceYellowVal();
					// BigDecimal fixedIpRedVal = threshold.getFixedIpRedVal();
					// BigDecimal fixedIpYellowVal =
					// threshold.getFixedIpYellowVal();
					// ResourceQuota cpuQuota =
					// resourceQuotaRepository.query(threshold.getTarget(),
					// catalog,
					// QuotaCategory.CPU.name());
					// if (cpuQuota != null) {
					// createAlarm(cpuQuota.getQuota(), cpuQuota.getUsed(),
					// cpuRedVal, cpuYellowVal,
					// catalog,
					// threshold.getTarget(), "CPU", cpuQuota.getTarget());
					// }
					// ResourceQuota memQuota =
					// resourceQuotaRepository.query(threshold.getTarget(),
					// catalog,
					// QuotaCategory.MEM.name());
					// if (memQuota != null) {
					// createAlarm(memQuota.getQuota(), memQuota.getUsed(),
					// memRedVal, memYellowVal,
					// catalog,
					// threshold.getTarget(), "内存", memQuota.getTarget());
					// }
					// ResourceQuota diskQuota =
					// resourceQuotaRepository.query(threshold.getTarget(),
					// catalog,
					// QuotaCategory.DISK.name());
					// if (diskQuota != null) {
					// createAlarm(diskQuota.getQuota(), diskQuota.getUsed(),
					// diskRedVal,
					// diskYellowVal, catalog,
					// threshold.getTarget(), "磁盘", diskQuota.getTarget());
					// }
					// ResourceQuota instanceQuota =
					// resourceQuotaRepository.query(threshold.getTarget(),
					// catalog,
					// QuotaCategory.INST.name());
					// if (instanceQuota != null) {
					// createAlarm(instanceQuota.getQuota(),
					// instanceQuota.getUsed(),
					// instanceRedVal,
					// instanceYellowVal, catalog, threshold.getTarget(), "实例数",
					// instanceQuota.getTarget());
					// }
					// ResourceQuota fixedIpQuota =
					// resourceQuotaRepository.query(threshold.getTarget(),
					// catalog,
					// QuotaCategory.FIP.name());
					// if (fixedIpQuota != null) {
					// createAlarm(fixedIpQuota.getQuota(),
					// fixedIpQuota.getUsed(), fixedIpRedVal,
					// fixedIpYellowVal,
					// catalog, threshold.getTarget(), "浮动ip",
					// fixedIpQuota.getTarget());
					// }
					break;
				default:
					break;
				}
			}

		}
	}

	/**
	 * 创建告警
	 *
	 * @param cpuUsage
	 * @param cpuRedVal
	 * @param cpuYellowVal
	 * @param memUsage
	 * @param memRedVal
	 * @param memYellowVal
	 * @param diskUsage
	 *            磁盘使用率
	 * @param diskRedVal
	 *            磁盘严重阈值
	 * @param diskYellowVal
	 *            磁盘警告阈值
	 * @param catalog
	 * @param target
	 * @throws Exception
	 * @since 2018-04-25 modify by sxp 增加磁盘使用状态的计算
	 */
	private String createAlarm(Double cpuUsage, BigDecimal cpuRedVal, BigDecimal cpuYellowVal, Double memUsage,
			BigDecimal memRedVal, BigDecimal memYellowVal, double diskUsage, BigDecimal diskRedVal,
			BigDecimal diskYellowVal, String catalog, Long target) throws Exception {
		String cpuStatus = null;
		String memStatus = null;
		String diskStatus = null;
		String name;

		// CPU使用率是否超过阈值
		if (cpuRedVal != null && cpuUsage >= cpuRedVal.doubleValue()) {
			name = "CPU使用情况严重告警";
			ResourceAlarm alarm = alarmRepository.getAlarm(name, AlarmLevel.DANGER.name(), catalog, target,
					cpuUsage.toString());
			if (null == alarm) {
				alarm = new ResourceAlarm(AlarmLevel.DANGER.name(), catalog, target, cpuUsage.toString(), new Date(),
						true);
				alarm.setCreatorId(1L);
				alarm.setGmtCreate(new Date());
				alarm.setStatus("UNCONFIRMED");
				alarm.setDeleted(false);
				alarm.setName(name);
				alarmRepository.save(alarm);
			}
			cpuStatus = DANGER;
		} else if (cpuYellowVal != null && cpuUsage >= cpuYellowVal.doubleValue()) {
			name = "CPU使用情况警告告警";
			ResourceAlarm alarm = alarmRepository.getAlarm(name, AlarmLevel.WARN.name(), catalog, target,
					cpuUsage.toString());
			if (null == alarm) {
				alarm = new ResourceAlarm(AlarmLevel.WARN.name(), catalog, target, cpuUsage.toString(), new Date(),
						true);
				alarm.setCreatorId(1L);
				alarm.setGmtCreate(new Date());
				alarm.setStatus("UNCONFIRMED");
				alarm.setDeleted(false);
				alarm.setName(name);
				alarmRepository.save(alarm);
			}
			cpuStatus = WARN;
		}

		// 内存使用率是否超过阈值
		if (memRedVal != null && memUsage >= memRedVal.doubleValue()) {
			name = "内存使用情况严重告警";
			ResourceAlarm alarm = alarmRepository.getAlarm(name, AlarmLevel.DANGER.name(), catalog, target,
					memUsage.toString());
			if (null == alarm) {
				alarm = new ResourceAlarm(AlarmLevel.DANGER.name(), catalog, target, memUsage.toString(), new Date(),
						true);
				alarm.setCreatorId(1L);
				alarm.setGmtCreate(new Date());
				alarm.setStatus("UNCONFIRMED");
				alarm.setDeleted(false);
				alarm.setName(name);
				alarmRepository.save(alarm);
			}
			memStatus = DANGER;
		} else if (memYellowVal != null && memUsage >= memYellowVal.doubleValue()) {
			name = "内存使用情况警告告警";
			ResourceAlarm alarm = alarmRepository.getAlarm(name, AlarmLevel.WARN.name(), catalog, target,
					memUsage.toString());
			if (null == alarm) {
				alarm = new ResourceAlarm(AlarmLevel.WARN.name(), catalog, target, memUsage.toString(), new Date(),
						true);
				alarm.setCreatorId(1L);
				alarm.setGmtCreate(new Date());
				alarm.setStatus("UNCONFIRMED");
				alarm.setDeleted(false);
				alarm.setName(name);
				alarmRepository.save(alarm);
			}
			memStatus = WARN;
		}

		// 磁盘使用率是否超过阈值
		if (diskRedVal != null && diskUsage >= diskRedVal.doubleValue()) {
			name = "存储使用情况严重告警";
			ResourceAlarm alarm = alarmRepository.getAlarm(name, AlarmLevel.DANGER.name(), catalog, target,
					String.valueOf(diskUsage));
			if (null == alarm) {
				alarm = new ResourceAlarm(AlarmLevel.DANGER.name(), catalog, target, String.valueOf(diskUsage),
						new Date(), true);
				alarm.setCreatorId(1L);
				alarm.setGmtCreate(new Date());
				alarm.setStatus("UNCONFIRMED");
				alarm.setDeleted(false);
				alarm.setName(name);
				alarmRepository.save(alarm);
			}
			diskStatus = DANGER;
		} else if (diskYellowVal != null && diskUsage >= diskYellowVal.doubleValue()) {
			name = "存储使用情况警告告警";
			ResourceAlarm alarm = alarmRepository.getAlarm(name, AlarmLevel.WARN.name(), catalog, target,
					String.valueOf(diskUsage));
			if (null == alarm) {
				alarm = new ResourceAlarm(AlarmLevel.WARN.name(), catalog, target, String.valueOf(diskUsage),
						new Date(), true);
				alarm.setCreatorId(1L);
				alarm.setGmtCreate(new Date());
				alarm.setStatus("UNCONFIRMED");
				alarm.setDeleted(false);
				alarm.setName(name);
				alarmRepository.save(alarm);
			}
			diskStatus = WARN;
		}

		String status = getStatus(cpuStatus, memStatus, diskStatus);
		return status;
	}

	/**
	 * 计算状态
	 * <p>
	 * 只要存在一个严重状态，返回严重状态；<br>
	 * 只要存在一个警告状态，返回警告状态；<br>
	 * 否则返回null值，即正常状态。<br>
	 * </p>
	 * 0
	 *
	 * @author sxp
	 * @since Apr 25, 2018
	 */
	private String getStatus(String cpuStatus, String memStatus, String diskStatus) {
		if (DANGER.equals(cpuStatus) || DANGER.equals(memStatus) || DANGER.equals(diskStatus)) {
			return DANGER;
		}
		if (WARN.equals(cpuStatus) || WARN.equals(memStatus) || WARN.equals(diskStatus)) {
			return WARN;
		}
		return null;
	}

}
