package com.njcn.csharmonic.utils.report;

import cn.hutool.core.collection.CollUtil;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.utils.FileStorageUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
@RequiredArgsConstructor
public class WordUtil2 {


    private final ResourceLoader resourceLoader;
    private final FileStorageUtil fileStorageUtil;


    public void getWord(String path, Map<String, Object> params, String fileName,List<List<String[]>> tableList, HttpServletResponse response)
            throws Exception {
        byte[] docBytes = null;

        path = ClearPathUtil.cleanString(path);
        InputStream is = null;
        CustomXWPFDocument doc = null;
        //读取报告模板
        try {
            Resource resource = resourceLoader.getResource("classpath:" + path);
            if (!resource.exists()) {
                throw new FileNotFoundException("模板文件未找到: " + path);
            }
            is = resource.getInputStream();
            doc = new CustomXWPFDocument(is);
            if(CollUtil.isNotEmpty(tableList)){
                this.replaceInTable(doc, params,tableList);
            }else{
                this.replaceInTable(doc, params);
            }
             // 替换表格里面的变量
            this.replaceInPara(doc, params); // 替换文本里面的变量
        } catch (IOException e) {
            getError("获取报告模板异常，原因为：" + e);
        } finally {
            if (null != is) {
                is.close();
            }
        }
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentType("application/octet-stream;charset=UTF-8");
            doc.write(outputStream);
            outputStream.close();
        } catch (Exception e) {
            getError("输出稳态报告异常，原因为：" + e);
        } finally {
            if (doc != null) {
                doc.close();
            }
        }
    }




    // 辅助方法：字节数组转十六进制（便于打印排查）
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }




    public InputStream getReportFile(String path, Map<String, Object> params,  List<List<String[]>> tableList)
            throws Exception {
        path = ClearPathUtil.cleanString(path);
        InputStream inStream = null,in = null;
        CustomXWPFDocument doc = null;
        //读取报告模板
        try {
            inStream =  new ClassPathResource(path).getInputStream();
            doc = new CustomXWPFDocument(inStream);
            this.replaceInTable(doc,params,tableList);
            this.replaceInPara(doc, params);
        } catch (IOException e) {
            getError("获取报告模板异常，原因为：" + e);
        } finally {
            if (null != inStream) {
                inStream.close();
            }
        }
        try {
            //临时缓冲区
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            doc.write(out);
            byte[] docByteAry = out.toByteArray();
            in = new ByteArrayInputStream(docByteAry);
            out.close();
        } catch (Exception e) {
            getError("输出稳态报告异常，原因为：" + e);
        } finally {
            if (doc != null) {
                doc.close();
            }
        }
        return in;
    }



    public String getReportFileUrl(String path,String fileName,List<List<String[]>> tableList, Map<String, Object> params)
            throws Exception {
        path = ClearPathUtil.cleanString(path);
        InputStream inStream = null,in = null;
        CustomXWPFDocument doc = null;
        //读取报告模板
        try {
            inStream =  new ClassPathResource(path).getInputStream();;
            doc = new CustomXWPFDocument(inStream);
            if(CollUtil.isNotEmpty(tableList)){
                this.replaceInTable(doc, params,tableList);
            }else{
                this.replaceInTable(doc, params);
            }
            this.replaceInPara(doc, params); // 替换文本里面的变量
            //临时缓冲区
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            doc.write(out);
            byte[] docByteAry = out.toByteArray();
            in = new ByteArrayInputStream(docByteAry);
            out.close();
        } catch (IOException e) {
            getError("获取报告模板异常，原因为：" + e);
        } finally {
            if (null != inStream) {
                inStream.close();
            }
            if (doc != null) {
                doc.close();
            }
        }
        //上传文件服务器
        return fileStorageUtil.uploadStreamSpecifyName(in, OssPath.ONLINE_REPORT, fileName);
    }


    private static void getError(String e) {
        log.error(e);
    }
    /**
     * 替换段落里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        List<XWPFParagraph> paragraphList = new ArrayList<>();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            paragraphList.add(para);
        }
        processParagraphs(paragraphList, params, doc);
    }

    private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFTable> it = doc.getTablesIterator();
        while (it.hasNext()) {
            XWPFTable table = it.next();
            List<XWPFTableRow> rows = table.getRows();
            for (XWPFTableRow row : rows) {
                List<XWPFTableCell> cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
                    processParagraphs(paragraphListTable, params, doc);
                }
            }
        }
    }

    public static void processParagraphs(List<XWPFParagraph> paragraphList, Map<String, Object> param,
                                         CustomXWPFDocument doc) {
        if (paragraphList != null && paragraphList.size() > 0) {
            for (XWPFParagraph paragraph : paragraphList) {
                List<XWPFRun> runs = paragraph.getRuns();
                if (runs.size() > 0) {
                    for (XWPFRun run : runs) {
                        String bflag = "";
                        String text = run.getText(0);
                        if (text != null) {
                            boolean isSetText = false;
                            for (Entry<String, Object> entry : param.entrySet()) {
                                String key = entry.getKey();
                                if (text.indexOf(key) != -1) {
                                    isSetText = true;
                                    Object value = entry.getValue();
                                    if (value instanceof String) {// 文本替换
                                        text = text.replace(key, value.toString());
                                    } else if (value instanceof Map) {// 图片替换
                                        text = text.replace(key, "");
                                        Map pic = (Map) value;
                                        int width = Integer.parseInt(pic.get("width").toString());
                                        int height = Integer.parseInt(pic.get("height").toString());
                                        int picType = getPictureType(pic.get("type").toString());
                                        byte[] byteArray = (byte[]) pic.get("content");
                                        ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                                        try {
                                            String s = doc.addPictureData(byteInputStream, picType);

                                            doc.createPicture(doc.getAllPictures().size() - 1, width, height,
                                                    s,paragraph);
                                            bflag = "break";
                                        } catch (Exception e) {
                                            getError("文本替换发生异常，异常是" + e.getMessage());
                                        }
                                    }
                                }
                            }
                            if (isSetText) {
                                run.setText(text, 0);
                            }
                        }
                        if (bflag == "break") {
                            break;
                        }
                    }
                }
            }
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param para   要替换的段落
     * @param params 参数
     */
    private void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
        List<XWPFRun> runs;
        Matcher matcher;
        // if (this.matcher(para.getParagraphText()).find()) {
        runs = para.getRuns();
        int start = -1;
        int end = -1;
        String str = "";
        for (int i = 0; i < runs.size(); i++) {
            XWPFRun run = runs.get(i);
            String runText = run.toString();
            if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) {
                start = i;
            }
            if ((start != -1)) {
                str += runText;
            }
            if ('}' == runText.charAt(runText.length() - 1)) {
                if (start != -1) {
                    end = i;
                    break;
                }
            }
        }

        for (int i = start; i <= end; i++) {
            para.removeRun(i);
            i--;
            end--;
        }

        for (Entry<String, Object> entry : params.entrySet()) {
            String key = entry.getKey();
            if (str.indexOf(key) != -1) {
                Object value = entry.getValue();
                if (value instanceof String) {
                    str = str.replace(key, value.toString());
                    para.createRun().setText(str, 0);
                    break;
                } else if (value instanceof Map) {
                    str = str.replace(key, "");
                    Map pic = (Map) value;
                    int width = Integer.parseInt(pic.get("width").toString());
                    int height = Integer.parseInt(pic.get("height").toString());
                    int picType = getPictureType(pic.get("type").toString());
                    byte[] byteArray = (byte[]) pic.get("content");
                    ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                    try {
                        // int ind = doc.addPicture(byteInputStream,picType);
                        // doc.createPicture(ind, width , height,para);
                        String s = doc.addPictureData(byteInputStream, picType);
                        doc.createPicture(doc.getAllPictures().size() - 1, width, height,s, para);
                        para.createRun().setText(str, 0);
                        break;
                    } catch (Exception e) {
                        getError("文件替换发生异常，异常是" + e.getMessage());
                    }
                }
            }
        }
        // }
    }

    /**
     * 为表格插入数据，行数不够添加新行
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     */
    private static void insertTable(XWPFTable table, List<String[]> tableList) {
        //删除占位符行数
        table.removeRow(1);
        if (CollUtil.isNotEmpty(tableList)) {
            // 创建行,根据需要插入的数据添加新行，不处理表头
            for (int i = 0; i < tableList.size(); i++) {
                XWPFTableRow row = table.createRow();
                List<XWPFTableCell> cells = row.getTableCells();
                for (int j = 0; j < cells.size(); j++) {
                    String s = tableList.get(i)[j];
                    XWPFTableCell cell = cells.get(j);
                    cell.removeParagraph(0);
                    XWPFParagraph paragraph = cell.addParagraph();
                    paragraph.setAlignment(ParagraphAlignment.CENTER);
                    // 在段落中添加文本
                    XWPFRun run = paragraph.createRun();
                    run.setText(s);
                }
            }
        }
    }

    /**
     * 替换表格里面的变量
     *
     * @param doc    要替换的文档
     * @param tableList    存放数据顺序要与表格一致
     * @param params 参数
     */
    private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<List<String[]>> tableList) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        Integer num=0;
        while (iterator.hasNext()) {
            table = iterator.next();
            if (table.getRows().size() > 1) {
                // 判断表格是需要替换还是需要插入，判断逻辑有$为替换，表格无$为插入
                if (this.matcher(table.getText()).find()||this.matcherS(table.getText()).find()) {
                    rows = table.getRows();
                    for (XWPFTableRow row : rows) {
                        cells = row.getTableCells();
                        for (XWPFTableCell cell : cells) {
                            List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
                            processParagraphs(paragraphListTable, params, doc);
                        }
                    }
                }else {
                    if (CollUtil.isNotEmpty(tableList.get(num))){
                        insertTable(table, tableList.get(num)); // 插入数据
                    }
                    num++;
                }
            }
        }
    }

    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    private Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }
    private Matcher matcherS(String str) {
        Pattern pattern = Pattern.compile("\\$(.*?)\\$");
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }

    /**
     * 根据图片类型，取得对应的图片类型代码
     *
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType) {
        int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
        if (picType != null) {
            if (picType.equalsIgnoreCase("image/png")) {
                res = CustomXWPFDocument.PICTURE_TYPE_PNG;
            } else if (picType.equalsIgnoreCase("image/dib")) {
                res = CustomXWPFDocument.PICTURE_TYPE_DIB;
            } else if (picType.equalsIgnoreCase("image/emf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_EMF;
            } else if (picType.equalsIgnoreCase("image/jpg") || picType.equalsIgnoreCase("image/jpeg")) {
                res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
            } else if (picType.equalsIgnoreCase("image/wmf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }

}
