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.HmcVirtualFibreChannelMappingDriver;
import com.bocloud.cmp.model.hmc.HmcVirtualFibreChannelMappingModel;
import com.bocloud.cmp.model.hmc.sdk.module.VirtualIOServer;
import com.bocloud.cmp.model.hmc.sdk.module.VirtualIOServerEntry;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.common.enums.HmcPartitionStatus;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.hmc.HmcPartition;
import com.bocloud.ims.entity.resource.hmc.HmcPhysicalFibreChannel;
import com.bocloud.ims.entity.resource.hmc.HmcServer;
import com.bocloud.ims.entity.resource.hmc.HmcVirtualFibreChannelMapping;
import com.bocloud.ims.interfaces.resource.hmc.HmcPartitionService;
import com.bocloud.ims.interfaces.resource.hmc.HmcVirtualFibreChannelMappingService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
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 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.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 虚拟光纤通道service
 * 
 * @author luyanchao
 * @since 2018/12/18 17:18
 */
@Service("virtualFibreChannelMappingService")
public class HmcVirtualFibreChannelMappingServiceImpl implements HmcVirtualFibreChannelMappingService {

	private static final Logger logger = LoggerFactory.getLogger(HmcVirtualFibreChannelMappingServiceImpl.class);
	@Autowired
	private ThreadPoolTaskExecutor taskExecutor;
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private HmcServerRepository hmcServerRepository;
	@Autowired
	private HmcPartitionRepository hmcPartitionRepository;
	@Autowired
	private HmcPhysicalFibreChannelRepository hmcPhysicalFibreChannelRepository;
	@Autowired
	private HmcVirtualFibreChannelMappingRepository hmcVirtualFibreChannelMappingRepository;
	@Autowired
	private HmcDatabaseHelper hmcDatabaseHelper;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;
	@Autowired
	private HmcPartitionDriver partitionDriver;
	@Autowired
	private HmcVirtualFibreChannelMappingDriver virtualFibreChannelDriver;
	@Autowired
	private HmcPartitionService hmcPartitionService;
	@Autowired
	private HmcPartitionVfcAdapterRepository hmcPartitionVfcAdapterRepository;
	@Autowired
	private LockFactory lockFactory;

	@Override
	public BocloudResult create(long viosId, Integer viosSlot, boolean viosRequired, long partitionId,
			Integer partitionSlot, long fcId, RequestContext context) {
		return create(viosId, viosSlot, viosRequired, partitionId, partitionSlot, fcId, context, true);
	}

	/**
	 * 增加vmc映射
	 *
	 * @param viosId        数据库中vios的id
	 * @param viosSlot      vios插槽号
	 * @param partitionId   数据库中逻辑分区id
	 * @param partitionSlot 逻辑分区插槽号
	 * @param fcId          物理光纤通道id
	 */
	@Override
	public BocloudResult create(long viosId, Integer viosSlot, boolean viosRequired, long partitionId,
			Integer partitionSlot, long fcId, RequestContext context, boolean sendMsgWhenSucceed) {
		try {
			// 先检查数据一致性
			HmcPartition vios = hmcPartitionRepository.getById(viosId);
			if (vios == null) {
				return new BocloudResult(false, "映射失败，未找到vios");
			}
			HmcPartition partition = hmcPartitionRepository.getById(partitionId);
			if (partition == null) {
				return new BocloudResult(false, "映射失败，未找逻辑分区");
			}
			HmcServer hmcServer = hmcServerRepository.getServerById(vios.getServerId());
			if (hmcServer == null) {
				return new BocloudResult(false, "映射失败，未找vios所在的主机");
			}
			CloudVendor cloudVendor = cloudVendorRepository.query(hmcServer.getVendorId());
			if (cloudVendor == null) {
				return new BocloudResult(false, "映射失败，未找vios所在的平台");
			}
			HmcPhysicalFibreChannel physicalFibreChannel = hmcPhysicalFibreChannelRepository
					.get(HmcPhysicalFibreChannel.class, fcId);
			if (physicalFibreChannel == null) {
				return new BocloudResult(false, "映射失败，未找物理光纤通道");
			}
			if (physicalFibreChannel.getAvailablePorts() == 0) {
				return new BocloudResult(false, "映射失败，物理光纤通道不可用");
			}
			// 同步下数据库
			BocloudResult bocloudResult = partitionDriver.getPartitionAttribute(cloudVendor.getUuid(), vios.getUuid(),
					true);
			if (bocloudResult.isFailed()) {
				return new BocloudResult(false, bocloudResult.getMessage());
			}
			VirtualIOServer viosFromDriver = ((VirtualIOServerEntry) bocloudResult.getData()).getContent()
					.getVirtualIOServer();
			if (viosFromDriver == null) {
				return new BocloudResult(false, "映射失败，未找到vios");
			}
			hmcPartitionService.updateVirtualFibreChannelMappingForVios(vios,
					viosFromDriver.getVirtualFibreChannelMappings());
			// 保存数据
			HmcVirtualFibreChannelMapping mapping = new HmcVirtualFibreChannelMapping();
			mapping.setCmpPartitionId(partition.getId());
			mapping.setClientPartitionId(partition.getHmcPartitionId());
			mapping.setClientPartitionSlot(partitionSlot);
			mapping.setCmpViosId(vios.getId());
			mapping.setServerViosId(vios.getHmcPartitionId());
			mapping.setServerViosSlot(viosSlot);
			mapping.setServerRequired(viosRequired);
			mapping.setPhysicalUniqueDeviceId(physicalFibreChannel.getUniqueDeviceId());
			mapping.setPhysicalFibreChannelId(physicalFibreChannel.getId());
			mapping.setFcName(physicalFibreChannel.getName());
			hmcVirtualFibreChannelMappingRepository.save(mapping);
			// 启动一个线程去操作hmc服务器
			taskExecutor.submit(() -> {
				String msgPrefix = "创建" + vios.getName() + "虚拟光纤适配器"
						+ (mapping.getServerViosSlot() == null ? "" : "(" + mapping.getServerViosSlot() + ")");
				OperateResult result = new OperateResult(true, "", "vios.vfc.add",
						MapTools.simpleMap("vendorId", hmcServer.getVendorId()),
						OperateResult.OperateCategory.ResourceEvent, context);
				try (BocloudACLock bLock = lockFactory
						.getACLock("HMC." + cloudVendor.getId() + ".vios." + vios.getId() + ".vfc");) {
					if (!bLock.acquire(10, TimeUnit.SECONDS)) {
						result.setSuccess(false);
						result.setMessage(msgPrefix + "失败：当前VIOS已有其他虚拟机光纤适配器操作任务正在执行，请稍后再试！");
						resourceEventPublisher.send(result);
						return;
					}

					BocloudResult updateResult = virtualFibreChannelDriver.addVfcToVios(cloudVendor.getUuid(),
							hmcServer.getUuid(), vios.getUuid(), JSONObject.parseObject(
									JSONObject.toJSONString(mapping), HmcVirtualFibreChannelMappingModel.class));
					if (updateResult.isFailed()) {
						hmcVirtualFibreChannelMappingRepository.deleteMappingByViosIdAndSlot(viosId, viosSlot);
						result.setSuccess(false);
						result.setMessage(msgPrefix + "失败：" + updateResult.getMessage());
						resourceEventPublisher.send(result);
						return;
					}

					VirtualIOServerEntry viosEnrty = (VirtualIOServerEntry) updateResult.getData();
					hmcPartitionService.updateVirtualFibreChannelMappingForVios(vios,
							viosEnrty.getContent().getVirtualIOServer().getVirtualFibreChannelMappings());

					if (sendMsgWhenSucceed) {
						result.setSuccess(true);
						result.setMessage(msgPrefix + "成功");
						resourceEventPublisher.send(result);
					}
				} catch (Exception e) {
					logger.error(msgPrefix + "失败：", e);
					result.setSuccess(false);
					result.setMessage(msgPrefix + "失败：" + e.getMessage());
					resourceEventPublisher.send(result);
				}
			});
			return new BocloudResult(true, "任务已经下发，正在执行...");
		} catch (Exception e) {
			logger.error("映射失败", e);
			return new BocloudResult(false, "映射失败：" + e.getMessage());
		}
	}

	@Override
	public BocloudResult remove(long id, RequestContext context) {
		return remove(id, context, true);
	}

	/**
	 * 删除vmc映射
	 *
	 * @param id 映射id
	 * @return
	 */
	@Override
	public BocloudResult remove(long id, RequestContext context, boolean sendMsgWhenSuccess) {
		try {
			// 先检查数据一致性
			HmcVirtualFibreChannelMapping mapping = hmcVirtualFibreChannelMappingRepository
					.get(HmcVirtualFibreChannelMapping.class, id);
			if (mapping == null) {
				return new BocloudResult(false, "删除映射失败，未找到该映射信息");
			}
			if (mapping.getCmpPartitionId() != null) {
				HmcPartition lpar = hmcPartitionRepository.getById(mapping.getCmpPartitionId());
				if (lpar != null) {
					if (HmcPartitionStatus.Open_Firmware.getStatus().equals(lpar.getStatus())
							|| HmcPartitionStatus.Running.getStatus().equals(lpar.getStatus())
							|| HmcPartitionStatus.Starting.getStatus().equals(lpar.getStatus())) {
						return new BocloudResult(false, "删除失败，请先将该逻辑分区关机");
					}
				}
			}
			HmcPartition vios = hmcPartitionRepository.getById(mapping.getCmpViosId());
			if (vios == null) {
				return new BocloudResult(false, "删除映射失败，未找到vios");
			}
			HmcServer hmcServer = hmcServerRepository.getServerById(vios.getServerId());
			if (hmcServer == null) {
				return new BocloudResult(false, "删除映射失败，未找vios所在的主机");
			}
			CloudVendor cloudVendor = cloudVendorRepository.query(hmcServer.getVendorId());
			if (cloudVendor == null) {
				return new BocloudResult(false, "删除映射失败，未找vios所在的平台");
			}
			// 同步下数据库
			BocloudResult bocloudResult = partitionDriver.getPartitionAttribute(cloudVendor.getUuid(), vios.getUuid(),
					true);
			if (bocloudResult.isFailed()) {
				return new BocloudResult(false, bocloudResult.getMessage());
			}
			VirtualIOServer viosFromDriver = ((VirtualIOServerEntry) bocloudResult.getData()).getContent()
					.getVirtualIOServer();
			if (viosFromDriver == null) {
				return new BocloudResult(false, "删除映射失败，未找到vios");
			}
			hmcPartitionService.updateVirtualFibreChannelMappingForVios(vios,
					viosFromDriver.getVirtualFibreChannelMappings());
			// 创建一个线程到hmc服务器删除该vfc映射
			taskExecutor.submit(() -> {

				String msgPrefix = "删除" + vios.getName() + "虚拟光纤适配器(" + mapping.getServerViosSlot() + ")";
				OperateResult result = new OperateResult(true, "", "vios.vfc.remove",
						MapTools.simpleMap("vendorId", hmcServer.getVendorId()),
						OperateResult.OperateCategory.ResourceEvent, context);
				try (BocloudACLock bLock = lockFactory
						.getACLock("HMC." + cloudVendor.getId() + ".vios." + vios.getId() + ".vfc")) {
					if (!bLock.acquire(10, TimeUnit.SECONDS)) {
						result.setSuccess(false);
						result.setMessage(msgPrefix + "失败：当前VIOS已有其他虚拟机光纤适配器操作任务正在执行，请稍后再试！");
						resourceEventPublisher.send(result);
						return;
					}

					BocloudResult updateResult = virtualFibreChannelDriver.removeVfcFromVios(cloudVendor.getUuid(),
							hmcServer.getUuid(), vios.getUuid(), JSONObject.parseObject(
									JSONObject.toJSONString(mapping), HmcVirtualFibreChannelMappingModel.class));
					if (updateResult.isFailed()) {
						result.setSuccess(false);
						result.setMessage(msgPrefix + "失败：" + updateResult.getMessage());
						resourceEventPublisher.send(result);
					}
					hmcVirtualFibreChannelMappingRepository.deleteMappingByViosIdAndSlot(mapping.getCmpViosId(),
							mapping.getServerViosSlot());
					if (sendMsgWhenSuccess) {
						result.setSuccess(true);
						result.setMessage(msgPrefix + "成功");
						resourceEventPublisher.send(result);
					}
				} catch (Exception e) {
					logger.error(msgPrefix + "失败：", e);
					result.setSuccess(false);
					result.setMessage(msgPrefix + "失败：" + e.getMessage());
					resourceEventPublisher.send(result);
				}
			});
			return new BocloudResult(true, "任务已经下发，正在执行...");
		} catch (Exception e) {
			logger.error("删除映射失败", e.getMessage());
			return new BocloudResult(false, "映射失败" + e.getMessage());
		}
	}

	/**
	 * 查询vfc映射列表
	 *
	 * @param page   当前页码
	 * @param rows   页面数据大小
	 * @param params 查询参数
	 * @param sorter 排序参数，例如：{"name":0|1,"password":0|1},0表示增序，1表示降序
	 * @param simple 简单查询标记，只有true和false,为false时返回资源池的详细信息，为true时只返回id和name值
	 * @return 查询结果集
	 */
	@Override
	public BocloudResult<GridBean<HmcVirtualFibreChannelMapping>> list(Pager pager) {
		try {
			int page = pager.getPage();
			int rows = pager.getRows();
			List<Param> params = pager.getParams();
			Map<String, String> sorter = pager.getSorter();
			boolean simple = pager.getSimple();

			int total = hmcVirtualFibreChannelMappingRepository.count(params);
			List list = hmcVirtualFibreChannelMappingRepository.list(page, rows, params, sorter);
			GridBean gridBean = GridHelper.getBean(page, rows, total, list);
			return new BocloudResult(true, gridBean, "查询成功");
		} catch (Exception e) {
			logger.error("查询失败：", e);
			return new BocloudResult(false, "查询失败");
		}
	}

	/**
	 * 按viosId查询所有vfc映射
	 *
	 * @param viosId viosId
	 * @return 查询结果集
	 */
	@Override
	public BocloudResult<List<HmcVirtualFibreChannelMapping>> listByVios(long viosId) {
		try {
			List<HmcVirtualFibreChannelMapping> mappings = hmcVirtualFibreChannelMappingRepository.listByViosId(viosId);
			return new BocloudResult(true, mappings, "查询成功");
		} catch (Exception e) {
			return new BocloudResult(false, "查询失败");
		}
	}

	// 2019-08-15 : 去掉自动获取槽号的相关功能，这个接口不需要了。暂时屏蔽。 详见PBSM-5039
	/**
	 * 查询vois中已经配置了但在lpar中尚未配置的vfc映射。</br>
	 * 如果不存在就直接返回vios和lpar的空闲的槽号
	 *
	 * @param viosId      viosId
	 * @param partitionId partitionId
	 * @return 查询结果集
	 */
//    @Override
//    public BocloudResult listByViosAndPartition(long viosId, long partitionId, List<Integer> slots) {
//        try {
//            List<HmcVirtualFibreChannelMapping> viosMappings = hmcVirtualFibreChannelMappingRepository.listByViosAndPartition(viosId, partitionId);
//            List<HmcPartitionVfcAdapter> lparMappings = hmcPartitionVfcAdapterRepository.listByViosAndPartition(viosId, partitionId);
//
//            List<HmcVirtualFibreChannelMapping> surplusMappings = new ArrayList<>();
//            for (HmcVirtualFibreChannelMapping viosMapping : viosMappings) {
//                boolean finded = false;
//                for (HmcPartitionVfcAdapter lparMapping : lparMappings) {
//                    if (lparMapping.getClientPartitionSlot().equals(viosMapping.getClientPartitionSlot()) &&
//                            lparMapping.getServerViosSlot().equals(viosMapping.getServerViosSlot())) {
//                        finded = true;
//                        break;
//                    }
//                }
//                if (finded) {
//                    surplusMappings.add(viosMapping);
//                }
//            }
//            if (surplusMappings.size() == 0) {
//                HmcVirtualFibreChannelMapping mapping = new HmcVirtualFibreChannelMapping();
//                mapping.setClientPartitionSlot(hmcPartitionRepository.getEmptyAdapterId(partitionId, slots));
//                mapping.setServerViosSlot(hmcPartitionRepository.getEmptyAdapterId(viosId, slots));
//                surplusMappings.add(mapping);
//            }
//            return new BocloudResult(true, surplusMappings, "查询成功");
//        } catch (Exception e) {
//            return new BocloudResult(false, "查询失败");
//        }
//    }
}
