package com.jwipc.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
import com.alibaba.fastjson2.JSON;
import com.jwipc.dto.ExportDataDTO;
import com.jwipc.dto.ExportEnumDataDTO;
import org.apache.commons.codec.binary.Base64;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author shenyang.ou
 * @date 2024/2/28 22:17
 * @desc
 */
public class EasyExcelUtils {

    /**
     * 动态导出
     *
     * @param fileName    文件路径名
     * @param sheetName   sheet名
     * @param data        查询出来的数据
     * @param exportFiled 组件传入的列信息
     */
    public static void trendsModelWrite(HttpServletResponse response, String fileName, String sheetName, List data,
                                        String exportFiled) throws IOException
    {
        List<ExportDataDTO> exportDataDTOS = JSON.parseArray(exportFiled, ExportDataDTO.class);
        trendsModelWrite(response, fileName, sheetName, data, exportDataDTOS);
    }

    /**
     * 动态导出
     *
     * @param fileName       文件路径名
     * @param sheetName      sheet名
     * @param data           查询出来的数据
     * @param exportDataDTOS 组件传入的列信息
     */
    public static void trendsModelWrite(HttpServletResponse response, String fileName, String sheetName, List data,
                                        List<ExportDataDTO> exportDataDTOS) throws IOException
    {
        trendsModelWrite(response, fileName, sheetName, data, exportDataDTOS, false);
    }

    /**
     * 动态导出
     *
     * @param fileName       文件路径名
     * @param sheetName      sheet名
     * @param data           查询出来的数据
     * @param exportDataDTOS 组件传入的列信息  增加第二行，英文不隐藏， 采购新需求 2024-09-10
     */
    public static void trendsModelWriteTow(HttpServletResponse response, String fileName, String sheetName, List data,
                                        List<ExportDataDTO> exportDataDTOS) throws IOException
    {
        trendsModelNGWrite(response, fileName, sheetName, data, exportDataDTOS, false);
    }

    public static void trendsModelWriteFile(File file, String sheetName, List data,
                                            List<ExportDataDTO> exportDataDTOS) throws IOException
    {

        EasyExcel.write(file).registerConverter(new LongStringConverter())
                .head(head(exportDataDTOS))
                .registerWriteHandler(new CustomCellWriteHandler(exportDataDTOS, false))
                .sheet(sheetName).doWrite(
                        dataList(data,
                                exportDataDTOS.stream().map(ExportDataDTO::getField).collect(Collectors.toList()),
                                exportDataDTOS));
    }

    /**
     * 动态导出
     *
     * @param fileName       文件路径名
     * @param sheetName      sheet名
     * @param data           查询出来的数据
     * @param exportDataDTOS 组件传入的列信息
     * @param tripleHeader   是否是三表头，包括隐藏行一共三行
     */
    public static void trendsModelWrite(HttpServletResponse response, String fileName, String sheetName, List data,
                                        List<ExportDataDTO> exportDataDTOS, boolean tripleHeader) throws IOException
    {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName) + ".xlsx");
        EasyExcel.write(response.getOutputStream()).registerConverter(new LongStringConverter())
                .head(head(exportDataDTOS)).automaticMergeHead(false)
                .registerWriteHandler(new CustomCellWriteHandler(exportDataDTOS, tripleHeader))
                .sheet(sheetName).doWrite(
                        dataList(data,
                                exportDataDTOS.stream().map(ExportDataDTO::getField).collect(Collectors.toList()),
                                exportDataDTOS));
    }

    /**
     * 动态导出
     *
     * @param fileName       文件路径名
     * @param sheetName      sheet名
     * @param data           查询出来的数据
     * @param exportDataDTOS 组件传入的列信息
     * @param tripleHeader   是否是三表头，包括隐藏行一共三行, 这里新需求，第二行英文不隐藏
     */
    public static void trendsModelNGWrite(HttpServletResponse response, String fileName, String sheetName, List data,
                                        List<ExportDataDTO> exportDataDTOS, boolean tripleHeader) throws IOException
    {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName) + ".xlsx");
        EasyExcel.write(response.getOutputStream()).registerConverter(new LongStringConverter())
                .head(head(exportDataDTOS)).automaticMergeHead(false)
                .registerWriteHandler(new CustomCellWriteHandlerTwo(exportDataDTOS, tripleHeader))
                .sheet(sheetName).doWrite(
                        dataList(data,
                                exportDataDTOS.stream().map(ExportDataDTO::getField).collect(Collectors.toList()),
                                exportDataDTOS));
    }

    /**
     * 设置Excel头
     *
     * @param exportDataDTOS 导出数据信息
     * @return
     */
    public static List<List<String>> head(List<ExportDataDTO> exportDataDTOS) {
        List<List<String>> list = new ArrayList<>();
        for (ExportDataDTO value : exportDataDTOS) {
            List<String> head = new ArrayList<>();
            if (StrUtil.isNotEmpty(value.getHeader())) {
                head.add(value.getHeader());
            }
            head.add(value.getTitle());
            head.add(value.getField());
            list.add(head);
        }
        return list;
    }

    /**
     * 设置表格信息
     *
     * @param dataList 查询出的数据
     * @param fileList 需要显示的字段
     * @return
     */
    public static List<List<Object>> dataList(List<Object> dataList, List<String> fileList,
                                              List<ExportDataDTO> exportDataDTOS)
    {
        List<List<Object>> list = new ArrayList<>();
        for (Object person : dataList) {
            List<Object> data = new ArrayList<>();
            for (String fieldName : fileList) {
                boolean su = false;
                for (ExportDataDTO exportDataDTO : exportDataDTOS) {
                    //枚举、字典、主数据格式化
                    if (fieldName.equals(exportDataDTO.getField()) &&
                            CollectionUtil.isNotEmpty(exportDataDTO.getExportEnum())) {
                        if (ObjectUtil.isNotEmpty(getFieldValue(fieldName, person))) {
                            ExportEnumDataDTO exportEnumDataDTO = exportDataDTO.getExportEnum().stream()
                                    .filter(s -> s.getCode().equals(String.valueOf(getFieldValue(fieldName, person))))
                                    .toList().stream().findFirst().orElse(null);
                            if (ObjectUtil.isNotNull(exportEnumDataDTO)) {
                                data.add(exportEnumDataDTO.getCodeWithDesc());
                                su = true;
                                break;
                            }
                        } else {
                            ExportEnumDataDTO exportEnumDataDTO = exportDataDTO.getExportEnum().stream()
                                    .filter(s -> StrUtil.isEmpty(s.getCode()) &&
                                            StrUtil.isNotEmpty(s.getCodeWithDesc()))
                                    .toList().stream().findFirst().orElse(null);
                            if (ObjectUtil.isNotNull(exportEnumDataDTO)) {
                                data.add(exportEnumDataDTO.getCodeWithDesc());
                                su = true;
                                break;
                            }
                        }
                    }
                    //日期格式化
                    if (fieldName.equals(exportDataDTO.getField()) &&
                            StrUtil.isNotEmpty(exportDataDTO.getExportDateFormatter())) {
                        if (ObjectUtil.isNotEmpty(getFieldValue(fieldName, person))) {
                            try {
                                LocalDateTime localDateTime = LocalDateTimeUtil.parse(
                                        String.valueOf(getFieldValue(fieldName, person)));
                                data.add(LocalDateTimeUtil.format(localDateTime, "dateTime".equals(
                                        exportDataDTO.getExportDateFormatter()) ? DatePattern.NORM_DATETIME_PATTERN :
                                        DatePattern.NORM_DATE_PATTERN));
                                su = true;
                                break;
                            } catch (Exception e) {
                                DateTime dateTime = DateUtil.parse(
                                        String.valueOf(getFieldValue(fieldName, person)));
                                data.add(DateUtil.format(dateTime, "dateTime".equals(
                                        exportDataDTO.getExportDateFormatter()) ? DatePattern.NORM_DATETIME_PATTERN :
                                        DatePattern.NORM_DATE_PATTERN));
                                su = true;
                                break;
                            }
                        }
                    }
                }
                if (!su) {
                    //通过反射根据需要显示的字段，获取对应的属性值
                    //是map直接处理
                    if (person instanceof Map) {
                        data.add(MapUtil.getStr((Map) person, fieldName));
                    } else {
                        data.add(getFieldValue(fieldName, person));
                    }
                }
            }
            list.add(data);
        }
        return list;
    }

    /**
     * 根据传入的字段获取对应的get方法，如name,对应的getName方法
     *
     * @param fieldName 字段名
     * @param person    对象
     * @return
     */
    public static Object getFieldValue(String fieldName, Object person) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = person.getClass().getMethod(getter);
            return method.invoke(person);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 通过userAgent 解析 下载文件
     *
     * @param exportTitle 下载文件名
     * @param agent       request userAgent
     * @return 解析后的文件名称
     */
    public static String parseAgentDownloadFIleName(String exportTitle, String agent) {
        String enableFileName;
        try {
            if (agent != null && StrUtil.containsIgnoreCase(agent, "firefox")) {
                //火狐浏览器下载附件乱码
                enableFileName = "=?UTF-8?B?" +
                        (new String(Base64.encodeBase64(exportTitle.getBytes("UTF-8")))) + "?=";

            } else if (agent != null && StrUtil.containsIgnoreCase(agent, "safari")) {
                //safari浏览器下载附件
                enableFileName = new String(exportTitle.getBytes("UTF-8"), "ISO-8859-1");
            } else {
                //其他浏览器下载附件
                enableFileName = URLEncoder.encode(exportTitle, "utf-8");
            }
        } catch (UnsupportedEncodingException e) {

            enableFileName = exportTitle;
        }
        return enableFileName;
    }

    /**
     * Excel中插入图片
     *
     * @param workbook 工作簿
     * @param sheet    工作簿中的表
     */
    public static void insertPatriarch(XSSFWorkbook workbook, XSSFSheet sheet, String picturePath) {
        // 画图的顶级管理器 注:一个sheet只能获取一个
        XSSFDrawing drawing = sheet.createDrawingPatriarch();

        try {
            ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
            BufferedImage bufferImg = ImageIO.read(new File(picturePath));
            ImageIO.write(bufferImg, "png", byteArrayOut);
            // anchor主要用于设置图片的属性 dx1,dy1,dx2,dy2指坐标(瞎猜的 没百度到解释 API代码上的解释没看懂)
            ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, (short) 0, 0, (short) 4, 5);
            anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);

            drawing.createPicture(anchor,
                    workbook.addPicture(byteArrayOut.toByteArray(), XSSFWorkbook.PICTURE_TYPE_BMP));
        } catch (IOException e) {
            e.printStackTrace();

        }
    }

    public static void trendsModelWriteForMap(HttpServletResponse response, String fileName, String sheetName,
                                              List<Map<String, Object>> planDetail, List<ExportDataDTO> exportDataDTOS,
                                              boolean tripleHeader) throws IOException
    {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName) + ".xlsx");
        //需要先生成模版
        final File tempFile = FileUtil.createTempFile();
        EasyExcel.write(tempFile).registerConverter(new LongStringConverter())
                .head(head1(exportDataDTOS))
                .registerWriteHandler(new CustomCellWriteHandler(exportDataDTOS, tripleHeader))
                .sheet(sheetName).doWrite(Collections.emptyList());
        //需要填充数据
        EasyExcel.write(response.getOutputStream()).withTemplate(tempFile).sheet().doFill(planDetail);
    }

    private static List<List<String>> head1(List<ExportDataDTO> exportDataDTOS) {
        List<List<String>> list = new ArrayList<>();
        for (ExportDataDTO value : exportDataDTOS) {
            List<String> head = new ArrayList<>();
            if (StrUtil.isNotEmpty(value.getHeader())) {
                head.add(value.getHeader());
            }
            head.add(value.getTitle());
            head.add(value.getField());
            head.add(StrUtil.DELIM_START + StrUtil.DOT + value.getField() + StrUtil.DELIM_END);
            list.add(head);
        }
        return list;
    }

}
