package com.yeejoin.amos.boot.module.das.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;

import com.yeejoin.amos.boot.module.das.entity.analysis.IndicatorData;
import com.yeejoin.amos.boot.module.das.entity.mysql.FrontGatewayDevicePoints;
import com.yeejoin.amos.boot.module.das.entity.iot.StbDtoData;
import com.yeejoin.amos.boot.module.das.mapper.mysql.FrontGatewayDevicePointsMapper;
import com.yeejoin.amos.boot.module.das.mapper.analysis.IndicatorDataMapper;
import com.yeejoin.amos.boot.module.das.mapper.iot.TdengineIotDataMapper;
import com.yeejoin.amos.boot.module.das.service.DasService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.typroject.tyboot.component.emq.EmqKeeper;

import java.util.*;
import java.util.stream.Collectors;


@RequiredArgsConstructor
@EnableAsync
@Slf4j
@Service
public class DasServiceImpl implements DasService {
    private final FrontGatewayDevicePointsMapper frontGatewayDevicePointsMapper;
    private final EmqKeeper emqKeeper;
    private final IndicatorDataMapper indicatorDataMapper;
    private final TdengineIotDataMapper tdengineIotDataMapper;
    private final List<String> booleans = Arrays.asList("true", "false");

    @Scheduled(cron = "0 */10 * * * ?")
    @Override
    /**
     * 数据固化方法。该方法用于将数据从物联监盘同步到TDengine数据库。
     * 这个过程首先会创建一个新的表，然后并行处理每个网关ID的数据固化过程。
     * 完成后，会记录此次操作所花费的时间。
     */
    public void dataSolidification() {
        String dasTime = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:00");
        log.info("数据采集开始执行-采集时间：：" + dasTime );
        // 记录操作开始时间
        Long  startTime = System.currentTimeMillis();
        // 创建新表
        indicatorDataMapper.createTable();
        // 获取所有网关ID
        List<String> gateWayIds = frontGatewayDevicePointsMapper.getGatewayIds();
        // 并行处理每个网关ID的数据凝固
        gateWayIds.parallelStream().forEach(gatewayId -> {
            dataSolidificationByGatewayId(gatewayId,dasTime);
        });
        // 记录操作结束时间
        Long endTime = System.currentTimeMillis();
        // 记录操作耗时日志
        log.info("同步物联监盘数据至TDengine耗时：" + (endTime - startTime) + "ms");
    }

    /**
     * 根据网关ID进行数据凝固操作。
     * 该操作首先查询指定网关ID的设备点信息，然后基于这些信息从TDengine数据库中获取相应的数据点值，并将这些值
     * 组装成新的数据模型存储到数据库中。此外，该操作还会向EMQX发送一条消息，通知数据同步成功。
     *
     * @param gatewayId 网关的唯一标识符，用于查询相关设备点信息和数据点值。
     */
    @Async("jxiopAsyncExecutor")
    public void dataSolidificationByGatewayId(String gatewayId,String dasTime) {
        // 根据网关ID查询设备点信息
        List<FrontGatewayDevicePoints> tempPoints = frontGatewayDevicePointsMapper.getFrontGatewayDevicePointsByGatewayId(gatewayId);
        if (!ObjectUtils.isEmpty(tempPoints)) {
            // 检查在TDengine中是否存在对应的表
            Long tableCount = tdengineIotDataMapper.getTtableCount("stb_" + gatewayId);
            if (!(tableCount > 0)) {
                return; // 如果表不存在，则直接返回
            }
            // 从TDengine中查询数据点值
            List<StbDtoData> stbDtoDataList = tdengineIotDataMapper.getStbDtoDataByStbName("stb_" + gatewayId);
            // 将查询到的数据点值转换为Map形式存储
            Map<String, String> stbMap = stbDtoDataList.parallelStream().collect(Collectors.toMap(StbDtoData::getPointSeq, StbDtoData::getValue));
            if (stbMap.size() > 0) {
                // 遍历设备点信息，将每个设备点与从TDengine获取的值匹配，并构建新的数据模型
                List<IndicatorData> listAll = new ArrayList<>();
                tempPoints.stream().forEach(point -> {
                    IndicatorData indicatorData = new IndicatorData();
                    // 设置数据模型的各项属性
                    indicatorData.setDataType(point.getDataType());
                    indicatorData.setPointSeq(point.getSequenceNbr().toString());
                    indicatorData.setPointAddress(point.getPointAddress());
                    indicatorData.setPointLocation(point.getPointLocation());
                    indicatorData.setPointName(point.getPointName());
                    indicatorData.setPointType(point.getPointType());
                    // 设置数据点的值，如果是布尔值则进行转换
                    indicatorData.setValue(stbMap.get(point.getSequenceNbr().toString()));
                    if (!ObjectUtils.isEmpty(indicatorData.getValue()) && !booleans.contains(indicatorData.getValue())) {
                        try {
                            indicatorData.setValueF(Double.valueOf(indicatorData.getValue()));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    listAll.add(indicatorData);
                });
                // 批量插入构建的数据模型到数据库
                Lists.partition(listAll, 1000).stream().forEach(
                        list -> {
                            indicatorDataMapper.insertBatch(list, gatewayId,dasTime);
                        }
                );
                // 向EMQX发送消息，通知数据同步成功
                try {
                    HashMap<String, String> syncFlag = new HashMap<>();
                    syncFlag.put("gateway_id", gatewayId);
                    syncFlag.put("sync_flag", "success");
                    emqKeeper.getMqttClient().publish("sync_esdata_to_tdengine_notice", JSON.toJSONString(syncFlag).getBytes(), 0, false);
                    log.info("同步物联监盘数据至发发送消息给业务发送通知成功！");
                } catch (Exception exception) {
                    exception.printStackTrace();
                    log.info("同步物联监盘数据至发发送消息给业务发送通知失败！");
                }
            }
        }
    }

}
