package com.njcn.event.common.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.njcn.advance.api.EventCauseFeignClient;
import com.njcn.advance.pojo.dto.EventAnalysisDTO;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.pq.pojo.vo.AreaLineInfoVO;
import com.njcn.echarts.pojo.constant.PicCommonData;
import com.njcn.echarts.util.DrawPicUtil;
import com.njcn.event.common.mapper.RmpEventDetailMapper;
import com.njcn.event.common.pojo.dto.EventEigDetail;
import com.njcn.event.common.pojo.dto.EventInfoDetailVO;
import com.njcn.event.common.pojo.dto.LineDetailDataCommDTO;
import com.njcn.event.common.service.CommMonitorEventReportService;
import com.njcn.event.common.service.EventAnalysisService;
import com.njcn.event.common.service.EventReportService;
import com.njcn.event.common.service.WaveService;
import com.njcn.event.common.utils.WordUtil;
import com.njcn.event.common.utils.WordUtils;
import com.njcn.event.file.component.WavePicComponent;
import com.njcn.event.file.pojo.bo.WaveDataDetail;
import com.njcn.event.file.pojo.dto.WaveDataDTO;
import com.njcn.event.file.utils.WaveUtil;
import com.njcn.event.pojo.param.ExportParam;
import com.njcn.event.pojo.param.StatisticsParam;
import com.njcn.event.pojo.po.EventDetail;
import com.njcn.event.pojo.po.RmpEventDetailPO;
import com.njcn.event.pojo.vo.*;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.pojo.po.DictData;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * pqs
 *
 * @author cdf
 * @date 2026/1/17
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class CommMonitorEventReportServiceImpl implements CommMonitorEventReportService {


    private final DicDataFeignClient dicDataFeignClient;
    private final EventReportService eventReportService;
    private final EventAnalysisService eventAnalysisService;
    private final RmpEventDetailMapper rmpEventDetailMapper;
    private final WaveService waveService;
    private final DrawPicUtil drawPicUtil;
    private final WavePicComponent wavePicComponent;
    private final FileStorageUtil fileStorageUtil;
    private final LineFeignClient lineFeignClient;
    private final EventCauseFeignClient eventCauseFeignClient;
    @Resource(name="asyncExecutor")
    private  Executor executor;

    /**
     * 监测点导出word
     *
     * @param exportParam .
     * @param response    .
     */
    @Override
    public void getLineExport(ExportParam exportParam, LineDetailDataCommDTO lineDetailData, HttpServletResponse response) {
        //创建word文档(poi生成word)
        XWPFDocument doc = new XWPFDocument(); //创建Word文件
        //设置标题样式
        WordUtils.setHeadingStyle(doc);
        XWPFParagraph p = doc.createParagraph(); //新建一个段落
        //设置对齐
        p.setAlignment(ParagraphAlignment.CENTER);
        p.setVerticalAlignment(TextAlignment.CENTER);
        XWPFRun r = p.createRun();//创建段落文本
        r.addBreak();
        r.addBreak();
        r.addBreak();
        r.addBreak();
        r.addBreak();
        r.addBreak();
        r.setText("");
        r.setBold(true);//设置为粗体
        r.setFontSize(14);//字体大小
        r.addBreak();

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun r1 = p.createRun();//创建段落文本
        r1.setText("电压暂降事件分析报告");
        r1.setBold(true);//设置为粗体
        r1.setFontSize(36);//字体大小
        r1.addBreak();
        r1.addBreak();
        r1.addBreak();
        r1.addBreak();
        r1.addBreak();
        r1.addBreak();
        r1.addBreak();

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun r2 = p.createRun();//创建段落文本
        //获取当前时间
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy 年 MM 月 dd 日");
        String time = dateFormat.format(date);

        r2.setText("日期: " + time);
        r2.setBold(true);//设置为粗体
        r2.setFontSize(14);//字体大小

        r2.addBreak();
        r2.addBreak();
        r2.addBreak();

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun r3 = p.createRun();//创建段落文本
        r3.setText("电压暂降事件区域报告");
        r3.setFontSize(24);//字体大小

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.LEFT);
        createTitle(doc, "1. 引言", "标题 1", 0, 15);

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.BOTH);
        XWPFRun r5 = p.createRun();//创建段落文本
        r5.setText("对所选中区间内电压暂降事件进行分析，能够直观清晰查看响应的暂降事件信息。");
        r5.setFontSize(11);//字体大小

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.LEFT);
        createTitle(doc, "2. 报告分析对象", "标题 1", 0, 15);

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.BOTH);
        XWPFRun r7 = p.createRun();//创建段落文本
        r7.setText(exportParam.getLineName());
        r7.setFontSize(11);//字体大小

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.LEFT);
        createTitle(doc, "3. 报告分析时间", "标题 1", 0, 15);

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.BOTH);
        XWPFRun r9 = p.createRun();//创建段落文本
        r9.setText(exportParam.getSearchBeginTime() + " 至 " + exportParam.getSearchEndTime());
        r9.setFontSize(11);//字体大小

        p = doc.createParagraph(); //新建一个段落
        p.setAlignment(ParagraphAlignment.LEFT);
        createTitle(doc, "4. 总汇信息", "标题 1", 0, 15);

        //查询参数
        StatisticsParam param = new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(), exportParam.getFlag(), lineDetailData.getIsDip());
        //获取暂降原因字典
        List<DictData> reasonData = dicDataFeignClient.getDicDataByTypeName(DicDataTypeEnum.EVENT_REASON.getName()).getData();
        //获取暂降类型字典
        List<DictData> typeData = dicDataFeignClient.getDicDataByTypeName(DicDataTypeEnum.EVENT_TYPE.getName()).getData();
        //influxdb查询结果（pqs_eventdetail表）
        List<EventDetail> info = info(param);

        //记录数
        int i = 1;
        //1.监测点信息
        if (exportParam.isXq()) {
            createTitle(doc, "4." + i + " 监测点信息", "标题 2", 200, 15);
            XWPFTable table = createTable(doc);
            XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
            insertRow(doc, table, centerParagraph, true, "项目", "描述");
            insertRow(doc, table, centerParagraph, false, "监测点名称", lineDetailData.getLineName());
            insertRow(doc, table, centerParagraph, false, "电压等级", lineDetailData.getScale());
            insertRow(doc, table, centerParagraph, false, "PT变比", lineDetailData.getPt());
            insertRow(doc, table, centerParagraph, false, "CT变比", lineDetailData.getCt());
            insertRow(doc, table, centerParagraph, false, "协议容量", lineDetailData.getDealCapacity() + "");
            insertRow(doc, table, centerParagraph, false, "设备容量", lineDetailData.getDevCapacity() + "");
            insertRow(doc, table, centerParagraph, false, "最小短路容量", lineDetailData.getShortCapacity() + "");
            insertRow(doc, table, centerParagraph, false, "接线方式", lineDetailData.getPtType());
            if (exportParam.getType() == 0) {
                insertRow(doc, table, centerParagraph, false, "基准容量", lineDetailData.getStandardCapacity() + "");
            }
            i++;
        }
        //2.暂降事件暂降事件列表和暂降点图
        //2.1.判断列表和点图是否是要导出
        if (exportParam.isLb() || exportParam.isSjdF47() || exportParam.isSjdITIC()) {
//            List<EventDetail> plot = eventAnalysisService.getPlot(param);
            List<EventDetail> plot = eventReportService.getPlot(info, reasonData, typeData);
            //暂降事件列表
            if (exportParam.isLb()) {
                createTitle(doc, "4." + i + " 暂降事件列表", "标题 2", 200, 15);
                XWPFTable table = createTable(doc);
                XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
                insertRow(doc, table, centerParagraph, true, "序号", "暂降发生时刻", "暂降幅值(%)", "持续时间(s)", "暂降类型", "暂降原因", "严重度");
                for (int j = 0; j < plot.size(); j++) {
                    EventDetail eventDetail = plot.get(j);
                    String s = eventDetail.getStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));

                    insertRow(doc, table, centerParagraph, false, j + 1 + "", s, BigDecimal.valueOf(eventDetail.getFeatureAmplitude() * 100).setScale(2, RoundingMode.HALF_UP).toString(), eventDetail.getDuration() + "", Objects.isNull(eventDetail.getAdvanceType()) ? "/" : eventDetail.getAdvanceType(), Objects.isNull(eventDetail.getAdvanceReason()) ? "/" : eventDetail.getAdvanceReason(), Objects.isNull(eventDetail.getSeverity()) ? "/" : eventDetail.getSeverity() + "");
                }
                i++;
            }
            //暂降事件点图
            if (exportParam.isSjdF47() || exportParam.isSjdITIC()) {
                ArrayList<List<Double>> ass = getAss(plot);
                createTitle(doc, "4." + i + " 暂降事件点图", "标题 2", 200, 15);
                int two = 1;
                if (exportParam.isSjdITIC()) {
                    createTitle(doc, "4." + i + "." + two + " ITIC 曲线", "标题 3", 400, 15);
                    String itic = drawPicUtil.drawItic(ass);
                    createPic(doc, itic, "ITIC曲线");
                    two++;
                }
                if (exportParam.isSjdF47()) {
                    createTitle(doc, "4." + i + "." + two + " F47 曲线", "标题 3", 400, 15);
                    String f47 = drawPicUtil.drawF47(ass);
                    createPic(doc, f47, "SEMI F47曲线");
                    two++;
                }
                i++;
            }
        }


        //3.暂降密度
        if (exportParam.isMdbg() || exportParam.isMdtx()) {
            createTitle(doc, "4." + i + " 暂降密度", "标题 2", 200, 15);
            int two = 1;
            if (exportParam.isMdtx()) {
                createTitle(doc, "4." + i + "." + two + " 暂降密度点图", "标题 3", 400, 15);
                Integer[][] eventDensityData = eventReportService.getCoords(info);
                String str = drawPicUtil.drawEventDensity(eventDensityData);
                createPic(doc, str, "暂降密度图");
                two++;
            }
            if (exportParam.isMdbg()) {
                XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
                createTitle(doc, "4." + i + "." + two + " DISDIP 表格:国际发配电联盟(UNIPEDE)", "标题 3", 400, 15);
//                List<DISDIPVO> eventDisdip = eventAnalysisService.eventDisdip(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
                List<DISDIPVO> eventDisdip = eventReportService.eventDisdip(info);

                XWPFTable table1 = createTable(doc);
                insertRow(doc, table1, centerParagraph, true, "剩余电压", "20ms", "100ms", "500ms", "1s", "3s", "20s", "60s", "180s");
                for (int j = 0; j < eventDisdip.size(); j++) {
                    DISDIPVO disdipvo = eventDisdip.get(j);
                    insertRow(doc, table1, centerParagraph, false, disdipvo.getName(), disdipvo.getTwentyMs(), disdipvo.getOneHundredMs(), disdipvo.getFiveHundredMs(), disdipvo.getOneS(), disdipvo.getThreeS(), disdipvo.getTwentyS(), disdipvo.getSixtyS(), disdipvo.getOneEightyS());
                }
                two++;
                createTitle(doc, "4." + i + "." + two + " IEC 61000-4-11:(用电终端的电压暂降抗度)", "标题 3", 400, 15);
//                List<IEC411VO> iec411VOS = eventAnalysisService.IEC411(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
                List<IEC411VO> iec411VOS = eventReportService.IEC411(info);
                XWPFTable table2 = createTable(doc);
                insertRow(doc, table2, centerParagraph, true, "剩余电压", "10~20ms", "20~100ms", "0.1~0.2s", "0.2~0.5s", "0.5~1s", ">1s");
                for (int j = 0; j < iec411VOS.size(); j++) {
                    IEC411VO iec411VO = iec411VOS.get(j);
                    insertRow(doc, table2, centerParagraph, false, iec411VO.getName(), iec411VO.getTenTwentyMs(), iec411VO.getTwentyOneHundredMs(), iec411VO.getZeroPiontOneTwoS(), iec411VO.getZeroPiontTwoFiveS(), iec411VO.getZeroPiontFive1S(), iec411VO.getGreater1S());
                }
                two++;
                createTitle(doc, "4." + i + "." + two + " IEC 61000-2-8:(公共电网电压暂降测量统计)", "标题 3", 400, 15);
//                List<IEC28VO> iec28VOS = eventAnalysisService.IEC28(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
                List<IEC28VO> iec28VOS = eventReportService.IEC28(info);
                XWPFTable table3 = createTable(doc);
                insertRow(doc, table3, centerParagraph, true, "剩余电压", "0.02~0.1s", "0.1~0.25s", "0.25~0.5s", "0.5s~1s", "1~3s", "3~20s", "20~60s", "60~180s");
                for (int j = 0; j < iec28VOS.size(); j++) {
                    IEC28VO iec28VO = iec28VOS.get(j);
                    insertRow(doc, table3, centerParagraph, false, iec28VO.getName(), iec28VO.getQ(), iec28VO.getW(), iec28VO.getE(), iec28VO.getR(), iec28VO.getT(), iec28VO.getY(), iec28VO.getU(), iec28VO.getI());
                }
                two++;
            }
            i++;
        }
        //4.暂降幅值概率分布
        if (exportParam.isGlfbfz() || exportParam.isGlfbsj()) {
            createTitle(doc, "4." + i + " 暂降幅值概率分布图", "标题 2", 200, 15);
//            ProbabilityVO probabilityVO = eventAnalysisService.getProbabilityDistribution(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
            ProbabilityVO probabilityVO = eventReportService.getProbabilityDistribution(info);
            int two = 1;
            if (exportParam.isGlfbfz()) {
                createTitle(doc, "4." + i + "." + two + " 暂降幅值的概率分函数", "标题 3", 400, 15);
                List<String> ybardata = probabilityVO.getPereventvalue();
                List<String> ylinedata = probabilityVO.getEventvalue();
                String fz = drawPicUtil.drawEventAmplitude(ylinedata, ybardata);
                createPic(doc, fz, "暂降幅值的概率分布函数");
                two++;
            }
            if (exportParam.isGlfbsj()) {
                createTitle(doc, "4." + i + "." + two + " 持续时间的概率分函数", "标题 3", 400, 15);
                List<String> ybardata = probabilityVO.getPersisttime();
                List<String> ylinedata = probabilityVO.getSisttime();
                String sj = drawPicUtil.drawPersistentTime(ylinedata, ybardata);
                createPic(doc, sj, "持续时间的概率分布函数");
                two++;
            }
            i++;
        }


        //5.月份统计
        if (exportParam.isTjbg() || exportParam.isTjtx()) {
            createTitle(doc, "4." + i + " 月份统计", "标题 2", 200, 15);
            int two = 1;
            List<TimeVO> reasonTypeTime = eventAnalysisService.getReasonTypeTime(param);
            //暂时时间端按月查询不能查询
//            List<TimeVO> reasonTypeTime = eventReportService.getReasonTypeTime(param,null);
            if (exportParam.isTjtx()) {
                createTitle(doc, "4." + i + "." + two + " 月份统计图", "标题 3", 400, 15);
                List<Integer> count = new ArrayList<>();
                List<String> name = new ArrayList<>();
                if (exportParam.getFlag() == 0) {
                    for (TimeVO timeVO : reasonTypeTime) {
                        name.add(timeVO.getMonth() + "");
                        count.add(Integer.parseInt(timeVO.getTimes()));
                    }
                } else {
                    for (TimeVO timeVO : reasonTypeTime) {
                        name.add(timeVO.getDay() + "");
                        count.add(Integer.parseInt(timeVO.getTimes()));
                    }
                }
                String yftj = drawPicUtil.drawMonth(name, count, reasonTypeTime.get(0).getYear(), exportParam.getFlag());
                createPic(doc, yftj, "月份统计图");
                two++;
            }
            if (exportParam.isTjbg()) {
                XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
                createTitle(doc, "4." + i + "." + two + " 时间统计表格", "标题 3", 400, 15);

                XWPFTable table1 = createTable(doc);
                if (exportParam.getFlag() == 0) {
                    insertRow(doc, table1, centerParagraph, true, "时间(月)", "电压暂降次数");
                } else {
                    insertRow(doc, table1, centerParagraph, true, "时间(天)", "电压暂降次数");
                }
                if (exportParam.getFlag() == 0) {
                    for (int j = 0; j < reasonTypeTime.size(); j++) {
                        TimeVO timeVO = reasonTypeTime.get(j);
                        insertRow(doc, table1, centerParagraph, false, timeVO.getMonth(), timeVO.getTimes());
                    }
                } else {
                    for (int j = 0; j < reasonTypeTime.size(); j++) {
                        TimeVO timeVO = reasonTypeTime.get(j);
                        insertRow(doc, table1, centerParagraph, false, timeVO.getFulltime(), timeVO.getTimes());
                    }
                }
                two++;
            }
            i++;
        }


        //6.原因统计
        //6.1整合提出查询语句
        Boolean fly = exportParam.isYybg() || exportParam.isYytx() || exportParam.isLxbg() || exportParam.isLxtx();
        if (fly) {
            List<DictData> tempDictType = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.EVENT_STATIS.getCode()).getData();
            List<String> typeIds = tempDictType.stream().filter(x -> DicDataEnum.VOLTAGE_DIP.getCode().equals(x.getCode()) || DicDataEnum.SHORT_INTERRUPTIONS.getCode().equals(x.getCode()))
                    .map(DictData::getId).collect(Collectors.toList());
            List<EventDetail> tempInfo = info.stream().filter(temp -> typeIds.contains(temp.getEventType())).collect(Collectors.toList());
            StatisticVO statistic = eventReportService.getStatistic(tempInfo, reasonData, typeData);
            if (exportParam.isYybg() || exportParam.isYytx()) {
                createTitle(doc, "4." + i + " 原因统计", "标题 2", 200, 15);
//            StatisticVO statistic = eventAnalysisService.getStatistic(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
                int two = 1;
                if (exportParam.isYytx()) {
                    createTitle(doc, "4." + i + "." + two + " 原因统计图", "标题 3", 400, 15);
                    List<String> xdata = new ArrayList<>();
                    List<Map<String, Object>> reasonList = new ArrayList<>();

                    List<ReasonsVO> reason = statistic.getReason();
                    for (ReasonsVO reasonsVO : reason) {
                        Map<String, Object> map = new LinkedHashMap<>();
                        map.put("value", reasonsVO.getTimes());
                        map.put("name", reasonsVO.getReason());
                        reasonList.add(map);
                        xdata.add(reasonsVO.getReason());
                    }
                    String tr = drawPicUtil.drawReason(xdata, reasonList);
                    createPic(doc, tr, "暂降原因图");
                    two++;
                }
                if (exportParam.isYybg()) {
                    XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
                    createTitle(doc, "4." + i + "." + two + " 原因统计表格", "标题 3", 400, 15);
                    XWPFTable table1 = createTable(doc);
                    insertRow(doc, table1, centerParagraph, true, "暂降原因", "电压暂降次数");
                    List<ReasonsVO> reason = statistic.getReason();
                    for (int j = 0; j < reason.size(); j++) {
                        ReasonsVO reasonsVO = reason.get(j);
                        insertRow(doc, table1, centerParagraph, false, reasonsVO.getReason(), reasonsVO.getTimes() + "");
                    }
                    two++;
                }
                i++;
            }
            //7.类型统计
            if (exportParam.isLxbg() || exportParam.isLxtx()) {
                createTitle(doc, "4." + i + " 类型统计", "标题 2", 200, 15);
//            StatisticVO statistic = eventAnalysisService.getStatistic(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
                int two = 1;
                if (exportParam.isLxtx()) {
                    createTitle(doc, "4." + i + "." + two + " 类型统计图", "标题 3", 400, 15);
                    List<String> xdata = new ArrayList<>();
                    List<TypesVO> types = statistic.getTypes();

                    List<Map<String, Object>> reasonList = new ArrayList<>();
                    for (TypesVO type : types) {
                        Map<String, Object> map = new LinkedHashMap<>();
                        map.put("value", type.getTimes());
                        map.put("name", type.getType());
                        reasonList.add(map);
                        xdata.add(type.getType());
                    }
                    String tr = drawPicUtil.drawType(xdata, reasonList);
                    createPic(doc, tr, "暂降类型图");
                    two++;
                }
                if (exportParam.isLxbg()) {
                    createTitle(doc, "4." + i + "." + two + " 类型统计表格", "标题 3", 400, 15);
                    XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
                    XWPFTable table1 = createTable(doc);
                    insertRow(doc, table1, centerParagraph, true, "暂降原因", "电压暂降次数");
                    List<TypesVO> types = statistic.getTypes();
                    for (int j = 0; j < types.size(); j++) {
                        TypesVO typesVO = types.get(j);
                        insertRow(doc, table1, centerParagraph, false, typesVO.getType(), typesVO.getTimes() + "");
                    }
                    two++;
                }
                i++;
            }
        }


        try {
            ServletOutputStream outputStream = response.getOutputStream();
            String fileName = URLEncoder.encode(exportParam.getLineName() + ".docx", "UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentType("application/octet-stream;charset=UTF-8");
            doc.write(outputStream);
            outputStream.close();
        } catch (Exception e) {
            throw new BusinessException(CommonResponseEnum.FAIL, "导出监测点暂降报告异常");
        }
    }

    /**
     * 创建标题
     *
     * @param document 文档
     * @param message  标题内容
     * @param style    标题等级
     * @param line     缩进
     * @param fontSize 字体大小
     */
    public void createTitle(XWPFDocument document, String message, String style, int line, int fontSize) {
        XWPFParagraph summaeTableParagraph = WordUtils.getLeftParagraph(document);
        summaeTableParagraph.setStyle(style);
        summaeTableParagraph.setIndentationFirstLine(line);
        XWPFRun summaeTableRun = summaeTableParagraph.createRun();
        WordUtils.addParagraph(summaeTableRun, "宋体", fontSize, "000000", message, false);
    }

    /**
     * 表格插入行数据
     *
     * @param document 文档
     * @param table    表格
     * @param head     是否是表头
     * @param values   数据内容
     */
    public static void insertRow(XWPFDocument document, XWPFTable table, XWPFParagraph excelParagraph, boolean head,
                                 String... values) {
        if (head) {
            XWPFTableRow summaTableRowOne = table.getRow(0);
            WordUtils.setExcelHeadContent(excelParagraph, summaTableRowOne, values);
        } else {
            XWPFTableRow summaTableRowOne = table.createRow();
            WordUtils.setExcelContent(excelParagraph, summaTableRowOne, values);
        }
    }


    /**
     * 创建表格
     *
     * @param document
     * @return
     */
    public XWPFTable createTable(XWPFDocument document) {
        XWPFTable summaTable = document.createTable();
        // 列宽自动分割
        CTTblWidth summaTableWidth = summaTable.getCTTbl().addNewTblPr().addNewTblW();
        summaTableWidth.setType(STTblWidth.DXA);
        summaTableWidth.setW(BigInteger.valueOf(8160));
        return summaTable;
    }



    /**
     * @param document 文档
     * @param image    图片base64
     * @param name     图片名
     * @throws IOException
     * @throws InvalidFormatException
     */
    public void createPic(XWPFDocument document, String image, String name) {
        try {
            XWPFParagraph picParagraph = WordUtils.getCenterParagraph(document);
            XWPFRun createRun = picParagraph.createRun();
            if (image.contains(PicCommonData.PNG_PREFIX)) {
                image = image.replace(PicCommonData.PNG_PREFIX, "");
            }
            byte[] bytes = Base64.getDecoder().decode(image);
            InputStream in = new ByteArrayInputStream(bytes);
            createRun.addPicture(in, 5, name, Units.toEMU(410), Units.toEMU(170));
        } catch (Exception e) {
            log.error("在word中创建图片异常:", e);
        }
    }


    /**
     * 监测点暂降事件点图赋值
     *
     * @param plot
     * @return
     */
    private ArrayList<List<Double>> getAss(List<EventDetail> plot) {
        ArrayList<List<Double>> list = new ArrayList<>();
        for (EventDetail eventDetail : plot) {
            ArrayList<Double> doubles = new ArrayList<>();
            doubles.add(eventDetail.getDuration());
            doubles.add(Double.parseDouble(String.valueOf(eventDetail.getFeatureAmplitude() * 100)));
            list.add(doubles);
        }
        return list;
    }


    /**
     * influxdb查询结果集
     *
     * @param statisticsParam
     * @return
     */
    private List<EventDetail> info(StatisticsParam statisticsParam) {
        // 构建查询条件
        LambdaQueryWrapper<RmpEventDetailPO> queryWrapper = new LambdaQueryWrapper<RmpEventDetailPO>()
                .eq(RmpEventDetailPO::getMeasurementPointId, statisticsParam.getLineIndex())
                .ge(StrUtil.isNotBlank(statisticsParam.getStartTime()), RmpEventDetailPO::getStartTime, DateUtil.beginOfDay(DateUtil.parse(statisticsParam.getStartTime())))
                .le(StrUtil.isNotBlank(statisticsParam.getEndTime()), RmpEventDetailPO::getStartTime, DateUtil.endOfDay(DateUtil.parse(statisticsParam.getEndTime())))
                .orderByDesc(RmpEventDetailPO::getStartTime);
        if (!Objects.isNull(statisticsParam.getIsDip()) && statisticsParam.getIsDip()) {
            List<DictData> data = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.EVENT_STATIS.getCode()).getData();
            List<String> typeList = data.stream().filter(it->it.getCode().equals(DicDataEnum.VOLTAGE_DIP.getCode()) || it.getCode().equals(DicDataEnum.SHORT_INTERRUPTIONS.getCode())).map(DictData::getId).collect(Collectors.toList());        List<TimeVO> list = new ArrayList<>();
            queryWrapper.in(RmpEventDetailPO::getEventType, typeList);
        }
        // 数据暂降查询
        List<RmpEventDetailPO> info = rmpEventDetailMapper.selectList(queryWrapper);
        return BeanUtil.copyToList(info, EventDetail.class);
    }


    /**
     * 生成暂降事件报告
     */
    @Override
    public void createEventReport(List<String> eventIndex, HttpServletResponse response) throws IOException, InvalidFormatException, ExecutionException, InterruptedException {
        WordUtil wordUtil = new WordUtil();
        // 创建专用的线程池（建议大小为CPU核心数或稍大，这里设为3个并行任务）
        for (String index : eventIndex) {
            RmpEventDetailPO detail = rmpEventDetailMapper.selectById(index);
            List<AreaLineInfoVO> lineDetail = lineFeignClient.getBaseLineAreaInfo(Stream.of(detail.getLineId()).collect(Collectors.toList())).getData();
            AreaLineInfoVO line = lineDetail.get(0);
            WaveDataDTO waveData = waveService.getWavedata(detail, line);
            //数据筛选,如果是双路电压的话，会存在2个波形数据
            List<WaveDataDetail> waveDataDetails = WaveUtil.filterWaveData(waveData);
            if (ObjUtil.isNull(waveData)) {
                throw new BusinessException(CommonResponseEnum.FAIL, "没有波形数据");
            } else {
                // 使用 CompletableFuture 并行执行三个耗时操作
                CompletableFuture<String> instantFuture = CompletableFuture.supplyAsync(() -> {
                    String instantPath = wavePicComponent.generateImageShun(waveData, waveDataDetails);
                    try (InputStream instantStream = fileStorageUtil.getFileStream(instantPath)) {
                        return cn.hutool.core.codec.Base64.encode(instantStream);
                    } catch (Exception e) {
                        throw new RuntimeException("生成瞬时波形失败", e);
                    }
                }, executor);

                CompletableFuture<String> rmsFuture = CompletableFuture.supplyAsync(() -> {
                    String rmsPath = wavePicComponent.generateImageRms(waveData, waveDataDetails);
                    try (InputStream rmsStream = fileStorageUtil.getFileStream(rmsPath)) {
                        return cn.hutool.core.codec.Base64.encode(rmsStream);
                    } catch (Exception e) {
                        throw new RuntimeException("生成RMS波形失败", e);
                    }
                }, executor);


                // 等待所有异步任务完成并获取结果（无超时，但可加）
                CompletableFuture<Void> allFutures = CompletableFuture.allOf(instantFuture, rmsFuture);
                allFutures.join();  // 阻塞直到2个任务都完成

                // 获取结果（此时所有任务已完成，get()不会阻塞）
                String imageShun64 = instantFuture.get();
                String rmsShun64 = rmsFuture.get();
                List<EventEigDetail> eventDetailEigenvalue =  waveService.eventDetailEigenvalue(index, line.getPtType());

                // 主线程顺序调用 WordUtil 方法（保证线程安全）
                wordUtil.translateShun(index, imageShun64);
                wordUtil.translateRms(index, rmsShun64);
                wordUtil.setEventDetailEigenvalue(index, eventDetailEigenvalue);
                // 设置事件基本信息（不涉及耗时操作）
                EventInfoDetailVO eventInfoList = new EventInfoDetailVO();
                eventInfoList.setLineName(line.getLineName());
                eventInfoList.setGdName(line.getGdName());
                eventInfoList.setBdzName(line.getSubName());
                eventInfoList.setDevName(line.getDeviceName());
                eventInfoList.setScale(line.getVoltageScale());
                eventInfoList.setIp(line.getIp());
                eventInfoList.setEventDetailIndex(detail.getEventId());
                eventInfoList.setTimeID(detail.getStartTime());
                eventInfoList.setPersistTime(detail.getDuration());
                eventInfoList.setMs(detail.getFirstMs());
                eventInfoList.setEventValue(detail.getFeatureAmplitude());
                wordUtil.setEventInfoList(index, eventInfoList);
            }
        }
        wordUtil.createReport(eventIndex);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            String fileName = URLEncoder.encode("暂降事件报告_" + DateUtil.format(new Date(),DatePattern.NORM_DATE_PATTERN)+ ".docx", "UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentType("application/octet-stream;charset=UTF-8");
            wordUtil.getDocument().write(outputStream);
            outputStream.close();
        } catch (Exception e) {
            throw new BusinessException(CommonResponseEnum.FAIL, "导出暂降事件报告异常");
        }
    }

    @Override
    public String saveStableEventReport(List<String> eventIndex, Map<String, AreaLineInfoVO> map, String fileName) {
        WordUtil wordUtil = new WordUtil();
        for (String index : eventIndex) {
            RmpEventDetailPO detail = rmpEventDetailMapper.selectById(index);
            AreaLineInfoVO line = map.get(index);

            //判断hdr文件是否存在，不存在先生成hdr文件
            String fullPath = detail.getWavePath();
            EventAnalysisDTO eventAnalysis = new EventAnalysisDTO();
            int lastSlashIndex = fullPath.lastIndexOf('/');
            eventAnalysis.setWaveName(fullPath.substring(lastSlashIndex + 1));
            eventAnalysis.setWlFilePath(fullPath);
            eventCauseFeignClient.analysisCauseAndType(eventAnalysis);

            WaveDataDTO waveData = waveService.getWavedata2(detail, line);
            //数据筛选，如果是双路电压的话，会存在 2 个波形数据
            List<WaveDataDetail> waveDataDetails = WaveUtil.filterWaveData(waveData);
            if (ObjUtil.isNull(waveData)) {
                throw new BusinessException(CommonResponseEnum.FAIL, "没有波形数据");
            } else {
                //获取瞬时波形
                String instantPath = wavePicComponent.generateImageShun(waveData, waveDataDetails);
                InputStream instantStream = fileStorageUtil.getFileStream(instantPath);
                String imageShun64 = cn.hutool.core.codec.Base64.encode(instantStream);

                wordUtil.translateShun(index, imageShun64);
                //获取 rms 波形
                String rmsPath = wavePicComponent.generateImageRms(waveData, waveDataDetails);
                InputStream rmsStream = fileStorageUtil.getFileStream(rmsPath);
                String rmsShun64 = cn.hutool.core.codec.Base64.encode(rmsStream);
                wordUtil.translateRms(index, rmsShun64);

                EventInfoDetailVO eventInfoList = new EventInfoDetailVO();
                eventInfoList.setLineName(line.getLineName());
                eventInfoList.setGdName(line.getGdName());
                eventInfoList.setBdzName(line.getSubName());
                eventInfoList.setDevName(line.getDeviceName());
                eventInfoList.setScale(line.getVoltageScale());
                eventInfoList.setIp(line.getIp());
                eventInfoList.setEventDetailIndex(detail.getEventId());
                eventInfoList.setTimeID(detail.getStartTime());
                eventInfoList.setPersistTime(detail.getDuration());
                eventInfoList.setMs(detail.getFirstMs());
                eventInfoList.setEventValue(detail.getFeatureAmplitude());
                wordUtil.setEventInfoList(index, eventInfoList);
                List<EventEigDetail> eventDetailEigenvalue = waveService.eventDetailEigenvalue2(index,line.getPtType());
                wordUtil.setEventDetailEigenvalue(index, eventDetailEigenvalue);
            }
        }
        try {
            InputStream inputStream = wordUtil.createReport2(eventIndex);
            String filePath;
            if (Objects.isNull(fileName)) {
                filePath = fileStorageUtil.uploadStream(inputStream, OssPath.APP_EVENT_REPORT, null);
            } else {
                filePath = fileStorageUtil.uploadStreamSpecifyName(inputStream, OssPath.APP_EVENT_REPORT, fileName);
            }
            return filePath;
        } catch (IOException | InvalidFormatException e) {
            throw new RuntimeException(e);
        }
    }
}
