package com.zd.laboratory.mqtt.config;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import com.zd.common.core.utils.SpringUtils;
import com.zd.laboratory.event.LabMessageEvent;
import com.zd.laboratory.event.SensorNewStatusEvent;
import com.zd.laboratory.mqtt.constants.MqttConstants;
import com.zd.laboratory.mqtt.entiy.MessageBody;
import com.zd.laboratory.mqtt.service.TerminalRouter;
import com.zd.laboratory.utils.HexUtils;
import org.apache.commons.lang.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.*;

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

/**
 * mqtt配置
 * @author dgs
 */
@Configuration
public class MqttConfig {
    static Logger logger = LoggerFactory.getLogger(MqttConfig.class);

    private static final byte[] WILL_DATA;

    private static String WILL_TOPIC = "willTopic";

    static {
        WILL_DATA = "offline".getBytes();
    }

    public static final String MQTT_INBOUND_CHANNEL = "mqttInboundChannel";


    public static final String MQTT_OUTBOUND_CHANNEL = "mqttOutboundChannel";

    @Value("${mqtt.username:}")
    private String username;

    @Value("${mqtt.password:}")
    private String password;

    @Value("${mqtt.url}")
    private String url;

    @Value("${mqtt.defaultTopic}")
    private String defaultTopic;

    @Value("${mqtt.producer.clientId}")
    private String producerClientId;

    @Value("${mqtt.consumer.clientId}")
    private String consumerClientId;

    @Value("${mqtt.consumer.maxInflight}")
    private Integer maxInflight;

    @Value("${mqtt.devicePrefix}")
    private String devicePrefix;

    @Value("${mqtt.steerPublishPrefix}")
    private String steerPublishPrefix;

    private MqttPahoMessageDrivenChannelAdapter adapter;

    final Map<String, List<LabMessageEvent>> labMessMap = new HashMap <>();

    public Map<String, List<LabMessageEvent>> getLabMessMap() {
        return labMessMap;
    }

    public  MqttConfig(List<LabMessageEvent> messageEvents)
    {
        Map<String, List<LabMessageEvent>> collect = Optional.ofNullable(messageEvents)
                .filter(a -> a.size() > 0)
                .orElseGet(Collections::emptyList)
                .stream()
                .collect(Collectors.groupingBy(LabMessageEvent::topic));
        labMessMap.putAll(collect);
    }
    /**
     * 连接mqtt配置
     *
     * @return
     */
    @Bean
    public MqttConnectOptions mqttConnectOptions() {
        MqttConnectOptions options = new MqttConnectOptions();
        // false，服务器会保留客户端的连接记录
        // true，表示每次连接到服务器都以新的身份连接
        options.setCleanSession(false);
        options.setAutomaticReconnect(true);
        options.setUserName(username);
        options.setPassword(password.toCharArray());
        options.setMaxInflight(maxInflight);
        options.setServerURIs(StringUtils.split(url, ","));
        //超时时间 单位为秒
        options.setConnectionTimeout(60);
        //会话心跳时间 单位: s, 间隔时间：1.5*20秒向客户端发送心跳判断客户端是否在线
        options.setKeepAliveInterval(60);
        //设置“遗嘱”消息的话题，若客户端与服务器之间的连接意外中断，服务器将发布客户端的“遗嘱”消息。
        options.setWill(WILL_TOPIC, WILL_DATA, 2, false);
        return options;
    }


    /**
     * MQTT客户端
     */
    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        factory.setConnectionOptions(mqttConnectOptions());
        return factory;
    }

    /**
     * 发送者消息通道
     */
    @Bean(name = MQTT_OUTBOUND_CHANNEL)
    public MessageChannel mqttOutboundChannel() {
        return new DirectChannel();
    }

    /**
     * 发送者消息处理
     *
     * @return
     */
    @Bean
    @ServiceActivator(inputChannel = MQTT_OUTBOUND_CHANNEL)
    public MessageHandler mqttOutbound() {
        MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(
                producerClientId,
                mqttClientFactory());
        messageHandler.setAsync(true);
        messageHandler.setDefaultTopic(defaultTopic);
        return messageHandler;
    }

    /**
     * 消息订阅
     *
     * @return
     */
    @Bean
    public MessageProducer inbound() {
        // 可同时消费（订阅）多个Topic
        adapter = new MqttPahoMessageDrivenChannelAdapter(consumerClientId, mqttClientFactory(), StringUtils.split(defaultTopic, ","));
        adapter.setCompletionTimeout(5000);
        adapter.setConverter(new DefaultPahoMessageConverter());
        adapter.setQos(2);
        // 设置订阅通道
        adapter.setOutputChannel(mqttInboundChannel());
        return adapter;
    }


    /**
     * 消费者消息通道
     *
     * @return
     */
    @Bean(name = MQTT_INBOUND_CHANNEL)
    public MessageChannel mqttInboundChannel() {
        return new DirectChannel();
    }

    /**
     * 消费者消息处理
     *设备心跳
     * @return
     */
    @Bean
    @ServiceActivator(inputChannel = MQTT_INBOUND_CHANNEL)
    public MessageHandler mqttInbound() {

        return new MessageHandler() {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException {
                MessageHeaders messageHeaders = message.getHeaders();
                String receivedTopic = (String) messageHeaders.get(MqttHeaders.RECEIVED_TOPIC);
//                logger.info("[通道] - [{}]",receivedTopic);
//                logger.info("[消息] - [{}]",message.getPayload());
                String messageStr=message.getPayload().toString();
                if(receivedTopic.startsWith(devicePrefix)){
                    if(receivedTopic.contains("788D4C6C6187ABC")){
                        logger.info("[原始消息] - [{}]",message.getPayload());
                    }
                    String topicEnd=receivedTopic.substring(receivedTopic.lastIndexOf("/")+1);
                    JSONObject jsonObject=JSONObject.parseObject(messageStr);
                    jsonObject.put("deviceNo",topicEnd);
                    SensorNewStatusEvent newStatusEvent=new SensorNewStatusEvent(jsonObject);
                    SpringUtils.getApplicationContext().publishEvent(newStatusEvent);
                }else if(receivedTopic.startsWith(MqttConstants.SYS_CHEMICAL_ON)){
                    // 化学品终端设备 心跳消息
                    String codeNum = receivedTopic.replaceAll(MqttConstants.SYS_CHEMICAL_ON, "");
                    logger.info("化学品终端设备心跳编号：" + codeNum);
                    TerminalRouter.routerMap.get("HxpTerminalService").onLine(codeNum);
                }else if(receivedTopic.startsWith(MqttConstants.SYS_CHEMICAL_OFF)){
                    logger.warn(messageHeaders.get(MqttHeaders.ID) + "意外中断！");
                    String codeNum = receivedTopic.replaceAll(MqttConstants.SYS_CHEMICAL_OFF, "");
                    logger.warn(" 中断设备编码： " + codeNum);
                    TerminalRouter.routerMap.get("HxpTerminalService").offLine(codeNum);
                }else if(receivedTopic.startsWith(steerPublishPrefix)){
//                    messageStr = byte2Hex(message.getPayload().toString().getBytes(StandardCharsets.UTF_8));
                    char[] str = message.getPayload().toString().toCharArray();
                    logger.info("柜锁mqtt消费：" + messageStr);
                    String replaceMess=messageStr.replace(" ","");
                    //坨机写指令返回数据
                    if(replaceMess.length()==12){
                        String command=replaceMess.substring(8,10);
                        //表示坨机成功
                        if(HexUtils.hexToInteger(command)==0){
                            //TODO 成功逻辑、、
                            Long lockId = Long.parseLong(HexUtils.hexToInteger(replaceMess.substring(4,6))+ "");
                            String[] prefix = steerPublishPrefix.split("/");
                            if(prefix != null && prefix.length > 0){
                                // 采集器编号
                                String relayCode = prefix[prefix.length-1];
                                TerminalRouter.routerMap.get("HxpLockService").onLine(relayCode + "/" + lockId);
                            }
                        }
                    //坨机读指令返回数据，用于在线监测
                    }else if(replaceMess.length()==16){
//                        String command=replaceMess.substring(4,6);
//                        Long lockId = Long.parseLong(HexUtils.hexToInteger(command) + "");
//                        System.out.println("坨机编号");
                        String[] prefix = steerPublishPrefix.split("/");
                        if(prefix != null && prefix.length > 0){
                            // 采集器编号
                            String relayCode = prefix[prefix.length-1];
                            TerminalRouter.routerMap.get("HxpLockService").offLine(relayCode);
                        }

                    }
                }

                // TODO
//                MessageBody messageBody = JSONObject.parseObject(messageStr, MessageBody.class);

//                mqttResHandler.deal(JSONUtil.toBean(msg,com.ffy.mqtt.model.Message.class));
                List<LabMessageEvent> labMessageEvents = labMessMap.get(receivedTopic);
                if(CollUtil.isEmpty(labMessageEvents)){
                    return ;
                }
            }
        };
    }

    public static String byte2Hex(byte[] bytes){
        StringBuilder builder = new StringBuilder();
        String temp;
        for (byte aByte : bytes) {
            temp = Integer.toHexString(aByte & 0xFF);
            if (temp.length() == 1) {
                //1得到一位的进行补0操作
                builder.append("0");
            }
            builder.append(temp);
        }
        return builder.toString();
    }
}
