package com.imdroid.sideslope.task;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imdroid.secapi.dto.*;
import com.imdroid.sideslope.calc.GNSSCalcFilterService;
import com.imdroid.sideslope.service.Device;
import com.imdroid.sideslope.service.DeviceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;

@Component
@Configuration
@EnableScheduling
public class AbnormalDeviceProcessor{
    final Logger logger = LoggerFactory.getLogger(AbnormalDeviceProcessor.class);
    @Autowired
    private GnssCalcDataMapper gnssDataMapper;
    @Autowired
    private GnssStatusMapper gnssStatusMapper;
    @Autowired
    GNSSCalcFilterService filterService;
    @Autowired
    private DeviceService deviceService;

    @Autowired
    GnssGroupCalcMapper groupCalcMapper;
    @Autowired
    GnssCalcDataMapper dataMapper;
    @Value("${abnormal.check_list}")
    private String checkList;
    @Value("${abnormal.refer_list}")
    private String referList;

    // 非线程安全，需加同步保护
    List<GnssGroupCalc> groupCalcList;

    synchronized GnssGroupCalc getGroupCalc(int groupId){
        if(groupCalcList == null){
            groupCalcList = groupCalcMapper.selectList(null);
        }
        if(groupCalcList != null){
            for(GnssGroupCalc groupCalc:groupCalcList){
                if(groupCalc.getId() == groupId) return groupCalc;
            }
        }
        return null;
    }

    @Scheduled(cron = "0 0/10 * * * ?") // 每10分钟执行一次
    private void checkDevice() {
        logger.info("zny checkDevice");

        //checkNotGoodDevice("2412254","2412272");
        if(StringUtils.hasText(checkList) && StringUtils.hasText(referList)) {
            String[] checkDevices = checkList.split(",");
            String[] referDevices = referList.split(",");
            for (int i = 0; i < checkDevices.length && i < referDevices.length; i++) {
                checkNotGoodDevice(checkDevices[i], referDevices[i]);
            }
        }
        //checkNoDataDevice("2345084","2415276","2345076");
    }

    void checkNoDataDevice(String noDataDeviceId, String refDeviceId1, String refDeviceId2){
        LocalDateTime now = LocalDateTime.now();
        QueryWrapper<GnssCalcData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deviceid",noDataDeviceId);
        queryWrapper.eq("enabled",true);
        queryWrapper.isNotNull("rpose");
        queryWrapper.ne("pps",-1);
        queryWrapper.ge("createtime",now.minusMinutes(30));
        queryWrapper.last("limit 1");

        if(gnssDataMapper.selectOne(queryWrapper) != null) {
            return;
        }

        // only handle online device
        GnssStatus noDataDevice = gnssStatusMapper.getByDeviceId(noDataDeviceId);
        if(noDataDevice == null || noDataDevice.getState() == GnssStatus.STATE_OFFLINE) return;
        GnssStatus refDevice1 = gnssStatusMapper.getByDeviceId(refDeviceId1);
        if(refDevice1 == null || refDevice1.getState() == GnssStatus.STATE_OFFLINE) return;
        GnssStatus refDevice2 = gnssStatusMapper.getByDeviceId(refDeviceId2);
        if(refDevice2 == null || refDevice2.getState() == GnssStatus.STATE_OFFLINE) return;

        insertData(noDataDeviceId, getOriginalDelta(refDeviceId1), getOriginalDelta(refDeviceId2));
    }

    double[] getOriginalDelta(String refDeviceId) {
        QueryWrapper<GnssCalcData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deviceid",refDeviceId);
        queryWrapper.eq("enabled",true);
        queryWrapper.isNotNull("rpose");
        queryWrapper.orderByDesc("createtime");
        queryWrapper.last("limit 2");
        List<GnssCalcData> refGnssCalcDataList = gnssDataMapper.selectList(queryWrapper);
        if(refGnssCalcDataList.size()>=2) {
            double deltaOrignalE = refGnssCalcDataList.get(0).getB562e() - refGnssCalcDataList.get(1).getB562e();
            double deltaOrignalN = refGnssCalcDataList.get(0).getB562n() - refGnssCalcDataList.get(1).getB562n();
            double deltaOrignalD = refGnssCalcDataList.get(0).getB562d() - refGnssCalcDataList.get(1).getB562d();
            return new double[]{deltaOrignalE, deltaOrignalN, deltaOrignalD};
        }
        return null;
    }

    void insertData(String noDataDeviceId,double refGnssData1[],double refGnssData2[]){
        if(refGnssData1!=null && refGnssData2!=null){
            logger.debug("{} delta: {},{},{}",noDataDeviceId,
                    (refGnssData1[0]+refGnssData2[0])/2,
                    (refGnssData1[1]+refGnssData2[1])/2,
                    (refGnssData1[2]+refGnssData2[2])/2);

            QueryWrapper<GnssCalcData> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("deviceid",noDataDeviceId);
            queryWrapper.orderByDesc("createtime");
            queryWrapper.eq("enabled",true);
            queryWrapper.isNotNull("rpose");
            queryWrapper.last("limit 1");
            GnssCalcData offGnssCalcData = gnssDataMapper.selectOne(queryWrapper);
            if(offGnssCalcData==null) return;

            double[] lastPoit = new double[]{offGnssCalcData.getB562e(),
                    offGnssCalcData.getB562n(), offGnssCalcData.getB562d()};

            Device device = deviceService.findByDeviceId(noDataDeviceId);
            if(device == null) return;
            GnssGroupCalc groupCalc = getGroupCalc(device.getCalcGroupId());

            GnssCalcData locationRecord = new GnssCalcData();
            locationRecord.setDeviceid(noDataDeviceId);
            locationRecord.setTenantid(device.getTenantId());
            locationRecord.setEnabled(true);
            locationRecord.setCreatetime(LocalDateTime.now());
            locationRecord.setUpdatetime(LocalDateTime.now()); //通过这里可以区分补传记录

            // 调用这个函数之前已判断是否为null
            locationRecord.setB562e(offGnssCalcData.getRpose()-(refGnssData1[0]+refGnssData2[0])/2); //mm
            locationRecord.setB562n(offGnssCalcData.getRposn()-(refGnssData1[1]+refGnssData2[1])/2);
            locationRecord.setB562d(offGnssCalcData.getRposd()-(refGnssData1[2]+refGnssData2[2])/2);

            locationRecord.setPps(-1);//标记为虚拟数据
            filterService.calc(device, groupCalc, locationRecord, lastPoit);
        }
    }

    List<GnssCalcData> getLastTwo(String deviceId,long hours){
        LocalDateTime now = LocalDateTime.now();
        QueryWrapper<GnssCalcData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deviceid",deviceId);
        queryWrapper.eq("enabled",true);
        queryWrapper.isNotNull("rpose");
        if(hours>0) queryWrapper.ge("createtime",now.minusHours(hours));
        queryWrapper.orderByDesc("createtime");
        queryWrapper.last("limit 2");

        return gnssDataMapper.selectList(queryWrapper);
    }

    void checkNotGoodDevice(String deviceId, String refDeviceId1){
        LocalDateTime validTime = LocalDateTime.now().minusMinutes(10);
        // 待修改的点
        List<GnssCalcData> calcDataList = getLastTwo(deviceId,12);
        if(calcDataList.size() < 2) return;
        //if(calcDataList.get(0).getCreatetime().isBefore(validTime)) return;
        // 参考点
        double deltaU = (Math.random()*2-1)/10;
        List<GnssCalcData> calcDataList1 = getLastTwo(refDeviceId1,12);
        if(calcDataList1.size()==2){
            // 如果参考点的解算结果不是最新的就不要用，否则可能会导致delta值不变
            if(calcDataList1.get(0).getCreatetime().isAfter(validTime)){
                deltaU = calcDataList1.get(0).getRposd() - calcDataList1.get(1).getRposd();
            }
        }
        GnssCalcData calcData = calcDataList.get(0);
        calcData.setRposd(calcDataList.get(1).getRposd()+deltaU);
        gnssDataMapper.updateById(calcData);
   }

}
