package com.usky.transfer.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.shaded.com.google.protobuf.Internal;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.core.util.UUIDUtils;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.transfer.domain.*;
import com.usky.transfer.mapper.QueryInfluxdbDataMapper;
import com.usky.transfer.service.*;
import com.usky.common.mybatis.core.AbstractCrudService;
import com.usky.transfer.service.config.mqtt.MqttOutConfig;
import com.usky.transfer.service.rocketmq.MyProducer;
import com.usky.transfer.service.utils.TsdbUtils;
import com.usky.transfer.service.vo.DeviceMapVO;
import com.usky.transfer.service.vo.ProductMapVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ya
 * @since 2024-07-29
 */
@Slf4j
@Service
public class QueryInfluxdbDataServiceImpl extends AbstractCrudService<QueryInfluxdbDataMapper, QueryInfluxdbData> implements QueryInfluxdbDataService {
    @Autowired
    private TsdbUtils tsdbUtils;
    @Autowired
    private DmpProductService dmpProductService;
    @Autowired
    private DmpDeviceService dmpDeviceService;
    @Autowired
    private DmpDeviceStatusService dmpDeviceStatusService;
    @Autowired
    private DmpDeviceCommandService dmpDeviceCommandService;
    @Autowired
    private DmpProductAttributeService dmpProductAttributeService;

    @Resource
    private MyProducer myProducer;
    @Resource
    private MqttOutConfig.MqttGateway mqttGateway;

    @Override
    public Map<String,Object> deviceControl(String productCode, String deviceUuid, String commandStr,Integer tenantId, Long userId, String userName){
        Map<String,Object> rec_map = new HashMap<>();

        JSONObject dataJson = JSONObject.parseObject(commandStr);

        //存储下发设备控制命令到数据库表中
        DmpDeviceCommand command = new DmpDeviceCommand();
        command.setProductCode(productCode);
        command.setDeviceUuid(dataJson.get("deviceUuid").toString());

        command.setCommandContent(commandStr);
        command.setCreatedTime(LocalDateTime.now());
//        if (Objects.nonNull(SecurityUtils.getLoginUser().getSysUser().getDeptId())){
//            command.setDeptId(SecurityUtils.getLoginUser().getSysUser().getDeptId().intValue());
//        }

        command.setTenantId(tenantId);
        command.setUserId(userId);
        command.setUserName(userName);
        dmpDeviceCommandService.save(command);
        int commandId = command.getId();

        dataJson.put("id",commandId);

        command.setCommandContent(dataJson.toJSONString());
        dmpDeviceCommandService.updateById(command);
        //推送下发设备控制mqtt
        if(StringUtils.isNotBlank(commandStr)){
            String topic = "/usky/devices/"+deviceUuid+"/control";
            mqttGateway.sendToMqtt(topic,dataJson.toJSONString());
        }

        long startTimeStamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
        while (true){
            LambdaQueryWrapper<DmpDeviceCommand> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(DmpDeviceCommand::getId,commandId);
            DmpDeviceCommand one = dmpDeviceCommandService.getOne(queryWrapper);
            if(Objects.nonNull(one.getCommandResponse())){
                rec_map.put("code",200);
                rec_map.put("message","下发命令成功");
                rec_map.put("data",one.getCommandResponse());
                break;
            }

            long endTimeStamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
            if((endTimeStamp - startTimeStamp) >= 3){ //请求超时3秒，返回失败
                rec_map.put("code",-1);
                rec_map.put("message","下发命令响应超时");

                dmpDeviceCommandService.lambdaUpdate().set(DmpDeviceCommand::getCommandStatus,2).eq(DmpDeviceCommand::getId,commandId).update();
                break;
            }
        }

        return rec_map;
    }

    @Override
    public Map<String,Object> sendDeviceDataToMQ(DeviceDataWriteVO writeVO){
        Map<String,Object> rec_map = new HashMap<>();
        DeviceDataInfoVO dataInfo = new DeviceDataInfoVO();
        Map<String,Object> metrics = writeVO.getMetrics();
        Map<String,String> tags = writeVO.getTags();
        if(metrics.size() > 0){
            Map<String,Object> mp = new HashMap<>();
            Map<String,Object> mp_tag = new HashMap<>();
            for(Map.Entry<String,Object> map:metrics.entrySet()){
                mp.put(map.getKey(),map.getValue());
            }
            dataInfo.setMetrics(mp);
            if(tags != null && tags.size() > 0){
                for(Map.Entry<String,String> map:tags.entrySet()){
                    mp_tag.put(map.getKey(),map.getValue());
                }
                dataInfo.setTags(mp_tag);
            }else{
                dataInfo.setTags(new HashMap<>());
            }

            String productCode = writeVO.getProductCode();
            String deviceUUId = writeVO.getDeviceUUId();
            String deviceId = tags.get("device_id");
            log.info("sendDeviceDataToMQ "+deviceId+" start222");

            //判断上报数据对应产品是否注册，如未注册则为非法
            Map<String,ProductMapVO> productMapList = dmpProductService.getProductMap();
            if(!productMapList.containsKey(productCode)){
                //通过查询数据库再确认下产品是否注册
                LambdaQueryWrapper<DmpProduct> queryWrapper = Wrappers.lambdaQuery();
                queryWrapper.eq(DmpProduct::getDeleteFlag,0)
                        .eq(DmpProduct::getProductCode,productCode);
                DmpProduct one = dmpProductService.getOne(queryWrapper);
                if(one == null){
                    rec_map.put("code",201) ;
                    rec_map.put("message","产品未注册!");
                    log.info("产品未注册");
                    return rec_map;
                }else{
                    dmpProductService.deleteProductCache();
                }
            }

            //判断上报数据设备是否已注册(要判断注册过的设备是不是属于本产品的)，未注册自动注册
            Map<String,DeviceMapVO> deviceMapList = dmpProductService.getDeviceMap(productCode);
            if(!deviceMapList.containsKey(deviceId)){
                DmpDevice dmpDeviceInfo = new DmpDevice();
                ProductMapVO productMapVO = productMapList.get(productCode);
                dmpDeviceInfo.setDeviceId(deviceId);
                dmpDeviceInfo.setDeviceName("");
                dmpDeviceInfo.setDeviceType(productMapVO.getDeviceType());
                dmpDeviceInfo.setProductId(productMapVO.getProductId());
                dmpDeviceInfo.setProductCode(productCode);
                dmpDeviceInfo.setCreatedBy(productMapVO.getCreatedBy());
                dmpDeviceInfo.setCreatedTime(LocalDateTime.now());
                dmpDeviceInfo.setTenantId(productMapVO.getTenantId());
                dmpDeviceInfo.setServiceStatus(1);
                if(StringUtils.isBlank(deviceUUId)){
                    dmpDeviceInfo.setDeviceUuid(UUIDUtils.uuid().substring(0,16));
                }else{
                    dmpDeviceInfo.setDeviceUuid(deviceUUId);
                }

                dmpDeviceService.save(dmpDeviceInfo);
                dmpProductService.deleteDeviceCache(productCode);

                deviceUUId = dmpDeviceInfo.getDeviceUuid();

                DmpDeviceStatus dmpDeviceStatus = new DmpDeviceStatus();
                dmpDeviceStatus.setDeviceId(dmpDeviceInfo.getDeviceId());
                dmpDeviceStatus.setProductId(dmpDeviceInfo.getProductId());
                dmpDeviceStatus.setDeviceStatus(2);
                dmpDeviceStatus.setLastOfflineTime(LocalDateTime.now());
                dmpDeviceStatus.setProductCode(dmpDeviceInfo.getProductCode());
                dmpDeviceStatus.setDeviceUuid(deviceUUId);
                dmpDeviceStatusService.save(dmpDeviceStatus);

                deviceMapList = dmpProductService.getDeviceMap(productCode);
            }else if(deviceMapList.containsKey(deviceId)){
                LambdaQueryWrapper<DmpDevice> queryWrapper = Wrappers.lambdaQuery();
                queryWrapper.eq(DmpDevice::getDeleteFlag,0)
                        .eq(DmpDevice::getProductCode,productCode)
                        .eq(DmpDevice::getDeviceId,deviceId);
                DmpDevice one = dmpDeviceService.getOne(queryWrapper);
                if(one == null){
                    DmpDevice dmpDeviceInfo = new DmpDevice();
                    ProductMapVO productMapVO = productMapList.get(productCode);
                    dmpDeviceInfo.setDeviceId(deviceId);
                    dmpDeviceInfo.setDeviceName("");
                    dmpDeviceInfo.setDeviceType(productMapVO.getDeviceType());
                    dmpDeviceInfo.setProductId(productMapVO.getProductId());
                    dmpDeviceInfo.setProductCode(productCode);
                    dmpDeviceInfo.setCreatedBy(productMapVO.getCreatedBy());
                    dmpDeviceInfo.setCreatedTime(LocalDateTime.now());
                    dmpDeviceInfo.setTenantId(productMapVO.getTenantId());
                    dmpDeviceInfo.setServiceStatus(1);
                    if(StringUtils.isBlank(deviceUUId)){
                        dmpDeviceInfo.setDeviceUuid(UUIDUtils.uuid().substring(0,16));
                    }else{
                        dmpDeviceInfo.setDeviceUuid(deviceUUId);
                    }
                    dmpDeviceService.save(dmpDeviceInfo);
                    dmpProductService.deleteDeviceCache(productCode);

                    deviceUUId = dmpDeviceInfo.getDeviceUuid();

                    DmpDeviceStatus dmpDeviceStatus = new DmpDeviceStatus();
                    dmpDeviceStatus.setDeviceId(dmpDeviceInfo.getDeviceId());
                    dmpDeviceStatus.setProductId(dmpDeviceInfo.getProductId());
                    dmpDeviceStatus.setDeviceStatus(2);
                    dmpDeviceStatus.setLastOfflineTime(LocalDateTime.now());
                    dmpDeviceStatus.setProductCode(dmpDeviceInfo.getProductCode());
                    dmpDeviceStatus.setDeviceUuid(deviceUUId);
                    dmpDeviceStatusService.save(dmpDeviceStatus);

                    deviceMapList = dmpProductService.getDeviceMap(productCode);
                }
            }

            if(StringUtils.isBlank(deviceUUId)){
                for (Map.Entry<String,DeviceMapVO> map:deviceMapList.entrySet()) {
                    String productCode1 = map.getValue().getProductCode();
                    String deviceId1 = map.getKey();
                    if((productCode.equals(productCode1)) && (deviceId.equals(deviceId1))){
                        deviceUUId = map.getValue().getDeviceUuid();
                        break;
                    }
                }
            }

            dataInfo.setProductCode(productCode);
            dataInfo.setDeviceUUId(deviceUUId);
            dataInfo.setTimestamp(writeVO.getTimestamp());
            log.info("推送tsdb "+JSONArray.toJSON(dataInfo).toString()+" start");

            myProducer.sendMessage("data-tsdb", JSONArray.toJSON(dataInfo).toString());
            log.info("推送tsdb "+JSONArray.toJSON(dataInfo).toString()+" end");
        }

        rec_map.put("code",200);
        rec_map.put("message","操作成功!");
        return rec_map;
    }

    @Override
    public Map<String,Object> mqttDeviceData(MqttDeviceDataVO mqttDeviceDataVO){
        Map<String,Object> rec_map = new HashMap<>();

        String topic = mqttDeviceDataVO.getTopic();
        String[] topics = topic.split("/");
        //String productCode1 = topics[1];
        //String deviceId1 = topics[2];
        String deviceUuid = topics[3];

        String payload = mqttDeviceDataVO.getDeviceData();

        JSONObject deviceDataJson = JSONObject.parseObject(payload);
        String productCode2 = deviceDataJson.get("productCode").toString();
        Object tag = JSONObject.toJSONString(deviceDataJson.get("tags"));
        JSONObject tagJson = JSON.parseObject(tag.toString());
        String deviceId2 = tagJson.get("device_id").toString();
        Object metric = JSONObject.toJSONString(deviceDataJson.get("metrics"));
        JSONObject metricJson = JSON.parseObject(metric.toString());

//        if(!productCode1.equals(productCode2) || !deviceId1.equals(deviceId2)){
//            throw new BusinessException("Topic和请求体中的产品编码、设备Id不一致，请修改");
//        }

        LambdaQueryWrapper<DmpProduct> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery.eq(DmpProduct::getProductCode,productCode2)
                .eq(DmpProduct::getDeleteFlag,0);
        List<DmpProduct> dmpProductList = dmpProductService.list(lambdaQuery);
        if(dmpProductList.size() <= 0){
            throw new BusinessException("产品编码不存在，请修改");
        }
        LambdaQueryWrapper<DmpDevice> lambdaQuery2 = Wrappers.lambdaQuery();
        lambdaQuery2.eq(DmpDevice::getDeviceUuid,deviceUuid)
                .eq(DmpDevice::getProductCode,productCode2)
                .eq(DmpDevice::getServiceStatus,2)
                .eq(DmpDevice::getDeleteFlag,0);
        List<DmpDevice> dmpDeviceList = dmpDeviceService.list(lambdaQuery2);
        if(dmpDeviceList.size() <= 0){
            throw new BusinessException("topic主题中的设备Uuid不存在或在本产品下不存在，请修改");
        }
        LambdaQueryWrapper<DmpDevice> lambdaQuery3 = Wrappers.lambdaQuery();
        lambdaQuery3.eq(DmpDevice::getDeviceId,deviceId2)
                .eq(DmpDevice::getProductCode,productCode2)
                .eq(DmpDevice::getServiceStatus,2)
                .eq(DmpDevice::getDeleteFlag,0);
        List<DmpDevice> dmpDeviceList1 = dmpDeviceService.list(lambdaQuery3);
        if(dmpDeviceList1.size() <= 0){
            throw new BusinessException("设备Id不存在或在本产品下不存在，请修改");
        }

        List<String> metricList = new ArrayList<>();
        if(Objects.nonNull(metricJson)){
            for(String entry: metricJson.keySet()){
                metricList.add(entry.toLowerCase());
            }
        }
        int count1 = metricList.size();
        LambdaQueryWrapper<DmpProductAttribute> lambdaQuery4 = Wrappers.lambdaQuery();
        lambdaQuery4.eq(DmpProductAttribute::getProductId,dmpProductList.get(0).getId())
                .in(DmpProductAttribute::getAttributeCode,metricList);
        int count2 = dmpProductAttributeService.count(lambdaQuery4);
        if(count1 > count2){
            throw new BusinessException("设备属性编码不存在，请检查");
        }


        mqttGateway.sendToMqtt(topic, deviceDataJson.toJSONString());

        rec_map.put("code",200);
        rec_map.put("message","发送成功");

        return rec_map;
    }
}
