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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.pq.pojo.po.RStatOnlinerateD;
import com.njcn.energy.pojo.constant.ModelState;
import com.njcn.influxdb.utils.InfluxDbUtils;
import com.njcn.prepare.harmonic.mapper.mysql.day.RStatOnlineRateDMapper;
import com.njcn.prepare.harmonic.pojo.dto.OnLineRateDTO;
import com.njcn.prepare.harmonic.pojo.param.LineParam;

import com.njcn.prepare.harmonic.pojo.influxdb.po.PqsCommunicatePO;
import com.njcn.prepare.harmonic.service.mysql.line.OnlineRateService;
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.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.njcn.influxdb.param.InfluxDBPublicParam.DAY_MINUTE;

/**
 * 生成在线率数据
 *
 * @author xiaoyao
 * @version 1.0.0
 * @createTime 2022/10/27 9:23
 */
@Slf4j
@Service
@AllArgsConstructor
public class OnlineRateServiceImpl extends MppServiceImpl<RStatOnlineRateDMapper, RStatOnlinerateD> implements OnlineRateService {

    private final InfluxDbUtils influxDbUtils;

    private final LineFeignClient lineFeignClient;

    @Override
    @Async("asyncExecutor")
    public void getOnlineRateData(LineParam lineParam) {
        List<String> lineIdList = lineParam.getLineIds();
        if (CollUtil.isEmpty(lineParam.getLineIds())){
            lineIdList = lineFeignClient.getDeviceList().getData();
        }
        List<PqsCommunicatePO> outCommunicateData = new ArrayList<>();
        for (String lineId : lineIdList){
            List<PqsCommunicatePO> communicateData = getCommunicateData(lineId);
            outCommunicateData.addAll(communicateData);
        }
        List<String> records = new ArrayList<>();
        Date dateOut = DateUtil.parse(lineParam.getDataDate());
        for (PqsCommunicatePO pqsCommunicate : outCommunicateData){
            Map<String, String> tags = new HashMap<>();
            Map<String, Object> fields = new HashMap<>();
            Date newDate = Date.from(pqsCommunicate.getTime());
            OnLineRateDTO onLineRate = onLineMinute(newDate,dateOut,pqsCommunicate.getType(),pqsCommunicate.getDevId(),lineParam.getDataDate());
            tags.put("dev_id",pqsCommunicate.getDevId());
            fields.put("online_min",onLineRate.getOnLineMinute());
            fields.put("offline_min",onLineRate.getOffLineMinute());
            fields.put("online_rate",onLineRate.getRate());
            Point point = influxDbUtils.pointBuilder("pqs_onlinerate", dateOut.getTime(), TimeUnit.MILLISECONDS,tags, fields);
            BatchPoints batchPoints = BatchPoints.database(influxDbUtils.getDbName()).tag("dev_id", pqsCommunicate.getDevId()).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 onlineRateData(LineParam lineParam,String time) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String data = time + " 00:00:00";
        LocalDateTime dateTime = LocalDateTime.parse(data,df);
        List<String> lineIdList = lineParam.getLineIds();
        if (CollUtil.isEmpty(lineParam.getLineIds())){
            lineIdList = lineFeignClient.getDeviceList().getData();
        }
        List<PqsCommunicatePO> outCommunicateData = new ArrayList<>();
        for (String lineId : lineIdList){
            List<PqsCommunicatePO> communicateData = getCommunicateData(lineId);
            outCommunicateData.addAll(communicateData);
        }
        List<RStatOnlinerateD> list = new ArrayList<>();
        Date dateOut = DateUtil.parse(time);
        for (PqsCommunicatePO pqsCommunicate : outCommunicateData){
            RStatOnlinerateD onlineRateDpo = new RStatOnlinerateD();
            Date newDate = Date.from(pqsCommunicate.getTime());
            OnLineRateDTO onLineRate = onLineMinute(newDate,dateOut,pqsCommunicate.getType(),pqsCommunicate.getDevId(),time);
            onlineRateDpo.setTimeId(dateTime);
            onlineRateDpo.setDevIndex(pqsCommunicate.getDevId());
            onlineRateDpo.setOnlineMin(onLineRate.getOnLineMinute());
            onlineRateDpo.setOfflineMin(onLineRate.getOffLineMinute());
            list.add(onlineRateDpo);
        }
        this.saveOrUpdateBatchByMultiId(list,50);
    }

    /**
     * 获取pqs_communicate数据
     */
    private List<PqsCommunicatePO> getCommunicateData(String lineId){
        QueryResult sqlResult = influxDbUtils.query("SELECT * FROM pqs_communicate where dev_id = '" + lineId +"' order by time desc limit 1 tz('Asia/Shanghai')");
        InfluxDBResultMapper resultMapper = new InfluxDBResultMapper();
        return resultMapper.toPOJO(sqlResult, PqsCommunicatePO.class);
    }

    /**
     * 获取范围时间内的pqs_communicate数据
     */
    private List<PqsCommunicatePO> getCommunicateData(String lineId, String date){
        QueryResult sqlResult = influxDbUtils.query("SELECT * FROM pqs_communicate where time >= '" + date + " 00:00:00' and time <= '" + date + " 23:59:59' and dev_id = '" + lineId +"' order by time asc tz('Asia/Shanghai')");
        InfluxDBResultMapper resultMapper = new InfluxDBResultMapper();
        return resultMapper.toPOJO(sqlResult, PqsCommunicatePO.class);
    }

    private List<PqsCommunicatePO> getCommunicateDataOld(String lineId, String date){
        QueryResult sqlResult = influxDbUtils.query("SELECT * FROM pqs_communicate where time <= '" + date + " 23:59:59' and dev_id = '" + lineId +"' order by time desc limit 1 tz('Asia/Shanghai')");
        InfluxDBResultMapper resultMapper = new InfluxDBResultMapper();
        return resultMapper.toPOJO(sqlResult, PqsCommunicatePO.class);
    }

    /**
     * new的时间和当前统计时间 不是/是 同一天
     */
    private OnLineRateDTO onLineMinute(Date newDate, Date date, Integer type,String lineId,String dateStr){
        OnLineRateDTO onLineRate = new OnLineRateDTO();
        Integer minute = 0;
        /*new的时间和当前统计时间是同一天*/
        if (DateUtil.isSameDay(newDate,date)){
            minute = processData(newDate, date, type, lineId, dateStr);
        }else {
            /*new的时间和当前统计时间不是同一天*/
            Date nowDate = new Date();
            /*数据补招的情况下*/
            if (DateUtil.between(date,nowDate,DateUnit.DAY) > DateUtil.between(newDate,nowDate,DateUnit.DAY)){
                minute = processData(newDate, date, null, lineId, dateStr);
            }else {
                if (ModelState.online.equals(type)){
                    minute = DAY_MINUTE;
                }
            }
        }
        onLineRate.setOnLineMinute(minute);
        onLineRate.setOffLineMinute(DAY_MINUTE - minute);
        BigDecimal onLine = BigDecimal.valueOf(onLineRate.getOnLineMinute());
        BigDecimal offLine = BigDecimal.valueOf(onLineRate.getOffLineMinute());
        double rate = onLine.divide(onLine.add(offLine),4, RoundingMode.HALF_UP).doubleValue();
        onLineRate.setRate(rate*100);
        return onLineRate;
    }

    private Integer processData(Date newDate, Date date, Integer type,String lineId,String dateStr){
        int minute = 0;
        List<PqsCommunicatePO> communicateData = getCommunicateData(lineId,dateStr);
        /*当前统计时间内存在多条数据*/
        if (communicateData.size() > 1){
            Date lastTime = null;
            long onlineTime = 0;
            long offlineTime = 0;
            for (int i=0; i<communicateData.size(); i++){
                long differ;
                if (i==0){
                    /*首次比较取统计时间*/
                    differ = DateUtil.between(date,Date.from(communicateData.get(i).getTime()),DateUnit.MINUTE);
                }else {
                    /*后续取上一次数据时间*/
                    differ = DateUtil.between(lastTime,Date.from(communicateData.get(i).getTime()),DateUnit.MINUTE);
                }
                if (ModelState.online.equals(communicateData.get(i).getType())){
                    offlineTime = offlineTime + differ;
                }else {
                    onlineTime = onlineTime + differ;
                }
                lastTime = Date.from(communicateData.get(i).getTime());
            }
            if (ModelState.online.equals(communicateData.get(communicateData.size()-1).getType())){
                minute = DAY_MINUTE - (int)offlineTime;
            }else {
                minute = (int)onlineTime;
            }
        }else {
            if(type != null){
                /*当前统计时间内仅有一条数据*/
                long differ = DateUtil.between(date,newDate,DateUnit.MINUTE);
                if (ModelState.online.equals(type)){
                    minute = DAY_MINUTE - (int)differ;
                }
            }else {
                List<PqsCommunicatePO> communicateDataOld = getCommunicateDataOld(lineId,dateStr);
                if (communicateDataOld.size() > 0){
                    if (ModelState.online.equals(communicateDataOld.get(0).getType())){
                        minute = DAY_MINUTE;
                    }
                }
            }
        }
        return minute;
    }
}
