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

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.hmc.HmcPartitionDriver;
import com.bocloud.cmp.driver.hmc.HmcProfileDriver;
import com.bocloud.cmp.model.hmc.HmcPartitionModel;
import com.bocloud.cmp.model.hmc.HmcProfileModel;
import com.bocloud.cmp.model.hmc.sdk.module.LogicalPartitionProfileEntry;
import com.bocloud.common.model.*;
import com.bocloud.common.model.OperateResult.OperateCategory;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.JSONTools;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.common.enums.ServerAction;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.ResourceEvent;
import com.bocloud.ims.entity.resource.hmc.*;
import com.bocloud.ims.interfaces.resource.hmc.HmcProfileService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.ResourceEventRepository;
import com.bocloud.ims.repository.resource.hmc.*;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
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.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

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

@Service("hmcProfileService")
public class HmcProfileServiceImpl implements HmcProfileService {
	private static final Logger logger = LoggerFactory.getLogger(HmcProfileServiceImpl.class);
	private static OperateCategory operateCategory = OperateCategory.ResourceEvent;
	@Autowired
	private ThreadPoolTaskExecutor taskExecutor;
	@Autowired
	private HmcPartitionDriver partitionDriver;
	@Autowired
	private HmcProfileDriver profileDriver;
	@Autowired
	private HmcPartitionRepository hmcPartitionRepository;
	@Autowired
	private HmcProfileIoslotRepository hmcProfileIoslotRepository;
	@Autowired
	private HmcProfileNetworkAdapterRepository hmcProfileNetworkAdapterRepository;
	@Autowired
	private HmcProfileFibreAdapterRepository hmcProfileFibreAdapterRepository;
	@Autowired
	private HmcProfileScsiAdapterRepository hmcProfileScsiAdapterRepository;
	@Autowired
	private HmcProfileLheaRepository hmcprofileLheaRepository;
	@Autowired
	private HmcServerRepository hmcServerRepository;
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private HmcDatabaseHelper hmcDatabaseHelper;
	@Autowired
	private HmcProfileRepository hmcProfileRepository;
	@Autowired
	private ResourceEventRepository resourceEventRepository;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;
	@Autowired
	private LockFactory lockFactory;
	@Autowired
	private HmcVirtualSwitchRepository hmcVirtualSwitchRepository;
	@Autowired
	private HmcPhysicalFibreChannelRepository hmcPhysicalFibreChannelRepository;
	@Autowired
	private HmcSystemSharedProcessorPoolRepository sharedProcessorPoolRepository;

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

	@Override
	public BocloudResult create(HmcProfile profile, RequestContext context) {
		try {
			// 分区
			Long partitionId = profile.getPartitionId();
			HmcPartition partition = hmcPartitionRepository.getById(partitionId);
			if (null == partition) {
				logger.error("get partition failure!");
				return new BocloudResult(false, "分区不存在!");
			}

			// 服务器
			HmcServer server = hmcServerRepository.getServerById(partition.getServerId());
			if (null == server) {
				logger.error("get HMC server failure!");
				return new BocloudResult(false, "服务器不存在!");
			}

			// 云供应商
			CloudVendor vendor = cloudVendorRepository.query(server.getVendorId());
			if (null == vendor) {
				logger.error("get cloudVendor failure!");
				return new BocloudResult(false, "云平台不存在!");
			}

			profile.setPartitionId(partition.getId());

			// 查询分区使用的共享存储池hmc端的id
			if (profile.getSharedProPoolId() != null) {
				HmcSystemSharedProcessorPool sharedProcessorPool = sharedProcessorPoolRepository
						.getById(profile.getSharedProPoolId());
				profile.setSharedProPoolId(Long.valueOf(sharedProcessorPool.getHmcPoolId()));
			}
			// 保存分区信息到DB
			hmcDatabaseHelper.saveProfile(profile, partition.getId(), context.getTarget());
			String vendorUuid = vendor.getUuid();
			// 启动一个线程从MQ上获取数据
			taskExecutor.submit(() -> {
				String operate = vendor.getType().toLowerCase() + ".profile";
				String content = "概要文件[" + profile.getName() + "]创建失败";
				try {
					ResourceEvent event = null;
					BocloudResult result = profileDriver.create(vendorUuid,
							JSONObject.parseObject(JSONObject.toJSONString(partition), HmcPartitionModel.class),
							JSONObject.parseObject(JSONObject.toJSONString(profile), HmcProfileModel.class));
					if (result.isSuccess()) {
						// update database for the new partition
						LogicalPartitionProfileEntry entry = (LogicalPartitionProfileEntry) result.getData();
						profile.setServerId(partition.getServerId());
						hmcDatabaseHelper.updateLparProfile(profile, entry, null, null);
						content = "概要文件[" + profile.getName() + "]创建成功，\n详情：[" + result.getMessage() + "]";
						event = new ResourceEvent(profile.getId(), ServerAction.CREATE.name(), "成功",
								context.getTarget(), new Date(), "HMC", vendor.getId(), context.getCatalog());
					} else {
						hmcDatabaseHelper.removeProfile(profile.getId(), context.getTarget());
						content = "概要文件[" + profile.getName() + "]创建失败，\n详情：[" + result.getMessage() + "]";
						// saveAlarm(partition.getName());
						event = new ResourceEvent(profile.getId(), ServerAction.CREATE.name(), "失败",
								context.getTarget(), new Date(), "HMC", vendor.getId(), context.getCatalog());
					}
					resourceEventRepository.save(event);
					resourceEventPublisher.send(new OperateResult(result.isSuccess(), content, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				} catch (Exception e) {
					logger.error("Get Message Exception:", e);
					resourceEventPublisher.send(new OperateResult(false, content, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				}
			});
			return new BocloudResult(true, "创建任务已经下发，正在执行……");
		} catch (Exception e) {
			logger.error("创建失败:", e);
			return new BocloudResult(false, "创建失败");
		}
	}

	@Override
	public BocloudResult<HmcProfile> detail(Long id) {
		try {
			// 基本信息(常规)
			HmcProfile profile = this.hmcProfileRepository.queryById(id);
			if (profile == null) {
				logger.warn("HMC partition profile does not exist!");
				return new BocloudResult(false, null, "分区概要文件不存在");
			}
			HmcPartition hmcPartition = hmcPartitionRepository.getById(profile.getPartitionId());
			HmcServer hmcServer = hmcServerRepository.getServerById(hmcPartition.getServerId());

			// io slot information is in the profile
			List<HmcProfileIoslot> ioslots = this.hmcProfileIoslotRepository.queryByProfileId(id);
			profile.setHmcProfileIoslot(ioslots);

			// virtual adapter information
			List<HmcProfileNetworkAdapter> networks = this.hmcProfileNetworkAdapterRepository.queryByProfileId(id);
			profile.setHmcProfileNetworkAdapters(networks);
			List<HmcProfileFibreAdapter> fibres = this.hmcProfileFibreAdapterRepository.queryByProfileId(id);
			for (HmcProfileFibreAdapter fibre : fibres) {
				if (fibre.getConnectingPartitionId() != null) {
					HmcPartition partition = this.hmcPartitionRepository
							.getByHmcPartitionId(fibre.getConnectingPartitionId(), hmcServer.getId());
					if (null != partition) {
						logger.warn("Don't correct partition ID : {}", fibre.getConnectingPartitionId());
						fibre.setConnectingPartitionName(partition.getName());
					}
				}
			}
			profile.setHmcProfileFibreAdapters(fibres);
			List<HmcProfileScsiAdapter> scsis = this.hmcProfileScsiAdapterRepository.queryByProfileId(id);
			for (HmcProfileScsiAdapter scsi : scsis) {
				if (null == scsi.getRemotePartitionId()) {
					continue;
				}
				HmcPartition partition = this.hmcPartitionRepository.getByHmcPartitionId(scsi.getRemotePartitionId(),
						hmcServer.getId());
				if (null != partition) {
					scsi.setRemotePartitionName(partition.getName());
				} else {
					logger.warn("partition ID : {} no correct.", scsi.getRemotePartitionId());
				}
			}
			profile.setHmcProfileScsiAdapters(scsis);

			// HEA logical ports
			List<HmcProfileLhea> lheas = this.hmcprofileLheaRepository.queryByProfileId(profile.getId());
			profile.setHmcProfileLheas(lheas);
			profile.setCurrentProfileSync(hmcPartition.getCurrentProfileSync());

			HmcSystemSharedProcessorPool processorPool = sharedProcessorPoolRepository
					.getById(profile.getSharedProPoolId());
			profile.setHmcSystemSharedProcessorPool(processorPool);
			return new BocloudResult(true, profile, "查询成功");
		} catch (Exception e) {
			logger.error("Query HMC partition profile detail fail:", e);
			return new BocloudResult(false, "查询失败", null, null);
		}
	}

	@Override
	public BocloudResult syncProfile(Long vendorId, Boolean isVios, HmcPartition partition) {
		try {
			List<HmcProfile> profiles = hmcProfileRepository.listByPartitionId(partition.getId());
			if (null == profiles) {
				logger.warn("Failed to get the profile by partitionId {}", partition.getId());
				return new BocloudResult(false, "概要文件不存在", null, null);
			}
			for (HmcProfile profile : profiles) {
				String path = HmcProfile.class.getSimpleName() + "_" + profile.getId();

				CloudVendor vendor = cloudVendorRepository.query(vendorId);
				BocloudResult result = profileDriver.getProfile(vendor.getUuid(), isVios, partition.getUuid(),
						profile.getUuid());
				if (result.isFailed()) {
					logger.error("Failed get partition profile.");
					return new BocloudResult(false, "获取概要文件失败", null, null);
				}
				LogicalPartitionProfileEntry entry = (LogicalPartitionProfileEntry) result.getData();
				hmcDatabaseHelper.updateLparProfile(profile, entry, null, null);
			}
			return new BocloudResult(true, "同步概要文件成功");
		} catch (Exception e) {
			logger.error("Failed sync partition profile:", e);
			return new BocloudResult(false, "同步概要文件失败", null, null);
		}
	}

	@Override
	public BocloudResult remove(Long id, RequestContext context) {
		String path = HmcProfile.class.getSimpleName() + "_" + id;
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			if (!lock.acquire(10, TimeUnit.SECONDS)) {
				logger.warn("Get BocloudLock time out!");
				return new BocloudResult(false, "请求超时");
			}
			HmcProfile profile = hmcProfileRepository.queryById(id);
			if (null == profile) {
				logger.warn("Partition profile data not exist, {}", id);
				return new BocloudResult(false, "数据不存在");
			}
			HmcPartition partition = hmcPartitionRepository.getById(profile.getPartitionId());
			if (null == partition) {
				logger.warn("Partition data not exist, {}", id);
				return new BocloudResult(false, "分区不存在");
			}
			HmcServer server = hmcServerRepository.getServerById(partition.getServerId());
			if (null == server) {
				logger.warn("HMC server data not exist, {}", partition.getServerId());
				return new BocloudResult(false, "服务器不存在");
			}
			CloudVendor vendor = cloudVendorRepository.query(server.getVendorId());
			if (null == vendor) {
				logger.error("get cloudVendor failure, {}", server.getVendorId());
				return new BocloudResult(false, "删除失败!");
			}
			taskExecutor.submit(() -> {
				String operate = vendor.getType().toLowerCase() + ".profile";
				String content = "HMC分区概要文件[" + profile.getName() + "]删除失败";
				try {
					ResourceEvent event = null;
					// 更新概要文件状态
					String oldStatus = profile.getStatus();
					profile.setStatus("PROFILE_DELETING");
					hmcProfileRepository.update(profile);

					CloudVendor vendor1 = cloudVendorRepository.query(server.getVendorId());

					boolean isVios = false;
					if ("Virtual IO Server".equals(partition.getPartitionType())) {
						isVios = true;
					}
					BocloudResult result = profileDriver.delete(vendor1.getUuid(), isVios, partition.getUuid(),
							profile.getUuid());
					if (result.isSuccess()) {
						hmcDatabaseHelper.removeProfile(profile.getId(), context.getTarget());
						event = new ResourceEvent(id, ServerAction.DELETE.name(), "成功", context.getTarget(), new Date(),
								"HMC概要文件", vendor.getId(), context.getCatalog());
						content = "HMC分区概要文件[" + profile.getName() + "]删除成功";
					} else {
						// 恢复概要文件状态
						profile.setStatus(oldStatus);
						hmcProfileRepository.update(profile);
						event = new ResourceEvent(id, ServerAction.DELETE.name(), "失败", context.getTarget(), new Date(),
								"HMC概要文件", vendor.getId(), context.getCatalog());
						content = "HMC分区概要文件[" + profile.getName() + "]删除失败";
					}
					resourceEventRepository.save(event);
					resourceEventPublisher.send(new OperateResult(result.isSuccess(), content, operate,
							MapTools.simpleMap("vendorId", vendor1.getId()), operateCategory, context));
				} catch (Exception e) {
					logger.error("删除HMC分区概要文件失败", e);
					resourceEventPublisher.send(new OperateResult(false, content, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				}
			});
			return new BocloudResult(true, "删除任务已下发，正在执行...");
		} catch (Exception e) {
			logger.error("remove HMC partition profile failure:", e);
			return new BocloudResult(false, "删除概要文件失败");
		}
	}

	@Override
	public BocloudResult modify(HmcProfile profile, RequestContext context) {
		Long profileId = profile.getId();
		String path = HmcProfile.class.getSimpleName() + "_" + profileId;
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			if (!lock.acquire(10, TimeUnit.SECONDS)) {
				logger.warn("Get BocloudLock time out!");
				return new BocloudResult(false, "请求超时");
			}
			HmcProfile oldProfile = hmcProfileRepository.queryById(profileId);
			if (null == oldProfile) {
				logger.warn("Partition profile data not exist, {}", profileId);
				return new BocloudResult(false, "ID为[" + profileId + "]的概要文件不存在");
			}
			HmcPartition partition = hmcPartitionRepository.getById(oldProfile.getPartitionId());
			if (null == partition) {
				logger.error("lpar {} does not exist!", oldProfile.getPartitionId());
				return new BocloudResult(false, "ID为[" + oldProfile.getPartitionId() + "]的逻辑分区不存在");
			}
			HmcServer server = hmcServerRepository.getServerById(partition.getServerId());
			if (null == server) {
				logger.error("HMC server {} does not exist!", partition.getServerId());
				return new BocloudResult(false, "ID为[" + partition.getServerId() + "]的HMC服务器不存在");
			}
			CloudVendor vendor = cloudVendorRepository.query(server.getVendorId());
			if (null == vendor) {
				logger.error("vendor {} does not exist!", server.getVendorId());
				return new BocloudResult(false, "ID为[" + server.getVendorId() + "]的云平台不存在");
			}

			// 获取虚拟交换机名称 2019-08-06 则不会创建虚拟以太网适配器，所以这里要增加非空判断
			if (profile.getHmcProfileNetworkAdapters() != null) {
				List<HmcProfileNetworkAdapter> hmcProfileNetworkAdapters = profile.getHmcProfileNetworkAdapters();
				for (HmcProfileNetworkAdapter hmcProfileNetworkAdapter : hmcProfileNetworkAdapters) {
					HmcVirtualSwitch hmcVirtualSwitch = hmcVirtualSwitchRepository
							.getBySwitchId(hmcProfileNetworkAdapter.getVirtualSwitchId(), server.getId());
					hmcProfileNetworkAdapter.setVirtualSwitchName(hmcVirtualSwitch.getName());
				}
			}

			// 查询分区使用的共享存储池hmc端的id
			if (profile.getSharedProPoolId() != null) {
				HmcSystemSharedProcessorPool sharedProcessorPool = sharedProcessorPoolRepository
						.getById(profile.getSharedProPoolId());
				profile.setSharedProPoolId(Long.valueOf(sharedProcessorPool.getHmcPoolId()));
			}
			// 启动一个线程从MQ上获取数据
			taskExecutor.submit(() -> {
				String operate = vendor.getType().toLowerCase() + ".profile";
				String content = "HMC概要文件[" + oldProfile.getName() + "]修改失败";
				try {
					ResourceEvent event = null;
					String vendorUuid = vendor.getUuid();

					// 更新概要文件状态
					String oldStatus = oldProfile.getStatus();
					oldProfile.setStatus("PROFILE_UPDATING");
					hmcProfileRepository.update(oldProfile);

					boolean isVios = false;
					if ("Virtual IO Server".equals(partition.getPartitionType())) {
						isVios = true;
					}
					// 如果开启了同步，先设置挂起同步
					if ("On".equals(partition.getCurrentProfileSync())) {
						if (isVios) {
							HmcPartition updateVios = new HmcPartition();
							updateVios.setCurrentProfileSync("Suspended");

							BocloudResult updateResult = partitionDriver.updateVirtualIOServer(vendorUuid, JSONObject
									.parseObject(JSONObject.toJSONString(updateVios), HmcPartitionModel.class),
									server.getUuid(), partition.getUuid());

							if (updateResult.isFailed()) {
								logger.error("Failed to update vios {} profileSync configuration {}",
										partition.getName(), updateResult);
								event = new ResourceEvent(partition.getId(), ServerAction.UPDATE.name(), "失败",
										context.getTarget(), new Date(), "HMC", vendor.getId(), context.getCatalog());
								content = "HMC VIOS[" + partition.getName() + "]的概要文件[" + oldProfile.getName()
										+ "]编辑失败，设置同步分区为挂起失败";
								resourceEventRepository.save(event);
								resourceEventPublisher.send(new OperateResult(updateResult.isSuccess(), content,
										operate, MapTools.simpleMap("vendorId", vendor.getId()), operateCategory,
										context));
								return;
							}
						} else {
							HmcPartition updateLpar = new HmcPartition();
							updateLpar.setCurrentProfileSync("Suspended");

							BocloudResult updateResult = partitionDriver.updateLogicalPartition(vendorUuid, JSONObject
									.parseObject(JSONObject.toJSONString(updateLpar), HmcPartitionModel.class),
									server.getUuid(), partition.getUuid());

							if (updateResult.isFailed()) {
								logger.error("Failed to update partition {} profileSync configuration {}",
										partition.getName(), updateResult);
								event = new ResourceEvent(partition.getId(), ServerAction.UPDATE.name(), "失败",
										context.getTarget(), new Date(), "HMC", vendor.getId(), context.getCatalog());
								content = "HMC逻辑分区[" + partition.getName() + "]的概要文件[" + oldProfile.getName()
										+ "]编辑失败，设置同步分区为挂起失败";
								resourceEventRepository.save(event);
								resourceEventPublisher.send(new OperateResult(updateResult.isSuccess(), content,
										operate, MapTools.simpleMap("vendorId", vendor.getId()), operateCategory,
										context));
								return;
							}
						}
						// 设置挂起同步成功，同步数据库信息
						partition.setCurrentProfileSync("Suspended");
						hmcPartitionRepository.update(partition);
					}
					BocloudResult result = profileDriver.update(vendor.getUuid(),
							JSONObject.parseObject(JSONObject.toJSONString(profile), HmcProfileModel.class), isVios,
							partition.getUuid(), oldProfile.getUuid());
					if (result.isSuccess()) {
						LogicalPartitionProfileEntry entry = (LogicalPartitionProfileEntry) result.getData();
						oldProfile.setServerId(partition.getServerId());
						hmcDatabaseHelper.updateLparProfile(oldProfile, entry, null, context.getTarget());
						event = new ResourceEvent(partition.getId(), ServerAction.UPDATE.name(), "成功",
								context.getTarget(), new Date(), "HMC", vendor.getId(), context.getCatalog());
						content = "HMC概要文件[" + oldProfile.getName() + "]编辑成功";
					} else {
						// 恢复概要文件状态
						oldProfile.setStatus(oldStatus);
						hmcProfileRepository.update(oldProfile);
						event = new ResourceEvent(partition.getId(), ServerAction.UPDATE.name(), "失败",
								context.getTarget(), new Date(), "HMC", vendor.getId(), context.getCatalog());
						content = "HMC概要文件[" + oldProfile.getName() + "]编辑失败：" + result.getMessage();
					}
					resourceEventRepository.save(event);
					resourceEventPublisher.send(new OperateResult(result.isSuccess(), content, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				} catch (Exception e) {
					logger.error("修改HMC概要文件失败", e);
					resourceEventPublisher.send(new OperateResult(false, content, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				}
			});
			return new BocloudResult(true, "修改任务已下发，正在执行...");
		} catch (Exception e) {
			logger.error("modify HMC partition profile failure:", e);
			return new BocloudResult(false, "修改概要文件失败");
		}
	}
}
