package com.imdroid.beidou_fwd.task;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.yulichang.query.MPJQueryWrapper;
import com.imdroid.beidou_fwd.entity.GZYMQTTData;
import com.imdroid.common.util.GsonUtil;
import com.imdroid.common.util.NumberUtils;
import com.imdroid.common.util.ThreadManager;
import com.imdroid.secapi.dto.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class Forwarder {
    final Logger logger = LoggerFactory.getLogger(Forwarder.class);
    static final byte FWD_DEVICE_ID = 0;
    static final byte FWD_DEVICE_NAME = 1;
    static final byte FWD_DEVICE_ALIAS_NAME = 2;

    String fwdGroupId;
    String description;
    Integer tenantId;
    byte fwdNameType = FWD_DEVICE_ID;
    int totalSendNum = 0;

    int fwdCycleMinutes = 30;
    static boolean isFwdTableInit = true;//false;

    @Autowired
    private GnssDeviceMapper deviceMapper;

    @Autowired
    private GnssCalcDataMapper gnssDataMapper;

    @Autowired
    private FwdRecordMapper fwdRecordsMapper;
    @Autowired
    private ResendRecordMapper resendRecordMapper;
    @Autowired
    private GnssStatusMapper gnssStatusMapper;

    @Autowired
    GnssGroupFwdMapper fwdMapper;

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /***
     *
     * @param fwdGroupId,推送组名
     * @param desc，推送组描述
     * @param tenantId，租户Id
     * @param fwdNameType，推送设备名：deviceid或fwdid或projectid-sector-name
     * @param cycle，发送周期
     */
    public void init(String fwdGroupId, String desc, Integer tenantId, byte fwdNameType, int cycle){
        this.fwdGroupId = fwdGroupId;
        this.description = desc;
        this.tenantId = tenantId;
        this.fwdNameType = fwdNameType;
        this.fwdCycleMinutes = cycle;

        if(!isFwdTableInit){
            isFwdTableInit = true;
            fwdMapper.delete(null);
        }
        QueryWrapper<GnssGroupFwd> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name",fwdGroupId);
        queryWrapper.last("limit 1");
        GnssGroupFwd gnssGroupFwd = fwdMapper.selectOne(queryWrapper);
        if(gnssGroupFwd == null){
            gnssGroupFwd = new GnssGroupFwd();
            gnssGroupFwd.setName(fwdGroupId);
            gnssGroupFwd.setDescription(description);
            gnssGroupFwd.setDevice_num(0);
            fwdMapper.insert(gnssGroupFwd);
        }
        else{
            gnssGroupFwd.setDescription(description);
            fwdMapper.updateById(gnssGroupFwd);
        }
    }

    public void forwardCurrentGnss() {
        if(forwardGnssRecords(LocalDateTime.now(), null)){
            // 推送失败记录、补发记录
            ThreadManager.getScheduledThreadPool().schedule(() -> {
                this.forwardHistoryGnss();
            },100, TimeUnit.SECONDS);
        }
    }

    /***
     * 推送指定企业、设备ID、时间的GNSS记录，查找指定时间前cycle分钟的有效数据推送
     * @param sendTime：要推送的记录的时间
     */

    private boolean forwardGnssRecords(LocalDateTime sendTime, ResendRecord resendRecord) {
        String endTime = sendTime.format(formatter);
        String beginTime = sendTime.minusMinutes(fwdCycleMinutes).format(formatter);

        // 查找属于指定推送组的设备列表
        List<GnssDeviceJoin> gnssDeviceList;
        MPJQueryWrapper jquery = null;

        if(resendRecord!=null && resendRecord.getDeviceid()!=null){
            jquery = new MPJQueryWrapper<GnssDevice> ()
                    .selectAll(GnssDevice.class)
                    .select("d.latitude as latitude")
                    .select("d.longitude as longitude")
                    .leftJoin("gnssstatus d on t.deviceid = d.deviceid")
                    .and(warpper->warpper.eq("fwd_group_id", fwdGroupId)
                            .or()
                            .eq("fwd_group_id2", fwdGroupId)).
                    eq("deviceid", resendRecord.getDeviceid());
        }
        else if(resendRecord!=null && resendRecord.getProjectid()!=null){
            jquery = new MPJQueryWrapper<GnssDevice> ()
                    .selectAll(GnssDevice.class)
                    .select("d.latitude as latitude")
                    .select("d.longitude as longitude")
                    .leftJoin("gnssstatus d on t.deviceid = d.deviceid")
                    .and(warpper->warpper.eq("fwd_group_id", fwdGroupId)
                            .or()
                            .eq("fwd_group_id2", fwdGroupId))
                    .and(warpper->warpper.eq("project_id",resendRecord.getProjectid())
                            .or()
                            .eq("project2_id",resendRecord.getProjectid()));
        }
        else{
            jquery = new MPJQueryWrapper<GnssDevice> ()
                    .selectAll(GnssDevice.class)
                    .select("d.latitude as latitude")
                    .select("d.longitude as longitude")
                    .leftJoin("gnssstatus d on t.deviceid = d.deviceid")
                    .and(warpper->warpper.eq("fwd_group_id", fwdGroupId)
                            .or()
                            .eq("fwd_group_id2", fwdGroupId));
        }
        gnssDeviceList = deviceMapper.selectJoinList(GnssDeviceJoin.class, jquery);
        logger.debug("candidate fwd devices {}", gnssDeviceList.size());

        // 查询最近半小时的GNSS记录
        QueryWrapper<GnssCalcData> gnssQueryWrapper = new QueryWrapper<>();
        gnssQueryWrapper.ge("createtime",beginTime);
        gnssQueryWrapper.le("createtime",endTime);
        gnssQueryWrapper.orderByDesc("createtime");
        gnssQueryWrapper.eq("enabled",true);
        gnssQueryWrapper.eq("stabled",true);
        gnssQueryWrapper.isNotNull("rpose");
        if(resendRecord != null && resendRecord.getDeviceid()!=null){
            gnssQueryWrapper.eq("deviceid", resendRecord.getDeviceid());
        }

        List<GnssCalcData> locationRecords = gnssDataMapper.selectList(gnssQueryWrapper);
        logger.debug("candidate fwd records {}", locationRecords.size());
        if(locationRecords.size() == 0) return false;

        // 构造按项目id分类的GNSS记录
        ConcurrentHashMap<String, List<GnssCalcData>> projects = new ConcurrentHashMap<>();
        for(GnssDeviceJoin device:gnssDeviceList){
            if(device.getOpmode() != GnssDevice.OP_MODE_USE) continue;
            String projectId = device.getProject_id();
            if(device.getProject2_id()!=null && !device.getProject2_id().isEmpty()) {
                // 推送组2用项目2的id号
                String fwdGroupId2 = device.getFwd_group_id2();
                if (fwdGroupId2 != null && fwdGroupId2.equals(fwdGroupId)) {
                    projectId = device.getProject2_id();
                }
            }
            if(projectId == null) continue;
            if(resendRecord != null && !projectId.equals(resendRecord.getProjectid())){
                continue;
            }

            List<GnssCalcData> recordsToSend = projects.get(projectId);
            if(recordsToSend == null){
                recordsToSend = new ArrayList<>();
                projects.put(projectId,recordsToSend);
            }
            for(GnssCalcData record:locationRecords){
                if(record.getDeviceid().equals(device.getDeviceid())) {
                    // 替换成推送用的名字和数值
                    if (device.getIpose() != null &&
                            device.getIposn() != null &&
                            device.getIposd() != null) {
                        record.setRpose(record.getRpose() - device.getIpose());
                        record.setRposn(record.getRposn() - device.getIposn());
                        record.setRposd(record.getRposd() - device.getIposd());
                    }
                    if (fwdNameType == FWD_DEVICE_ALIAS_NAME || (device.getName()!=null&&device.getName().contains("替换"))) {
                        if (StringUtils.hasText(device.getFwddeviceid())) {
                            record.setDeviceid(device.getFwddeviceid());
                        }
                    }
                    else if(fwdNameType == FWD_DEVICE_NAME){
                        String devName = "";
                        if(StringUtils.hasText(device.getProject_id())) devName = devName+device.getProject_id()+"-";
                        if(StringUtils.hasText(device.getSector())) devName = devName+device.getSector()+"-";
                        if(StringUtils.hasText(device.getName())) devName = devName+device.getName();
                        if(StringUtils.hasText(devName)) {
                            record.setDeviceid(devName);
                        }
                    }
                    // 用r9250来传设备经纬度
                    record.setR9250e(device.getLongitude());
                    record.setR9250n(device.getLatitude());
                    // 用aux来传基站经纬度
                    recordsToSend.add(record);
                    break;
                }
            }
        }

        // 按项目打包推送
        totalSendNum = 0;
        boolean fwdResult = true;
        for (Map.Entry<String, List<GnssCalcData>> entry: projects.entrySet()){
            String projectId = entry.getKey();
            List<GnssCalcData> records = entry.getValue();
            if(records.size() == 0) continue;

            int sendNum = send(projectId, records, sendTime);

            // 非重发的才产生推送记录
            if(resendRecord==null) {
                FwdRecord fwdRecord = new FwdRecord();
                fwdRecord.setProjectid(projectId);
                fwdRecord.setTenantid(tenantId);
                fwdRecord.setDevicenum((short) records.size());
                fwdRecord.setRecordtime(sendTime);
                fwdRecord.setCreatetime(LocalDateTime.now());
                fwdRecord.setFwd_group_id(fwdGroupId);
                if (sendNum > 0) {
                    totalSendNum += sendNum;
                    fwdRecord.setResult(FwdRecord.RESULT_OK);
                } else {
                    fwdResult = false;
                    fwdRecord.setResult(FwdRecord.RESULT_FAILED);
                    // 新增重发记录
                    ResendRecord resendRecord1 = new ResendRecord();
                    resendRecord1.setProjectid(projectId);
                    resendRecord1.setTenantid(tenantId);
                    resendRecord1.setCreatetime(LocalDateTime.now());
                    resendRecord1.setStarttime(sendTime);
                    resendRecord1.setEndtime(sendTime);
                    resendRecord1.setFwd_group_id(fwdGroupId);
                    resendRecord1.setState(ResendRecord.STATE_FWD_FAILED);
                    resendRecordMapper.insert(resendRecord1);
                }
                fwdRecordsMapper.insert(fwdRecord);
            }
        }

        // 更新推送记录
        if(resendRecord==null) {
            if (totalSendNum > 0) updateFwd(totalSendNum, true);
            else if (!fwdResult) updateFwd(totalSendNum, false);
        }

        return fwdResult;
    }


    void forwardHistoryGnss() {
        // 1.从转发记录表里检索待补传记录时间表，含设备Id，时间段
        QueryWrapper<ResendRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("fwd_group_id",fwdGroupId);
        queryWrapper.ne("state",ResendRecord.STATE_FWD_OK);
        queryWrapper.ne("state",ResendRecord.STATE_FWDING);
        queryWrapper.ge("createtime", LocalDateTime.now().minusDays(30));
        List<ResendRecord> resendRecordsList = resendRecordMapper.selectList(queryWrapper);

        if(resendRecordsList!=null && resendRecordsList.size()>0){
            //修改状态
            UpdateWrapper<ResendRecord> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("fwd_group_id",fwdGroupId);
            updateWrapper.ne("state",ResendRecord.STATE_FWD_OK);
            updateWrapper.ne("state",ResendRecord.STATE_FWDING);
            updateWrapper.ge("createtime", LocalDateTime.now().minusDays(30));
            updateWrapper.set("state",ResendRecord.STATE_FWDING);
            int updateNum = resendRecordMapper.update(null, updateWrapper);
            logger.debug("{} forward history records: {}, update {}",fwdGroupId, resendRecordsList.size(),updateNum);
            // 2.检索这个这个时间段的解算结果，如果有数据则单个终端转发，标志记录为已补传
            for(ResendRecord record:resendRecordsList){
                if(record.getProjectid()!=null)
                    logger.debug("{} forward history {}",fwdGroupId, record.getProjectid());
                forwardBatchGnssRecords(record);
            }
        }
    }

    void forwardBatchGnssRecords(ResendRecord record) {
        LocalDateTime sendTime = record.getStarttime();

        int totalSend = 0;

        while(sendTime.isBefore(record.getEndtime()) || sendTime.isEqual(record.getEndtime())){
            if(forwardGnssRecords(sendTime,record)) totalSend++;
            sendTime = sendTime.plusMinutes(fwdCycleMinutes);
        }
        if(totalSend>0) {
            record.setState(ResendRecord.STATE_FWD_OK);
        }
        else{
            record.setState(ResendRecord.STATE_FWD_FAILED);
        }
        resendRecordMapper.updateById(record);

    }

    int send(String projectId, List<GnssCalcData> records, LocalDateTime sentTime) {
        return 0;
    }

    void updateFwd(int fwd_num, boolean isFwdOK){
        QueryWrapper<GnssGroupFwd> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name",fwdGroupId);
        queryWrapper.last("limit 1");
        GnssGroupFwd gnssGroupFwd = fwdMapper.selectOne(queryWrapper);
        if(gnssGroupFwd != null){
            gnssGroupFwd.setFwd_device_num(fwd_num);
            gnssGroupFwd.setResult(isFwdOK);
            gnssGroupFwd.setUpdatetime(LocalDateTime.now());
            fwdMapper.updateById(gnssGroupFwd);
        }
    }

    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).format(formatter));
        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;

        // 插入数据
        int day = now.getDayOfYear();
        insertData(noDataDeviceId, ((day%4)>1)?refDeviceId1:refDeviceId2);
    }

    void insertData(String noDataDeviceId,String refDeviceId){
        logger.info("insertData based on {}",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 deltaE = refGnssCalcDataList.get(0).getRpose() - refGnssCalcDataList.get(1).getRpose();
            double deltaN = refGnssCalcDataList.get(0).getRposn() - refGnssCalcDataList.get(1).getRposn();
            double deltaD = refGnssCalcDataList.get(0).getRposd() - refGnssCalcDataList.get(1).getRposd();
            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();
            logger.info("delta {},{},{}",deltaE,deltaN,deltaD);
            if(Math.abs(deltaE)>1) deltaE = Math.random()*0.2 - 0.1;
            if(Math.abs(deltaN)>1) deltaN = Math.random()*0.2 - 0.1;
            if(Math.abs(deltaD)>2) deltaD = Math.random()*0.4 - 0.2;

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

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

            // 调用这个函数之前已判断是否为null
            locationRecord.setB562e(offGnssCalcData.getB562e()+deltaOrignalE*0.5+deltaOrignalN*0.5); //mm
            locationRecord.setB562n(offGnssCalcData.getB562n()+deltaOrignalN*0.8+deltaOrignalD*0.2);
            locationRecord.setB562d(offGnssCalcData.getB562d()+deltaOrignalD*0.8+deltaOrignalE*0.2);

            locationRecord.setRpose(offGnssCalcData.getRpose()+deltaE*0.5+deltaN*0.5);
            locationRecord.setRposn(offGnssCalcData.getRposn()+deltaN*0.8+deltaD*0.2);
            locationRecord.setRposd(offGnssCalcData.getRposd()+deltaD*0.8+deltaE*0.2);
            locationRecord.setPps(-1);//标记为虚拟数据
            gnssDataMapper.insert(locationRecord);
        }
    }

    int sendTest(String projectId, List<GnssCalcData> records, LocalDateTime sentTime) {
        int sendNum = 0;

        for (GnssCalcData locationRecord : records) {
            GZYMQTTData tranData = new GZYMQTTData();
            tranData.setCollectTime(locationRecord.getCreatetime().format(formatter));
            double n = NumberUtils.scale(locationRecord.getRposn(), 2);
            double e = NumberUtils.scale(locationRecord.getRpose(), 2);
            double d = NumberUtils.scale(locationRecord.getRposd(), 2);
            tranData.setX(n);
            tranData.setY(e);
            tranData.setZ(d);
            tranData.setDeviceType(2);
            tranData.setDeviceSn(locationRecord.getDeviceid());
            String json = GsonUtil.toJson(tranData);
            logger.debug("forward to GZY mqtt: {}",json);

            sendNum++;
        }

        return sendNum;
    }
}