package com.yuchen.saas.park.service.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.fastjson.JSON;
import com.yuchen.saas.park.api.dto.NoModelWriteDataDTO;
import com.yuchen.saas.park.service.exl.ExcelCellWriteHandler;
import com.yuchen.saas.park.service.exl.ExcelRowWriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
public class ExcelUtils {

    public static <T> void fillExcelWithTemplate(HttpServletResponse response, InputStream in, Class<T> cls, List<T> list) {
        fillExcelWithTemplate(response, in, cls, null, list, null, null);
    }

    /**
     * @param response  返回响应
     * @param in        模板
     * @param cls       每个行数据对应的实体类
     * @param obj       统计数据
     * @param list      填充数据list
     * @param map       表格的特殊字段，例如：出表日期等
     * @param sheetName 表格的sheet名
     * @author Guess
     */
    public static <T> void fillExcelWithTemplate(HttpServletResponse response, InputStream in,
                                                 Class<T> cls, Object obj, List<T> list, Map<String, Object> map, String sheetName) {
        responseSet(response);
        ExcelWriter excelWriter = null;
        try {
            if (in != null) {
                excelWriter = EasyExcel.write(response.getOutputStream(), cls)
                        .withTemplate(in).build();
                WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                excelWriter.fill(list, fillConfig, writeSheet);
                if (obj != null) {
                    excelWriter.fill(obj, writeSheet);
                }
                if (map != null) {
                    excelWriter.fill(map, writeSheet);
                }
            }
        } catch (IOException e) {
            log.info("ExcelUtil | fillExcelWithTemplate | error:{}", e.getMessage(), e);
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    /**
     * @param in   模板
     * @param cls  每个行数据对应的实体类
     * @param list 填充数据list
     * @author Guess
     */
    public static <T> byte[] fillExcel(InputStream in, Class<T> cls, List<T> list) throws IOException {
        ExcelWriter excelWriter = null;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            if (in != null) {
                excelWriter = EasyExcel.write(os, cls).excelType(ExcelTypeEnum.XLS)
                        .withTemplate(in).build();
                if (CollectionUtils.isNotEmpty(list)) {
                    WriteSheet writeSheet = EasyExcel.writerSheet().build();
                    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                    excelWriter.fill(list, fillConfig, writeSheet);
                }
            }
        } catch (Exception e) {
            log.info("ExcelUtil | fillExcel | error: {}", e.getMessage());
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
                in.close();
            }
        }
        return os.toByteArray();
    }

    /**
     * @param in   模板
     * @param cls  每个行数据对应的实体类
     * @param list 填充数据list
     * @author Guess
     */
    public static <T> byte[] fillExcelXLSX(InputStream in, Class<T> cls, List<T> list) throws IOException {
        ExcelWriter excelWriter = null;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            if (in != null) {
                excelWriter = EasyExcel.write(os, cls).excelType(ExcelTypeEnum.XLSX)
                        .withTemplate(in).build();
                if (CollectionUtils.isNotEmpty(list)) {
                    WriteSheet writeSheet = EasyExcel.writerSheet().build();
                    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                    excelWriter.fill(list, fillConfig, writeSheet);
                }
            }
        } catch (Exception e) {
            log.info("ExcelUtil | fillExcel | error: {}", e.getMessage());
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
                in.close();
            }
        }
        return os.toByteArray();
    }

    public static <T> void fillExcel(HttpServletResponse response, Class<T> cls, List<T> list, String sheetName) {
        responseSet(response);

        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(response.getOutputStream(), cls).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
            excelWriter.write(list, writeSheet);
        } catch (IOException e) {
            log.info("ExcelUtil.fillExcelWithTemplate: errorMsg: {}", e.getMessage());
        } finally {
            assert excelWriter != null;
            excelWriter.finish();
        }
    }

    public static <T> void fillExcel(HttpServletResponse response, Class<T> cls, List<T> list, String sheetName, CellWriteHandler handler) {
        responseSet(response);

        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(response.getOutputStream(), cls).registerWriteHandler(handler).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
            excelWriter.write(list, writeSheet);
        } catch (IOException e) {
            log.info("ExcelUtil.fillExcelWithTemplate: errorMsg: {}", e.getMessage());
        } finally {
            assert excelWriter != null;
            excelWriter.finish();
        }
    }

    public static <T> byte[] fillExcelReturnByte(Class<T> cls, List<T> list, String sheetName) {
        ExcelWriter excelWriter = null;
        OutputStream outputStream = new ByteArrayOutputStream();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        responseSet(response);
        try {
            excelWriter = EasyExcel.write(response.getOutputStream(), cls).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
            excelWriter.write(list, writeSheet);
            response.flushBuffer();
            return new byte[0];
        } catch (Exception e) {
            log.info("ExcelUtil.fillExcelReturnByte: errorMsg: {}", e.getMessage());
        } finally {
            assert excelWriter != null;
            excelWriter.finish();
            try {
                outputStream.close();
            } catch (IOException e) {
            }
        }
        return new byte[0];
    }

    public static void fillExcel(HttpServletResponse response, List<List<String>> list, List<List<String>> headline, String sheetName) {
        responseSet(response);

        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(response.getOutputStream()).head(headline).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
            excelWriter.write(list, writeSheet);
        } catch (IOException e) {
            log.info("ExcelUtil.fillExcelWithTemplate: errorMsg: {}", e.getMessage());
        } finally {
            assert excelWriter != null;
            excelWriter.finish();
        }
    }

    //设置表头
    private List<List<String>> head(String[] headMap) {
        List<List<String>> list = new ArrayList<List<String>>();

        for (String head : headMap) {
            List<String> headList = new ArrayList<String>();
            headList.add(head);
            list.add(headList);
        }
        return list;
    }

    //设置导出的数据内容
    private List<List<Object>> dataList(List<Map<String, Object>> dataList, String[] dataStrMap) {
        List<List<Object>> list = new ArrayList<List<Object>>();
        for (Map<String, Object> map : dataList) {
            List<Object> data = new ArrayList<Object>();
            for (int i = 0; i < dataStrMap.length; i++) {
                data.add(map.get(dataStrMap[i]));
            }
            list.add(data);
        }
        return list;
    }

    public static <T> void fillTaxExcel(String fileName, HttpServletResponse response, Class<T> cls, List<T> list) {
        responseSet(response, fileName);

        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(response.getOutputStream(), cls).build();
            WriteSheet writeSheet = EasyExcel.writerSheet("sheet1").build();
            excelWriter.write(list, writeSheet);
        } catch (IOException e) {
            log.info("ExcelUtil.fillExcelWithTemplate: errorMsg: {}", e.getMessage());
        } finally {
            assert excelWriter != null;
            excelWriter.finish();
        }
    }

    /**
     * @param in  模板
     * @param cls 每个行数据对应的实体类
     * @param map 表格的特殊字段，例如：出表日期等
     * @author Guess
     */
    public static <T> void fillExcel(HttpServletResponse response, InputStream in, Class<T> cls, Map<String, Object> map) {
        responseSet(response);
        ExcelWriter excelWriter = null;
        try {
            if (in != null) {
                excelWriter = EasyExcel.write(response.getOutputStream(), cls)
                        .withTemplate(in).build();
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                if (map != null) {
                    excelWriter.fill(map, writeSheet);
                }
            }

        } catch (Exception e) {
            log.info("error: {}", e.getMessage());
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    public static void noModelTempWriteExport(NoModelWriteDataDTO data, HttpServletResponse response) throws IOException {
        try {
            responseSet(response, data.getFileName());
            // 这里需要设置不关闭流
            EasyExcel.write(response.getOutputStream())
                    .head(data.getHeadMap())
                    .sheet(data.getFileName())
                    .registerWriteHandler(new ExcelCellWriteHandler())
                    .registerWriteHandler(new ExcelRowWriteHandler())
                    .doWrite(data.getDataList());
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }

    }

    private static void responseSet(HttpServletResponse response) {
        response.reset();
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + System.currentTimeMillis() + ".xlsx");
    }

    private static void responseSet(HttpServletResponse response, String fileName) {
        try {
            response.reset();
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf-8");
            // URLEncoder.encode可以防止中文乱码
            String file = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + file + ".xlsx");
        } catch (Exception e) {
            log.warn(e.getMessage());
        }
    }

    /**
     * 将bytes写进zipOutputStream
     *
     * @param bytes
     * @param zipOutputStream
     * @param templateName
     */
    public static void setToZip(byte[] bytes, ZipOutputStream zipOutputStream, String templateName) {
        try {
            zipOutputStream.putNextEntry(new ZipEntry(templateName + ".xls"));
            IOUtils.write(bytes, zipOutputStream);
            zipOutputStream.closeEntry();
        } catch (IOException e) {
            log.info("ExcelUtil | setToZip | error: {}", e.getMessage());
        }
    }

    /**
     * @param in
     * @param cls
     * @param list
     * @param <t>
     * @return
     */
    public static <t> byte[] getBytes(InputStream in, Class<t> cls, List<t> list) {
        byte[] bytes = new byte[0];
        try {
            bytes = fillExcel(in, cls, list);
        } catch (IOException e) {
            log.info("ExcelUtil | getBytes | error: {}", e.getMessage());
        }
        return bytes;
    }

    /**
     * @param in
     * @param cls
     * @param list
     * @param <t>
     * @return XLSX
     */
    public static <t> byte[] getBytesXLSX(InputStream in, Class<t> cls, List<t> list) {
        byte[] bytes = new byte[0];
        try {
            bytes = fillExcelXLSX(in, cls, list);
        } catch (IOException e) {
            log.info("ExcelUtil | getBytes | error: {}", e.getMessage());
        }
        return bytes;
    }

    public static void resetResponseHeader(HttpServletResponse response, byte[] bytes, String fileName) {
        response.reset();
        response.setHeader("Accept-ranges", "bytes");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        response.addHeader("Content-Length", "" + bytes.length);
        response.setContentType("application/octet-stream; charset=UTF-8");
    }

}
