using Edge.Core.IndustryStandardInterface.NetworkController;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using static DeviceInfoToAliIotHubViaGateway.App;

namespace DeviceInfoToAliIotHubViaGateway
{
    /// <summary>
    /// helper for open and connect to a AliIotHub MqttServer.
    /// </summary>
    internal class AliIotHubMqttClientInitializer
    {
        private IMqttClientNetworkController mqttClient;
        //private static readonly AliIotHubMqttClientInitializer instance = new AliIotHubMqttClientInitializer();
        public IMqttClientNetworkController MqttClient => this.mqttClient;

        //private ILogger logger = null;

        //public static AliIotHubMqttClientInitializer New(ILogger logger)
        //{
        //    return new AliIotHubMqttClientInitializer(logger);
        //}

        internal AliIotHubMqttClientInitializer(IMqttClientNetworkController mqttClient)
        {
            this.mqttClient = mqttClient;
        }

        //public static AliIotHubMqttClientInitializer Default => instance;

        /// <summary>
        /// 一型一密免预注册的设备动态注册方式,通过此次交互,可以获得设备的ProductKey、DeviceName、ClientID、DeviceToken,相当于在IOT上建立了此设备,再就可以像普通设备一样发起通讯了.
        /// </summary>
        /// <param name="mqttServerUrl"></param>
        /// <param name="port"></param>
        /// <param name="mqttClientIdPrefix">表示客户端ID,建议使用设备的MAC地址或SN码</param>
        /// <param name="productKey"></param>
        /// <param name="productSecret"></param>
        /// <param name="dynamicDeviceName"></param>
        /// <returns></returns>
        public async Task<DynamicDeviceInfo> DynamicRegDeviceWithoutPreRegAsync(string mqttServerUrl, int port,
            string mqttClientIdPrefix, string productKey, string productSecret, string dynamicDeviceName)
        {
            var rebootSucceed = await this.mqttClient.ResetAsync();
            if (!rebootSucceed)
            {
                await Task.Delay(500);
                return null;
            }

            await Task.Delay(2000);
            //this.logger.LogInformation("    Querying Mqtt client status...");
            var status = await this.mqttClient.QueryStatusAsync();
            //this.logger.LogInformation("        Mqtt client Status is: " + status);
            if (status != NetworkState.NetworkConnected)
                return null;

            bool succeed = false;

            // looks like the sample in BC35g Mqtt protocol doc is not work via below command.
            //succeed = await this.mqttClient.ConfigNetwork(AliIotHub_ProductKey,
            //    AliIotHub_DeviceName, AliIotHub_DeviceSecret);
            //if (!succeed)
            //{
            //    await Task.Delay(500);
            //    return false;
            //}

            succeed = await this.mqttClient.OpenAsync(mqttServerUrl, port);
            if (!succeed)
            {
                await Task.Delay(500);
                await this.mqttClient.CloseAsync();
                return null;
            }

            Random r = new Random();
            int random = 199;// r.Next(1, 1000000);
            var toBeHashing = $"deviceName{dynamicDeviceName}productKey{productKey}random{random}";
            var aliIotHubMqttPwd = Encoding.UTF8.GetBytes(toBeHashing).SignWithHMacSHA1(Encoding.UTF8.GetBytes(productSecret));
            string aliIotHub_MqttComplexClientId = mqttClientIdPrefix + "|securemode=2,authType=regnwl,random=" + random + ",signmethod=hmacsha1|";
            var aliIotHubMqttUserName = dynamicDeviceName + "&" + productKey;

            var onMsgReceivedTCS = new TaskCompletionSource<string>();
            EventHandler<OnMqttMessageReceivedEventArg> evtHandler = (s, a) =>
            {
                if (a.Message.Topic == "/ext/regnwl")
                    onMsgReceivedTCS.SetResult(Encoding.UTF8.GetString(a.Message.Message));
            };
            this.mqttClient.OnMessageReceived += evtHandler;
            succeed = await this.mqttClient.ConnectAsync(aliIotHub_MqttComplexClientId, aliIotHubMqttUserName, aliIotHubMqttPwd);
            if (!succeed)
            {
                await Task.Delay(500);
                await this.mqttClient.DisconnectAsync();
                return null;
            }

            try
            {
                var ti = Task.WaitAny(new[] { Task.Delay(6000), onMsgReceivedTCS.Task });
                if (ti == 0)
                    return null;
                var dynamicRegistedGatewayDeviceInfo = JsonSerializer.Deserialize<DynamicDeviceInfo>(onMsgReceivedTCS.Task.Result);
                return dynamicRegistedGatewayDeviceInfo;
            }
            finally
            {
                this.mqttClient.OnMessageReceived -= evtHandler;
            }
        }

        /// <summary>
        /// 用设备的ProductKey、DeviceName、ClientID、DeviceToken,像普通设备一样发起通讯.
        /// </summary>
        /// <param name="dynamicDeviceInfo">网关设备</param>
        /// <returns></returns>
        public async Task<bool> DynamicDeviceConnAsync(string mqttServerUrl, int port, DynamicDeviceInfo dynamicDeviceInfo)
        {
            var rebootSucceed = await this.mqttClient.ResetAsync();
            if (!rebootSucceed)
            {
                await Task.Delay(500);
                return false;
            }

            await Task.Delay(2000);
            //this.logger.LogInformation("    Querying Mqtt client status...");
            var status = await this.mqttClient.QueryStatusAsync();
            //this.logger.LogInformation("        Mqtt client Status is: " + status);
            if (status != NetworkState.NetworkConnected)
                return false;

            bool succeed = false;

            // looks like the sample in BC35g Mqtt protocol doc is not work via below command.
            //succeed = await this.mqttClient.ConfigNetwork(AliIotHub_ProductKey,
            //    AliIotHub_DeviceName, AliIotHub_DeviceSecret);
            //if (!succeed)
            //{
            //    await Task.Delay(500);
            //    return false;
            //}

            succeed = await this.mqttClient.OpenAsync(mqttServerUrl, port);
            if (!succeed)
            {
                await Task.Delay(500);
                await this.mqttClient.CloseAsync();
                return false;
            }

            var aliIotHubMqttPwd = dynamicDeviceInfo.deviceToken;
            string aliIotHub_MqttComplexClientId = dynamicDeviceInfo.clientId + "|securemode=-2,authType=connwl|";
            var aliIotHubMqttUserName = dynamicDeviceInfo.deviceName + "&" + dynamicDeviceInfo.productKey;
            succeed = await this.mqttClient.ConnectAsync(aliIotHub_MqttComplexClientId, aliIotHubMqttUserName, aliIotHubMqttPwd);
            if (!succeed)
            {
                await Task.Delay(500);
                await this.mqttClient.DisconnectAsync();
                return false;
            }

            return true;
        }
    }
}