package com.njcn.harmonic.service.influxdb.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.njcn.common.utils.HarmonicTimesUtil;


import com.njcn.dataProcess.param.LineCountEvaluateParam;
import com.njcn.dataProcess.pojo.dto.DataHarmDto;
import com.njcn.harmonic.service.influxdb.IDataHarmRateV;
import com.njcn.influx.constant.InfluxDbSqlConstant;
import com.njcn.influx.imapper.DataHarmRateVMapper;
import com.njcn.influx.pojo.po.DataHarmRateV;
import com.njcn.influx.query.InfluxQueryWrapper;
import com.njcn.redis.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.lang.reflect.Type;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xy
 */
@Service
@RequiredArgsConstructor
public class InfluxdbDataHarmRateVImpl implements IDataHarmRateV {
    private final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
    private final DataHarmRateVMapper DataHarmRateVMapper;
    private static final Map<String, String> PHASE_MAPPING = new HashMap<String, String>() {{
        put("AB", "A");
        put("BC", "B");
        put("CA", "C");
        put("M", "T");
    }};

    private final RedisUtil redisUtil;
    private static final Set<String> LINE_VOLTAGE_TYPES =
            Collections.unmodifiableSet(new HashSet<>(Arrays.asList("AB", "BC", "CA", "T")));
    private static final Set<String> PHASE_VOLTAGE_TYPES =
            Collections.unmodifiableSet(new HashSet<>(Arrays.asList("A", "B", "C", "T")));

    @Override
    public List<DataHarmDto> getRawData(LineCountEvaluateParam lineParam) {
        List<DataHarmDto> result = new ArrayList<>();
        List<DataHarmRateV> list = getMinuteData(lineParam);
        list.forEach(item->{
            DataHarmDto dto = new DataHarmDto();
            BeanUtils.copyProperties(item,dto);
            dto.setMinTime(DATE_TIME_FORMATTER.format(item.getTime()));
            result.add(dto);
        });
        return result;
    }



    /**
     * 按监测点集合、时间条件获取分钟数据
     * timeMap参数来判断是否进行数据处理 timeMap为空则不进行数据处理
     * 需要进行剔除异常数据时,这里会有三种情况判断
     * 1.无异常数据，则直接返回集合；
     * 2.异常数据和无异常数据参杂，剔除异常数据，只计算正常数据；
     * 3.全是异常数据，则使用异常数据进行计算，但是日表中需要标记出来，此数据有异常
     */
    public List<DataHarmRateV> getMinuteData(LineCountEvaluateParam lineParam) {
        List<DataHarmRateV> dataList;
        List<DataHarmRateV> result = new ArrayList<>();
        List<DataHarmRateV> data = new ArrayList<>();
        //获取监测点、接线方式数据
        Type type = new TypeToken<Map<String, Integer>>(){}.getType();
        Map<String, Integer> map = new Gson().fromJson(
                String.valueOf(redisUtil.getObjectByKey("wlLineDetail")),
                type
        );
        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(DataHarmRateV.class);
        influxQueryWrapper.samePrefixAndSuffix(InfluxDbSqlConstant.V, InfluxDbSqlConstant.V, HarmonicTimesUtil.harmonicTimesList(1, 50, 1));
        influxQueryWrapper.regular(DataHarmRateV::getLineId, lineParam.getLineId())
                .select(DataHarmRateV::getLineId)
                .select(DataHarmRateV::getPhaseType)
                .select(DataHarmRateV::getValueType)
                .select(DataHarmRateV::getQualityFlag)
                .select(DataHarmRateV::getAbnormalFlag)
                .between(DataHarmRateV::getTime, lineParam.getStartTime(), lineParam.getEndTime())
                .eq(DataHarmRateV::getQualityFlag,"0");
        if(CollUtil.isNotEmpty(lineParam.getPhasicType())){
            influxQueryWrapper.regular(DataHarmRateV::getPhaseType,lineParam.getPhasicType());
        }
        List<DataHarmRateV> list = DataHarmRateVMapper.selectByQueryWrapper(influxQueryWrapper);
        if(CollUtil.isNotEmpty(list)){
            //过滤掉暂态事件影响的数据 true过滤 false不过滤
            if (lineParam.getDataType()) {
                dataList = list.stream().filter(item -> Objects.isNull(item.getAbnormalFlag())).collect(Collectors.toList());
            } else {
                dataList = list;
            }
            Map<String,List<DataHarmRateV>> lineMap = dataList.stream().collect(Collectors.groupingBy(DataHarmRateV::getLineId));
            //有异常数据
            if (CollectionUtil.isNotEmpty(lineParam.getAbnormalTime())) {
                lineMap.forEach((k,v)->{
                    List<String> timeList = lineParam.getAbnormalTime().get(k);
                    //有异常数据,当前监测点自身的异常数据
                    if (CollectionUtil.isNotEmpty(timeList)) {
                        List<DataHarmRateV> filterList = v.stream().filter(item -> !timeList.contains(DATE_TIME_FORMATTER.format(item.getTime()))).collect(Collectors.toList());
                        //1.过滤掉异常数据后还有正常数据，则用正常数据计算
                        if (CollectionUtil.isNotEmpty(filterList)) {
                            result.addAll(filterList);
                        }
                        //2.过滤掉异常数据后没有正常数据，则用所有异常数据计算，但是需要标记数据为异常的
                        else {
                            v.parallelStream().forEach(item -> item.setQualityFlag("1"));
                            result.addAll(v);
                        }
                    }
                    //没有异常数据，则使用原数据
                    else {
                        result.addAll(v);
                    }
                });
            }
            //没有异常数据，则使用原数据
            else {
                result.addAll(dataList);
            }
        }
        if (CollectionUtil.isNotEmpty(result)) {
            if (!Objects.isNull(map)) {
                //现根据监测点分组，然后根据接线方式排除多于数据，在修改相别
                Map<String, List<DataHarmRateV>> lineMap = result.stream().collect(Collectors.groupingBy(DataHarmRateV::getLineId));
                lineMap.forEach((k,v)->{
                    if (Objects.isNull(map.get(k))) {
                        return;
                    }
                    Integer conType = map.get(k);
                    Set<String> validPhasicTypes = (conType != 0) ? LINE_VOLTAGE_TYPES : PHASE_VOLTAGE_TYPES;
                    List<DataHarmRateV> result2 = v.stream().filter(item -> validPhasicTypes.contains(item.getPhaseType())).collect(Collectors.toList());
                    data.addAll(result2);
                });
            } else {
                data.addAll(result);
            }
            if (CollectionUtil.isNotEmpty(data)) {
                data.forEach(item -> {
                    String newType = PHASE_MAPPING.get(item.getPhaseType());
                    if (newType != null) {
                        item.setPhaseType(newType);
                    }
                });
            }
        }
        return data;
    }
}
