package com.xinda.mq.redischannel.consumer;

import com.xinda.common.constant.XinDaConstant;
import com.xinda.common.core.mq.message.DeviceDownMessage;
import com.xinda.common.core.mq.message.ModbusPollMsg;
import com.xinda.common.core.mq.message.PropRead;
import com.xinda.common.core.protocol.Message;
import com.xinda.common.core.redis.RedisCache;
import com.xinda.common.core.redis.RedisKeyBuilder;
import com.xinda.common.enums.DeviceStatus;
import com.xinda.common.enums.ServerType;
import com.xinda.common.enums.TopicType;
import com.xinda.common.utils.DateUtils;
import com.xinda.common.utils.gateway.mq.TopicsUtils;
import com.xinda.common.utils.gateway.protocol.ByteUtils;
import com.xinda.common.utils.modbus.ModbusUtils;
import com.xinda.iot.model.DeviceStatusVO;
import com.xinda.iot.service.IDeviceService;
import com.xinda.iot.service.IFunctionLogService;
import com.xinda.mq.service.impl.MessageManager;
import com.xinda.mqttclient.PubMqttClient;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 平台定时批量获取设备属性(或单个获取)
 *
 * @author bill
 */
@Slf4j
@Component
public class DevicePropFetchConsumer {


    @Autowired
    private PubMqttClient pubMqttClient;
    @Autowired
    private RedisCache redisCache;
    @Resource
    private MessageManager messageManager;
    @Resource
    private TopicsUtils topicsUtils;
    @Resource
    private IFunctionLogService functionLogService;
    //锁集合
    private final ConcurrentHashMap<String, Lock> taskLocks = new ConcurrentHashMap<>();


    @Async(XinDaConstant.TASK.DEVICE_FETCH_PROP_TASK)
    public void consume(ModbusPollMsg task) {
        String serialNumber = task.getSerialNumber();
        //获取一个线程锁
        Lock lock = taskLocks.computeIfAbsent(serialNumber, k -> new ReentrantLock());
        //阻塞直到获取到锁
        lock.lock();
        try {
            Long productId = task.getProductId();
            List<String> commandList = task.getCommandList();
            ServerType serverType = ServerType.explain(task.getTransport());
            String topic = topicsUtils.buildTopic(productId, serialNumber, TopicType.FUNCTION_GET);
            for (String command : commandList) {
                String cacheKey = RedisKeyBuilder.buildModbusRuntimeCacheKey(serialNumber);
                redisCache.zSetAdd(cacheKey, command, DateUtils.getTimestampSeconds());
                switch (serverType) {
                    //通过mqtt内部客户端 下发指令
                    case MQTT:
                        publish(topic, ByteBufUtil.decodeHexDump(command));
                        log.info("=>MQTT-线程=[{}],轮询指令:[{}],主题:[{}]", Thread.currentThread().getName(), command, topic);
                        break;
                    //  下发TCP客户端
                    case TCP:
                        Message msg = new Message();
                        msg.setClientId(serialNumber);
                        msg.setPayload(Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(command)));
                        messageManager.requestR(serialNumber, msg, Message.class);
                        log.info("=>TCP-线程=[{}],轮询指令:[{}]", Thread.currentThread().getName(), command);
                        break;
                }
                //指令间隔时间
                Thread.sleep(1000);
            }

        } catch (Exception e) {
            log.error("线程错误e", e);
        } finally {
            lock.unlock();
        }
    }

    public void publish(String topic, byte[] pushMessage) {
        redisCache.incr2(XinDaConstant.REDIS.MESSAGE_SEND_TOTAL, -1L);
        redisCache.incr2(XinDaConstant.REDIS.MESSAGE_SEND_TODAY, 60 * 60 * 24);
        pubMqttClient.publish(pushMessage, topic, false, 0);
    }
}
