package com.njcn.prepare.harmonic.service.mysql.Impl.line;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.biz.pojo.po.Overlimit;
import com.njcn.harmonic.pojo.po.day.RStatAssesDPO;
import com.njcn.influx.pojo.po.DataPlt;
import com.njcn.influx.pojo.po.DataV;
import com.njcn.influx.utils.InfluxDbUtils;
import com.njcn.prepare.harmonic.mapper.mysql.day.RStatAssesDMapper;
import com.njcn.prepare.harmonic.pojo.bo.CalculatedParam;
import com.njcn.prepare.harmonic.pojo.param.LineParam;
import com.njcn.prepare.harmonic.service.mysql.line.HarmonicMetricService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.influxdb.InfluxDB;
import org.influxdb.dto.BatchPoints;
import org.influxdb.dto.Point;
import org.influxdb.dto.QueryResult;
import org.influxdb.impl.InfluxDBResultMapper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 稳态指标评估
 * @author xiaoyao
 * @version 1.0.0
 * @createTime 2022/11/3 9:08
 */
@Slf4j
@Service
@AllArgsConstructor
public class HarmonicMetricServiceImpl extends MppServiceImpl<RStatAssesDMapper, RStatAssesDPO> implements HarmonicMetricService {

    private final InfluxDbUtils influxDbUtils;

    private final LineFeignClient lineFeignClient;

    @Override
    @Async("asyncExecutor")
    public void metricData(LineParam lineParam) {
        List<String> lineIdList;
        if (CollUtil.isEmpty(lineParam.getLineIds())){
            List<Overlimit> overLimitList = getAllLineData();
            lineIdList = overLimitList.stream().map(Overlimit::getId).collect(Collectors.toList());
        }else {
            lineIdList = new ArrayList<>(lineParam.getLineIds());
        }
        Date dateOut = DateUtil.parse(lineParam.getDataDate());
        List<String> records = new ArrayList<>();
        for (String lineId : lineIdList){
            Map<String, String> tags = new HashMap<>();
            tags.put("line_id",lineId);
            Map<String, Object> fields = getMetricData(lineId,lineParam.getDataDate());
            Point point = influxDbUtils.pointBuilder("pqs_asses", dateOut.getTime(), TimeUnit.MILLISECONDS,tags, fields);
            BatchPoints batchPoints = BatchPoints.database(influxDbUtils.getDbName()).tag("line_id", lineId).retentionPolicy("").consistency(InfluxDB.ConsistencyLevel.ALL).build();
            batchPoints.point(point);
            records.add(batchPoints.lineProtocol());
        }
        influxDbUtils.batchInsert(influxDbUtils.getDbName(),"", InfluxDB.ConsistencyLevel.ALL, records);
    }

    @Override
    @Async("asyncExecutor")
    public void metricDataToMysql(LineParam lineParam,String time) {
        List<RStatAssesDPO> list = new ArrayList<>();
        List<String> lineIdList;
        if (CollUtil.isEmpty(lineParam.getLineIds())){
            List<Overlimit> overLimitList = getAllLineData();
            lineIdList = overLimitList.stream().map(Overlimit::getId).collect(Collectors.toList());
        }else {
            lineIdList = new ArrayList<>(lineParam.getLineIds());
        }
        Date dateOut = DateUtil.parse(time);
        for (String lineId : lineIdList){
            Map<String, Object> fields = getMetricData(lineId,time);
            if(CollUtil.isNotEmpty(fields)) {
                RStatAssesDPO rStatAssesDpo = new RStatAssesDPO();
                rStatAssesDpo.setTime(dateOut);
                rStatAssesDpo.setLineId(lineId);
                rStatAssesDpo.setVuDev(Double.parseDouble(fields.get("vu_dev").toString()));
                rStatAssesDpo.setFreqDev(Double.parseDouble(fields.get("freq_dev").toString()));
                rStatAssesDpo.setDataPlt(Double.parseDouble(fields.get("data_plt").toString()));
                rStatAssesDpo.setVUnbalanceCp95(Double.parseDouble(fields.get("v_unbalance_cp95").toString()));
                rStatAssesDpo.setVThdCp95(Double.parseDouble(fields.get("v_thd_cp95").toString()));
                list.add(rStatAssesDpo);
            }
        }
        if(CollUtil.isNotEmpty(list)){
            this.saveOrUpdateBatchByMultiId(list,50);
        }
    }

    @Override
    public void newMetricData(CalculatedParam calculatedParam) {
        List<RStatAssesDPO> list = new ArrayList<>();
        List<String> lineIdList;
        if (CollUtil.isEmpty(calculatedParam.getIdList())){
            List<Overlimit> overLimitList = getAllLineData();
            lineIdList = overLimitList.stream().map(Overlimit::getId).collect(Collectors.toList());
        }else {
            lineIdList = new ArrayList<>(calculatedParam.getIdList());
        }
        Date dateOut = DateUtil.parse(calculatedParam.getDataDate());
        for (String lineId : lineIdList){
            Map<String, Object> fields = getMetricData(lineId,calculatedParam.getDataDate());
            if(CollUtil.isNotEmpty(fields)) {
                RStatAssesDPO rStatAssesDpo = new RStatAssesDPO();
                rStatAssesDpo.setTime(dateOut);
                rStatAssesDpo.setLineId(lineId);
                rStatAssesDpo.setVuDev(Double.parseDouble(fields.get("vu_dev").toString()));
                rStatAssesDpo.setFreqDev(Double.parseDouble(fields.get("freq_dev").toString()));
                rStatAssesDpo.setDataPlt(Double.parseDouble(fields.get("data_plt").toString()));
                rStatAssesDpo.setVUnbalanceCp95(Double.parseDouble(fields.get("v_unbalance_cp95").toString()));
                rStatAssesDpo.setVThdCp95(Double.parseDouble(fields.get("v_thd_cp95").toString()));
                list.add(rStatAssesDpo);
            }
        }
        if(CollUtil.isNotEmpty(list)){
            this.saveOrUpdateBatchByMultiId(list,50);
        }
    }

    private Map<String, Object> getMetricData(String lineId,String date){
        String processParam = " and line_id = '"+lineId+"' and time >= '"+date+" 00:00:00' and time <= '"+date+" 23:59:59' tz('Asia/Shanghai')";
        QueryResult vuDevResult = influxDbUtils.query("select abs(vu_dev) as vu_dev from data_v where phasic_type != 'T' and value_type = 'AVG'"+processParam);
        QueryResult vThdResult = influxDbUtils.query("select mean(v_thd) from data_v where phasic_type != 'T' and value_type = 'CP95'"+processParam);
        QueryResult freqDevResult = influxDbUtils.query("select abs(freq_dev) as freq_dev from data_v where phasic_type = 'T' and value_type = 'AVG'"+processParam);
        QueryResult vUnResult = influxDbUtils.query("select mean(v_unbalance) from data_v where phasic_type = 'T' and value_type = 'CP95'"+processParam);
        QueryResult pltResult = influxDbUtils.query("select mean(plt) from data_plt where phasic_type != 'T'"+processParam);
        InfluxDBResultMapper resultMapper = new InfluxDBResultMapper();
        List<DataV> vuDev = resultMapper.toPOJO(vuDevResult, DataV.class);
        List<DataV> vThd = resultMapper.toPOJO(vThdResult, DataV.class);
        List<DataV> freqDev = resultMapper.toPOJO(freqDevResult, DataV.class);
        List<DataV> vUn = resultMapper.toPOJO(vUnResult, DataV.class);
        List<DataPlt> plt = resultMapper.toPOJO(pltResult, DataPlt.class);
        Map<String, Object> outMap = new HashMap<>();
        if(CollUtil.isNotEmpty(vuDev) || CollUtil.isNotEmpty(vThd) || CollUtil.isNotEmpty(freqDev) || CollUtil.isNotEmpty(vUn) || CollUtil.isNotEmpty(plt)){
            outMap.put("vu_dev",(vuDev.size()>0)?vuDev.stream().collect(Collectors.averagingDouble(DataV::getVuDev)):0.0);
            outMap.put("v_thd_cp95",(vThd.size()>0)?vThd.get(0).getMean():0.0);
            outMap.put("freq_dev",(freqDev.size()>0)?freqDev.stream().collect(Collectors.averagingDouble(DataV::getFreqDev)):0.0);
            outMap.put("v_unbalance_cp95",(vUn.size()>0)?vUn.get(0).getMean():0.0);
            outMap.put("data_plt",(plt.size()>0)?plt.get(0).getMean():0.0);
        }
        return outMap;
    }

    private List<Overlimit> getAllLineData() {
        return lineFeignClient.getAllLineOverLimit("harmonic-boot","").getData();
    }
}
