package com.njcn.cloud.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.cloud.enums.app.EventMsgCodeEnum;
import com.njcn.cloud.enums.app.RateCodeEnum;
import com.njcn.cloud.mapper.AppReportMapper;
import com.njcn.cloud.pojo.dto.AppEventInfo;
import com.njcn.cloud.pojo.dto.AppReport;
import com.njcn.cloud.pojo.param.AppReportParam;
import com.njcn.cloud.pojo.vo.EventEigDetailVO;
import com.njcn.cloud.pojo.vo.EventInfoDetailVO;
import com.njcn.cloud.pojo.vo.ReportVO;
import com.njcn.cloud.service.IAppEventInfoService;
import com.njcn.cloud.service.IAppReportService;
import com.njcn.cloud.utils.PubUtil;
import com.njcn.cloud.utils.WordUtil;
import com.njcn.common.pojo.dto.SimpleDTO;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.FileUtil;
import com.njcn.device.pq.api.GeneralDeviceInfoClient;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.pq.pojo.param.DeviceInfoParam;
import com.njcn.device.pq.pojo.vo.LineDetailVO;
import com.njcn.event.api.EventDetailFeignClient;
import com.njcn.event.api.ReportFeignClient;
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.EventCountParam;
import com.njcn.event.pojo.po.RmpEventDetailPO;
import com.njcn.harmonic.api.HarmDataFeignClient;
import com.njcn.harmonic.pojo.param.StatSubstationBizBaseParam;
import com.njcn.harmonic.pojo.po.day.RStatLimitRateDPO;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.system.pojo.enums.StatisticsEnum;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.user.api.UserFeignClient;
import com.njcn.user.pojo.po.Dept;
import com.njcn.user.pojo.po.User;

import java.io.*;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


/**
 * <p>
 * 报告信息 服务实现类
 * </p>
 *
 * @author wr
 * @since 2023-11-02
 */
@Service
@RequiredArgsConstructor
public class AppReportServiceImpl extends ServiceImpl<AppReportMapper, AppReport> implements IAppReportService {

    private final IAppEventInfoService appEventInfoService;
    private final WavePicComponent wavePicComponent;
    private final FileStorageUtil fileStorageUtil;
    private final EventDetailFeignClient eventDetailFeignClient;
    private final LineFeignClient lineFeignClient;
    private final UserFeignClient userFeignClient;
    private final GeneralDeviceInfoClient generalDeviceInfoClient;
    private final HarmDataFeignClient harmDataFeignClient;
    private final DeptFeignClient deptFeignClient;
    private final ReportFeignClient reportFeignClient;


    @Override
    @Transactional(rollbackFor = {Exception.class})
    public String eventDetailReportApply(String userId, String eventDetailIndex) {
        AppEventInfo info = new AppEventInfo();
        info.setEventdetailIndex(eventDetailIndex);
        info.setEvaluate(0);
        info.setUserIndex(userId);
        info.setUpdateTime(LocalDateTime.now());

        AppEventInfo one = appEventInfoService.getOne(new LambdaUpdateWrapper<AppEventInfo>()
                .eq(AppEventInfo::getEventdetailIndex, eventDetailIndex)
                .ne(AppEventInfo::getReportState, 4)
        );
        if (ObjectUtil.isNull(one) || one.getReportState() != 1) {
            try {
                Map<String, String> map = this.createEventDetailReport(eventDetailIndex, one);
                if (map.containsKey("noData")) {
                    return map.get("noData");
                }
                info.setInstantwavePath(map.get("instantPath"));
                info.setRmswavePath(map.get("rmsPath"));
                info.setReportState(1);
                info.setReportPath(map.get("reportPath"));
                appEventInfoService.saveOrUpdate(info);
                return "0";
            } catch (Exception e) {
                e.printStackTrace();
                info.setReportState(2);
                appEventInfoService.saveOrUpdate(info);
                return "1";
            }
        }
        return "0";
    }

    @Override
    public AppEventInfo getDownLoadPath(String userId, String eventDetailIndex) {
        AppEventInfo one = appEventInfoService.getOne(new LambdaQueryWrapper<AppEventInfo>()
                .eq(AppEventInfo::getUserIndex, userId)
                .eq(AppEventInfo::getEventdetailIndex, eventDetailIndex)
        );
        if (ObjectUtil.isNull(one)) {
            throw new BusinessException(EventMsgCodeEnum.EVENTDETAILREPORTDOWNLOAD_NOT_FAIL.getMsg());
        }
        if (one.getReportState() == 3) {
            throw new BusinessException(EventMsgCodeEnum.EVENTDETAILREPORTDOWNLOAD_ERROR.getMsg());
        }
        return one;
    }

    @Override
    public Page<ReportVO> reportList(AppReportParam param) {
        Page<ReportVO> page = new Page<>();
        List<ReportVO> info = new ArrayList<>();
        //先获取用户部门，在根据部门信息获取监测点信息
        List<User> users = userFeignClient.getUserByIdList(Collections.singletonList(param.getUserId())).getData();
        if (CollUtil.isNotEmpty(users)) {
            User user = users.get(0);
            //获取所有监测点id
            List<String> lineIds = generalDeviceInfoClient.deptGetRunLine(user.getDeptId()).getData();
            Page<AppReport> page1 = this.page(new Page<>(param.getPage(), param.getNum()), new LambdaQueryWrapper<AppReport>()
                    .in(param.getSystemType() == 2, AppReport::getLineIndex, lineIds)
                    .eq(param.getSystemType() == 1, AppReport::getDeptsIndex, user.getDeptId())
                    .eq(param.getSystemType() != null, AppReport::getSystemType, param.getSystemType())
                    .eq(param.getReportType() != null, AppReport::getReportType, param.getReportType())
                    .ne(AppReport::getState, 4)
                    .eq(AppReport::getUserIndex, param.getUserId())
                    .ge(StrUtil.isNotBlank(param.getStartTime()), AppReport::getStartTime, DateUtil.beginOfDay(DateUtil.parse(param.getStartTime())))
                    .le(StrUtil.isNotBlank(param.getEndTime()), AppReport::getStartTime, DateUtil.endOfDay(DateUtil.parse(param.getEndTime())))
                    .orderByDesc(AppReport::getUpdateTime)
            );
            //数据组装
            BeanUtil.copyProperties(page1, page);
            List<AppReport> records = page1.getRecords();
            if (EventMsgCodeEnum.STEADY_TYPE.getCode() == param.getSystemType()) {
                if (CollUtil.isNotEmpty(records)) {
                    rateAdd(param, info, records);
                }
            }
            if (EventMsgCodeEnum.TRANSIENT_TYPE.getCode() == param.getSystemType()) {
                if (CollUtil.isNotEmpty(records)) {
                    Dept dept = deptFeignClient.getDeptById(user.getDeptId()).getData();
                    //获取分页报告监测点id
                    EventCountParam lineParam;
                    ReportVO vo;
                    for (AppReport record : records) {
                        lineParam = new EventCountParam();
                        lineParam.setIds(lineIds);
                        lineParam.setStartTime(LocalDateTimeUtil.format(record.getStartTime(), DatePattern.NORM_DATETIME_PATTERN));
                        lineParam.setEndTime(LocalDateTimeUtil.format(record.getEndTime(), DatePattern.NORM_DATETIME_PATTERN));
                        Integer count = eventDetailFeignClient.getEventDetailByLineCount(lineParam).getData();
                        vo = BeanUtil.copyProperties(record, ReportVO.class);
                        vo.setTotal(count);
                        vo.setListResult(new ArrayList<String>() {{
                            add("共发生" + count + "次暂降事件");
                        }});
                        vo.setName(dept.getName() + "报告");
                        info.add(vo);
                    }
                }
            }
        }
        page.setRecords(info);
        return page;
    }

    @Override
    public int customReport(AppReportParam param) {
        //查询报告是否存在
        LambdaQueryWrapper<AppReport> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(AppReport::getStartTime, param.getStartTime());
        lambdaQueryWrapper.eq(AppReport::getEndTime, param.getEndTime());
        lambdaQueryWrapper.eq(AppReport::getReportType, param.getReportType());
        String name = getFileName(param.getLineIndex(), param.getStartTime(), param.getEndTime());
        String deptId = "";
        if (param.getSystemType() == 1) {
            List<User> users = userFeignClient.getUserByIdList(Collections.singletonList(param.getUserId())).getData();
            deptId = users.get(0).getDeptId();
            lambdaQueryWrapper.eq(AppReport::getDeptsIndex, deptId);
            name = getFileName(param.getUserId(), param.getStartTime(), param.getEndTime());
        }
        AppReport reportData = this.getOne(lambdaQueryWrapper);
        String path = createReport(deptId, param.getSystemType(), param.getLineIndex(), param.getStartTime(), param.getEndTime());
//        String path = "";
        if (reportData == null) {
            String userId = param.getReportType() == 5 ? param.getUserId() : "";
            AppReport appReport = new AppReport();
            appReport.setReportName(name);
            appReport.setLineIndex(param.getLineIndex());
            appReport.setUserIndex(userId);
            appReport.setUpdateTime(LocalDateTime.now());
            appReport.setStartTime(LocalDateTimeUtil.parse(param.getStartTime(), DatePattern.NORM_DATETIME_PATTERN));
            appReport.setEndTime(LocalDateTimeUtil.parse(param.getEndTime(), DatePattern.NORM_DATETIME_PATTERN));
            appReport.setSystemType(param.getSystemType());
            appReport.setReportType(param.getReportType());
            appReport.setReportPath(path);
            appReport.setDeptsIndex(deptId);
            if (StrUtil.isBlank(path)) {
                appReport.setState(2);
                appReport.setErrorCount(1);
            } else {
                appReport.setState(param.getReportType() == 5 ? 3 : 1);
                appReport.setErrorCount(0);
            }
            this.save(appReport);
        } else if (reportData.getState() == 2) {
            if (reportData.getErrorCount() < 2) {
                if (reportData.getUserIndex().equals(param.getUserId())) {
                    if (path.equals("")) {
                        this.update(new LambdaUpdateWrapper<AppReport>()
                                .set(AppReport::getUpdateTime, LocalDateTime.now())
                                .set(AppReport::getErrorCount, reportData.getErrorCount() + 1)
                                .set(AppReport::getState, reportData.getState())
                                .eq(AppReport::getReportIndex, reportData.getReportIndex())
                        );
                    } else {
                        this.update(new LambdaUpdateWrapper<AppReport>()
                                .set(AppReport::getUpdateTime, LocalDateTime.now())
                                .set(AppReport::getErrorCount, 0)
                                .set(AppReport::getState, param.getReportType() == 5 ? 3 : 1)
                                .set(AppReport::getReportPath, path)
                                .eq(AppReport::getReportIndex, reportData.getReportIndex())
                        );
                    }
                } else {
                    String userId = param.getReportType() == 5 ? param.getUserId() : "";
                    AppReport appReport = new AppReport();
                    appReport.setReportName(name);
                    appReport.setLineIndex(param.getLineIndex());
                    appReport.setUserIndex(userId);
                    appReport.setUpdateTime(LocalDateTime.now());
                    appReport.setStartTime(LocalDateTimeUtil.parse(param.getStartTime(), DatePattern.NORM_DATETIME_PATTERN));
                    appReport.setEndTime(LocalDateTimeUtil.parse(param.getEndTime(), DatePattern.NORM_DATETIME_PATTERN));
                    appReport.setSystemType(param.getSystemType());
                    appReport.setReportType(param.getReportType());
                    appReport.setReportPath(path);
                    appReport.setDeptsIndex(deptId);
                    if (StrUtil.isBlank(path)) {
                        appReport.setState(2);
                        appReport.setErrorCount(1);
                    } else {
                        appReport.setState(param.getReportType() == 5 ? 3 : 1);
                        appReport.setErrorCount(0);
                    }
                    this.save(appReport);
                }
            }
        }
        return 0;
    }

    /**
     * @param deptId    部门id
     * @param type      区分是稳态还暂态
     * @param lineId    监测点ia
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return
     */
    private String createReport(String deptId, Integer type, String lineId, String startTime, String endTime) {
        //稳态系统
        if (type == 2) {

        }
        //暂态系统(周报)
        if (type == 1) {
            DeviceInfoParam.BusinessParam param = new DeviceInfoParam.BusinessParam();
            param.setDeptIndex(deptId);
            param.setSearchBeginTime(startTime.substring(0,10));
            param.setSearchEndTime(endTime.substring(0,10));
            param.setServerName("event-boot");
            //统计类型
            SimpleDTO simpleDTO = new SimpleDTO();
            simpleDTO.setCode(String.valueOf(StatisticsEnum.POWER_NETWORK));
            param.setStatisticalType(simpleDTO);
            return reportFeignClient.appEventExport(param).getData();
        }
        return "";
    }


    private String getFileName(String lineId, String startTime, String endTime) {
        String start = startTime.substring(0, 10);
        String end = endTime.substring(0, 10);
        return lineId + "_" + start + "_" + end + ".xlsx";
    }

    /**
     * 谐波数据组装
     *
     * @param param
     * @param info
     * @param records
     */
    private void rateAdd(AppReportParam param, List<ReportVO> info, List<AppReport> records) {
        //获取分页报告监测点id
        List<String> lineReportIds = records.stream().map(AppReport::getLineIndex).distinct().collect(Collectors.toList());
        StatSubstationBizBaseParam rateLine = new StatSubstationBizBaseParam();
        rateLine.setIds(lineReportIds);
        rateLine.setStartTime(param.getStartTime());
        rateLine.setEndTime(param.getEndTime());
        //获取越线信息
        List<RStatLimitRateDPO> limitRates = harmDataFeignClient.getLinesRate(rateLine).getData();
        Map<String, RStatLimitRateDPO> limitRateMap = limitRates.stream()
                .collect(Collectors.toMap(RStatLimitRateDPO::getLineId, Function.identity()));
        //获取监测点信息
        List<LineDetailVO.Detail> lineDetails = lineFeignClient.getLineDetailByIds(lineReportIds).getData();
        Map<String, LineDetailVO.Detail> lineDetailMap = lineDetails.stream()
                .collect(Collectors.toMap(LineDetailVO.Detail::getLineId, Function.identity()));
        ReportVO vo;
        List<String> listResult;
        for (AppReport record : records) {
            vo = BeanUtil.copyProperties(record, ReportVO.class);
            if (lineDetailMap.containsKey(record.getLineIndex())) {
                vo.setName(lineDetailMap.get(record.getLineIndex()).getLineName());
                vo.setTinterval(lineDetailMap.get(record.getLineIndex()).getTimeInterval());
            }
            listResult = new ArrayList<>();
            if (limitRateMap.containsKey(record.getLineIndex())) {
                RStatLimitRateDPO rStatLimitRateDPO = limitRateMap.get(record.getLineIndex());
                vo.setListResult(RateList(listResult, rStatLimitRateDPO, vo.getTinterval()));
            } else {
                listResult.add("该监测点暂无指标越限");
                vo.setListResult(listResult);
            }
            info.add(vo);
        }

    }

    private List<String> RateList(List<String> info, RStatLimitRateDPO rateDPO, Integer timeInterval) {
        //反射获取所有属性
        Field[] declaredFields = rateDPO.getClass().getDeclaredFields();
        String s;
        for (Field item : declaredFields) {
            item.setAccessible(true);
            String name = item.getName();    // 获取对象属性名
            try {
                String codeByMsg = RateCodeEnum.getMsgByCode(name);
                if (StrUtil.isNotBlank(codeByMsg)) {
                    Integer o = Integer.valueOf(item.get(rateDPO).toString());
                    if (codeByMsg.equals("闪变越限")) {
                        s = codeByMsg + o * 120 + "分钟";
                        info.add(s);
                    } else {
                        s = codeByMsg + o * timeInterval + "分钟";
                        info.add(s);
                    }
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return info;
    }


    /**
     * @param eventIndex
     * @Description: 生成报告
     * @return: java.lang.String
     * @Author: wr
     * @Date: 2023/11/2 14:44
     */
    private Map<String, String> createEventDetailReport(String eventIndex, AppEventInfo appEventInfo) throws Exception {
        Map<String, String> map = new HashMap<>();
        WaveDataDTO waveData = appEventInfoService.analyseWave(eventIndex, 2);

        if (waveData.getListWaveData().size() == 0) {
            map.put("noData", "2");
            return map;
        }
        //数据筛选,如果是双路电压的话，会存在2个波形数据
        List<WaveDataDetail> waveDataDetails = WaveUtil.filterWaveData(waveData);
        WordUtil wordUtil = new WordUtil();
        //获取瞬时波形
        String instantPath;
        if (ObjectUtil.isNotNull(appEventInfo) && StrUtil.isNotBlank(appEventInfo.getInstantwavePath())) {
            instantPath = appEventInfo.getInstantwavePath();
        } else {
            instantPath = wavePicComponent.generateImageShun(waveData, waveDataDetails);
        }
        InputStream instantStream = fileStorageUtil.getFileStream(instantPath);
        String imageShun64 = Base64.encode(instantStream);
        //获取rms波形
        String rmsPath;
        if (ObjectUtil.isNotNull(appEventInfo) && StrUtil.isNotBlank(appEventInfo.getRmswavePath())) {
            rmsPath = appEventInfo.getRmswavePath();
        } else {
            rmsPath = wavePicComponent.generateImageRms(waveData, waveDataDetails);

        }
        InputStream rmsStream = fileStorageUtil.getFileStream(rmsPath);
        String rmsShun64 = Base64.encode(rmsStream);

        wordUtil.translate(imageShun64, rmsShun64);
        List<EventEigDetailVO> eventDetailEigenvalue = new ArrayList<>();
        List<String> tmpWaveTitle = waveData.getWaveTitle();
        int picCounts = (tmpWaveTitle.size() - 1) / waveData.getIPhasic();
        if (picCounts == 3) {
            eventDetailEigenvalue = appEventInfoService.eventDetailEigenvalue(eventIndex).getList();

        }
        wordUtil.setEventDetailEigenvalue(eventDetailEigenvalue);
        EventInfoDetailVO eventInfoList = new EventInfoDetailVO();
        List<RmpEventDetailPO> eventDetails = eventDetailFeignClient.getEventDetailByIdsList(Arrays.asList(eventIndex)).getData();
        List<String> linedIds = eventDetails.stream().map(RmpEventDetailPO::getLineId).distinct().collect(Collectors.toList());
        List<LineDetailVO.Detail> lineDetail = lineFeignClient.getLineDetailByIds(linedIds).getData();
        RmpEventDetailPO detail = eventDetails.get(0);
        LineDetailVO.Detail line = lineDetail.get(0);
        eventInfoList.setLineName(PubUtil.lineName(detail.getSagsource(), line.getLineName()));
        eventInfoList.setGdName(line.getGdName());
        eventInfoList.setBdzName(line.getSubName());
        eventInfoList.setDevName(line.getDevName());
        eventInfoList.setScale(line.getVoltageLevel());
        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(eventInfoList);
        wordUtil.createReport();

        //临时缓冲区
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        //创建临时文件
        wordUtil.getDocument().write(out);
        byte[] bookByteAry = out.toByteArray();
        InputStream in = new ByteArrayInputStream(bookByteAry);
        String reportPath = fileStorageUtil.uploadStream(in, OssPath.RESPONSIBILITY_APP_REPORT,
                FileUtil.generateFileName("docx")
        );
        map.put("instantPath", instantPath);
        map.put("rmsPath", rmsPath);
        map.put("reportPath", reportPath);
        return map;
    }
}
