package com.njcn.poi.excel;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import com.njcn.web.utils.HttpServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;

/**
 * @author hongawen
 * @version 1.0.0
 * @date 2022年04月01日 10:48
 */
@Slf4j
public class ExcelUtil {

    /**
     * 指定名称、数据下载报表
     *
     * @param fileName 文件名
     */
    public static void exportExcel(String fileName, Class<?> pojoClass, Collection<?> dataSet) {
        HttpServletResponse response = HttpServletUtil.getResponse();
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            fileName = URLEncoder.encode(fileName, CharsetUtil.UTF_8);
            response.reset();
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentType("application/octet-stream;charset=UTF-8");
            ExportParams exportParams = new ExportParams();
            Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, dataSet);
            workbook.write(outputStream);
        } catch (IOException e) {
            log.error(">>> 导出数据异常：{}", e.getMessage());
        }
    }

    /**
     * 指定名称、sheet名、数据下载报表
     *
     * @param fileName 文件名
     */
    public static void exportExcel(String fileName, String sheetName, Class<?> pojoClass, Collection<?> dataSet) {
        HttpServletResponse response = HttpServletUtil.getResponse();
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            fileName = URLEncoder.encode(fileName, CharsetUtil.UTF_8);
            response.reset();
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentType("application/octet-stream;charset=UTF-8");
            ExportParams exportParams = new ExportParams();
            exportParams.setSheetName(sheetName);
            Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, dataSet);
            workbook.write(outputStream);
        } catch (IOException e) {
            log.error(">>> 导出数据异常：{}", e.getMessage());
        }
    }

    /**
     * 处理好的表格直接下载
     *
     * @param fileName 文件名
     */
    public static void exportExcelByWorkbook(String fileName, Workbook workbook) {
        HttpServletResponse response = HttpServletUtil.getResponse();
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            fileName = URLEncoder.encode(fileName, CharsetUtil.UTF_8);
            response.reset();
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentType("application/octet-stream;charset=UTF-8");
            workbook.write(outputStream);
        } catch (IOException e) {
            log.error(">>> 导出数据异常：{}", e.getMessage());
        }
    }

    /**
     * 指定名称、数据下载报表(带指定标题将*显示比必填信息)
     *
     * @param fileName 文件名
     */
    public static void exportExcel(ExportParams exportParams, String fileName, Class<?> pojoClass, Collection<?> dataSet) {
        exportExcelPullDown(exportParams, fileName, null, pojoClass, dataSet);
    }


    /**
     * 指定名称、数据下载报表(带指定标题将*显示比必填信息),带有下拉信息
     *
     * @param fileName 文件名
     */
    public static void exportExcelPullDown(ExportParams exportParams, String fileName, List<PullDown> pullDowns, Class<?> pojoClass, Collection<?> dataSet) {
        HttpServletResponse response = HttpServletUtil.getResponse();
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            fileName = URLEncoder.encode(fileName, CharsetUtil.UTF_8);
            response.reset();
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setContentType("application/octet-stream;charset=UTF-8");
            Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, dataSet);
            setTopLevel(workbook, pullDowns);
            if (CollUtil.isNotEmpty(pullDowns)) {
                int num = 1;
                CellStyle cellStyle = workbook.createCellStyle();
                DataFormat format = workbook.createDataFormat();
                cellStyle.setDataFormat(format.getFormat("@"));
                for (PullDown pullDown : pullDowns) {
                    if (pullDown.getIsText()) {
                        ExcelUtil.selectListText(workbook, pullDown.getFirstCol(),cellStyle);
                    } else {
                        String colName = numberToExcelColumn(num);
                        int sum = pullDown.getStrings().stream().mapToInt(String::length).sum();
                        if (sum == 0) {
                            continue;
                        }
                        if (sum > 255) {
                            ExcelUtil.selectListMaxLength(workbook, pullDown.getFirstCol(), pullDown.getLastCol(), pullDown.getStrings().size(), colName);
                            num++;
                        } else {
                            ExcelUtil.selectList(workbook, pullDown.getFirstCol(), pullDown.getLastCol(), pullDown.getStrings().toArray(new String[]{}));
                        }
                    }

                }
            }
            Sheet sheetAt = workbook.getSheetAt(0);
            //获取列数
            int physicalNumberOfCells = sheetAt.getRow(0).getPhysicalNumberOfCells();
            //没有表格标题,只有表格头
            for (int i = 0; i < 2; i++) {
                //获取行
                Row row = sheetAt.getRow(i);
                if (Objects.isNull(row)) {
                    continue;
                }
                for (int j = 0; j < physicalNumberOfCells; j++) {
                    //获取单元格对象
                    Cell cell = row.getCell(j);
                    //获取单元格样式对象
                    CellStyle cellStyle = workbook.createCellStyle();
                    //获取单元格内容对象
                    Font font = workbook.createFont();
                    font.setFontHeightInPoints((short) 12);
                    //一定要装入 样式中才会生效
                    cellStyle.setFont(font);
                    //设置居中对齐
                    cellStyle.setAlignment(HorizontalAlignment.CENTER);
                    cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
                    //设置单元格字体颜色
                    cell.setCellStyle(cellStyle);
                    //获取当前值
                    String stringCellValue = cell.getStringCellValue();
                    if (stringCellValue.contains("*")) {
                        // 创建一个富文本
                        XSSFRichTextString xssfRichTextString = new XSSFRichTextString(stringCellValue);
                        int startIndex = stringCellValue.indexOf("*");
                        int entIndex = stringCellValue.lastIndexOf("*");
                        if (entIndex != 0) {
                            Font font3 = workbook.createFont();
                            font3.setFontHeightInPoints((short) 12);
                            font3.setColor(Font.COLOR_NORMAL);
                            xssfRichTextString.applyFont(0, entIndex, font3);
                        }
                        //设置带*样式
                        Font font1 = workbook.createFont();
                        font1.setFontHeightInPoints((short) 12);
                        font1.setColor(Font.COLOR_RED);
                        xssfRichTextString.applyFont(startIndex, entIndex + 1, font1);
                        //其他样式
                        Font font2 = workbook.createFont();
                        font2.setFontHeightInPoints((short) 12);
                        font2.setColor(Font.COLOR_NORMAL);
                        xssfRichTextString.applyFont(entIndex + 1, stringCellValue.length(), font2);
                        cell.setCellValue(xssfRichTextString);
                    }
                }
            }
            workbook.write(outputStream);
        } catch (IOException e) {
            log.error(">>> 导出数据异常：{}", e.getMessage());
        }
    }

    /**
     * @param excel          目标excel
     * @param statisticsType 统计类型
     * @param statisticsName 统计名称
     * @param pojoClass      目标class
     * @param dataSet        数据集合
     */
    public static void exportExcelWithTargetFile(File excel, String statisticsType, String statisticsName, Class<?> pojoClass, Collection<?> dataSet) {
        try (FileOutputStream outputStream = new FileOutputStream(excel)) {
            if (!excel.getParentFile().exists()) {
                excel.getParentFile().mkdirs();
            }
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(statisticsType, statisticsName), pojoClass, dataSet);
            workbook.write(outputStream);
        } catch (IOException e) {
            log.error(">>> 导出数据异常：{}", e.getMessage());
        }
    }

    /**
     * @param excel     目标excel
     * @param pojoClass 目标class
     * @param dataSet   数据集合
     */
    public static void exportExcelWithTargetFile(File excel, Class<?> pojoClass, Collection<?> dataSet) {
        try (FileOutputStream outputStream = new FileOutputStream(excel)) {
            if (!excel.getParentFile().exists()) {
                excel.getParentFile().mkdirs();
            }
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), pojoClass, dataSet);
            workbook.write(outputStream);
        } catch (IOException e) {
            log.error(">>> 导出数据异常：{}", e.getMessage());
        }
    }


    /**
     * firstRow 開始行號 根据此项目，默认为2(下标0开始)
     * lastRow  根据此项目，默认为最大65535
     *
     * @param firstCol 区域中第一个单元格的列号 (下标0开始)
     * @param lastCol  区域中最后一个单元格的列号
     * @param strings  下拉内容
     */
    public static void selectList(Workbook workbook, int firstCol, int lastCol, String[] strings) {
//        Sheet sheet = workbook.getSheetAt(0);
//        //  生成下拉列表
//        //  只对(x，x)单元格有效
//        CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(2, 65535, firstCol, lastCol);
//        //  生成下拉框内容
//        DataValidationHelper dvHelper = sheet.getDataValidationHelper();
//        XSSFDataValidationConstraint dvConstraint = (XSSFDataValidationConstraint)
//                dvHelper.createExplicitListConstraint(strings);
//        XSSFDataValidation validation = (XSSFDataValidation) dvHelper.createValidation(
//                dvConstraint, cellRangeAddressList);
//        //  对sheet页生效
//        sheet.addValidationData(validation);

        Sheet  targetSheet = workbook.getSheetAt(0);

        // ===================== 关键：每个下拉用唯一名称的隐藏Sheet =====================
        String uniqueHiddenName = "dropdown_" + firstCol + "_" + lastCol + "_" + System.currentTimeMillis();
        Sheet hiddenSheet = workbook.createSheet(uniqueHiddenName);
        // 隐藏数据源Sheet
        workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet), true);

        // 写入选项到隐藏Sheet
        for (int i = 0; i < strings.length; i++) {
            Row row = hiddenSheet.createRow(i);
            Cell cell = row.createCell(0);
            cell.setCellValue(strings[i]);
        }

        // 构造引用公式（无长度限制）
        String formula = uniqueHiddenName + "!$A$1:$A$" + strings.length;

        // 下拉作用范围：第3行 ~ 65536行
        CellRangeAddressList range = new CellRangeAddressList(2, 65535, firstCol, lastCol);
        DataValidationHelper helper = targetSheet.getDataValidationHelper();
        DataValidationConstraint constraint = helper.createFormulaListConstraint(formula);
        DataValidation validation = helper.createValidation(constraint, range);

        // 必加：防止下拉冲突、不显示
        validation.setShowErrorBox(true);
        validation.setShowPromptBox(true);
        targetSheet.addValidationData(validation);
    }

    /**
     * @param workbook
     * @param firstCol
     * @param lastCol
     * @param colName  列A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
     * @Description: 当下拉框超过最大字符255，设置隐藏表单来进行展示
     * @Author: wr
     * @Date: 2024/8/13 15:07
     */
    public static void selectListMaxLength(Workbook workbook, int firstCol, int lastCol, int row, String colName) {
        Sheet sheet = workbook.getSheetAt(0);
        //  生成下拉列表
        // 设置数据有效性加载在哪个单元格上,四个参数分别是：起始行、终止行、起始列、终止列
        CellRangeAddressList regions = new CellRangeAddressList(2, 65535, firstCol, lastCol);
        String strFormula = workbook.getSheetName(1) + "!$" + colName + "$1:$" + colName + "$" + row;
        XSSFDataValidationConstraint hiddentConstraint = new XSSFDataValidationConstraint(DataValidationConstraint.ValidationType.LIST, strFormula);
        // 数据有效性对象
        DataValidationHelper help = new XSSFDataValidationHelper((XSSFSheet) sheet);
        DataValidation validation = help.createValidation(hiddentConstraint, regions);
        sheet.addValidationData(validation);
    }
    /**
     * @Description:
     * @param workbook
     * @param firstCol
     * @Author: wr
     * @Date: 2024/8/20 10:44
     */
    public static void selectListText(Workbook workbook, int firstCol,CellStyle cellStyle) {
        Sheet sheet = workbook.getSheetAt(0);
        for (int i = 2; i < 65535; i++) {
            Row row = sheet.getRow(i);
            if(ObjectUtil.isNull(row)){
                row = sheet.createRow(i);
            }
            Cell cell = row.createCell(firstCol);
            cell.setCellStyle(cellStyle);
        }
    }

    /**
     * 指定名称、数据下载报表(带指定标题将*显示比必填信息),带有下拉信息
     *
     * @param fileName 文件名
     */
    public static void exportExcelPullDownOne(ExportParams exportParams, String fileName, List<PullDown> pullDowns, Class<?> pojoClass, Collection<?> dataSet) {
        HttpServletResponse response = HttpServletUtil.getResponse();
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            fileName = URLEncoder.encode(fileName, CharsetUtil.UTF_8);
            response.reset();
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setContentType("application/octet-stream;charset=UTF-8");
            Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, dataSet);
            if (CollUtil.isNotEmpty(pullDowns)) {
                for (PullDown pullDown : pullDowns) {
                    ExcelUtil.selectListOne(workbook, pullDown.getFirstCol(), pullDown.getLastCol(), pullDown.getStrings().toArray(new String[]{}));
                }
            }
            Sheet sheetAt = workbook.getSheetAt(0);
            //获取列数
            int physicalNumberOfCells = sheetAt.getRow(0).getPhysicalNumberOfCells();
            //没有表格标题,只有表格头
            for (int i = 0; i < 2; i++) {
                //获取行
                Row row = sheetAt.getRow(i);
                if (Objects.isNull(row)) {
                    continue;
                }
                for (int j = 0; j < physicalNumberOfCells; j++) {
                    //获取单元格对象
                    Cell cell = row.getCell(j);
                    //获取单元格样式对象
                    CellStyle cellStyle = workbook.createCellStyle();
                    //获取单元格内容对象
                    Font font = workbook.createFont();
                    font.setFontHeightInPoints((short) 12);
                    //一定要装入 样式中才会生效
                    cellStyle.setFont(font);
                    //设置居中对齐
                    cellStyle.setAlignment(HorizontalAlignment.CENTER);
                    cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
                    //设置单元格字体颜色
                    cell.setCellStyle(cellStyle);
                    //获取当前值
                    String stringCellValue = cell.getStringCellValue();
                    if (stringCellValue.contains("*")) {
                        // 创建一个富文本
                        XSSFRichTextString xssfRichTextString = new XSSFRichTextString(stringCellValue);
                        int startIndex = stringCellValue.indexOf("*");
                        int entIndex = stringCellValue.lastIndexOf("*");
                        if (entIndex != 0) {
                            Font font3 = workbook.createFont();
                            font3.setFontHeightInPoints((short) 12);
                            font3.setColor(Font.COLOR_NORMAL);
                            xssfRichTextString.applyFont(0, entIndex, font3);
                        }
                        //设置带*样式
                        Font font1 = workbook.createFont();
                        font1.setFontHeightInPoints((short) 12);
                        font1.setColor(Font.COLOR_RED);
                        xssfRichTextString.applyFont(startIndex, entIndex + 1, font1);
                        //其他样式
                        Font font2 = workbook.createFont();
                        font2.setFontHeightInPoints((short) 12);
                        font2.setColor(Font.COLOR_NORMAL);
                        xssfRichTextString.applyFont(entIndex + 1, stringCellValue.length(), font2);
                        cell.setCellValue(xssfRichTextString);
                    }
                }
            }
            workbook.write(outputStream);
        } catch (IOException e) {
            log.error(">>> 导出数据异常：{}", e.getMessage());
        }
    }


    /**
     * firstRow 開始行號 根据此项目，默认为2(下标0开始)
     * lastRow  根据此项目，默认为最大65535
     *
     * @param firstCol 区域中第一个单元格的列号 (下标0开始)
     * @param lastCol  区域中最后一个单元格的列号
     * @param strings  下拉内容
     */
    public static void selectListOne(Workbook workbook, int firstCol, int lastCol, String[] strings) {
        Sheet sheet = workbook.getSheetAt(0);
        //  生成下拉列表
        //  只对(x，x)单元格有效
        CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 65535, firstCol, lastCol);
        //  生成下拉框内容
        DataValidationHelper dvHelper = sheet.getDataValidationHelper();
        XSSFDataValidationConstraint dvConstraint = (XSSFDataValidationConstraint)
                dvHelper.createExplicitListConstraint(strings);
        XSSFDataValidation validation = (XSSFDataValidation) dvHelper.createValidation(
                dvConstraint, cellRangeAddressList);
        //  对sheet页生效
        sheet.addValidationData(validation);
    }

    /**
     * 设置隐藏表单来进行下拉框
     *
     * @param workbook
     * @param pullDowns
     */
    private static void setTopLevel(Workbook workbook, List<PullDown> pullDowns) {
        int num = 0;
        for (PullDown pullDown : pullDowns) {
            if (!pullDown.getIsText()) {
                int sum = pullDown.getStrings().stream().mapToInt(String::length).sum();
                if (sum == 0) {
                    continue;
                }
                if (sum > 255) {
                    // 创建隐藏sheet
                    String hiddenSheetName = "hiddenSheetA";
                    if (num == 0) {
                        workbook.createSheet(hiddenSheetName);
                    }
                    //false展示隐藏sheet ,true不展示隐藏sheet
                    workbook.setSheetHidden(workbook.getSheetIndex(workbook.getSheet(hiddenSheetName)), true);
                    Sheet sheet = workbook.getSheet(hiddenSheetName);
                    if (num == 0) {
                        //sheet.getLastRowNum无法区分 有一行和没有 所以这里先建一行
                        sheet.createRow(0);
                    }
                    Row row; //创建数据行
                    sheet.setColumnWidth(num, 4000); //设置每列的列宽
                    for (int j = 0; j < pullDown.getStrings().size(); j++) {
                        if (sheet.getLastRowNum() < j) {
                            row = sheet.createRow(j); //创建数据行
                        } else {
                            row = sheet.getRow(j);
                        }
                        //设置对应单元格的值
                        row.createCell(num).setCellValue(pullDown.getStrings().get(j));
                    }
                    num++;
                }
            }
        }
    }

    /**
     * 获取根据数值获取列字母(1=A,2=B,3=C,.......,27=AA)
     *
     * @param n
     * @return
     */
    public static String numberToExcelColumn(int n) {
        CellReference cellRef = new CellReference(0, n - 1, false, false); // Row and column are 0-based
        int colIndex = cellRef.getCol();
        StringBuilder columnName = new StringBuilder();
        while (colIndex >= 0) {
            columnName.insert(0, (char) ('A' + (colIndex % 26)));
            colIndex /= 26;
            colIndex--;
        }
        return columnName.toString();
    }

}
