package com.jwipc.util;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.jwipc.dto.ExportDataDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.common.Duplicatable;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author shenyang.ou
 * @date 2024/2/28 23:02
 * @desc excel导出自适应列宽
 */
@Slf4j
public class CustomCellWriteHandlerTwo extends AbstractColumnWidthStyleStrategy implements SheetWriteHandler {
    private List<ExportDataDTO> exportDataDTOS;
    private boolean tripleHeader = false;
    private CellStyle cellStyle;
    private List<Integer> hideIndex = new ArrayList<>();
    private static final int MAX_COLUMN_WIDTH = 255;
    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);

    public CustomCellWriteHandlerTwo() {
    }

    public CustomCellWriteHandlerTwo(List<ExportDataDTO> exportDataDTOS) {
        this.exportDataDTOS = exportDataDTOS;
    }

    public CustomCellWriteHandlerTwo(List<ExportDataDTO> exportDataDTOS, boolean tripleHeader) {
        this.exportDataDTOS = exportDataDTOS;
        this.tripleHeader = tripleHeader;
    }

    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
                                 Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead)
    {
        if (row.getRowNum() == 1 && !tripleHeader) {
//            row.setHeightInPoints(0);
            row.setHeight((short) 600);
        } else if (row.getRowNum() == 2 && tripleHeader) {
            row.setHeightInPoints(0);
        } else {
            row.setHeight((short) 600);
        }
    }

    public void afterCellDispose(CellWriteHandlerContext context) {
        Cell cell = context.getCell();
        Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
        if (cellStyle == null) {
            cellStyle = workbook.createCellStyle();
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            cellStyle.setBorderBottom(BorderStyle.THIN);
            cellStyle.setBorderLeft(BorderStyle.THIN);
            cellStyle.setBorderRight(BorderStyle.THIN);
            cellStyle.setBorderTop(BorderStyle.THIN);
        }

        if (cellStyle instanceof Duplicatable) {
            CellStyle style = (CellStyle) ((Duplicatable) cellStyle).copy();
            cell.setCellStyle(style);
        }
        if (context.getHead() && CollectionUtils.isNotEmpty(exportDataDTOS)) {
            exportDataDTOS.forEach(exportDataDTO -> {
                if (exportDataDTO.getColor() != null && cell.getStringCellValue().equals(exportDataDTO.getTitle())) {
                    Font font = workbook.createFont();
                    font.setFontHeightInPoints((short) 14);
                    font.setFontName("宋体");
                    font.setBold(true);
                    if (cellStyle instanceof Duplicatable) {
                        CellStyle style = (CellStyle) ((Duplicatable) cellStyle).copy();
//                        cell.setCellStyle(style);
                        style.setFont(font);
                        style.setFillForegroundColor(exportDataDTO.getColor());
                        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                        cell.setCellStyle(style);
                    }

                    // 由于这里没有指定dataformat 最后展示的数据 格式可能会不太正确
                    // 这里要把 WriteCellData的样式清空， 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到
                    // cell里面去 会导致自己设置的不一样（很关键）
                    context.getFirstCellData().setWriteCellStyle(null);
                }
            });
        }
//        if(context.getRowIndex()==3){
//            Font font = workbook.createFont();
//            font.setFontHeightInPoints((short) 11);
//            font.setFontName("宋体");
//            font.setBold(false);
//            if (cellStyle instanceof Duplicatable) {
//                CellStyle style = (CellStyle) ((Duplicatable) cellStyle).copy();
////                        cell.setCellStyle(style);
//                style.setFont(font);
//                style.setFillForegroundColor(IndexedColors.WHITE.getIndex());
//                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//                cell.setCellStyle(style);
//            }
//
//            // 由于这里没有指定dataformat 最后展示的数据 格式可能会不太正确
//            // 这里要把 WriteCellData的样式清空， 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到
//            // cell里面去 会导致自己设置的不一样（很关键）
//            context.getFirstCellData().setWriteCellStyle(null);
//        }
        setColumnWidth(context);
    }

    //主要处理下拉框
    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

        // 构造下拉选项单元格列的位置，以及下拉选项可选参数值的map集合
        // key：下拉选项要放到哪个单元格，比如A列的单元格那就是0，C列的单元格，那就是2
        // value：key对应的那个单元格下拉列表里的数据项，比如这里就是下拉选项1..100
        Map<Integer, List<String>> selectParamMap = new HashMap<>();
        int selectIndex = 0;
        if (CollectionUtils.isNotEmpty(exportDataDTOS)) {
            for (ExportDataDTO exportDataDTO : exportDataDTOS) {
                if (CollectionUtils.isNotEmpty(exportDataDTO.getSelectList())) {
                    selectParamMap.put(selectIndex, exportDataDTO.getSelectList());
                }
                selectIndex++;
            }
        }
        // 获取第一个sheet页
        Sheet sheet = writeSheetHolder.getCachedSheet();
        // 获取sheet页的数据校验对象
        DataValidationHelper helper = sheet.getDataValidationHelper();
        // 获取工作簿对象，用于创建存放下拉数据的字典sheet数据页
        Workbook workbook = writeWorkbookHolder.getWorkbook();

        // 迭代索引，用于存放下拉数据的字典sheet数据页命名
        int index = 1;
        for (Map.Entry<Integer, List<String>> entry : selectParamMap.entrySet()) {

            // 设置存放下拉数据的字典sheet，并把这些sheet隐藏掉，这样用户交互更友好
            String dictSheetName = "dict_hide_sheet" + index;
            Sheet dictSheet = workbook.createSheet(dictSheetName);
            // 隐藏字典sheet页
            workbook.setSheetHidden(index++, true);

            // 设置下拉列表覆盖的行数，从第一行开始到最后一行，这里注意，Excel行的
            // 索引是从0开始的，我这边第0行是标题行，第1行开始时数据化，可根据实
            // 际业务设置真正的数据开始行，如果要设置到最后一行，那么一定注意，
            // 最后一行的行索引是1048575，千万别写成1048576，不然会导致下拉列表
            // 失效，出不来
            CellRangeAddressList infoList = new CellRangeAddressList(1, 1048575, entry.getKey(), entry.getKey());
            int rowLen = entry.getValue().size();
            for (int i = 0; i < rowLen; i++) {
                // 向字典sheet写数据，从第一行开始写，此处可根据自己业务需要，自定
                // 义从第几行还是写，写的时候注意一下行索引是从0开始的即可
                dictSheet.createRow(i).createCell(0).setCellValue(entry.getValue().get(i));
            }

            // 设置关联数据公式，这个格式跟Excel设置有效性数据的表达式是一样的
            String refers = dictSheetName + "!$A$1:$A$" + entry.getValue().size();
            Name name = workbook.createName();
            name.setNameName(dictSheetName);
            // 将关联公式和sheet页做关联
            name.setRefersToFormula(refers);

            // 将上面设置好的下拉列表字典sheet页和目标sheet关联起来
            DataValidationConstraint constraint = helper.createFormulaListConstraint(dictSheetName);
            DataValidation dataValidation = helper.createValidation(constraint, infoList);
            // 阻止输入非下拉选项的值
            dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);
            dataValidation.setShowErrorBox(true);
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.createErrorBox("提示", "请输入下拉选项中的内容");
            sheet.addValidationData(dataValidation);
        }
    }


    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell,
                                  Head head, Integer relativeRowIndex, Boolean isHead)
    {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        if (needSetWidth) {
            Map<Integer, Integer> maxColumnWidthMap = (Map) CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null) {
                maxColumnWidthMap = new HashMap(16);
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }
            //如果列名为空，隐藏列
            if (isHead && StrUtil.isEmpty(cell.getStringCellValue())) {
//                hideIndex.add(cell.getColumnIndex());
            }
            if (CollectionUtils.isNotEmpty(hideIndex) && hideIndex.contains(cell.getColumnIndex())) {
                if (isHead) {
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 0);
                } else {
                    cell.getCellStyle().setHidden(true);
                }
            } else {
                Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
                if (columnWidth >= 0) {
                    if (columnWidth > 100) {
                        columnWidth = 100;
                    }
                    if (columnWidth < 10) {
                        columnWidth = 10;
                    }
                    Integer maxColumnWidth = (Integer) ((Map) maxColumnWidthMap).get(cell.getColumnIndex());
                    if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                        ((Map) maxColumnWidthMap).put(cell.getColumnIndex(), columnWidth);
                        writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
                    }

                }
            }

        }
    }

    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = (CellData) cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            String name = cellData.getStringValue();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }
}
