package com.njcn.energy.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.energy.mapper.EleInformationMapper;
import com.njcn.energy.pojo.constant.ModelState;
import com.njcn.energy.pojo.dto.OnlineRateDTO;
import com.njcn.energy.pojo.po.*;
import com.njcn.energy.pojo.vo.EleIntegrityVO;
import com.njcn.energy.service.IEleInformationService;
import com.njcn.energy.service.ILogicAccessService;
import com.njcn.energy.service.ILogicDeviceLineService;
import com.njcn.energy.service.INetDevService;
import com.njcn.influx.utils.InfluxDbUtils;
import lombok.AllArgsConstructor;
import org.influxdb.dto.QueryResult;
import org.influxdb.impl.InfluxDBResultMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 类的介绍：
 *
 * @author xuyang
 * @version 1.0.0
 * @createTime 2022/4/19 10:51
 */
@Service
@AllArgsConstructor
public class EleInformationServiceImpl extends ServiceImpl<EleInformationMapper, EleInformation> implements IEleInformationService {

    private final INetDevService netDevService;

    private final ILogicDeviceLineService logicDeviceLineService;

    private final InfluxDbUtils influxDbUtils;

    private final ILogicAccessService logicAccessService;

    private final Integer DAY_SECOND = 60*60*24;

    @Override
    public void addInformation(List<EleInformation> list) {
        this.saveBatch(list);
    }

    @Override
    public List<OnlineRateDTO> getDeviceTime(String startTime, String endTime) {
        List<OnlineRateDTO> result = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<String> list = new ArrayList<>();
        //获取当天information表中的数据
        List<EleInformation> informationList = this.lambdaQuery().ge(EleInformation::getUpdateTime,startTime).le(EleInformation::getUpdateTime,endTime).list();
        if (!CollectionUtils.isEmpty(informationList)){
            list = informationList.stream().map(EleInformation::getDid).collect(Collectors.toList());
        }
        //获取所有的装置
        List<String> onlineList = logicAccessService.getAllDev().stream().map(LogicAccess::getDid).collect(Collectors.toList());
        onlineList.removeAll(list);
        //根据装置的id进行分组
        Map<String,List<EleInformation>> groupMap = informationList.stream().collect(Collectors.groupingBy(EleInformation::getDid));
        //先处理information表中的数据
        try {
            for (String key : groupMap.keySet()) {
                int offTime = 0;
                int onTime = 0;
                OnlineRateDTO onlineRateDTO = new OnlineRateDTO();
                List<EleInformation> infoList = groupMap.get(key);
                if (infoList.size() > 1){
                    //获取最早一条记录
                    EleInformation first = infoList.stream().min(Comparator.comparing(EleInformation::getUpdateTime)).get();
                    //频繁的上下线
                    //将上线和下线分组
                    Map<Integer,List<EleInformation>> typeMap = infoList.stream().collect(Collectors.groupingBy(EleInformation::getType));
                    List<EleInformation> off = typeMap.get(0);
                    List<EleInformation> on = typeMap.get(1);
                    if (first.getType() == 0){
                        if (off.size() == on.size()){
                            for (int i = 0; i < off.size(); i++) {
                                offTime = offTime + (int) (on.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - off.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) / 1000;
                            }
                        } else {
                            for (int i = 0; i < off.size(); i++) {
                                if (i == off.size() - 1){
                                    offTime = offTime + (int) (sdf.parse(endTime).getTime() - off.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) / 1000;
                                } else {
                                    offTime = offTime + (int) (on.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - off.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) / 1000;
                                }
                            }
                        }
                        onTime = DAY_SECOND-offTime;
                    } else {
                        if (off.size() == on.size()){
                            for (int i = 0; i < on.size(); i++) {
                                onTime = onTime + (int) (off.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - on.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) / 1000;
                            }
                        } else {
                            for (int i = 0; i < on.size(); i++) {
                                if (i == on.size() - 1){
                                    onTime = onTime + (int) (sdf.parse(endTime).getTime() - on.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) / 1000;
                                } else {
                                    onTime = onTime + (int) (off.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - on.get(i).getUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) / 1000;
                                }
                            }
                        }
                        offTime = DAY_SECOND-onTime;
                    }
                } else {
                    LocalDateTime updateTime = infoList.get(0).getUpdateTime();
                    if (Objects.equals(infoList.get(0).getType(),0)) {
                        onTime = (int) (updateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - sdf.parse(startTime).getTime()) / 1000;
                        offTime = (int) (sdf.parse(endTime).getTime() - updateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) / 1000;
                    } else {
                        offTime = (int) (updateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - sdf.parse(startTime).getTime()) / 1000;
                        onTime = (int) (sdf.parse(endTime).getTime() - updateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) / 1000;
                    }
                }
                onlineRateDTO.setDeviceId(infoList.get(0).getDid());
                onlineRateDTO.setOnlineTime(onTime);
                onlineRateDTO.setOfflineTime(offTime);
                onlineRateDTO.setOnlineRate(Double.parseDouble(String.format("%.2f",onTime*1.0/DAY_SECOND*100)));
                onlineRateDTO.setTime(sdf.parse(startTime).getTime());
                result.add(onlineRateDTO);
            }
            //后处理information表中没有的数据
            //1.判断有没有最新的记录，没有则不做记录 2.如果有最新的记录，判断最后一条记录是上线还是下线
            if(!CollectionUtils.isEmpty(onlineList)){
                for (String s : onlineList) {
                    List<EleInformation> eleInformationList = this.lambdaQuery().eq(EleInformation::getDid,s).orderByDesc(EleInformation::getUpdateTime).list();
                    if (!CollectionUtils.isEmpty(eleInformationList)){
                        EleInformation pojo = eleInformationList.stream().findFirst().get();
                        OnlineRateDTO onlineRateDTO = new OnlineRateDTO();
                        if (Objects.equals(pojo.getType(), ModelState.offline)){
                            onlineRateDTO.setOfflineTime(DAY_SECOND);
                            onlineRateDTO.setOnlineTime(0);
                            onlineRateDTO.setOnlineRate(0.0);
                        } else {
                            onlineRateDTO.setOfflineTime(0);
                            onlineRateDTO.setOnlineTime(DAY_SECOND);
                            onlineRateDTO.setOnlineRate(100.0);
                        }
                        onlineRateDTO.setTime(sdf.parse(startTime).getTime());
                        onlineRateDTO.setDeviceId(s);
                        result.add(onlineRateDTO);
                    }
                }
            }
        } catch (ParseException e) {
            e.getMessage();
        }
        return result;
    }

    @Override
    public List<EleIntegrityVO> getOnlineRateTree(String startTime, String endTime) {
        List<EleIntegrityVO> netDevIntegrity = new ArrayList<>(),logicDevIntegrity = new ArrayList<>();
        Map<String, List<EleOnlineRate>> deviceGroup = new HashMap<>();
        //获取所有网关
        List<NetDev> netDevList = netDevService.getAllNetDev();
        List<String> netDevId = netDevList.stream().map(NetDev::getId).collect(Collectors.toList());
        //获取网关下所有的逻辑设备
        List<LogicDev> logicDevList = logicDeviceLineService.getLogicDevByList(netDevId);
        List<String> devId = logicDevList.stream().map(LogicDev::getDid).collect(Collectors.toList());
        //获取装置通讯状态
        List<LogicAccess> logicAccessList = logicAccessService.getLogicDevByList(netDevId);
        netDevList.forEach(item->{
            EleIntegrityVO eleIntegrityVO = new EleIntegrityVO();
            BeanUtil.copyProperties(item,eleIntegrityVO);
            eleIntegrityVO.setComFlag(item.getComFlag());
            eleIntegrityVO.setPid("0");
            netDevIntegrity.add(eleIntegrityVO);
        });
        List<EleOnlineRate> devList =  getAreaList(startTime,endTime,devId);
        if (!CollectionUtils.isEmpty(devList)){
           deviceGroup = devList.stream().collect(Collectors.groupingBy(EleOnlineRate::getDeviceId));
        }
        for (LogicDev item : logicDevList) {
            EleIntegrityVO eleIntegrityVO = new EleIntegrityVO();
            BeanUtil.copyProperties(item,eleIntegrityVO);
            eleIntegrityVO.setInfo(item.getPosition());
            logicAccessList.forEach(item2->{
                if (Objects.equals(item.getDid(),item2.getDid())){
                    if (Objects.equals(item2.getState(), ModelState.ACCESS)){
                        eleIntegrityVO.setComFlag(DataStateEnum.ENABLE.getCode());
                    } else {
                        eleIntegrityVO.setComFlag(DataStateEnum.DELETED.getCode());
                    }
                }
            });
            for (String device:deviceGroup.keySet()) {
                if (Objects.equals(device,item.getDid())){
                    double dd = 0.0;
                    List<EleOnlineRate> list = deviceGroup.get(device);
                    if (!CollectionUtils.isEmpty(list)){
                        dd = BigDecimal.valueOf(Math.min(list.stream().mapToDouble(EleOnlineRate::getOnlineRate).sum()/list.size(), 100.00)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                    }
                    eleIntegrityVO.setIntegrity(dd);
                }
            }
            logicDevIntegrity.add(eleIntegrityVO);
        }
        dealAverageAndChildrenData(netDevIntegrity,logicDevIntegrity,startTime,endTime);
        return netDevIntegrity;
    }


    private void dealAverageAndChildrenData(List<EleIntegrityVO> targetData, List<EleIntegrityVO> childrenData,String startTime,String endTime) {
        Map<String, List<EleIntegrityVO>> groupLine = childrenData.stream().collect(Collectors.groupingBy(EleIntegrityVO::getPid));
        targetData = targetData.stream().peek(integrityVO -> {
            if (!CollectionUtils.isEmpty(groupLine.get(integrityVO.getId()))) {
                List<EleIntegrityVO> integrityVOList = new ArrayList<>();
                integrityVOList = groupLine.get(integrityVO.getId());
                integrityVO.setChildren(groupLine.get(integrityVO.getId()));
                List<String> devList = new ArrayList<>();
                List<EleOnlineRate> areaList = new ArrayList<>();
                devList = integrityVOList.stream().map(EleIntegrityVO::getDid).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(devList)){
                    //查询结果
                    areaList = getAreaList(startTime,endTime,devList);
                    if(!CollectionUtils.isEmpty(areaList)){
                        integrityVO.setIntegrity(getAreaData(areaList));
                    }
                }
            }
        }).collect(Collectors.toList());
    }



    /**
     * 终端在线率
     * 区域(日/月统计)
     */
    private List<EleOnlineRate> getAreaList(String startTime, String endTime, List<String> lineList) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("(");
        for (int i = 0; i < lineList.size(); i++) {
            if (lineList.size() - i != 1) {
                stringBuilder.append("device_id = '").append(lineList.get(i)).append("' or ");
            } else {
                stringBuilder.append("device_id = '").append(lineList.get(i)).append("')");
            }
        }
        String sql = "SELECT * FROM ele_online_rate where "+stringBuilder+" and time >='"+startTime+"' and time <= '"+endTime+"' tz('Asia/Shanghai')";
        QueryResult sqlResult = influxDbUtils.query(sql);
        InfluxDBResultMapper resultMapper = new InfluxDBResultMapper();
        List<EleOnlineRate> onlineRateList = resultMapper.toPOJO(sqlResult, EleOnlineRate.class);
        onlineRateList.forEach(item->{
            item.setTime(item.getTime().plusMillis(TimeUnit.HOURS.toMillis(8)));
        });
        return onlineRateList;
    }

    /**
     * 根据时间分组，求区域的终端在线率
     */
    private Double getAreaData(List<EleOnlineRate> list) {
        double result = 0.0;
        Map<Instant, List<EleOnlineRate>> map = list.stream().collect(Collectors.groupingBy(EleOnlineRate::getTime));
        for (Instant key : map.keySet()) {
            List<EleOnlineRate> value = map.get(key);
            double data = value.stream().mapToDouble(EleOnlineRate::getOnlineRate).sum();
            //区域某一天的终端在线率
            data = data/value.size();
            //区域时间范围终端在线率之和
            result = result + data;
        }
        return BigDecimal.valueOf(Math.min(result/map.size(), 100.00)).setScale(2, RoundingMode.HALF_UP).doubleValue();
    }
}
