using BPA.Message.IOT;
using BPASmartClient.Helper;
using BPASmartClient.IoT;
using BPASmartClient.IoT.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;

namespace BPASmartDatavDeviceClient.IoT
{
    /// <summary>
    /// DataV客户端
    /// </summary>
    public class DataVReport
    {
        #region 外部调用
        /// <summary>
        /// 初始化IOT连接
        /// </summary>
        public bool Initialize(string url, string _clientId, string _deviceId, ref string message)
        {
            if (string.IsNullOrEmpty(url)) return false;
            deviceId = _deviceId;
            if (!CreateLinks(url, _clientId, out deviceTable, _deviceId))
            {
                message += $"客户端{_clientId}设备{_deviceId}阿里云上没有该设备。";
                return false;
            }
            IOT_Subscribe(BroadcastTopic);//订阅广播主题
            IOT_Subscribe(FileUpLoadReplyTopic);
            IOT_Subscribe(FileUpLoadSendReplyTopic);
            IOT_Subscribe(CancelFileUpLoadSendTopic);

            if (!DatavDeviceClient.IsConnected) message += $"客户端:【{_clientId}】,设备名称{deviceTable.devicename}阿里云连接失败.不能上报业务信息";
            return DatavDeviceClient.IsConnected;
        }

        /// <summary>
        /// 初始化IOT连接
        /// </summary>
        public bool InitializeNo(string _productkey, string _devicename, string _devicesecret, ref string message)
        {
            try
            {
                if (deviceTable == null) deviceTable = new DeviceTable();
                deviceTable.devicename = _devicename;
                SetValue(_productkey, _devicename, _devicesecret);
                IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
                string _clientIp = host.AddressList.FirstOrDefault(
                    ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).ToString();
                string t = Convert.ToString(DateTimeOffset.Now.ToUnixTimeMilliseconds());
                string signmethod = "hmacmd5";

                Dictionary<string, string> dict = new Dictionary<string, string>();
                dict.Add("productKey", ProductKey);
                dict.Add("deviceName", DeviceName);
                dict.Add("clientId", _clientIp);
                dict.Add("timestamp", t);

                mqttUserName = DeviceName + "&" + ProductKey;
                mqttPassword = IotSignUtils.sign(dict, DeviceSecret, signmethod);
                mqttClientId = _clientIp + "|securemode=3,signmethod=" + signmethod + ",timestamp=" + t + "|";
                targetServer = ProductKey + ".iot-as-mqtt." + RegionId + ".aliyuncs.com";
                ConnectMqtt(targetServer, mqttClientId, mqttUserName, mqttPassword);
            }
            catch (Exception ex)
            {
                message += $"阿里云{_devicename}连接失败,{ex.Message}";
                return false;
            }
            IOT_Subscribe(BroadcastTopic);//订阅广播主题
            if (!DatavDeviceClient.IsConnected) message += $"客户端:【】,设备名称{deviceTable.devicename}阿里云连接失败.不能上报业务信息";
            return DatavDeviceClient.IsConnected;
        }

        /// <summary>
        /// 获取连接状态
        /// </summary>
        public bool GetIsConnected()
        {
            try
            {
                if (DatavDeviceClient == null || !DatavDeviceClient.IsConnected)
                    return false;
                else return true;
            }
            catch (Exception ex)
            {
                return false;
                throw;
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            if (DatavDeviceClient != null)
            {
                DatavDeviceClient.Disconnect();
            }
        }

        /// <summary>
        /// 发布消息
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="message"></param>
        public void IOT_Publish(string topic, string message)
        {
            var id = DatavDeviceClient.Publish(topic, Encoding.UTF8.GetBytes(message));
        }

        public void IOT_Publish(string topic, byte[] message)
        {
            var id = DatavDeviceClient.Publish(topic, message);
        }

        /// <summary>
        /// 订阅主题
        /// </summary>
        /// <param name="topic"></param>
        public void IOT_Subscribe(string topic)
        {
            if (SubTopicList.Contains(topic))
            {
                SubTopicList.Add(topic);
            }
            DatavDeviceClient.Subscribe(new string[] { topic }, new byte[] { 0 });
        }

        public void Chkin_Up()
        {
            OSS_Helper ss = new OSS_Helper();

            string objectName = "Project/aa.bak";
            string downloadFilename = @"\\aa.bak";
            //记录进度的文件路径
            string checkpointDir = @"D:\checkin\";
            //ss.Multipar_tUp(objectName, downloadFilename);
            ss.chkin_Up(objectName, downloadFilename, checkpointDir);
        }
        #endregion

        #region 私有函数
        /// <summary>
        /// 设置变量
        /// </summary>
        /// <param name="_ProductKey"></param>
        /// <param name="_DeviceName"></param>
        /// <param name="_DeviceSecret"></param>
        /// <param name="_RegionId"></param>
        private void SetValue(string _ProductKey, string _DeviceName, string _DeviceSecret, string _RegionId = "cn-shanghai")
        {
            ProductKey = _ProductKey;
            DeviceName = _DeviceName;
            DeviceSecret = _DeviceSecret;
            RegionId = _RegionId;
            PubTopic = "/sys/" + ProductKey + "/" + DeviceName + "/thing/event/property/post";
            SubTopic = "/sys/" + ProductKey + "/" + DeviceName + "/thing/event/property/set";
            UserPubTopic = "/" + ProductKey + "/" + DeviceName + "/user/update";
            UserSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/get";
            BroadcastTopic = "/broadcast/" + ProductKey + "/" + DeviceName + "_SetDevice";
            AlarmSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/AlarmMessage";
            LogsSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/ExceptionLogs";
            HeartbeatSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/HeartbeatAndState";
            TargetStatusSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/TargetStatus";
            ScreenShowPubTopic = "/" + ProductKey + "/" + DeviceName + "/user/ScreenShow";
            FileUpLoadTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/init";
            FileUpLoadReplyTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/init_reply";
            FileUpLoadSendTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/send";
            FileUpLoadSendReplyTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/send_reply";
            CancelFileUpLoadSendTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/cancel";
            CancelFileUpLoadSendReplyTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/cancel_reply";
        }

        /// <summary>
        /// 创建连接
        /// </summary>
        private bool CreateLinks(string url, string clientId, out DeviceTable device, string deviceId = "")
        {
            try
            {
                string json = string.Empty;
                if (string.IsNullOrEmpty(deviceId))
                    json = HttpRequestHelper.HttpGetRequest($"{url}/api/Device/Query?clientId={clientId}", 1000);
                else
                    json = HttpRequestHelper.HttpGetRequest($"{url}/api/Device/Query?clientId={clientId}&deviceId={deviceId}");
                JsonMsg<List<DeviceTable>> jsonMsg = Tools.JsonToObjectTools<JsonMsg<List<DeviceTable>>>(json);
                if (jsonMsg.obj != null && jsonMsg.obj.data != null)
                {
                    device = jsonMsg.obj.data.FirstOrDefault();
                    if (device == null) return false;
                    SetValue(device.productkey, device.devicename, device.devicesecret);
                    IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
                    string _clientIp = host.AddressList.FirstOrDefault(
                        ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).ToString();
                    string t = Convert.ToString(DateTimeOffset.Now.ToUnixTimeMilliseconds());
                    string signmethod = "hmacmd5";

                    Dictionary<string, string> dict = new Dictionary<string, string>();
                    dict.Add("productKey", ProductKey);
                    dict.Add("deviceName", DeviceName);
                    dict.Add("clientId", _clientIp);
                    dict.Add("timestamp", t);

                    mqttUserName = DeviceName + "&" + ProductKey;
                    mqttPassword = IotSignUtils.sign(dict, DeviceSecret, signmethod);
                    mqttClientId = _clientIp + "|securemode=3,signmethod=" + signmethod + ",timestamp=" + t + "|";
                    targetServer = ProductKey + ".iot-as-mqtt." + RegionId + ".aliyuncs.com";
                    ConnectMqtt(targetServer, mqttClientId, mqttUserName, mqttPassword);
                    return true;
                }
                else
                {
                    device = null;
                    return false;
                }
            }
            catch (Exception ex)
            {
                device = null;
                return false;
            }
        }

        /// <summary>
        /// MQTT创建连接
        /// </summary>
        /// <param name="targetServer"></param>
        /// <param name="mqttClientId"></param>
        /// <param name="mqttUserName"></param>
        /// <param name="mqttPassword"></param>
        private void ConnectMqtt(string targetServer, string mqttClientId, string mqttUserName, string mqttPassword)
        {
            DatavDeviceClient = new MqttClient(targetServer);
            DatavDeviceClient.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;
            DatavDeviceClient.Connect(mqttClientId, mqttUserName, mqttPassword, false, 60);
            DatavDeviceClient.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
            DatavDeviceClient.ConnectionClosed += Client_ConnectionClosed;
        }

        /// <summary>
        /// MQTT 断开事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Client_ConnectionClosed(object sender, EventArgs e)
        {
            // 尝试重连
            _TryContinueConnect();
        }

        /// <summary>
        /// 订阅数据接收
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
        {
            string topic = e.Topic;
            string message = Encoding.UTF8.GetString(e.Message);
            if (DataVMessageAction != null)
            {
                DataVMessageAction.Invoke(deviceId, topic, message);
            }
        }

        /// <summary>
        /// 自动重连主体
        /// </summary>
        private void _TryContinueConnect()
        {
            Thread retryThread = new Thread(new ThreadStart(delegate
            {
                while (DatavDeviceClient == null || !DatavDeviceClient.IsConnected)
                {
                    if (DatavDeviceClient.IsConnected) break;

                    if (DatavDeviceClient == null)
                    {
                        DatavDeviceClient = new MqttClient(targetServer);
                        DatavDeviceClient.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;
                        DatavDeviceClient.Connect(mqttClientId, mqttUserName, mqttPassword, false, 60);
                        DatavDeviceClient.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
                        DatavDeviceClient.ConnectionClosed += Client_ConnectionClosed;
                        if (DatavDeviceClient.IsConnected)
                        {
                            SubTopicList?.ForEach(par => { DatavDeviceClient.Subscribe(new string[] { par }, new byte[] { 0 }); });
                        }
                        Thread.Sleep(3000);
                        continue;
                    }

                    try
                    {
                        DatavDeviceClient.Connect(mqttClientId, mqttUserName, mqttPassword, false, 60);
                        if (DatavDeviceClient.IsConnected)
                        {
                            SubTopicList?.ForEach(par => { DatavDeviceClient.Subscribe(new string[] { par }, new byte[] { 0 }); });
                            UnConnectMqtt?.Invoke("重新连接阿里云MQTT成功！");
                        }
                    }
                    catch (Exception ce)
                    {
                        UnConnectMqtt?.Invoke("重新连接阿里云MQTT失败！");
                    }
                    // 如果还没连接不符合结束条件则睡2秒
                    if (!DatavDeviceClient.IsConnected)
                    {
                        Thread.Sleep(2000);
                    }
                }
            }));

            retryThread.Start();
        }
        #endregion

        #region 私有IOT连接变量
        private static string ProductKey = "grgpECHSL7q";
        private static string DeviceName = "hbldev";
        private static string DeviceSecret = "4ec120de0c866199183b22e2e3135aeb";
        private static string RegionId = "cn-shanghai";
        private static string mqttUserName = string.Empty;
        private static string mqttPassword = string.Empty;
        private static string mqttClientId = string.Empty;
        private static string targetServer = string.Empty;
        private static string deviceId = string.Empty;
        #endregion

        #region 公有变量
        /// <summary>
        /// 设备消息数据回调
        /// </summary>
        public Action<string, string, string> DataVMessageAction { get; set; }
        /// <summary>
        /// 重连事件
        /// </summary>
        public static Action<string> UnConnectMqtt { get; set; }
        /// <summary>
        /// 客户端
        /// </summary>
        public static MqttClient DatavDeviceClient { get; set; }
        /// <summary>
        /// 当前设备
        /// </summary>
        public DeviceTable deviceTable = new DeviceTable();
        #endregion

        #region 发布或订阅主题或URL地址
        /// <summary>
        /// 属性发布消息主题
        /// </summary>
        public string PubTopic = "/" + ProductKey + "/" + DeviceName + "/user/update";
        /// <summary>
        /// 属性接收消息主题
        /// </summary>
        public static string SubTopic = "/" + ProductKey + "/" + DeviceName + "/user/get";
        /// <summary>
        /// 自定义发布消息主题
        /// </summary>
        public static string UserPubTopic = "/" + ProductKey + "/" + DeviceName + "/user/update";
        /// <summary>
        /// 自定义接收消息主题
        /// </summary>
        public static string UserSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/get";
        /// <summary>
        /// 告警订阅主题
        /// </summary>
        public static string AlarmSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/AlarmMessage";
        /// <summary>
        /// 日志订阅主题
        /// </summary>
        public static string LogsSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/ExceptionLogs";
        /// <summary>
        /// 上下线订阅主题
        /// </summary>
        public static string HeartbeatSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/HeartbeatAndState";
        /// <summary>
        /// 属性状态主题
        /// </summary>
        public static string TargetStatusSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/TargetStatus";
        /// <summary>
        /// 大屏展示发布主题
        /// </summary>
        public static string ScreenShowPubTopic = "/" + ProductKey + "/" + DeviceName + "/user/ScreenShow";
        /// <summary>
        /// 广播主题
        /// </summary>
        public string BroadcastTopic = "/broadcast/" + "grgpECHSL7q" + "/" + DeviceName + "_SetDevice";
        /// <summary>
        /// 文件上传请求Topic
        /// </summary>
        public string FileUpLoadTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/init";
        /// <summary>
        /// 文件上传请求响应Topic
        /// </summary>
        public string FileUpLoadReplyTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/init_reply";
        /// <summary>
        /// 文件发送Topic
        /// </summary>
        public string FileUpLoadSendTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/send";
        /// <summary>
        /// 文件发送响应Topic
        /// </summary>
        public string FileUpLoadSendReplyTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/send_reply";
        /// <summary>
        /// 取消文件发送Topic
        /// </summary>
        public string CancelFileUpLoadSendTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/cancel";
        /// <summary>
        /// 取消文件发送响应Topic
        /// </summary>
        public string CancelFileUpLoadSendReplyTopic = $"/sys/{ProductKey}/{DeviceName}/thing/file/upload/mqtt/cancel_reply";
        /// <summary>
        /// 订阅主题集合
        /// </summary>
        public static List<string> SubTopicList = new List<string>();
        #endregion
    }

    /// <summary>
    /// Iot 设备上报
    /// </summary>
    public class IotSignUtils
    {
        public static string sign(Dictionary<string, string> param,
                            string deviceSecret, string signMethod)
        {
            string[] sortedKey = param.Keys.ToArray();
            Array.Sort(sortedKey);

            StringBuilder builder = new StringBuilder();
            foreach (var i in sortedKey)
            {
                builder.Append(i).Append(param[i]);
            }

            byte[] key = Encoding.UTF8.GetBytes(deviceSecret);
            byte[] signContent = Encoding.UTF8.GetBytes(builder.ToString());
            //这里根据signMethod动态调整，本例子硬编码了： 'hmacmd5'
            var hmac = new HMACMD5(key);
            byte[] hashBytes = hmac.ComputeHash(signContent);

            StringBuilder signBuilder = new StringBuilder();
            foreach (byte b in hashBytes)
                signBuilder.AppendFormat("{0:x2}", b);

            return signBuilder.ToString();
        }
    }


}
