package com.yeejoin.amos.boot.module.common.api.excel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import com.google.common.collect.Lists;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

public class ExcelUtil {


	private ExcelUtil() {
	}

	/**
	 * 生成excel模板
	 *
	 * @param response
	 * @param fileName  下载的文件名，
	 * @param sheetName sheet名
	 * @param data      导出的数据
	 * @param model     导出的头
	 * @param flag      true模板填充下拉 false 不填充
	 */
	public static void    createTemplate(HttpServletResponse response, String fileName, String sheetName,
			List<? extends Object> data, Class<?> model, DataSources dataDictionaryMapper, boolean flag) {

		HorizontalCellStyleStrategy horizontalCellStyleStrategy = setMyCellStyle();
		try {
			// 下拉列表集合
			Map<Integer, String[]> explicitListConstraintMap = new HashMap<>();
			if (flag) {
				// 循环获取对应列得下拉列表信息
				Field[] declaredFields = model.getDeclaredFields();
				for (int i = 0; i < declaredFields.length; i++) {
					Field field = declaredFields[i];
					// 解析注解信息
					ExplicitConstraint explicitConstraint = field.getAnnotation(ExplicitConstraint.class);
					resolveExplicitConstraint(explicitListConstraintMap, explicitConstraint, dataDictionaryMapper);
				}
			}
			EasyExcel.write(getOutputStream(fileName, response, ExcelTypeEnum.XLSX), model)
					.excelType(ExcelTypeEnum.XLSX).sheet(sheetName)
					.registerWriteHandler(new TemplateCellWriteHandlerDate(explicitListConstraintMap))
					.registerWriteHandler(new TemplateCellWriteHandler())
					.registerWriteHandler(horizontalCellStyleStrategy).doWrite(data);
		} catch (Exception e) {
			e.printStackTrace();
			throw new BadRequest("系统错误！");

		}
	}

	/**
	 * 生成excel模板
	 *
	 * @param response
	 * @param fileName  下载的文件名，
	 * @param sheetName sheet名
	 * @param data      导出的数据
	 * @param model     导出的头
	 * @param flag      true模板填充下拉 false 不填充
	 */
	public static void createDutyTemplate(HttpServletResponse response, String fileName, String sheetName,
			List<? extends Object> data, Class<?> model, List<String> dayByMonth, String[] dutyNameList,
			DataSources dataDictionaryMapper, boolean flag, String  typeFlag,boolean isTemplete) {

		HorizontalCellStyleStrategy horizontalCellStyleStrategy = setMyCellStyle();
		try {
			// 组装表头
			List<List<String>> dutyCarTitleList = new ArrayList<>();
			Field[] declaredFields = model.getDeclaredFields();
			for (int i = 0; i < declaredFields.length; i++) {
				Field field = declaredFields[i];
				ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
				if (excelProperty != null) {
					ArrayList<String> head = new ArrayList<>();
					head.add(excelProperty.value()[0]);
					dutyCarTitleList.add(head);
				}
			}
			int size = dutyCarTitleList.size();
			if (dayByMonth != null) {
				for (int i = 0; i < dayByMonth.size(); i++) {
					ArrayList<String> dutyDay = new ArrayList<>();
					dutyDay.add(dayByMonth.get(i));
					dutyCarTitleList.add(dutyDay);
				}
			}
			// 下拉列表集合
			Map<Integer, String[]> explicitListConstraintMap = new HashMap<>();
			if (flag) {
				// 组装下拉列表
				for (int i = 0; i < declaredFields.length; i++) {
					Field field = declaredFields[i];
					// 解析注解信息
					ExplicitConstraint explicitConstraint = field.getAnnotation(ExplicitConstraint.class);
					resolveExplicitConstraint(explicitListConstraintMap, explicitConstraint, dataDictionaryMapper);
				}
				if (dayByMonth != null) {
					for (int i = 0; i < dayByMonth.size(); i++) {
						explicitListConstraintMap.put(size + i, dutyNameList);
					}
				}
			}

			ExcelWriterSheetBuilder	excelWriterSheetBuilder =EasyExcel
					.write(getOutputStream(fileName, response, ExcelTypeEnum.XLSX))
					.head(dutyCarTitleList)
					.excelType(ExcelTypeEnum.XLSX).sheet(sheetName);
			if ("WXXFZB".equals(typeFlag) && isTemplete) {
				List<Map<Integer, String[]>> fireStationExplicitListConstraintMap = new ArrayList<Map<Integer, String[]>>();
				List<List<Object>> resultList =new ArrayList<List<Object>>();
				data.stream().forEach(i -> {
					Map<Integer, String[]> map = new HashMap<>();
					List<Object> detail = (List<Object>) i;
					// 微型消防站中的对应单位微型消防站下拉列表数据的集合
					List<String> fireStationDetailList = (List<String>) detail.get(detail.size()-1);
					String[] strings = new String[fireStationDetailList.size()];
					map.put(4, fireStationDetailList.toArray(strings));
					map.putAll(explicitListConstraintMap);
					fireStationExplicitListConstraintMap.add(map);
					detail.remove(detail.size()-1);
					resultList.add(detail);
				});
				excelWriterSheetBuilder.registerWriteHandler(
							new TemplateDynamicCellWriteHandlerDate(fireStationExplicitListConstraintMap))
					.registerWriteHandler(new TemplateCellWriteHandler())
					.registerWriteHandler(horizontalCellStyleStrategy);
				excelWriterSheetBuilder.doWrite(resultList);
			} 
			else if("JJZB".equals(typeFlag) && isTemplete){
				List<Map<Integer, String[]>> fireStationExplicitListConstraintMap = new ArrayList<Map<Integer, String[]>>();
				List<List<Object>> resultList =new ArrayList<List<Object>>();
				data.stream().forEach(i -> {
					Map<Integer, String[]> map = new HashMap<>();
					List<Object> detail = (List<Object>) i;
					// 微型消防站中的对应单位微型消防站下拉列表数据的集合
					List<String> fireStationDetailList = (List<String>) detail.get(detail.size()-1);
					String[] strings = new String[fireStationDetailList.size()];
					List<String> postTypeNameDetailList = (List<String>) detail.get(detail.size()-2);
					String[] postTypeNamestrings = new String[postTypeNameDetailList.size()];
					List<String> userNameDetailList = (List<String>) detail.get(detail.size()-3);
					String[] userNamestrings = new String[userNameDetailList.size()];
					List<String> companyNameList = (List<String>) detail.get(detail.size()-4);
					String[] companyNameLists = new String[companyNameList.size()];
					map.put(4, fireStationDetailList.toArray(strings));
					map.put(3, postTypeNameDetailList.toArray(postTypeNamestrings));
					map.put(2, userNameDetailList.toArray(userNamestrings));
					map.put(1, companyNameList.toArray(companyNameLists));
					map.putAll(explicitListConstraintMap);
					fireStationExplicitListConstraintMap.add(map);
					detail.remove(detail.size()-1);
					detail.remove(detail.size()-1);
					detail.remove(detail.size()-1);
					detail.remove(detail.size()-1);
					resultList.add(detail);
				});
				excelWriterSheetBuilder
					.registerWriteHandler(
							new TemplateDynamicCellWriteHandlerDate(fireStationExplicitListConstraintMap))
					.registerWriteHandler(new TemplateCellWriteHandler())
					.registerWriteHandler(horizontalCellStyleStrategy);
				excelWriterSheetBuilder.doWrite(resultList);
			}else {
				excelWriterSheetBuilder = excelWriterSheetBuilder
					.registerWriteHandler(
							new TemplateCellWriteHandlerDate(explicitListConstraintMap))
					.registerWriteHandler(new TemplateCellWriteHandler())
					.registerWriteHandler(horizontalCellStyleStrategy);
				excelWriterSheetBuilder.doWrite(data);
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("系统异常！");

		}
	}

	/**
	 * 读取 Excel(第一个 sheet) 指定行开始读取
	 *
	 * @param excel   文件
	 * @param rowType 模板实体类
	 * @param header  指定不读取的表头行数，
	 * @param <T>
	 * @return 集合数据
	 * @throws Exception
	 */
	public static <T> List<T> readFirstSheetExcel(MultipartFile excel, Class<T> rowType, int header) throws Exception {
		ExcelReader reader = getReader(excel, header);
		if (reader == null) {
			return new ArrayList<>();
		}
		return readExcel(reader, rowType, 0);
	}

	public static <T> List<T> readManySheetExcel(MultipartFile excel, Class<T> rowType, int header,int sheetCount) throws Exception {
		ExcelReader reader = getReader(excel, header);
		if (reader == null) {
			return new ArrayList<>();
		}
		return readExcel(reader, rowType, 0);
	}

	/**
	 * 读取 Excel(多个 sheet)
	 *
	 * @param reader     读取的excel
	 * @param rowModel   excel模板实体类
	 * @param sheetCount sheet
	 * @param <T>
	 * @return
	 */
	public static <T> List<T> readExcel(ExcelReader reader, Class<T> rowModel, int sheetCount) {
		if (reader == null) {
			return new ArrayList<>();
		}
		ExcelListener<T> excelListener = new ExcelListener<>();
		ReadSheet readSheet = EasyExcel.readSheet(sheetCount).head(rowModel).registerReadListener(excelListener)
				.build();
		reader.read(readSheet);
		return excelListener.getList();
	}

	/**
	 * @param excel  需要解析的 Excel 文件
	 * @param header 指定不读取表头行数，
	 * @return
	 * @throws Exception
	 */
	public static ExcelReader getReader(MultipartFile excel, int header) throws Exception {
		String fileName = excel.getOriginalFilename();
		if (fileName == null) {
			throw new Exception("文件不存在！");
		}
		if (!fileName.toLowerCase().endsWith(ExcelTypeEnum.XLS.getValue())
				&& !fileName.toLowerCase().endsWith(ExcelTypeEnum.XLSX.getValue())) {
			throw new Exception("文件类型异常！");
		}
		InputStream inputStream;
		try {
			inputStream = excel.getInputStream();
			return EasyExcel.read(inputStream).headRowNumber(header).build();
		} catch (IOException e) {
			// do something
		}
		return null;
	}

	/**
	 * 解析注解内容 获取下列表信息
	 *
	 * @param explicitConstraint
	 * @return
	 */
	public static Map<Integer, String[]> resolveExplicitConstraint(Map<Integer, String[]> explicitListConstraintMap,
			ExplicitConstraint explicitConstraint, DataSources dataDictionaryMapper) {
		Map<Integer, String[]> map =new HashMap<>();
		if (explicitConstraint == null) {
			return map;
		}
		// 固定下拉信息
		String[] source = explicitConstraint.source();
		if (source.length > 0) {
			explicitListConstraintMap.put(explicitConstraint.indexNum(), source);
		}
		// 动态下拉信息
		Class<? extends ExplicitInterface>[] classes = explicitConstraint.sourceClass();
		if (classes.length > 0) {
			ExplicitInterface explicitInterface = null;
			try {
				explicitInterface = classes[0].newInstance();
				String[] source1 = explicitInterface.source(explicitConstraint.type(), explicitConstraint.method(),
						dataDictionaryMapper);
				if (source1.length > 0) {
					explicitListConstraintMap.put(explicitConstraint.indexNum(), source1);
				}
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return map;
	}

	/**
	 * 导出文件时为Writer生成OutputStream
	 */
	private static OutputStream getOutputStream(String fileName, HttpServletResponse response,
			ExcelTypeEnum excelTypeEnum) throws Exception {
		// 创建本地文件
		String filePath = fileName + excelTypeEnum.getValue();
		try {
			fileName = new String(filePath.getBytes(), "UTF-8");
			response.setCharacterEncoding(StandardCharsets.UTF_8.name());
			response.setContentType("application/vnd.ms-excel");
			response.addHeader("Content-Disposition", "filename=" + fileName);
			response.setHeader("Content-Disposition",
					"attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
			return response.getOutputStream();
		} catch (IOException e) {
			throw new Exception("系统异常");
		}
	}

	/**
	 * 创建我的cell 策略
	 *
	 * @return
	 */
	public static HorizontalCellStyleStrategy setMyCellStyle() {



		// 表头样式策略
		WriteCellStyle headWriteCellStyle = new WriteCellStyle();
		// 设置表头居中对齐
		headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
		// 表头前景色
		headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
		WriteFont headWriteFont = new WriteFont();
		headWriteFont.setBold(true);
		headWriteFont.setFontName("宋体");
		headWriteFont.setFontHeightInPoints((short) 10);
		headWriteCellStyle.setWriteFont(headWriteFont);

		// 2 内容样式策略
		WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
		WriteFont contentWriteFont = new WriteFont();
		// 内容字体大小
		contentWriteFont.setFontName("宋体");
		contentWriteFont.setFontHeightInPoints((short) 10);
		contentWriteCellStyle.setWriteFont(contentWriteFont);
		// 设置自动换行
		contentWriteCellStyle.setWrapped(true);
		// 设置垂直居中
		contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
		// 设置水平居中
		contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

		// 设置边框样式
		contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
		contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
		contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
		contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);

		return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
	}



	/**
	 * 生成excel模板
	 *
	 * @param response
	 * @param fileName  下载的文件名，
	 * @param sheetName sheet名
	 * @param data      导出的数据
	 */
	public static void createTemplateWithHeaders(HttpServletResponse response, String fileName, String sheetName,
									  List<? extends Object> data, List<List<String>> heads, List<String> headstr, String fileType) {

		HorizontalCellStyleStrategy horizontalCellStyleStrategy = setMyCellStyle();
		try {
			// 下拉列表集合
			List<List<Object>> listData = Lists.newArrayList();
            for (Object t : data) {
                List<Object> rowLine = new ArrayList<>();
                for(String head: headstr) {
                    String getMethodName = "get" + head.substring(0,1).toUpperCase() + head.substring(1);
                    Class clazz = t.getClass();
                    Method getMethod;
                    getMethod = clazz.getMethod(getMethodName, new Class[]{} );
                    Object value = getMethod.invoke(t, new Object[]{});
                    rowLine.add(value);
                }
                listData.add(rowLine);
            }

			ExcelTypeEnum typeEnum = null;
            if("1039".equals(fileType)) {
				typeEnum = ExcelTypeEnum.XLS;
			} else if("1040".equals(fileType)) {
				typeEnum = ExcelTypeEnum.XLSX;
			}

			EasyExcel.write(getOutputStream(fileName, response, typeEnum))
					.excelType(typeEnum).sheet(sheetName)
					.registerWriteHandler(new TemplateCellWriteHandler())
					.registerWriteHandler(horizontalCellStyleStrategy)
					.head(heads).doWrite(listData);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("系统异常！");

		}
	}

	/**
	 * 解析表头类中的下拉注解
	 * @param head 表头类
	 * @param <T> 泛型
	 * @return Map<下拉框列索引, 下拉框内容> map
	 */
	public static <T> Map<Integer, ExcelSelectedResolve> resolveSelectedAnnotation(Class<T> head,DataSources dataDictionaryMapper) {
		Map<Integer, ExcelSelectedResolve> selectedMap = new HashMap<>();

		// getDeclaredFields(): 返回全部声明的属性；getFields(): 返回public类型的属性
		Field[] fields = head.getDeclaredFields();
		for (int i = 0; i < fields.length; i++){
			Field field = fields[i];
			// 解析注解信息
			ExplicitConstraint selected = field.getAnnotation(ExplicitConstraint.class);
			ExcelProperty property = field.getAnnotation(ExcelProperty.class);
			if (selected != null) {
				ExcelSelectedResolve excelSelectedResolve = new ExcelSelectedResolve();
				// 处理下拉框内容
				String[] source = excelSelectedResolve.resolveSelectedSource(selected,dataDictionaryMapper);
				if (source != null && source.length > 0){
					excelSelectedResolve.setSource(source);
					excelSelectedResolve.setFirstRow(selected.firstRow());
					excelSelectedResolve.setLastRow(selected.lastRow());
					if (property != null && property.index() >= 0){
						selectedMap.put(property.index(), excelSelectedResolve);
					} else {
						selectedMap.put(i, excelSelectedResolve);
					}
				}
			}
		}

		return selectedMap;
	}
}
