package cn.superdesk.uniorder.common.excel.strategy.postil;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.superdesk.uniorder.common.enums.common.CommonProcessResultEnum;
import cn.superdesk.uniorder.common.excel.model.ExcelRow;
import cn.superdesk.uniorder.common.excel.validate.HibernateValidator;
import cn.superdesk.uniorder.common.excel.validate.group.RelationCheckFromCacheRedis;
import cn.superdesk.uniorder.common.json.AnJsonUtils;
import cn.superdesk.uniorder.common.mq.redis.dto.ExcelImportTaskMessage;
import cn.superdesk.uniorder.common.mq.redis.producer.MessageProducerService;
import cn.superdesk.uniorder.common.security.dto.LoginSysUser;
import cn.superdesk.uniorder.common.spring.SpringContextHolder;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.write.handler.AbstractRowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;

import javax.validation.ConstraintViolation;
import javax.validation.groups.Default;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 自定义拦截器-注解定义, Excel Cell 批注反馈
 * 校验模式:
 * 1. 快速失败返回模式
 * 2. 普通模式
 * 反馈模式:
 * 1. 批注-错误的Cell
 * 2. 行尾-添加两列[结果,报告]
 * @Author: linjinyu
 * @CreateDate: 2021/8/24 上午11:14
 * @UpdateUser: linjinyu
 * @UpdateDate: 2021/8/24 上午11:14
 * @UpdateRemark: 修改内容
 * @Version: 1.0
 */
@Slf4j
@Builder(toBuilder = true)
@NoArgsConstructor
public class ExcelImportExportAddPostilRowWriteHandler<T extends ExcelRow> extends AbstractRowWriteHandler {

	private Class<T> headClazz;
	private T        instance;


	// Declared fields
	// private Field[]  fields; // 弃用
	private Map<Integer, Field>  sortedAllFiledMap;
	private Map<Integer, Field>  indexFiledMap;
	private Map<String, Integer> filedIndexMap;

	/**
	 * Fields ignored
	 */
	private Map<String, Field> ignoreMap;

	private Boolean convertAllFiled;
	private Boolean needIgnore;

	private String batchId;

	private MessageProducerService messageProducerService;

	private LoginSysUser loginSysUser;

	public ExcelImportExportAddPostilRowWriteHandler(Class<T> headClazz,
	                                                 T instance,
	                                                 Map<Integer, Field> sortedAllFiledMap,
	                                                 Map<Integer, Field> indexFiledMap,
	                                                 Map<String, Integer> filedIndexMap,
	                                                 Map<String, Field> ignoreMap,
	                                                 Boolean convertAllFiled,
	                                                 Boolean needIgnore,
	                                                 String batchId,
	                                                 MessageProducerService messageProducerService,
	                                                 LoginSysUser loginSysUser) {
		this.headClazz = headClazz;
		this.instance = instance;

		this.sortedAllFiledMap = sortedAllFiledMap;
		this.indexFiledMap = indexFiledMap;
		this.filedIndexMap = filedIndexMap;
//		this.sortedAllFiledMap = new TreeMap<Integer, Field>();
//		this.indexFiledMap = new TreeMap<Integer, Field>();
//		this.filedIndexMap = new HashMap<>();

//		this.ignoreMap = ignoreMap;
		this.ignoreMap = new HashMap<String, Field>(16);

		this.convertAllFiled = convertAllFiled;
		this.needIgnore = needIgnore;
//		this.convertAllFiled = false;
//		this.needIgnore = false;

		this.batchId = batchId;

//		this.messageProducerService = messageProducerService;
		this.messageProducerService = SpringContextHolder.getBean(MessageProducerService.class);

		this.loginSysUser = loginSysUser;

		ClassUtils.declaredFields(headClazz, sortedAllFiledMap, indexFiledMap, ignoreMap, convertAllFiled, needIgnore, null);

//		log.info("ExcelImportExportAddPostilRowWriteHandler\n{}\n{}", JsonUtils.toJson(sortedAllFiledMap), JsonUtils.toJson(indexFiledMap));

		for (Map.Entry<Integer, Field> entry : sortedAllFiledMap.entrySet()) {
			filedIndexMap.put(sortedAllFiledMap.get(entry.getKey()).getName(), entry.getKey());
		}
	}


	@Override
	public void beforeRowCreate(WriteSheetHolder writeSheetHolder,
	                            WriteTableHolder writeTableHolder,
	                            Integer rowIndex,
	                            Integer relativeRowIndex,
	                            Boolean isHead) {

//		ClassUtils.declaredFields(headClazz, sortedAllFiledMap, indexFiledMap, ignoreMap, convertAllFiled, needIgnore, writeSheetHolder);
//		log.info("ExcelImportExportAddPostilRowWriteHandler\nbeforeRowCreate\n{}", JsonUtils.toJson(sortedAllFiledMap));

	}

	@Override
	public void afterRowCreate(WriteSheetHolder writeSheetHolder,
	                           WriteTableHolder writeTableHolder,
	                           Row row,
	                           Integer relativeRowIndex,
	                           Boolean isHead) {

	}

	/**
	 * 作为注入部分
	 *
	 * @param writeSheetHolder
	 * @param writeTableHolder
	 * @param row
	 * @param relativeRowIndex
	 * @param isHead
	 */
	@Override
	public void afterRowDispose(WriteSheetHolder writeSheetHolder,
	                            WriteTableHolder writeTableHolder,
	                            Row row /* POI的东西 */,
	                            Integer relativeRowIndex,
	                            Boolean isHead) {

		log.info("[{}] start", relativeRowIndex);

		// 如果false 就读取行数据.
		if (!isHead) {

			StringBuffer reportStrBuf = new StringBuffer();

			Sheet sheet = writeSheetHolder.getSheet();

			instance = ReflectUtil.newInstance(headClazz);

//			Method[] methods = ReflectUtil.getMethods(headClazz);
//			log.info("SSSS {}", methods);


			/****************************************** 转换校验 start ******************************************/
			int cellIndex = 0;
			for (Map.Entry<Integer, Field> entry : indexFiledMap.entrySet()) {

				cellIndex = entry.getKey();

//				log.info("[{}][{}] start", relativeRowIndex, cellIndex);

				Cell cell = row.getCell(cellIndex);

//				log.info("说是 Field Type [{}][{}] | {}", relativeRowIndex, cellIndex, indexFiledMap.get(cellIndex).getGenericType().toString());
				try {
					switch (indexFiledMap.get(cellIndex).getGenericType().toString()) {
						case "class java.util.Date":


//							log.info("Field Type [{}][{}] | type {} | value {}", relativeRowIndex, cellIndex, indexFiledMap.get(cellIndex).getGenericType().toString(), cell.getStringCellValue());
							if (StrUtil.isNotBlank(cell.getStringCellValue())) {
								ReflectUtil.setFieldValue(instance, indexFiledMap.get(cellIndex).getName(), DateUtil.parseDate(cell.getStringCellValue()));
							}
							break;
						case "class java.lang.String":
//							log.info("Field Type [{}][{}] | type {} | value {}", relativeRowIndex, cellIndex, indexFiledMap.get(cellIndex).getGenericType().toString(), cell.getStringCellValue());
							ReflectUtil.setFieldValue(instance, indexFiledMap.get(cellIndex).getName(), cell.getStringCellValue());
							break;
						case "class java.math.BigDecimal":
//							log.info("Field Type [{}][{}] | type {} | value {}", relativeRowIndex, cellIndex, indexFiledMap.get(cellIndex).getGenericType().toString(), cell.getNumericCellValue());
							ReflectUtil.setFieldValue(instance, indexFiledMap.get(cellIndex).getName(), cell.getNumericCellValue());
							break;
					}

				} catch (java.lang.IllegalArgumentException iae) {
					setReport(reportStrBuf, sheet, relativeRowIndex, cellIndex, iae.getMessage());
					setPostil(sheet, relativeRowIndex, cellIndex, iae.getMessage());
				} catch (Exception e) {
					setReport(reportStrBuf, sheet, relativeRowIndex, cellIndex, e.getMessage());
					setPostil(sheet, relativeRowIndex, cellIndex, e.getMessage());
				}


			}

//			log.info("instance {}", JsonUtils.toJsonIgnoreNullField(instance));


			/****************************************** 基础校验 start ******************************************/
			List<String> validateReport001 = null;
			Set<ConstraintViolation<T>> validateSet001 = HibernateValidator
					.getValidatorModeFailFastFalse()
					.validate(instance, Default.class);

			if (validateSet001 != null && !validateSet001.isEmpty()) {
//				ConstraintViolation<T> constraint = validateSet.stream().findAny().orElse(null);
//				log.info("SSSS {}", constraint.getMessage());

				validateReport001 = validateSet001.stream()
						.map(c -> c.getPropertyPath() + "#" + c.getMessage())
						.collect(Collectors.toList());
//				log.info("SSSS {}", validateReport001);

			}

			if (CollUtil.isNotEmpty(validateReport001)) {
				validateReport001.forEach(item -> {
					String[] msg = item.split("#");
					setReport(reportStrBuf, sheet, relativeRowIndex, filedIndexMap.get(msg[0]), msg[1]);
					setPostil(sheet, relativeRowIndex, filedIndexMap.get(msg[0]), msg[1]);
				});
			}

			/****************************************** 关联校验 start ******************************************/

			List<String> validateReport002 = null;
			Set<ConstraintViolation<T>> validateSet002 = HibernateValidator
					.getValidatorModeFailFastFalse()
					.validate(instance, RelationCheckFromCacheRedis.class);

			log.info("validateSet002 {}", validateSet002);

			if (validateSet002 != null && !validateSet002.isEmpty()) {
//				ConstraintViolation<T> constraint = validateSet.stream().findAny().orElse(null);
//				log.info("SSSS {}", constraint.getMessage());

				validateReport002 = validateSet002.stream()
						.map(c -> c.getPropertyPath() + "#" + c.getMessage())
						.collect(Collectors.toList());
//				log.info("SSSS {}", validateReport002);

			}

			if (CollUtil.isNotEmpty(validateReport002)) {
				validateReport002.forEach(item -> {
					String[] msg = item.split("#");
					setReport(reportStrBuf, sheet, relativeRowIndex, filedIndexMap.get(msg[0]), msg[1]);
					setPostil(sheet, relativeRowIndex, filedIndexMap.get(msg[0]), msg[1]);
				});
			}


			/****************************************** 反馈结果 start ******************************************/

			if (StrUtil.isNotBlank(reportStrBuf.toString())) {

				row.createCell(indexFiledMap.size() + 0, CellType.STRING).setCellValue(CommonProcessResultEnum.异常_数据本身缺失.getDesc());
				row.createCell(indexFiledMap.size() + 1, CellType.STRING).setCellValue(reportStrBuf.toString());

//				ReflectUtil.setFieldValue(instance, "processResult", CommonProcessResultEnum.异常_数据本身问题.getKey());
//				ReflectUtil.setFieldValue(instance, "processResultStr", CommonProcessResultEnum.异常_数据本身问题.getDesc());
//				ReflectUtil.setFieldValue(instance, "processReport", reportStrBuf.toString());

				ReflectUtil.setFieldValue(instance, "validateCode", CommonProcessResultEnum.异常_数据本身缺失.getDesc());
				ReflectUtil.setFieldValue(instance, "validateMessage", reportStrBuf.toString());


			} else {

				row.createCell(indexFiledMap.size() + 0, CellType.STRING).setCellValue(CommonProcessResultEnum.正常.getDesc());
				row.createCell(indexFiledMap.size() + 1, CellType.STRING).setCellValue("");

//				ReflectUtil.setFieldValue(instance, "processResult", CommonProcessResultEnum.正常.getKey());
//				ReflectUtil.setFieldValue(instance, "processResultStr", CommonProcessResultEnum.正常.getDesc());
//				ReflectUtil.setFieldValue(instance, "processReport", "");

				ReflectUtil.setFieldValue(instance, "validateCode", CommonProcessResultEnum.正常.getDesc());
				ReflectUtil.setFieldValue(instance, "validateMessage", "");

			}

			ReflectUtil.setFieldValue(instance, "batchId", batchId);

			messageProducerService.pushMessage2CHANNEL_QUEUE_F(ExcelImportTaskMessage
					.builder()
					.batchId(batchId)
					.topic(headClazz.getName())
					.content(AnJsonUtils.toJson(instance))
					.loginSysUser(AnJsonUtils.toJson(loginSysUser))
					.build()
			);

			log.info("最终行: {}", AnJsonUtils.toJson(instance));


		} else {

			row.createCell(indexFiledMap.size() + 0, CellType.STRING).setCellValue("结果");
			row.createCell(indexFiledMap.size() + 1, CellType.STRING).setCellValue("反馈");
		}
	}

	/**
	 * 目前的问题点就是,把全部字段都要用字符串来判定
	 *
	 * @param row
	 * @return
	 */
//	private boolean customValidate(Row row) {
//
//		for (Field field : fields) {
//
//			log.info("custom validate field {}", JsonUtils.toJson(field));
//
//			String name  = field.getName();
//			String value = null;
//
////			try {
////				field.setAccessible(true);
////				value = (String) field.get(t);
////				field.setAccessible(false);
////			} catch (IllegalAccessException e) {
////				e.printStackTrace();
////			}
////
////			if (value != null && !"".equals(value) && !notDuplicateValidator.validate(field, name, value, t)) {
////				return false;
////			}
//		}
//
//		return true;
//	}
//
//	private void hibernateValidate(Row row) {
//
//		Set<ConstraintViolation<T>> validateSet = HibernateValidator.getValidator().validate(t, Default.class);
//
//		if (validateSet != null && !validateSet.isEmpty()) {
//			ConstraintViolation<T> constraint = validateSet.stream().findAny().orElse(null);
//			t.setValidateCode(CommonProcessResultEnum.异常_数据本身问题.getKey());
//			t.setValidateMessage(constraint.getMessage());
//		} else {
//			t.setValidateCode(CommonProcessResultEnum.正常.getKey());
//			t.setValidateMessage("正常");
//		}
//	}
	private void setReport(StringBuffer reportStrBuf, Sheet sheet, Integer relativeRowIndex, Integer relativeCellIndex, String msg) {

		reportStrBuf.append(StrUtil.format("R[{}]C[{}]ERROR[{}]\n", relativeRowIndex, relativeCellIndex, msg));
	}

	private void setPostil(Sheet sheet, Integer relativeRowIndex, Integer relativeCellIndex, String msg) {

		Workbook workbook = sheet.getWorkbook();

		CellStyle cellStyle = workbook.createCellStyle();
		// 设置前景填充样式
		cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
		// 设置前景色为黄色
		cellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
		//设置垂直居中
		cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

		Drawing<?> drawingPatriarch = sheet.createDrawingPatriarch();

		// 在第一行 第二列创建一个批注
		Comment comment = drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6));
		// 输入批注信息
		comment.setString(new XSSFRichTextString(msg));

//		// 将批注添加到单元格对象中 对空处理的话是需要创建的 createCell
//		sheet.getRow(relativeRowIndex + 1).createCell(relativeCellIndex).setCellComment(comment);
//		sheet.getRow(relativeRowIndex + 1).createCell(relativeCellIndex).setCellStyle(cellStyle);

		sheet.getRow(relativeRowIndex + 1).getCell(relativeCellIndex).setCellComment(comment);
		sheet.getRow(relativeRowIndex + 1).getCell(relativeCellIndex).setCellStyle(cellStyle);
	}


	// 判断Excel导入的数据类型,转换成数据库可识别的数据类型
	@Deprecated
	private String getCellTypes(Cell cell) {

		String cellValue = null;

		if (null != cell) {

			// 以下是判断数据的类型
			switch (cell.getCellType()) {
				case HSSFCell.CELL_TYPE_NUMERIC: // 数字
					// 处理日期格式、时间格式
					if (HSSFDateUtil.isCellDateFormatted(cell)) {
						Date       d        = cell.getDateCellValue();
						DateFormat formater = new SimpleDateFormat("yyyy-MM-dd");
						cellValue = formater.format(d);
					} else {
						cellValue = cell.getNumericCellValue() + "";
					}


					break;

				case HSSFCell.CELL_TYPE_STRING: // 字符串
					cellValue = cell.getStringCellValue();
					break;

				case HSSFCell.CELL_TYPE_BOOLEAN: // Boolean
					cellValue = cell.getBooleanCellValue() + "";
					break;

				case HSSFCell.CELL_TYPE_FORMULA: // 公式
					// cellValue = cell.getCellFormula() + "";
					try {
						DecimalFormat df = new DecimalFormat("0.0000");
						cellValue = String.valueOf(df.format(cell.getNumericCellValue()));

					} catch (IllegalStateException e) {
						cellValue = String.valueOf(cell.getRichStringCellValue());
					}
					break;

				case HSSFCell.CELL_TYPE_BLANK: // 空值
					cellValue = "";
					break;

				case HSSFCell.CELL_TYPE_ERROR: // 故障
					cellValue = "非法字符";
					break;

				default:
					cellValue = "未知类型";
					break;
			}
		}
		return cellValue;
	}
}



