package com.bocloud.ims.service.utils.excel;

import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.model.RequestContext.Catalog;
import com.bocloud.ims.entity.resource.DataCenter;
import com.bocloud.ims.entity.resource.File;
import com.bocloud.ims.entity.resource.Rack;
import com.bocloud.ims.entity.resource.Room;
import com.bocloud.ims.entity.resource.ServerProperty;
import com.bocloud.ims.entity.resource.ServerPropertyValue;
import com.bocloud.ims.interfaces.resource.ServerService;
import com.bocloud.ims.repository.resource.DataCenterRepository;
import com.bocloud.ims.repository.resource.FileRepository;
import com.bocloud.ims.repository.resource.RackRepository;
import com.bocloud.ims.repository.resource.RoomRepository;
import com.bocloud.ims.service.utils.ExcelHelper;
import com.bocloud.ims.service.utils.ExcelHelper.EnumHelper;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.sf.json.JSONObject;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 导入数据处理
 *
 * @author liuyuanyuan
 */
public class ImportHelper {
	private static Logger logger = LoggerFactory.getLogger(ImportHelper.class);
	private ServerService serverService;
	private FileRepository fileRepository;
//	private AmqpTemplate amqpTemplate;
	private DataCenterRepository dataCenterRepository;
	private RoomRepository roomRepository;
	private RackRepository rackRepository;
	private ArrayList<ServerProperty> serverPropertys = new ArrayList<>();
//	private static final String EXCHANGE = BoCloudExchange.Default.TOPIC.getExchange();
//	private static final String PROGRESS_QUEUE = "bocloud.message.queue.#";
	// 上一次刷新进度 优化发送进度频率
	double progressLast = 0L;
	// 每条新增请求的中数据中心和机房信息
	private String basicDataCenter;
	private String basicRoom;
	private ResourceEventPublisher resourceEventPublisher;

	public ImportHelper() {
		super();
	}

	public ImportHelper(ServerService serverService, FileRepository fileRepository, AmqpTemplate amqpTemplate,
			DataCenterRepository dataCenterRepository, RoomRepository roomRepository, RackRepository rackRepository,
			ResourceEventPublisher resourceEventPublisher) {
		super();
		this.serverService = serverService;
		this.fileRepository = fileRepository;
//		this.amqpTemplate = amqpTemplate;
		this.dataCenterRepository = dataCenterRepository;
		this.roomRepository = roomRepository;
		this.rackRepository = rackRepository;
		this.resourceEventPublisher = resourceEventPublisher;
	}

	/**
	 * 处理excel
	 *
	 * @param wb
	 * @param uuid
	 * @param userId
	 * @param fileName
	 * @throws Exception
	 */
	public void readExcel(Workbook wb, String uuid, Long userId, String fileName, String instance) {
		boolean validateSuccess = true;
		// 校验模板
		try {
			validataWb(wb);
		} catch (Exception e) {
			validateSuccess = false;
			// 醒目提示 第一行第一个单元格加宽
			wb.getSheetAt(0).setColumnWidth(0, 20000);
			ExcelHelper.promptErrorMessage(wb, e.getMessage());
		}
		int total = countDataNum(wb);
		int success = 0;
		int fail = 0;
		if (validateSuccess) {
			// 遍历sheet页
			for (int n = 0; n < wb.getNumberOfSheets(); n++) {
				Sheet sheet = wb.getSheetAt(n);
				if (sheet.getSheetName().equals(EnumHelper.Schedulevalue.getValue())) {
					continue;
				}
				String moduleCode = ExcelHelper.regexSheetModuleCode(sheet.getSheetName());
				if ("".equals(BoStringUtils.toString(moduleCode))
						|| !"SERVER".equals(BoStringUtils.toString(moduleCode))) {
					continue;
				}
				try {
					// 属性
					Map<String, Integer> header = ExcelHelper.readHeader4NameKey(sheet.getRow(1));
					// 属性id：表头索引
					Map<Integer, ServerProperty> headerProperty = Maps.newHashMap();
					// 添加属性配置
					for (ServerProperty property : serverPropertys) {
						// 转换下拉值
						if ("assetCategory".equals(property.getCode())) {
							HashMap<String, String> assetCategorys = Maps.newHashMap();
							assetCategorys.put("大型机", "MAINFRAME");
							assetCategorys.put("小型机", "MINICOMPUTER");
							assetCategorys.put("PC", "PC");
							assetCategorys.put("X86服务器", "X86_SERVER");
							property.setConversion(assetCategorys);
						}
						if ("environment".equals(property.getCode())) {
							HashMap<String, String> environments = Maps.newHashMap();
							environments.put("开发环境", "DEVELOP");
							environments.put("测试环境", "TEST");
							environments.put("生产环境", "PRODUCTION");
							environments.put("预发环境", "READY");
							environments.put("UAT环境", "UAT");
							property.setConversion(environments);
						}
						Integer index = header.get(property.getName());
						if (index > 0) {
							headerProperty.put(index, property);
						}
					}
					// 表头单元格开始位置，依次每行按照其索引位置开始读取
					int startIndex = 1;
					// 最后一列数
					int lastColumn = header.size() + startIndex;
					// 遍历行
					for (int i = 3; i <= sheet.getLastRowNum(); i++) {
						Row row = sheet.getRow(i);
						if (row == null) {
							throw new Exception("请检查第" + (i + 1) + "行下面是否有干扰数据。");
						}
						List<ServerPropertyValue> serverPropertyValues = Lists.newArrayList();
						String messageTemp = "";
						// 组装属性值
						for (int j = startIndex; j < lastColumn; j++) {
							try {
								setInstanceValue(j, row, headerProperty, serverPropertyValues);
							} catch (Exception e) {
								logger.info("导入错误", e);
								messageTemp += e.getMessage();
								continue;
							}
						}
						if (BoStringUtils.isNotEmpty(messageTemp)) {
							ExcelHelper.promptErrorMessage(row, messageTemp);
							fail++;
							processMessage(uuid, userId, total, success, fail, null);
							continue;
						}
						// 拼接请求参数
						HashMap<String, Object> serverMaps = Maps.newHashMap();
						HashMap<String, Object> serverAssetMaps = Maps.newHashMap();
						HashMap<String, Object> serverConfigMaps = Maps.newHashMap();
						for (ServerPropertyValue serverPropertyValue : serverPropertyValues) {
							String groupName = serverPropertyValue.getGroupName();
							switch (groupName) {
							case "基本信息":
								if ("dcId".equals(serverPropertyValue.getCode())
										|| "roomId".equals(serverPropertyValue.getCode())
										|| "rackId".equals(serverPropertyValue.getCode())) {
									serverMaps.put(serverPropertyValue.getCode(),
											Long.parseLong(serverPropertyValue.getValue()));
								} else {
									serverMaps.put(serverPropertyValue.getCode(), serverPropertyValue.getValue());
								}
								break;
							case "资产信息":
								serverAssetMaps.put(serverPropertyValue.getCode(), serverPropertyValue.getValue());
								break;
							case "配置信息":
								serverConfigMaps.put(serverPropertyValue.getCode(), serverPropertyValue.getValue());
								break;
							default:
								break;
							}
						}
						serverMaps.put("assets", serverAssetMaps);
						serverMaps.put("config", serverConfigMaps);
						JSONObject jsonObject = JSONObject.fromObject(serverMaps);
						BocloudResult result = serverService.create(jsonObject.toString(),
								new RequestContext(Catalog.Manager, userId, 0L, null, null), true, instance);
						if (result.isSuccess()) {
							success++;
							ExcelHelper.promptErrorMessage(row, "新增成功。");
						} else {
							fail++;
							ExcelHelper.promptErrorMessage(row, "新增失败，" + result.getMessage());
						}
						processMessage(uuid, userId, total, success, fail, null);
					}

				} catch (Exception e) {
					total = 0;
					wb.getSheetAt(0).setColumnWidth(0, 20000);
					ExcelHelper.promptErrorMessage(wb, e.getMessage());
					logger.info("导入错误", e);
				}
			}
		}
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			wb.write(baos);
			byte[] bytes = baos.toByteArray();
			// 文件保存至数据库
			File file = new File();
			file.setCreatorId(userId);
			file.setFileName(fileName);
			file.setUuid(uuid);
			file.setGmtCreate(new Date());
			file.setBytesForFile(bytes);
			fileRepository.save(file);
			if (validateSuccess) {
				processMessage(uuid, userId, total, success, fail, uuid);
			} else {
				processMessage(uuid, userId, 0, 0, 0, uuid);
			}
		} catch (Exception e) {
			logger.info("保存文件错误", e);
		} finally {
			if (baos != null) {
				try {
					baos.close();
					wb.close();
				} catch (IOException e) {
					logger.info("关闭流错误", e);
				}
			}
		}
	}

	private void processMessage(String uuid, Long userId, int total, int success, int fail, String fileUuid) {
		Map<String, Object> messageMap = Maps.newHashMap();
		messageMap.put("total", total);
		if (total == 0) {
			messageMap.put("progress", 100);
			messageMap.put("success", 0);
			messageMap.put("fail", "fail");
			messageMap.put("fileName", fileUuid);
		} else {
			BigDecimal progress = new BigDecimal(success + fail).multiply(new BigDecimal(100))
					.divide(new BigDecimal(total), 10, RoundingMode.HALF_DOWN);
			double progresses = Math.floor(progress.doubleValue());
			messageMap.put("progress", progresses);
			// 进度没变的话 就不推送了
			if (progresses == progressLast && progresses != 100) {
				return;
			}
			progressLast = progresses;
			messageMap.put("success", success);
			messageMap.put("fail", fail);
			messageMap.put("fileName", fileUuid);
		}
		messageMap.put("uuid", uuid);
		RequestContext context = new RequestContext();
		context.setTarget(userId);
		context.setCatalog(Catalog.Manager);
		context.setTarget(0L);
		HashMap<String, Object> resultMap = Maps.newHashMap();
		resultMap.put("operate", "SERVERIMPORT");
		resultMap.put("success", true);
		resultMap.put("context", context);
		resultMap.put("data", messageMap);
		resultMap.put("category", "server");
		// 将执行结果写入mq 由web写入websock给前端
		resourceEventPublisher.send(resultMap);
	}

	/**
	 * 导入时 拼装属性值
	 *
	 * @param index
	 * @param row
	 * @param headerProperty
	 * @throws Exception
	 */
	private void setInstanceValue(int index, Row row, Map<Integer, ServerProperty> headerProperty,
			List<ServerPropertyValue> serverPropertyValues) throws Exception {
		Cell cell = row.getCell(index);
		// 拿到每行数据的前两列用作后面查询数据中心，机房，机柜备用
		if (1 == index) {
			basicDataCenter = ExcelHelper.getCellValue(cell);
		}
		if (2 == index) {
			basicRoom = ExcelHelper.getCellValue(cell);
		}
		ServerProperty property = headerProperty.get(new Integer(index));
		// 当前单元格属性
		if (property != null) {
			setPropertyValue(serverPropertyValues, cell, property, basicDataCenter, basicRoom);
			return;
		}
	}

	/**
	 * 拼装值内容
	 *
	 * @param serverPropertyValues
	 * @param cell
	 * @param property
	 * @throws Exception
	 */
	private void setPropertyValue(List<ServerPropertyValue> serverPropertyValues, Cell cell, ServerProperty property,
			String dc, String rm) throws Exception {
		ServerPropertyValue serverPropertyValue = new ServerPropertyValue();
		serverPropertyValue.setGroupName(property.getGroupName());
		serverPropertyValue.setName(property.getName());
		serverPropertyValue.setCode(property.getCode());
		String cellValue = ExcelHelper.getCellValue(cell);
		if (BoStringUtils.isEmpty(cellValue)) {
			if (property.isRequired()) {
				throw new Exception("配置[" + property.getName() + "]为必填值，请检查;");
			} else {
				serverPropertyValue.setValue(null);
				return;
			}
		}
		// 处理excel传入的下拉值，转换为存储在数据库中的值
		if ("assetCategory".equals(property.getCode()) || "environment".equals(property.getCode())) {
			Map<String, String> conversion = property.getConversion();
			cellValue = conversion.get(cellValue);
		}
		// 处理基本信息中三级联动的信息,将excel导入的信息转换成存在数据库中的id
		if ("dc".equals(property.getCode())) {
			// 去除数据多余的"_"标记
			if (cellValue.charAt(0) == '_') {
				cellValue = cellValue.substring(1);
			}
			DataCenter dataCenter = dataCenterRepository.queryByName(cellValue);
			if (null == dataCenter) {
				throw new Exception("数据中心[" + cellValue + "]不存在，请检查是否为最新模板。");
			}
			serverPropertyValue.setCode("dcId");
			basicDataCenter = String.valueOf(dataCenter.getId());
			cellValue = basicDataCenter;
		}
		if ("room".equals(property.getCode())) {
			// 去除数据多余的"_"标记
			if (cellValue.charAt(0) == '_') {
				cellValue = cellValue.substring(1);
			}
			Room room = roomRepository.queryByName(cellValue, Long.parseLong(dc));
			if (null == room) {
				throw new Exception("机房[" + cellValue + "]不存在，请检查是否为最新模板。");
			}
			serverPropertyValue.setCode("roomId");
			basicRoom = String.valueOf(room.getId());
			cellValue = basicRoom;
		}
		if ("rack".equals(property.getCode())) {
			Rack rack = rackRepository.queryByName(cellValue, Long.parseLong(rm));
			if (null == rack) {
				throw new Exception("机柜[" + cellValue + "]不存在，请检查是否为最新模板。");
			}
			serverPropertyValue.setCode("rackId");
			cellValue = String.valueOf(rack.getId());
		}
		serverPropertyValue.setValue(cellValue);
		serverPropertyValues.add(serverPropertyValue);
	}

	/**
	 * 统计数据量
	 *
	 * @param wb
	 * @return
	 */
	private int countDataNum(Workbook wb) {
		int total = 0;
		for (int n = 0; n < wb.getNumberOfSheets(); n++) {
			Sheet sheet = wb.getSheetAt(n);
			if (sheet.getSheetName().equals(EnumHelper.Schedulevalue.getValue())) {
				continue;
			}
			String moduleCode = ExcelHelper.regexSheetModuleCode(sheet.getSheetName());
			if ("".equals(BoStringUtils.toString(moduleCode))) {
				continue;
			}
			if ("SERVER".equals(BoStringUtils.toString(moduleCode))) {
				total += ExcelHelper.getRowNumberOfSheet(sheet) - 3;
			}
		}
		return total;

	}

	/**
	 * 校验文件内容是否合格
	 *
	 * @param wb
	 * @throws Exception
	 */
	private void validataWb(Workbook wb) throws Exception {
		for (int n = 0; n < 1; n++) {
			Sheet sheet = wb.getSheetAt(n);
			if (sheet.getSheetName().equals(EnumHelper.Schedulevalue.getValue())) {
				continue;
			}
			String moduleCode = ExcelHelper.regexSheetModuleCode(sheet.getSheetName());
			if ("".equals(moduleCode)) {
				ExcelHelper.promptErrorMessage(wb, "无效sheet页[" + sheet.getSheetName() + "],请使用标准模板。");
			}
			if (!"SERVER".equals(moduleCode)) {
				ExcelHelper.promptErrorMessage(wb, "无效sheet名[" + sheet.getSheetName() + "],请使用标准模板。");
				continue;
			}
			// 属性
			Map<String, Integer> header = ExcelHelper.readHeader4NameKey(sheet.getRow(1));
			serverPropertys = ExportHelper.fillHeaderOfServer();
			validateHeader(header, serverPropertys);
		}
	}

	/**
	 * 验证表头
	 *
	 * @param header
	 * @param serverPropertys
	 * @throws Exception
	 */
	private void validateHeader(Map<String, Integer> header, ArrayList<ServerProperty> serverPropertys)
			throws Exception {
		Set<String> propSetImport = new HashSet<String>();
		Set<String> propSetNew = new HashSet<String>();
		for (String name : header.keySet()) {
			propSetImport.add(name);
		}
		for (ServerProperty serverProperty : serverPropertys) {
			propSetNew.add(serverProperty.getName());
		}
		Set<String> propSetImportTemp = new HashSet<String>();
		Set<String> propSetNewTemp = new HashSet<String>();
		propSetImportTemp.addAll(propSetImport);
		propSetNewTemp.addAll(propSetNew);

		propSetImportTemp.removeAll(propSetNew);
		if (!propSetImportTemp.isEmpty()) {
			throw new Exception("表头中不应该包含无用信息：" + propSetImportTemp.toString());
		}
		propSetNewTemp.removeAll(propSetImport);
		if (!propSetNewTemp.isEmpty()) {
			throw new Exception("表头中缺少以下属性：" + propSetNewTemp.toString());
		}
	}

}
