using FrontendWorkerService.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Quartz;
using Repository.FrontendRepository.Interface;
using Repository.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace FrontendWorkerService.Quartz.Jobs
{
    /// <summary>
    /// 透過obix API向Niagara 訂閱告警設備
    /// </summary>
    [DisallowConcurrentExecution]
    class OntimeAlarmDeviceSubscriptionJob : IJob
    {
        private readonly ILogger<OntimeAlarmDeviceSubscriptionJob> logger;
        private readonly IFrontendRepository frontendRepository;

        public OntimeAlarmDeviceSubscriptionJob(
            ILogger<OntimeAlarmDeviceSubscriptionJob> logger,
            IFrontendRepository frontendRepository)
        {
            this.logger = logger;
            this.frontendRepository = frontendRepository;
        }

        public async Task Execute(IJobExecutionContext context)
        {
            try
            {
                string watch_id = string.Empty; //紀錄watch id的編號
                XmlDocument xmlDocument = new XmlDocument();

                logger.LogInformation("【OntimeAlarmDeviceSubscriptionJob】【任務開始】");

                EDFunction ed = new EDFunction();
                var obixApiConfig = new ObixApiConfig();

                var sqlObix = $@"SELECT system_value as Value, system_key as Name FROM variable WHERE deleted = 0 AND system_type = 'obixConfig'";

                var variable = await frontendRepository.GetAllAsync<KeyValue>(sqlObix);
                obixApiConfig.ApiBase = variable.Where(x => x.Name == "ApiBase").Select(x => x.Value).FirstOrDefault();
                obixApiConfig.UserName = ed.AESDecrypt(variable.Where(x => x.Name == "UserName").Select(x => x.Value).FirstOrDefault());
                obixApiConfig.Password = ed.AESDecrypt(variable.Where(x => x.Name == "Password").Select(x => x.Value).FirstOrDefault());

                String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(obixApiConfig.UserName + ":" + obixApiConfig.Password));

                ObixAlarmConfig obixAlarmConfig = new ObixAlarmConfig();
                obixAlarmConfig.Alarm_watch_id = variable.Where(x => x.Name == "alarm_watch_id").Select(x => x.Value).FirstOrDefault();

                #region step 1 檢查watch id是否存活,未存活則重新建立
                HttpWebRequest watchServiceRequest = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/watchService/");
                watchServiceRequest.Method = "GET";
                watchServiceRequest.Headers.Add("Authorization", "Basic " + encoded);
                watchServiceRequest.PreAuthenticate = true;

                HttpWebResponse watchServiceResponse = (HttpWebResponse)watchServiceRequest.GetResponse();
                var watchServiceResponseContent = new StreamReader(watchServiceResponse.GetResponseStream()).ReadToEnd();

                xmlDocument.LoadXml(watchServiceResponseContent);
                string watchServiceJson = JsonConvert.SerializeXmlNode(xmlDocument);
                JObject watchServiceJsonResult = (JObject)JsonConvert.DeserializeObject(watchServiceJson);

                if (watchServiceJsonResult.ContainsKey("err")) //抓取錯誤
                {
                    logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【檢查watch service失敗】");
                    logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【檢查watch service失敗】[錯誤內容]:{0}", watchServiceJson);
                    return;
                }

                if (watchServiceJsonResult.ContainsKey("obj")) //表示可以讀取到內容
                {
                    if (watchServiceJsonResult["obj"]["ref"] != null && watchServiceJsonResult["obj"]["ref"].HasValues)
                    {
                        var watchServices = watchServiceJsonResult["obj"]["ref"];
                        var temp_name_count = 0; //用來計算總共有幾個name
                        foreach (var watchService in watchServices.Children())
                        {
                            var tempKeyValue = watchService.ToString();
                            if (tempKeyValue.Contains("@name"))
                            {
                                temp_name_count++;
                            }
                        }

                        if (temp_name_count > 1)
                        {   //有多個watch
                            foreach (var watchService in watchServices.Children())
                            {
                                var tempKeyValue = watchService.ToString();

                                if (tempKeyValue.Contains("@name"))
                                {
                                    var name = watchService["@name"].ToString();
                                    watch_id = name.Replace("watch", "");

                                    if (watch_id == obixAlarmConfig.Alarm_watch_id)
                                    {   //表示有找到id
                                        break;
                                    }
                                    watch_id = string.Empty;
                                }
                            }
                        }
                        else
                        {   //單一個watch
                            var tempKeyValue = watchServices.ToString();

                            if (tempKeyValue.Contains("@name"))
                            {
                                var name = watchServices["@name"].ToString();
                                watch_id = name.Replace("watch", "");
                                if (watch_id != obixAlarmConfig.Alarm_watch_id)
                                {
                                    watch_id = string.Empty;
                                }
                            }
                        }
                    }
                }

                if (string.IsNullOrEmpty(watch_id))
                {   //建立watch service
                    HttpWebRequest makeWatchServiceRequest = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/watchService/make/");
                    makeWatchServiceRequest.Method = "POST";
                    makeWatchServiceRequest.Headers.Add("Authorization", "Basic " + encoded);
                    makeWatchServiceRequest.PreAuthenticate = true;

                    HttpWebResponse makeWatchServiceResponse = (HttpWebResponse)makeWatchServiceRequest.GetResponse();
                    var makeWatchServiceResponseContent = new StreamReader(makeWatchServiceResponse.GetResponseStream()).ReadToEnd();

                    xmlDocument.LoadXml(makeWatchServiceResponseContent);
                    string makeWatchServiceJson = JsonConvert.SerializeXmlNode(xmlDocument);
                    JObject makeWatchServiceJsonResult = (JObject)JsonConvert.DeserializeObject(makeWatchServiceJson);


                    if (makeWatchServiceJsonResult.ContainsKey("err")) //抓取錯誤
                    {
                        logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【重新建立watch service失敗】");
                        logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【重新建立watch service失敗】[錯誤內容]:{0}", makeWatchServiceJson);
                        return;
                    }

                    if (makeWatchServiceJsonResult.ContainsKey("obj")) //表示可以讀取到內容
                    {
                        var makeSplit = makeWatchServiceJsonResult["obj"]["@href"].ToString().Split("/");
                        watch_id = makeSplit[makeSplit.Length - 2].Replace("watch", "");
                    }

                    if (!string.IsNullOrEmpty(watch_id))
                    {
                        //儲存至variable
                        Dictionary<string, object> updateWatchId = new Dictionary<string, object>()
                        {
                            { "@system_value", watch_id}
                        };

                        await frontendRepository.UpdateOneByCustomTable(updateWatchId, "variable", "system_type = 'obixConfig' AND system_key = 'alarm_watch_id'");

                        //修改watch service 存活時間
                        HttpWebRequest changeWatchServiceRequest = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/watchService/watch{watch_id}/lease/");
                        changeWatchServiceRequest.Method = "PUT";
                        changeWatchServiceRequest.Headers.Add("Authorization", "Basic " + encoded);
                        changeWatchServiceRequest.PreAuthenticate = true;

                        var realTime = $@"<reltime val='PT24H0S' />";
                        byte[] realTimeByteArray = Encoding.UTF8.GetBytes(realTime);
                        using (Stream reqStream = changeWatchServiceRequest.GetRequestStream())
                        {
                            reqStream.Write(realTimeByteArray, 0, realTimeByteArray.Length);
                        }

                        HttpWebResponse changeWatchServiceResponse = (HttpWebResponse)changeWatchServiceRequest.GetResponse();
                        var changeWatchServiceResponseContent = new StreamReader(changeWatchServiceResponse.GetResponseStream()).ReadToEnd();

                        xmlDocument.LoadXml(changeWatchServiceResponseContent);
                        string changeWatchServiceJson = JsonConvert.SerializeXmlNode(xmlDocument);
                        JObject changeWatchServiceJsonResult = (JObject)JsonConvert.DeserializeObject(changeWatchServiceJson);

                        if (changeWatchServiceJsonResult.ContainsKey("err")) //抓取錯誤
                        {
                            logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【修改watch service存活時間失敗】");
                            logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【修改watch service存活時間失敗】[錯誤內容]:{0}", changeWatchServiceJsonResult);
                            return;
                        }
                    }

                }
                #endregion step 1 檢查watch id是否存活,未存活則重新建立

                #region step 2 訂閱alarm feed,並將回傳的值新增至資料庫
                HttpWebRequest addWatchServiceRequest = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/watchService/watch{watch_id}/add/");
                addWatchServiceRequest.Method = "POST";
                addWatchServiceRequest.Headers.Add("Authorization", "Basic " + encoded);
                addWatchServiceRequest.PreAuthenticate = true;

                var subscripFullString = $@"<obj is='obix: WatchIn'>
                                                <list names = 'hrefs'>
                                                    <uri val = '{obixApiConfig.ApiBase}obix/config/Services/OrionAlarmService/~alarmFeed/'/>
                                                </list>
                                            </obj>";
                byte[] byteArray = Encoding.UTF8.GetBytes(subscripFullString);
                using (Stream reqStream = addWatchServiceRequest.GetRequestStream())
                {
                    reqStream.Write(byteArray, 0, byteArray.Length);
                }

                HttpWebResponse addWatchServiceResponse = (HttpWebResponse)addWatchServiceRequest.GetResponse();
                var addWatchServiceResponseContent = new StreamReader(addWatchServiceResponse.GetResponseStream()).ReadToEnd();

                xmlDocument.LoadXml(addWatchServiceResponseContent);
                string addWatchServiceJson = JsonConvert.SerializeXmlNode(xmlDocument);
                JObject addWatchServiceJsonResult = (JObject)JsonConvert.DeserializeObject(addWatchServiceJson);

                if (addWatchServiceJsonResult.ContainsKey("err")) //抓取錯誤
                {
                    logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【告警設備訂閱失敗】");
                    logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【告警設備訂閱失敗】[錯誤內容]:{0}", addWatchServiceJsonResult);
                    return;
                }

                if (addWatchServiceJsonResult.ContainsKey("obj")) //表示可以讀取到內容
                {
                    List<Dictionary<string, object>> ontimeAlarmRawDatas = new List<Dictionary<string, object>>();
                    List<string> tempDeviceNumbers = new List<string>(); //用來找出設備的區域、系統大小類GUID
                    List<OntimeAlarmRawDataPreprocess> alarmRawDataPreprocesses = new List<OntimeAlarmRawDataPreprocess>();

                    var alarmList = addWatchServiceJsonResult["obj"]["list"]["feed"];
                    if (alarmList["obj"] != null && alarmList["obj"].HasValues)
                    {
                        //讀取資料
                        foreach (var obj in alarmList["obj"])
                        {
                            var sourceState = string.Empty;
                            var sourceName = string.Empty;
                            //找出
                            foreach (var str in obj["str"])
                            {
                                var tempName = str["@name"].ToString();
                                if(tempName == "toState")
                                {
                                    sourceState = str["@val"].ToString();
                                }

                                if (tempName == "sourceName")
                                {
                                    sourceName = str["@val"].ToString();
                                }
                            }
                            
                            if (!string.IsNullOrEmpty(sourceName))
                            {
                                var sourceNameSplit = sourceName.Split("_");

                                var device_number = string.Join("_", sourceNameSplit, 0, sourceNameSplit.Length - 1); //找出設備編號
                                var name = sourceNameSplit[sourceNameSplit.Length - 1];//找出點位

                                tempDeviceNumbers.Add(device_number);

                                OntimeAlarmRawDataPreprocess alarmRawDataPreprocess = new OntimeAlarmRawDataPreprocess()
                                { 
                                    DeviceNumber = device_number,
                                    Name = name,
                                    SourceStatus = sourceState
                                };

                                alarmRawDataPreprocesses.Add(alarmRawDataPreprocess);
                            }
                        }
                    }

                    if(alarmRawDataPreprocesses.Count() > 0)
                    {
                        //找出設備的區域、系統大小類GUID
                        var sql = $@"SELECT * FROM device d WHERE d.deleted = 0 AND d.device_number IN @DeviceNumbers";

                        var deviceInfos = await frontendRepository.GetAllAsync<DeviceInfo>(sql, new { DeviceNumbers = tempDeviceNumbers });

                        foreach(var alarmRawDataPreprocess in alarmRawDataPreprocesses)
                        {
                            var selectedDeviceInfo = deviceInfos.Where(x => x.device_number == alarmRawDataPreprocess.DeviceNumber).FirstOrDefault();

                            if(selectedDeviceInfo != null)
                            {
                                Dictionary<string, object> ontimeAlarmRawData = new Dictionary<string, object>()
                                {
                                    { "@building_guid", selectedDeviceInfo.building_guid },
                                    { "@main_system_guid", selectedDeviceInfo.main_system_guid },
                                    { "@sub_system_guid", selectedDeviceInfo.sub_system_guid },
                                    { "@floor_guid", selectedDeviceInfo.floor_guid },
                                    { "@device_number", alarmRawDataPreprocess.DeviceNumber},
                                    { "@name", alarmRawDataPreprocess.Name},
                                    { "@source_state", alarmRawDataPreprocess.SourceStatus},
                                };

                                ontimeAlarmRawDatas.Add(ontimeAlarmRawData);
                            }
                        }
                    }

                    if (ontimeAlarmRawDatas.Count() > 0)
                    {
                        try
                        {
                            var insert_update_sql = $@"INSERT INTO ontime_alarm_device_rawdata (building_guid, main_system_guid, sub_system_guid, device_number, name, source_state) VALUES(@building_guid, @main_system_guid, @sub_system_guid, @device_number, @name, @source_state)
                                                    ON DUPLICATE KEY UPDATE source_state=@source_state";
                            await frontendRepository.ExecuteSql(insert_update_sql, ontimeAlarmRawDatas);
                        }
                        catch (Exception exception)
                        {
                            logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【新增設備點位原始資料失敗】");
                            logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【新增設備點位原始資料失敗】[Exception]:{0}", exception.ToString());
                        }
                    }
                }

                #endregion step 2 訂閱alarm feed,並將回傳的值新增至資料庫

                logger.LogInformation("【OntimeAlarmDeviceSubscriptionJob】【任務完成】");
            }
            catch (Exception exception)
            {
                logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【任務失敗】");
                logger.LogError("【OntimeAlarmDeviceSubscriptionJob】【任務失敗】[Exception]:{0}", exception.ToString());
            }

        }
    }
}