package org.springblade.utils.excel.controller;

import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFDataValidationHelper;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springblade.community.residential.entity.Residential;
import org.springblade.community.residential.service.IResidentialService;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.secure.BladeUser;
import org.springblade.smartapplication.smartsmoke.entity.SmartDevice;
import org.springblade.utils.excel.model.EnterpriseImportModule;
import org.springblade.utils.excel.model.HomeDeviceImportModule;
import org.springblade.utils.excel.tool.ExcelTool;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * title: ExportExcelController
 * package: com.sptg.gateway.controller.common.excel
 * description: 导出处理
 */
@RestController
@AllArgsConstructor
public class ExportExcelController{

    private final static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private IResidentialService residentialService;

	/**
	 * 功能描述:将导入小区时产生错误的小区数据导出
	 *
	 * @param httpServletResponse
	 */
	public void exportErrorHomeDevice(List<SmartDevice> list, HttpServletResponse httpServletResponse) {
		List<HomeDeviceImportModule> homeDeviceImportModuleList = new ArrayList<>();
		list.parallelStream().forEach(
			smartDevice -> {
				HomeDeviceImportModule homeDeviceImportModule = new HomeDeviceImportModule();
				homeDeviceImportModule.setDeviceId(smartDevice.getDeviceId());
				homeDeviceImportModule.setDeviceName(smartDevice.getDeviceName());
				homeDeviceImportModule.setPerson(smartDevice.getPerson());
				homeDeviceImportModule.setPersonTel(smartDevice.getPersonTel());
				homeDeviceImportModule.setIdcard(smartDevice.getIdcard());
				homeDeviceImportModule.setDeviceInstallAddress(smartDevice.getDeviceInstallAddress());
				homeDeviceImportModuleList.add(homeDeviceImportModule);
			}
		);

		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		writeExcel(httpServletResponse, homeDeviceImportModuleList, "智家设备导入错误记录" + sdf.format(new Date()), null, new HomeDeviceImportModule());
	}


	/**
     * 导出 Excel ：一个 sheet，带表头
     *
     * @param response  HttpServletResponse
     * @param list      数据 list，每个元素为一个 BaseRowModel
     * @param fileName  导出的文件名
     * @param sheetName 导入文件的 sheet 名
     * @param object    映射实体类，Excel 模型
     */
    public void writeExcel(HttpServletResponse response, List<? extends BaseRowModel> list,
						   String fileName, String sheetName, BaseRowModel object) {

        ExcelWriter writer = new ExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX);
        if (list.size() > 50000) {
            List<? extends List<? extends BaseRowModel>> lt = Lists.partition(list, 50000);
            for (int i = 0; i < lt.size(); i++) {
                Sheet sheet = new Sheet((i + 1), 0, object.getClass());
                sheet.setSheetName(sheetName);
                writer.write(lt.get(i), sheet);
            }
        } else {
            Sheet sheet = new Sheet(1, 0, object.getClass());
            sheet.setSheetName(sheetName);
            writer.write(list, sheet);
        }
        writer.finish();
    }

    /**
     * 导出文件时为Writer生成OutputStream
     */
    private OutputStream getOutputStream(String fileName, HttpServletResponse response) {
        //创建本地文件
        String filePath = fileName + ".xlsx";
        File dbfFile = new File(filePath);
        OutputStream outputStream = null;
        try {
            if (!dbfFile.exists() || dbfFile.isDirectory()) {
                dbfFile.createNewFile();
            }
            fileName = new String(filePath.getBytes(), "ISO-8859-1");
            response.addHeader("Content-Disposition", "filename=" + fileName);
            outputStream = response.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (dbfFile.exists()) {
                dbfFile.delete();
            }
        }
        return outputStream;
    }

    /**
     * 功能描述:获取住户类型
     *
     * @param type
     * @return String
     */
    private String getTypeNameByType(int type) {
        switch (type) {
            case 0:
                return "业主";
            case 1:
                return "成员";
            case 2:
                return "租客";
            default:
                return "业主";
        }
    }

    /**
     * 功能描述:获取住户性别
     *
     * @param sex
     * @return String
     */
    private String getSexNameByUserSex(int sex) {
        switch (sex) {
            case 0:
                return "未定义";
            case 1:
                return "男";
            case 2:
                return "女";
            default:
                return "未定义";
        }
    }

    /**
     * 功能描述:获取住户创建类型
     *
     * @param createType
     * @return String
     */
    private String getCreateTypeNameByCreateType(int createType) {
        switch (createType) {
            case 0:
                return "小程序添加";
            case 1:
                return "后台录入";
            case 2:
                return "后台导入";
            case 3:
                return "APP添加";
            default:
                return "";
        }
    }

    /**
     * 功能描述:获取审核状态
     *
     * @param checkState
     * @return String
     */
    private String getCheckStateNameByCheckState(int checkState) {
        switch (checkState) {
            case 0:
                return "待审核";
            case 1:
                return "审核通过";
            case 2:
                return "审核不通过";
            default:
                return "";
        }
    }


    private String getDeviceTypeNameByDeviceType(int deviceType) {
        switch (deviceType) {
            case 0:
                return "待审核";
            case 1:
                return "审核通过";
            case 2:
                return "审核不通过";
            default:
                return "";
        }
    }

	public void writeExcelWithArea(HttpServletResponse response, ArrayList<Object> objects, BaseRowModel object) throws IOException {
		// 查询所有的省名称
		List<String> provNameList = ExcelTool.makeProvNameList();
		// 整理数据，放入一个Map中，mapkey存放父地点，value 存放该地点下的子区域
		Map<String, List<String>> siteMap =  ExcelTool.makeSiteMap();
		// 获取实体类名称
		List<String> colummaNames = ReflectUtils.getColumnName(object.getClass());

		// 创建一个excel
		Workbook book = new XSSFWorkbook();
		// 创建需要用户填写的数据页
		// 设计表头
		org.apache.poi.ss.usermodel.Sheet sheet1 = book.createSheet("sheet1");
		Row row0 = sheet1.createRow(0);

		for (int i = 0; i < colummaNames.size(); i++) {
			Cell cell = row0.createCell(i);
			RichTextString text = new XSSFRichTextString(colummaNames.get(i));
			cell.setCellValue(text);
			sheet1.setColumnWidth(i, 28 * 256);
		}

		//设置一下高度
		row0.setHeightInPoints(30);

		//创建一个专门用来存放地区信息的隐藏sheet页
		//因此也不能在现实页之前创建，否则无法隐藏。
		org.apache.poi.ss.usermodel.Sheet hideSheet = book.createSheet("site");
		book.setSheetHidden(book.getSheetIndex(hideSheet), true);

		XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet)sheet1);

		// 省规则
//		DataValidationConstraint provConstraint = dvHelper.createExplicitListConstraint(provNameList.toArray(new String[]{}));
		DataValidationConstraint provConstraint = dvHelper.createFormulaListConstraint("site!$B$1:$AF$1");
		CellRangeAddressList provRangeAddressList = new CellRangeAddressList(1, 50000, 0, 0);
		DataValidation provinceDataValidation = dvHelper.createValidation(provConstraint, provRangeAddressList);
		provinceDataValidation.createErrorBox("error", "请选择正确的省份");
		provinceDataValidation.setShowErrorBox(true);
		provinceDataValidation.setSuppressDropDownArrow(true);
		sheet1.addValidationData(provinceDataValidation);

		int rowId = 0;
		// 设置第一行，存省的信息
		Row proviRow = hideSheet.createRow(rowId++);
		proviRow.createCell(0).setCellValue("省列表");
		for(int i = 0; i < provNameList.size(); i ++){
			Cell proviCell = proviRow.createCell(i + 1);
			proviCell.setCellValue(provNameList.get(i));
		}
		// 将具体的数据写入到每一行中，行开头为父级区域，后面是子区域。
		Iterator<String> keyIterator = siteMap.keySet().iterator();
		while(keyIterator.hasNext()){
			String key = keyIterator.next();
			List<String> son = siteMap.get(key);

			Row row = hideSheet.createRow(rowId);
			row.createCell(0).setCellValue(key);
			for(int i = 0; i < son.size(); i ++){
				Cell cell = row.createCell(i + 1);
				cell.setCellValue(son.get(i));
			}

			// 添加名称管理器
			String range = ExcelTool.getRange(1, rowId + 1, son.size());
			Name name = book.createName();
			name.setNameName(key);
			String formula = "site!" + range;
			name.setRefersToFormula(formula);

			// 添加有效性规则
			// 市以规则，此处仅作一个示例
			// "INDIRECT($A$" + 2 + ")" 表示规则数据会从名称管理器中获取key与单元格 A2 值相同的数据，如果A2是浙江省，那么此处就是 浙江省下的区域信息。
			DataValidationConstraint formulaList = dvHelper.createFormulaListConstraint("INDIRECT(A" + (rowId + 1) + ")");
			CellRangeAddressList rangeAddressList = new CellRangeAddressList(rowId,rowId,1,1);
			DataValidation cacse = dvHelper.createValidation(formulaList, rangeAddressList);
			cacse.createErrorBox("error", "请选择正确的市");
			cacse.setShowErrorBox(true);
			cacse.setSuppressDropDownArrow(true);
			sheet1.addValidationData(cacse);

			// 区规则
			formulaList = dvHelper.createFormulaListConstraint("INDIRECT(B" + (rowId + 1) + ")");
			rangeAddressList = new CellRangeAddressList(rowId,rowId,2,2);
			cacse = dvHelper.createValidation(formulaList, rangeAddressList);
			cacse.createErrorBox("error", "请选择正确的区");
			cacse.setEmptyCellAllowed(true);
			cacse.setShowErrorBox(true);
			cacse.setSuppressDropDownArrow(true);
			sheet1.addValidationData(cacse);

			// 街道规则
			formulaList = dvHelper.createFormulaListConstraint("INDIRECT(C" + (rowId + 1) + ")");
			rangeAddressList = new CellRangeAddressList(rowId,rowId,3,3);
			cacse = dvHelper.createValidation(formulaList, rangeAddressList);
			cacse.createErrorBox("error", "请选择正确的街道");
			cacse.setEmptyCellAllowed(true);
			cacse.setShowErrorBox(true);
			cacse.setSuppressDropDownArrow(true);
			sheet1.addValidationData(cacse);

			rowId++;
		}

		JSONArray streetData = ExcelTool.getStreetData();

//		int rowId2 = 450;
		for (int i = 0; i < streetData.size(); i++) {
			JSONObject jsonobs = new JSONObject().parseObject(streetData.get(i).toString());
			Map<String, Object> innerMap = jsonobs.getInnerMap();
			Set<String> keys = jsonobs.keySet();
			//遍历，通过迭代器
			Iterator<String> it = keys.iterator();
			while(it.hasNext())
			{
				String key = it.next();
				JSONArray o = (JSONArray) jsonobs.get(key);

				Row row = hideSheet.createRow(rowId);
				row.createCell(0).setCellValue(key);

				for (int j = 0; j < o.size(); j++) {
					String value = o.get(j).toString();
					Cell cell = row.createCell(j + 1);
					cell.setCellValue(value);
				}

				try {
//						throw new IllegalArgumentException("Threw an IllegalArgumentException");
					// 添加名称管理器
					String range = ExcelTool.getRange(1, rowId + 1, o.size());
					Name name = book.createName();
					name.setNameName(key);
					String formula = "site!" + range;
					name.setRefersToFormula(formula);

					// 添加有效性规则
					// 市以规则，此处仅作一个示例
					// "INDIRECT($A$" + 2 + ")" 表示规则数据会从名称管理器中获取key与单元格 A2 值相同的数据，如果A2是浙江省，那么此处就是 浙江省下的区域信息。
					DataValidationConstraint formulaList = dvHelper.createFormulaListConstraint("INDIRECT(A" + (rowId + 1) + ")");
					CellRangeAddressList rangeAddressList = new CellRangeAddressList(rowId,rowId,1,1);
					DataValidation cacse = dvHelper.createValidation(formulaList, rangeAddressList);
					cacse.createErrorBox("error", "请选择正确的市");
					cacse.setShowErrorBox(true);
					cacse.setSuppressDropDownArrow(true);
					sheet1.addValidationData(cacse);

					// 区规则
					formulaList = dvHelper.createFormulaListConstraint("INDIRECT(B" + (rowId + 1) + ")");
					rangeAddressList = new CellRangeAddressList(rowId,rowId,2,2);
					cacse = dvHelper.createValidation(formulaList, rangeAddressList);
					cacse.createErrorBox("error", "请选择正确的区");
					cacse.setEmptyCellAllowed(true);
					cacse.setShowErrorBox(true);
					cacse.setSuppressDropDownArrow(true);
					sheet1.addValidationData(cacse);

					// 街道规则
					formulaList = dvHelper.createFormulaListConstraint("INDIRECT(C" + (rowId + 1) + ")");
					rangeAddressList = new CellRangeAddressList(rowId,rowId,3,3);
					cacse = dvHelper.createValidation(formulaList, rangeAddressList);
					cacse.createErrorBox("error", "请选择正确的街道");
					cacse.setEmptyCellAllowed(true);
					cacse.setShowErrorBox(true);
					cacse.setSuppressDropDownArrow(true);
					sheet1.addValidationData(cacse);

					rowId++;
				} catch(IllegalArgumentException e) {
					System.out.println("Caught an IllegalArgumentException..." +  e.getMessage());
				}

			}
		}

		/*XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet)sheet1);

		// 省规则
//		DataValidationConstraint provConstraint = dvHelper.createExplicitListConstraint(provNameList.toArray(new String[]{}));
		DataValidationConstraint provConstraint = dvHelper.createFormulaListConstraint("site!$B$1:$AF$1");
		CellRangeAddressList provRangeAddressList = new CellRangeAddressList(1, 50000, 0, 0);
		DataValidation provinceDataValidation = dvHelper.createValidation(provConstraint, provRangeAddressList);
		provinceDataValidation.createErrorBox("error", "请选择正确的省份");
		provinceDataValidation.setShowErrorBox(true);
		provinceDataValidation.setSuppressDropDownArrow(true);
		sheet1.addValidationData(provinceDataValidation);*/


		/*// 市以规则，此处仅作一个示例
		// "INDIRECT($A$" + 2 + ")" 表示规则数据会从名称管理器中获取key与单元格 A2 值相同的数据，如果A2是浙江省，那么此处就是 浙江省下的区域信息。
		DataValidationConstraint formula = dvHelper.createFormulaListConstraint("INDIRECT($A$" + 2 + ")");
		CellRangeAddressList rangeAddressList = new CellRangeAddressList(1,50000,1,1);
		DataValidation cacse = dvHelper.createValidation(formula, rangeAddressList);
		cacse.createErrorBox("error", "请选择正确的市");
		cacse.setShowErrorBox(true);
		cacse.setSuppressDropDownArrow(true);
		sheet1.addValidationData(cacse);

		// 区规则
		formula = dvHelper.createFormulaListConstraint("INDIRECT($B$" + 2 + ")");
		rangeAddressList = new CellRangeAddressList(1,50000,2,2);
		cacse = dvHelper.createValidation(formula, rangeAddressList);
		cacse.createErrorBox("error", "请选择正确的区");
		cacse.setEmptyCellAllowed(true);
		cacse.setShowErrorBox(true);
		cacse.setSuppressDropDownArrow(true);
		sheet1.addValidationData(cacse);

		// 街道规则
		formula = dvHelper.createFormulaListConstraint("INDIRECT($C$" + 2 + ")");
		rangeAddressList = new CellRangeAddressList(1,50000,3,3);
		cacse = dvHelper.createValidation(formula, rangeAddressList);
		cacse.createErrorBox("error", "请选择正确的街道");
		cacse.setEmptyCellAllowed(true);
		cacse.setShowErrorBox(true);
		cacse.setSuppressDropDownArrow(true);
		sheet1.addValidationData(cacse);*/

		//导出数据
		try {
			//设置Http响应头告诉浏览器下载这个附件
			response.setHeader("Content-Disposition", "attachment;Filename=" + System.currentTimeMillis() + ".xls");
			OutputStream outputStream = response.getOutputStream();
			book.write(outputStream);

			outputStream.close();
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new IOException("导出Excel出现严重异常，异常信息：" + ex.getMessage());
		}

	}

	public void writeExcelWithResidential(HttpServletResponse response, ArrayList<Object> objects, BaseRowModel object, BladeUser bladeUser) throws IOException {
		List<Residential> list = residentialService.list(new QueryWrapper<>(new Residential()).lambda().eq(Residential::getTenantId, bladeUser.getTenantId()));
//		String nameStr = "";
		String[] names = new String[list.size()];
		for (int i = 0; i < list.size(); i++) {
			Residential residential = list.get(i);
//			nameStr += (residential.getName() + "_" + residential.getId()) + ",";
			names[i] = residential.getName() + "_" + residential.getId();
		}


		// 获取实体类名称
		List<String> colummaNames = ReflectUtils.getColumnName(object.getClass());

		// 创建一个excel
		Workbook book = new XSSFWorkbook();
		// 创建需要用户填写的数据页
		// 设计表头
		org.apache.poi.ss.usermodel.Sheet sheet1 = book.createSheet("sheet1");
		Row row0 = sheet1.createRow(0);

		for (int i = 0; i < colummaNames.size(); i++) {
			Cell cell = row0.createCell(i);
			RichTextString text = new XSSFRichTextString(colummaNames.get(i));
			cell.setCellValue(text);
			if (i == 0){
				sheet1.setColumnWidth(i, 35 * 256);
			}else{
				sheet1.setColumnWidth(i, 28 * 256);
			}

		}

		//设置一下高度
		row0.setHeightInPoints(30);


		XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet)sheet1);

		// 归属分区规则
		DataValidationConstraint provConstraint = dvHelper.createExplicitListConstraint(names);
//		DataValidationConstraint provConstraint = dvHelper.createFormulaListConstraint(nameStr.substring(0, nameStr.length() - 1));
		CellRangeAddressList provRangeAddressList = new CellRangeAddressList(1, 50000, 0, 0);
		DataValidation provinceDataValidation = dvHelper.createValidation(provConstraint, provRangeAddressList);
		provinceDataValidation.createErrorBox("error", "请选择正确归属");
		provinceDataValidation.setShowErrorBox(true);
		provinceDataValidation.setSuppressDropDownArrow(true);
		sheet1.addValidationData(provinceDataValidation);


		//导出数据
		try {
			//设置Http响应头告诉浏览器下载这个附件
			response.setHeader("Content-Disposition", "attachment;Filename=" + System.currentTimeMillis() + ".xls");
			OutputStream outputStream = response.getOutputStream();
			book.write(outputStream);

			outputStream.close();
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new IOException("导出Excel出现严重异常，异常信息：" + ex.getMessage());
		}


	}
}
