package com.ruoyi.system.core.clientExecute;


import com.alibaba.fastjson.JSON;
import com.ruoyi.system.core.ClientExecute;
import com.ruoyi.system.core.DeviceStatus;
import com.ruoyi.system.core.annotate.Execute;
import com.ruoyi.system.core.domain.DeviceFunc;
import com.ruoyi.system.core.mqtt.MqttClientCallback;
import com.ruoyi.system.core.mqtt.MqttClientConnect;
import com.ruoyi.system.core.mqtt.MqttFuncTopic;
import com.ruoyi.system.core.mqtt.MqttPackage;
import com.ruoyi.system.domain.SysDevice;
import com.ruoyi.system.domain.SysDeviceCommand;
import com.ruoyi.system.service.ISysDeviceCommandService;
import com.ruoyi.system.service.ISysDeviceService;
import com.ruoyi.system.util.SpringContextHolder;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;
import java.util.Random;

@Execute(type = "MQTT")
@Component
public class MqttStart implements ClientExecute {

    private static final Logger log = LoggerFactory.getLogger(MqttStart.class);

    @Override
    public void createClient(SysDevice device) {
        MqttClientConnect mqttClientConnect = new MqttClientConnect();
        mqttClientConnect.setMqttClientId(device.getDeviceNo());
        String host = "tcp://" + device.getIp() + ":" + device.getPort();
        try {
            MqttClientCallback callback = new MqttClientCallback(device.getDeviceNo());
            mqttClientConnect.setMqttClient(host, device.getDeviceNo(), "admin", "asd123...", true, callback);
            MqttClientConnect.mqttClients.put(device.getDeviceNo(), mqttClientConnect);
            String topic = device.getDeviceType() + "/" + device.getDeviceNo() + "/#";
            mqttClientConnect.sub(StringUtils.commaDelimitedListToStringArray(topic));
            log.info("设备:" + device.getDeviceNo() + "启动成功");
        } catch (MqttException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    @Override
    public boolean send(SysDevice device, DeviceFunc msg) {
        MqttClientConnect mqttClientConnect = MqttClientConnect.mqttClients.get(device.getDeviceNo());
        if (mqttClientConnect == null) {
            this.close(device);
            return false;
        }
        try {
            mqttClientConnect.pub("/"+device.getDeviceType() + "/" + device.getDeviceNo() + "/" + msg.getFuncTopic().getValue(), JSON.toJSONString(new MqttPackage(device.getDeviceNo(), JSON.parseObject(msg.getMessage()))));
            if (device.getStatus().equals(DeviceStatus.OFFLINE)) {
                device.setStatus(DeviceStatus.ONLINE);
                ISysDeviceService bean = SpringContextHolder.getBean(ISysDeviceService.class);
                bean.update(device).subscribe();
            }
            return true;
        } catch (MqttException e) {
            return false;
        }
    }

    @Override
    public boolean connection(SysDevice device) {
        try {
            this.createClient(device);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void close(SysDevice device) {
        MqttClientConnect mqttClientConnect = MqttClientConnect.mqttClients.get(device.getDeviceNo());
        if (mqttClientConnect != null) {
            try {
                MqttClientConnect.mqttClients.remove(device.getDeviceNo());
                mqttClientConnect.close();
            } catch (MqttException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public Mono<SysDeviceCommand> getInstruct(String receive, String deviceType) {
        ISysDeviceCommandService bean = SpringContextHolder.getBean(ISysDeviceCommandService.class);
        SysDeviceCommand query = new SysDeviceCommand();
        query.setSubscribe(receive);
        query.setDeviceType(deviceType);
        return bean.selectList(query).flatMap(x -> Mono.just(this.getRandomItem(x)));
    }

    @Override
    public Mono<SysDeviceCommand> getByFunc(String funcId, String deviceType) {
        ISysDeviceCommandService bean = SpringContextHolder.getBean(ISysDeviceCommandService.class);
        SysDeviceCommand query = new SysDeviceCommand();
        query.setFunc(funcId);
        query.setDeviceType(deviceType);
        return bean.selectList(query).flatMap(x -> {
            if (x.size() != 0) {
                return Mono.just(x.get(0));
            } else {
                return Mono.empty();
            }
        });
    }

    public <T> T getRandomItem(List<T> list) {
        if (list == null || list.isEmpty()) {
            throw new IllegalArgumentException("List must not be null or empty");
        }
        Random random = new Random();
        int randomIndex = random.nextInt(list.size());
        return list.get(randomIndex);
    }
}
