package com.njcn.event.service.majornetwork.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.njcn.common.pojo.enums.common.ServerEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.device.biz.enums.DeviceResponseEnum;
import com.njcn.device.pq.api.GeneralDeviceInfoClient;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.pq.pojo.dto.GeneralDeviceDTO;
import com.njcn.device.pq.pojo.param.DeviceInfoParam;
import com.njcn.device.pq.pojo.vo.AreaLineInfoVO;
import com.njcn.device.pq.pojo.vo.SubstationDetailVO;
import com.njcn.event.mapper.majornetwork.PqDeviceMapper;
import com.njcn.event.mapper.majornetwork.RStatOnlinerateDMapper;
import com.njcn.event.pojo.po.PqDevice;
import com.njcn.event.pojo.po.PqsOnlinerate;
import com.njcn.event.pojo.po.RmpEventDetailPO;
import com.njcn.event.pojo.vo.*;
import com.njcn.event.pojo.vo.TerminalRunningStatisticsVO.TerminalRunningInfoVO;
import com.njcn.event.pojo.vo.TerminalRunningStatisticsVO.TerminalRunningVO;
import com.njcn.event.service.majornetwork.AreaLineService;
import com.njcn.event.common.service.EventDetailService;
import com.njcn.event.service.majornetwork.LargeScreenService;
import com.njcn.harmonic.pojo.dto.ComAssessDTO;
import com.njcn.harmonic.pojo.po.PQSComAssesPO;
import com.njcn.harmonic.utils.HarmonicComAssesUtil;
import com.njcn.influx.deprecated.InfluxDBPublicParam;
import com.njcn.influx.utils.InfluxDbUtils;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.pojo.enums.StatisticsEnum;
import com.njcn.system.pojo.po.DictData;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.influxdb.dto.QueryResult;
import org.influxdb.impl.InfluxDBResultMapper;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.njcn.event.influxdb.QueryBuilder.beginOfDay;
import static com.njcn.event.influxdb.QueryBuilder.endOfDay;

/**
 * @author denghuajun
 * @version 1.0.0
 * @date 2022年04月07日 13:39
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AreaLineServiceImpl implements AreaLineService {

    private final GeneralDeviceInfoClient generalDeviceInfoClient;

    private final LineFeignClient lineFeignClient;

    private final InfluxDbUtils influxDbUtils;

   // private final PqsOnlinerateQuery pqsOnlinerateQuery;

    private final RStatOnlinerateDMapper rStatOnlinerateDMapper;

    private final PqDeviceMapper pqDeviceMapper;
    private final EventDetailService eventDetailService;
    private final LargeScreenService largeScreenService;
    private final HarmonicComAssesUtil comAssesUtil;
    private final DicDataFeignClient dicDataFeignClient;


    @Override
    public AreaLineVO getAreaLineVO(DeviceInfoParam deviceInfoParam) {
        AreaLineVO areaLineVO = new AreaLineVO();
        deviceInfoParam.setServerName(ServerEnum.EVENT.getName());
        List<List<Object>> listObject = new ArrayList<>();
        // 获取暂降监测点
        List<GeneralDeviceDTO> generalDeviceDTOList = generalDeviceInfoClient.getPracticalAllDeviceInfo(deviceInfoParam).getData();
        List<String> lineIds = generalDeviceDTOList.stream().flatMap(x -> x.getLineIndexes().stream()).collect(Collectors.toList());
        List<AreaLineInfoVO> lineDetails = lineFeignClient.getBaseLineAreaInfo(lineIds).getData();
        List<SubstationDetailVO> substationDetailVOList = new ArrayList<>();
        // 获取所有终端信息
        for (GeneralDeviceDTO generalDeviceDTO : generalDeviceDTOList) {
            List<Object> objectList = new ArrayList<>();
            List<AreaLineInfoVO> areaLineInfoVOS = lineDetails.stream().filter(x -> generalDeviceDTO.getLineIndexes().contains(x.getLineId())).collect(Collectors.toList());
            Map<String, List<AreaLineInfoVO>> subMap = areaLineInfoVOS.stream().collect(Collectors.groupingBy(x->x.getSubId()+"_"+x.getSubName()));
            subMap.forEach((key,value)->{
                int j = 0;
                // 总的监测点个数
                for (AreaLineInfoVO line : value) {
                    SubstationDetailVO substationDetail = new SubstationDetailVO();
                    substationDetail.setId(line.getLineId());
                    substationDetail.setSrbName(line.getLineName());
                    substationDetail.setSubName(line.getSubName());
                    substationDetail.setCoordY(line.getLng().floatValue() + 0.0035 * Math.cos(2 * Math.PI * j / value.size()));
                    substationDetail.setCoordX(line.getLat().floatValue() + 0.0035 * Math.sin(2 * Math.PI * j / value.size()));
                    substationDetail.setColor(line.getComFlag() == 1?"green":"red");
                    substationDetailVOList.add(substationDetail);
                    j++;
                }
            });
            // 获取变电站
            areaLineVO.setSubstationDetailVOList(substationDetailVOList);
            objectList.add(generalDeviceDTO.getName());
            objectList.add(areaLineInfoVOS.size());
            objectList.add(areaLineInfoVOS.stream().filter(x->x.getComFlag()==1).count());
            objectList.add(areaLineInfoVOS.stream().filter(x->x.getComFlag()==0).count());
            listObject.add(objectList);
        }
        areaLineVO.setAreaValue(listObject);
        return areaLineVO;
    }

    @Override
    public EventHeatMapVO getEventHeatMap(DeviceInfoParam.BusinessParam deviceInfoParam) {
        //新增需求添加中断
        //获取暂降字典信息
        DictData voltageData = dicDataFeignClient.getDicDataByCode(DicDataEnum.VOLTAGE_DIP.getCode()).getData();
        DictData shortInterruptionsData = dicDataFeignClient.getDicDataByCode(DicDataEnum.SHORT_INTERRUPTIONS.getCode()).getData();

        if(ObjectUtil.isNull(voltageData)||ObjectUtil.isNull(shortInterruptionsData)){
            throw new BusinessException(DeviceResponseEnum.DIC_GET_EMPTY);
        }
        EventHeatMapVO eventHeatMapVO = new EventHeatMapVO();
        List<List<AreaLineInfoVO>> eventHeatMapDetailList = new ArrayList<>();
        List<BaseVO> listObject = new ArrayList<>();

        // 获取暂降监测点
        deviceInfoParam.setServerName(ServerEnum.EVENT.getName());
        List<GeneralDeviceDTO> generalDeviceDTOList = generalDeviceInfoClient.getPracticalAllDeviceInfo(deviceInfoParam).getData();
        List<String> lineIndexs;
        int i = 0, generalDeviceListSize = generalDeviceDTOList.size();
        while (i < generalDeviceListSize) {
            GeneralDeviceDTO generalDeviceDTO = generalDeviceDTOList.get(i);
            lineIndexs = generalDeviceDTO.getLineIndexes();
            // 获取暂降数据
            if (!lineIndexs.isEmpty()) {
                int tail = 0;
                //数据暂降查询
                List<RmpEventDetailPO> info = eventDetailService.list(new QueryWrapper<RmpEventDetailPO>()
                        .select("measurement_point_id as measurementPointId,count(file_flag) as fileFlag")
                        .in("measurement_point_id", lineIndexs)
                        .in("event_type", Stream.of(voltageData.getId(),shortInterruptionsData.getId()).collect(Collectors.toList()))
                        .ge("start_time" ,DateUtil.beginOfDay(DateUtil.parse(deviceInfoParam.getSearchBeginTime())))
                        .le("start_time", DateUtil.endOfDay(DateUtil.parse(deviceInfoParam.getSearchEndTime())))
                        .groupBy("measurement_point_id")
                );

                if (!info.isEmpty()) {
                    Map<String, RmpEventDetailPO> map = info.stream().collect(Collectors.toMap(RmpEventDetailPO::getMeasurementPointId, Function.identity()));
                    List<String> lineIds = info.stream().map(RmpEventDetailPO::getMeasurementPointId).distinct().collect(Collectors.toList());
                    List<AreaLineInfoVO> areaAnalysisVOList = lineFeignClient.getBaseLineAreaInfo(lineIds).getData();

                    for (AreaLineInfoVO areaLineInfoVO : areaAnalysisVOList) {
                        List<AreaLineInfoVO> ev = new ArrayList<>();
                        RmpEventDetailPO po = map.get(areaLineInfoVO.getLineId());
                        if (po != null) {
                            tail = tail + po.getFileFlag();
                            areaLineInfoVO.setTail(po.getFileFlag());
                        }
                        ev.add(areaLineInfoVO);
                        eventHeatMapDetailList.add(ev);
                    }
                    //获取综合评估
                    List<PQSComAssesPO> comAccessData = largeScreenService.getComAccessData(lineIndexs, deviceInfoParam.getSearchBeginTime(), deviceInfoParam.getSearchEndTime());
                    List<ComAssessDTO> comAssessDTOS = new ArrayList<>();
                    largeScreenService.setResults(comAccessData, comAssessDTOS);
                    float allComAss = comAssesUtil.getAllComAss(comAssessDTOS);
                    BaseVO baseVO = new BaseVO();
                    baseVO.setName(generalDeviceDTO.getName()+"\n("+generalDeviceDTO.getLineIndexes().size()+")");
                    baseVO.setValue(BigDecimal.valueOf(lineIndexs.size()));
                    baseVO.setData(String.valueOf(allComAss));
                    baseVO.setCount(tail);
                    baseVO.setSize(lineIndexs.size());
                    listObject.add(baseVO);
                }else {
                    setDto(listObject, generalDeviceDTO);
                }
            } else {
                setDto(listObject, generalDeviceDTO);
            }
            i++;
        }
        eventHeatMapVO.setAreaValue(listObject);
        eventHeatMapVO.setEventHeatMapValue(eventHeatMapDetailList);
        return eventHeatMapVO;
    }

    public void setDto(List<BaseVO> listObject, GeneralDeviceDTO generalDeviceDTO) {
        BaseVO baseVO = new BaseVO();
        baseVO.setName(generalDeviceDTO.getName()+"\n("+generalDeviceDTO.getLineIndexes().size()+")");
        baseVO.setValue(BigDecimal.valueOf(3.141592));
        baseVO.setData("-1");
        baseVO.setCount(0);
        baseVO.setSize(0);
        listObject.add(baseVO);
    }

    @Override
    public EventSeverityVO getEventSeverity(DeviceInfoParam.BusinessParam deviceInfoParam) {
        //获取暂降字典信息
        DictData voltageData = dicDataFeignClient.getDicDataByCode(DicDataEnum.VOLTAGE_DIP.getCode()).getData();
        if(ObjectUtil.isNull(voltageData)){
            throw new BusinessException(DeviceResponseEnum.DIC_GET_EMPTY);
        }
        EventSeverityVO eventSeverityVO = new EventSeverityVO();
        List<EventSeverityValueVO> eventSeverityValueList = new ArrayList<>();
        deviceInfoParam.setServerName(ServerEnum.EVENT.getName());
        List<List<Object>> listObject = new ArrayList<>();
        // 获取暂降监测点
        List<GeneralDeviceDTO> generalDeviceDTOList = generalDeviceInfoClient.getPracticalAllDeviceInfo(deviceInfoParam).getData();
        List<String> lineIndexs;
        int i = 0, generalDeviceListSize = generalDeviceDTOList.size();
        while (i < generalDeviceListSize) {
            List<Object> objectList = new ArrayList<>();
            GeneralDeviceDTO generalDeviceDTO = generalDeviceDTOList.get(i);
            lineIndexs = generalDeviceDTO.getLineIndexes();
            // 获取暂降数据
            if (lineIndexs.size() > 0) {
                int tail = 0;
                EventSeverityValueVO eventSeverityValueVO = new EventSeverityValueVO();
                List<RmpEventDetailPO> info = eventDetailService.list(new QueryWrapper<RmpEventDetailPO>()
                        .select("measurement_point_id as measurementPointId,count(file_flag) as fileFlag")
                        .in("measurement_point_id", lineIndexs)
                        .eq("event_type", voltageData.getId())
                        .ge(StrUtil.isNotBlank(deviceInfoParam.getSearchBeginTime()),"start_time" ,DateUtil.beginOfDay(DateUtil.parse(deviceInfoParam.getSearchBeginTime())))
                        .le(StrUtil.isNotBlank(deviceInfoParam.getSearchEndTime()), "start_time", DateUtil.endOfDay(DateUtil.parse(deviceInfoParam.getSearchEndTime())))
                        .groupBy("measurement_point_id")
                );
                List<EventHeatDeatilVO> eventdetailList = BeanUtil.copyToList(info, EventHeatDeatilVO.class);
                if (info.size() > 0) {
                    for (int eventNum = 0; eventNum < info.size(); eventNum++) {
                        tail += info.get(eventNum).getFileFlag();
                    }
                    eventSeverityValueVO.setAreaName(generalDeviceDTO.getName()+"\n("+generalDeviceDTO.getLineIndexes().size()+")");
                    eventSeverityValueVO.setLineNum(lineIndexs.size());
                    eventSeverityValueVO.setEventNum(tail);
                    eventSeverityValueList.add(eventSeverityValueVO);
                    objectList.add(generalDeviceDTO.getName()+"\n("+generalDeviceDTO.getLineIndexes().size()+")");
                    objectList.add(tail);
                    listObject.add(objectList);
                }else {
                    setObjectDto(listObject, generalDeviceDTO);
                }
            } else {
                setObjectDto(listObject, generalDeviceDTO);
            }
            i++;
        }
        eventSeverityVO.setAreaValue(listObject);
        eventSeverityVO.setEventSeverityValue(eventSeverityValueList);
        return eventSeverityVO;
    }

    public void setObjectDto(List<List<Object>> listObject, GeneralDeviceDTO generalDeviceDTO) {
        List<Object> info=new ArrayList<>();
        info.add(generalDeviceDTO.getName()+"\n("+generalDeviceDTO.getLineIndexes().size()+")");
        info.add(BigDecimal.valueOf(3.141592));
        info.add("-1");
        info.add(0);
        info.add(0);
        info.add(0);
        listObject.add(info);
    }
    /**
     * 获取终端运行统计
     *
     * @param deviceInfoParam 参数
     * @return 结果
     */
    @Override
    public TerminalRunningStatisticsVO getTerminalRunningStatistics(DeviceInfoParam.BusinessParam deviceInfoParam) {
        // 区域计算
        TerminalRunningVO area = analyzeTerminalRun(deviceInfoParam);

        // 厂家计算
        deviceInfoParam.getStatisticalType().setCode(StatisticsEnum.MANUFACTURER.getCode());
        TerminalRunningVO factory = analyzeTerminalRun(deviceInfoParam);

        return TerminalRunningStatisticsVO.buildVO(area, factory);
    }

    private TerminalRunningVO analyzeTerminalRun(DeviceInfoParam.BusinessParam deviceInfoParam) {
        List<GeneralDeviceDTO> generalDeviceDTOList = generalDeviceInfoClient.getPracticalAllDeviceInfo(deviceInfoParam).getData();
        List<String> deviceIndexList = generalDeviceDTOList.stream().flatMap(dto -> dto.getDeviceIndexes().stream()).collect(Collectors.toList());

        if (CollUtil.isEmpty(deviceIndexList)) {
            return TerminalRunningVO.empty();
        }

        List<PqsOnlinerate> pqsOnlinerateList = rStatOnlinerateDMapper.getOnlineRateByDevIds(deviceIndexList,beginOfDay(deviceInfoParam.getSearchBeginTime()), endOfDay(deviceInfoParam.getSearchEndTime()));

        List<PqDevice> pqDeviceList = pqDeviceMapper.queryRunFlagByDeviceIndexs(deviceIndexList);

        List<TerminalRunningInfoVO> terminalRun = generalDeviceDTOList.parallelStream().map(dto -> {
            List<String> deviceIndexes = dto.getDeviceIndexes();

            TerminalRunningInfoVO terminalRunningInfoVO = new TerminalRunningInfoVO();
            terminalRunningInfoVO.setAreaName(dto.getName());
            terminalRunningInfoVO.setNumberOfTerminals(deviceIndexes.size());
            terminalRunningInfoVO.setNormal(countDeviceRunStatus(pqDeviceList).apply(0, deviceIndexes));
            terminalRunningInfoVO.setBreaks(countDeviceRunStatus(pqDeviceList).apply(1, deviceIndexes));
            terminalRunningInfoVO.setShutdown(countDeviceRunStatus(pqDeviceList).apply(2, deviceIndexes));
            if (deviceIndexes.size() == 0) {
                terminalRunningInfoVO.setNormalRate(3.14159);
                terminalRunningInfoVO.setBreaksRate(3.14159);
                terminalRunningInfoVO.setShutdownRate(3.14159);
            } else {
                double normalRate = terminalRunningInfoVO.getNormal().doubleValue() / terminalRunningInfoVO.getNumberOfTerminals() * 100;
                terminalRunningInfoVO.setNormalRate(new BigDecimal(normalRate).setScale(2, RoundingMode.HALF_UP).doubleValue());
                double breaksRate = terminalRunningInfoVO.getBreaks().doubleValue() / terminalRunningInfoVO.getNumberOfTerminals() * 100;
                terminalRunningInfoVO.setBreaksRate(new BigDecimal(breaksRate).setScale(2, RoundingMode.HALF_UP).doubleValue());
                double shutdownRate = terminalRunningInfoVO.getShutdown().doubleValue() / terminalRunningInfoVO.getNumberOfTerminals() * 100;
                terminalRunningInfoVO.setShutdownRate(new BigDecimal(shutdownRate).setScale(2, RoundingMode.HALF_UP).doubleValue());
            }
            terminalRunningInfoVO.setOnlineRate(computingDeviceOnlineRate(pqsOnlinerateList).apply(deviceIndexes));
            return terminalRunningInfoVO;
        }).collect(Collectors.toCollection(() -> Collections.synchronizedList(new ArrayList<>())));

        Integer terminalSum = terminalRun.stream().mapToInt(TerminalRunningInfoVO::getNumberOfTerminals)
                .sum();
        Long normalSum = terminalRun.stream().mapToLong(TerminalRunningInfoVO::getNormal)
                .sum();
        Long breaksSum = terminalRun.stream().mapToLong(TerminalRunningInfoVO::getBreaks)
                .sum();
        Long shutdownSum = terminalRun.stream().mapToLong(TerminalRunningInfoVO::getShutdown)
                .sum();
        Double normalRateSum = 3.14159, breaksRateSum = 3.14159, shutdownRateSum = 3.14159;
        if (terminalSum != 0) {
            normalRateSum = new BigDecimal(normalSum.doubleValue() / terminalSum * 100).setScale(2, RoundingMode.HALF_UP).doubleValue();
            breaksRateSum = new BigDecimal(breaksSum.doubleValue() / terminalSum * 100).setScale(2, RoundingMode.HALF_UP).doubleValue();
            shutdownRateSum = new BigDecimal(shutdownSum.doubleValue() / terminalSum * 100).setScale(2, RoundingMode.HALF_UP).doubleValue();
        }
        Double onlineRateAvg = computingDeviceOnlineRate(pqsOnlinerateList).apply(deviceIndexList);
        return TerminalRunningVO.buildVO(terminalSum, normalSum, normalRateSum, breaksSum, breaksRateSum, shutdownSum,
                shutdownRateSum, onlineRateAvg, terminalRun);
    }

    /**
     * 计算装置在线率,在线率计算公式
     * OnlineRate = OnLineMin/( OnLineMin+ OffLineMin) * 100%；
     *
     * @param pqsOnlinerateList 装置在线记录
     * @return 在线率
     */
    private Function<List<String>, Double> computingDeviceOnlineRate(List<PqsOnlinerate> pqsOnlinerateList) {
        return deviceIndexes -> {
            List<PqsOnlinerate> value = pqsOnlinerateList.stream()
                    .filter(t -> deviceIndexes.contains(t.getDevId()))
                    .collect(Collectors.toList());

            int onlineSum = value.stream().mapToInt(PqsOnlinerate::getOnlinemin).sum();
            int offlineSum = value.stream().mapToInt(PqsOnlinerate::getOfflinemin).sum();
            BigDecimal b1 = new BigDecimal(onlineSum);
            BigDecimal b2 = new BigDecimal(offlineSum);
            BigDecimal c = new BigDecimal(100);
            if (b1.compareTo(BigDecimal.ZERO) == 0) {
                return 3.14159D;
            }
            return b1.divide(b1.add(b2), 4, RoundingMode.HALF_UP).multiply(c).doubleValue();
        };
    }

    /**
     * 根据终端运行状态（0：投运；1：热备用；2：停运）查询数据数量
     *
     * @param pqDeviceList 设备列表
     * @return 数据数量
     */
    private BiFunction<Integer, List<String>, Long> countDeviceRunStatus(List<PqDevice> pqDeviceList) {
        return (runFlag, deviceIndexes) -> pqDeviceList.stream()
                .filter(t -> Objects.equals(t.getRunFlag(), runFlag))
                .filter(t -> deviceIndexes.contains(t.getId()))
                .count();
    }

    public List<EventHeatDeatilVO> getContion(DeviceInfoParam.BusinessParam deviceInfoParam, List<String> lineIndexs) {
        // 组装sql语句
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("time >= '").append(DateUtil.beginOfDay(DateUtil.parse(deviceInfoParam.getSearchBeginTime()))).append("' and ").append("time <= '").append(DateUtil.endOfDay(DateUtil.parse(deviceInfoParam.getSearchEndTime()))).append("' and ");
        int j = 0;
        // sql语句
        for (String line : lineIndexs) {
            if (j == 0) {
                stringBuilder.append("line_id ='").append(line).append("'");
            } else {
                stringBuilder.append(" or line_id ='").append(line).append("'");
            }
            j++;
        }
        stringBuilder.append(" group by line_id").append(InfluxDBPublicParam.TIME_ZONE);
        // 获取暂降事件
        String sql = "select count(file_flag) from  pqs_eventdetail  where " + stringBuilder.toString();
        QueryResult result = influxDbUtils.query(sql);
        InfluxDBResultMapper influxDBResultMapper = new InfluxDBResultMapper();
        List<EventHeatDeatilVO> eventdetailList = influxDBResultMapper.toPOJO(result, EventHeatDeatilVO.class);
        return eventdetailList;
    }
}
