package com.dji.sample.component.mqtt.config;

import com.auth0.jwt.algorithms.Algorithm;
import com.dji.sample.common.util.JwtUtil;
import com.dji.sample.component.mqtt.model.MqttClientOptions;
import com.dji.sample.component.mqtt.model.MqttProtocolEnum;
import com.dji.sample.component.mqtt.model.MqttUseEnum;
import com.dji.sdk.cloudapi.control.DrcModeMqttBroker;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.util.StringUtils;

import java.util.Map;

/**
 *
 * @author sean.zhou
 * @date 2021/11/10
 * @version 0.1
 */
@Slf4j
@Configuration
@Data
@ConfigurationProperties
public class MqttPropertyConfiguration {

    private static Map<MqttUseEnum, MqttClientOptions> mqtt;

    public void setMqtt(Map<MqttUseEnum, MqttClientOptions> mqtt) {
        MqttPropertyConfiguration.mqtt = mqtt;
    }

    /**
     * Get the configuration options of the basic link of the mqtt client.
     * @return
     */
    static MqttClientOptions getBasicClientOptions() {
        if (!mqtt.containsKey(MqttUseEnum.BASIC)) {
            throw new Error("Please configure the basic mqtt connection parameters first, otherwise application cannot be started.");
        }
        return mqtt.get(MqttUseEnum.BASIC);
    }

    /**
     * Get the mqtt address of the basic link.
     * @return
     */
    public static String getBasicMqttAddress() {
        return getMqttAddress(getBasicClientOptions());
    }

    /**
     * Get the configuration options of the net link of the mqtt client.
     * @return
     */
    static MqttClientOptions getNetClientOptions() {
        if (!mqtt.containsKey(MqttUseEnum.NET)) {
            throw new Error("Please configure the net mqtt connection parameters first, otherwise application cannot be started.");
        }
        return mqtt.get(MqttUseEnum.NET);
    }

    /**
     * Get the mqtt address of the net link.
     * @return
     */
    public static String getNetMqttAddress() {
        return getMqttAddress(getNetClientOptions());
    }

    /**
     * Splice the mqtt address according to the parameters of different clients.
     * @param options
     * @return
     */
    private static String getMqttAddress(MqttClientOptions options) {
        StringBuilder addr = new StringBuilder()
                .append(options.getProtocol().getProtocolAddr())
                .append(options.getHost().trim())
                .append(":")
                .append(options.getPort());
        if ((options.getProtocol() == MqttProtocolEnum.WS || options.getProtocol() == MqttProtocolEnum.WSS)
                && StringUtils.hasText(options.getPath())) {
            addr.append(options.getPath());
        }
        return addr.toString();
    }

    /**
     * Web 前端 DRC 连接（mqtt.js）：需要带 scheme 的完整 URL，如 wss://host:443/mqtt
     */
    public static DrcModeMqttBroker getMqttBrokerWithDrc(String clientId, String username, Long age, Map<String, ?> map) {
        return buildDrcBroker(clientId, username, age, map, false);
    }

    /**
     * 下发给机场/Pilot 的 DRC Broker（官方格式）：address=host:port，无 wss:// 前缀。
     */
    public static DrcModeMqttBroker getMqttBrokerWithDrcForDevice(String clientId, String username, Long age, Map<String, ?> map) {
        return buildDrcBroker(clientId, username, age, map, true);
    }

    private static DrcModeMqttBroker buildDrcBroker(String clientId, String username, Long age, Map<String, ?> map, boolean forDevice) {
        if (!mqtt.containsKey(MqttUseEnum.DRC)) {
            throw new RuntimeException("Please configure the drc link parameters of mqtt in the backend configuration file first.");
        }
        Algorithm algorithm = JwtUtil.algorithm;
        String token = JwtUtil.createToken(map, age, algorithm, null, null);

        MqttClientOptions drcOptions = mqtt.get(MqttUseEnum.DRC);
        String address;
        boolean enableTls;
        if (forDevice) {
            String host = StringUtils.hasText(drcOptions.getDeviceHost())
                    ? drcOptions.getDeviceHost().trim()
                    : drcOptions.getHost().trim();
            int port;
            if (drcOptions.getDevicePort() != null) {
                port = drcOptions.getDevicePort();
            } else if (drcOptions.getProtocol() == MqttProtocolEnum.WSS
                    || drcOptions.getProtocol() == MqttProtocolEnum.WS) {
                // Web 用 WSS:443；设备官方示例为 MQTTS:8883。未单独配置时默认 8883，避免把 443/wss 路径塞给 Pilot。
                port = 8883;
                log.warn("mqtt.DRC.device-port unset; using {}:8883 for device DRC (set device-port to override)", host);
            } else {
                port = drcOptions.getPort();
            }
            address = host + ":" + port;
            if (drcOptions.getDeviceEnableTls() != null) {
                enableTls = drcOptions.getDeviceEnableTls();
            } else if (port == 8883) {
                enableTls = true;
            } else if (drcOptions.getDevicePort() != null) {
                // 显式配了非 8883 设备端口（如 1883/54418）：默认明文，勿因 Web 的 WSS 误开 TLS
                enableTls = false;
            } else {
                enableTls = drcOptions.getProtocol() == MqttProtocolEnum.MQTTS;
            }
        } else {
            address = getMqttAddress(drcOptions);
            enableTls = drcOptions.getProtocol() == MqttProtocolEnum.WSS
                    || drcOptions.getProtocol() == MqttProtocolEnum.MQTTS;
        }

        log.info("Build DRC mqtt broker: forDevice={}, address={}, enableTls={}, clientId={}, username={}",
                forDevice, address, enableTls, clientId, username);

        return new DrcModeMqttBroker()
                .setAddress(address)
                .setUsername(username)
                .setClientId(clientId)
                .setExpireTime(System.currentTimeMillis() / 1000 + age)
                .setPassword(token)
                .setEnableTls(enableTls);
    }


    @Bean
    public MqttConnectOptions mqttConnectOptions() {
        MqttClientOptions customizeOptions = getBasicClientOptions();
        MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
        mqttConnectOptions.setServerURIs(new String[]{ getBasicMqttAddress() });
        mqttConnectOptions.setUserName(customizeOptions.getUsername());
        mqttConnectOptions.setPassword(StringUtils.hasText(customizeOptions.getPassword()) ?
                customizeOptions.getPassword().toCharArray() : new char[0]);
        mqttConnectOptions.setAutomaticReconnect(true);
        mqttConnectOptions.setKeepAliveInterval(60);
        return mqttConnectOptions;
    }

    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        factory.setConnectionOptions(mqttConnectOptions());
        return factory;
    }
}
