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

namespace BackendWorkerService.Quartz.Jobs
{
    /// <summary>
    /// 停車場管理
    /// </summary>
    [DisallowConcurrentExecution]
    class ParkingJob : IJob
    {
        private readonly ILogger<ParkingJob> logger;
        private readonly IBackgroundServiceRepository backgroundServiceRepository;
        private readonly IBackendRepository backendRepository;
        private readonly ILogger<Task_Detail> loggers;

        public ParkingJob(
            ILogger<ParkingJob> logger,
            IBackgroundServiceRepository backgroundServiceRepository, ILogger<Task_Detail> loggers, IBackendRepository backendRepository)
        {
            this.logger = logger;
            this.backgroundServiceRepository = backgroundServiceRepository;
            this.backendRepository = backendRepository;
            this.loggers = loggers;
        }

        public async Task Execute(IJobExecutionContext context)
        {
            Task_Detail task_Detail = new Task_Detail(loggers, backendRepository);
            try
            {
                if(await task_Detail.GetNeedWorkTask("ParkingJob", "All"))
                {
                    await task_Detail.InsertWorkTime("ParkingJob", "All", "任務開始");
                    EDFunction ed = new EDFunction();
                    var parkingConfig = new ParkingConfig();

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

                    var variable = await backgroundServiceRepository.GetAllAsync<KeyValue>(sqlParking);
                    parkingConfig.Host = variable.Where(x => x.Name == "Host").Select(x => x.Value).FirstOrDefault();
                    parkingConfig.Prefix = variable.Where(x => x.Name == "Prefix").Select(x => x.Value).FirstOrDefault();
                    parkingConfig.ApiBase = variable.Where(x => x.Name == "ApiBase").Select(x => x.Value).FirstOrDefault();
                    parkingConfig.UserName = ed.AESDecrypt(variable.Where(x => x.Name == "UserName").Select(x => x.Value).FirstOrDefault());
                    parkingConfig.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(parkingConfig.UserName + ":" + parkingConfig.Password));

                    #region 取得停車場資訊
                    if (await task_Detail.GetNeedWorkTask("ParkingJob", "Parking"))
                    {
                        try
                        {
                            await task_Detail.InsertWorkTime("ParkingJob", "Parking", "開始執行停車場剩餘車位Job");
                            HttpWebRequest spaceRequest = (HttpWebRequest)WebRequest.Create($"{parkingConfig.Host}{parkingConfig.Prefix}/api/space/details");
                            spaceRequest.Method = "GET";
                            //request.Headers.Add("Authorization", "Basic " + encoded);
                            spaceRequest.PreAuthenticate = true;

                            //Stopwatch stopWatch = new Stopwatch();
                            //stopWatch.Start();

                            HttpWebResponse spaceResponse = (HttpWebResponse)spaceRequest.GetResponse();
                            var spaceResponseContent = new StreamReader(spaceResponse.GetResponseStream()).ReadToEnd();
                            //logger.LogInformation("【ParkingJob】【取得成功停車場車位資訊】");
                            //stopWatch.Stop();
                            //logger.LogInformation("【ParkingJob】【取得停車場車位資訊】【效能檢驗】[取得資料花費時間]{0} 毫秒", stopWatch.ElapsedMilliseconds);

                            var spaceResponseResult = JsonConvert.DeserializeObject<SpaceResponse>(spaceResponseContent);

                            //取得停車場車位對應表
                            var sqlSapceMapping = $@"SELECT * FROM variable WHERE deleted = 0 AND system_type = 'parkingSapceMapping'";
                            var parkingSapceMapping = await backgroundServiceRepository.GetAllAsync<VariableInfo>(sqlSapceMapping);

                            if (spaceResponseResult != null && spaceResponseResult.Code == "20000")
                            {
                                foreach (var area in spaceResponseResult.Payload.Areas)
                                {
                                    //找出對定的設備代碼
                                    var selectedMapping = parkingSapceMapping.Where(x => x.System_key == area.Name).FirstOrDefault();
                                    if (selectedMapping != null)
                                    {
                                        var tagName = selectedMapping.system_value;
                                        var apiFormat = @"{0}obix/config/Arena/{1}/{2}/{3}/{4}/{5}/CV/set";

                                        var tagNameSplit = tagName.Split("_");

                                        var parames = new List<object>();
                                        parames.Add(parkingConfig.ApiBase);
                                        for (var i = 0; i < tagNameSplit.Length; i++)
                                        {
                                            if (i != tagNameSplit.Length - 1)
                                            {
                                                parames.Add(tagNameSplit[i]);
                                            }
                                            else
                                            {
                                                parames.Add(tagName);
                                            }
                                        }

                                        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(apiFormat, parames.ToArray()));
                                        request.Method = "POST";
                                        request.Headers.Add("Authorization", "Basic " + encoded);
                                        request.PreAuthenticate = true;

                                        var real = $@"<real val='{area.Remain}' />";
                                        byte[] realByteArray = Encoding.UTF8.GetBytes(real);
                                        using (Stream reqStream = request.GetRequestStream())
                                        {
                                            reqStream.Write(realByteArray, 0, realByteArray.Length);
                                        }

                                        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                                        var responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd();

                                        XmlDocument xmlDocument = new XmlDocument();
                                        xmlDocument.LoadXml(responseContent);
                                        string json = JsonConvert.SerializeXmlNode(xmlDocument);
                                        JObject jsonResult = (JObject)JsonConvert.DeserializeObject(json);

                                        if (jsonResult.ContainsKey("err")) //抓取錯誤
                                        {
                                            logger.LogError("【ParkingJob】【停車場剩餘車位資訊】");
                                            logger.LogError("【ParkingJob】【停車場剩餘車位資訊】[錯誤內容]:{0}", json);
                                        }
                                        else
                                        {
                                            if (jsonResult.ContainsKey("real")) //表示可以讀取到內容
                                            {
                                                List<Dictionary<string, object>> ontimeRawDatas = new List<Dictionary<string, object>>();

                                                var realList = jsonResult["real"];
                                                var display = realList["@display"];
                                                if (display != null)
                                                {
                                                    var tempStrSplit = display.ToString().Split(" ");
                                                    if (tempStrSplit[0] != area.Remain.ToString())
                                                    {
                                                        logger.LogError("【ParkingJob】【停車場剩餘車位資訊】[修改失敗]:{0}", display.ToString());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        logger.LogWarning("【ParkingJob】【停車場剩餘車位資訊】[查無該名稱對應表]:{0}", area.Name);
                                    }
                                }

                            }
                            else
                            {
                                logger.LogWarning("【ParkingJob】【停車場剩餘車位資訊】 - [查無資料]");
                            }
                            await task_Detail.InsertWorkTime_End("ParkingJob", "Parking", "執行成功停車場剩餘車位Job");
                            //logger.LogInformation("【ParkingJob】【執行成功停車場剩餘車位Job】");
                        }
                        catch (Exception exception)
                        {
                            await task_Detail.WorkFail("ParkingJob", "Parking", exception.ToString());
                            logger.LogInformation("【ParkingJob】【執行失敗停車場剩餘車位Job】");
                            logger.LogInformation("【ParkingJob】【執行失敗停車場剩餘車位Job】[Exception]:{0}", exception.ToString());
                        }
                    }
                    #endregion

                    #region 取得設備資訊
                    if (await task_Detail.GetNeedWorkTask("ParkingJob", "Device"))
                    {
                        try
                        {
                            await task_Detail.InsertWorkTime("ParkingJob", "Device", "開始執行設備資訊Job");
                            //logger.LogInformation("【ParkingJob】【開始執行設備資訊Job】");

                            HttpWebRequest equipmentRequest = (HttpWebRequest)WebRequest.Create($"{parkingConfig.Host}{parkingConfig.Prefix}/api/equipment/state");
                            equipmentRequest.Method = "GET";
                            //request.Headers.Add("Authorization", "Basic " + encoded);
                            equipmentRequest.PreAuthenticate = true;

                            HttpWebResponse equipmentResponse = (HttpWebResponse)equipmentRequest.GetResponse();
                            var equipmentResponseContent = new StreamReader(equipmentResponse.GetResponseStream()).ReadToEnd();

                            var equipmentResponseResult = JsonConvert.DeserializeObject<EquipmentResponse>(equipmentResponseContent);

                            //取得設備對應表
                            var sqlEquipmentMapping = $@"SELECT * FROM variable WHERE deleted = 0 AND system_type = 'parkingEquipmentMapping'";
                            var parkingEquipmentMapping = await backgroundServiceRepository.GetAllAsync<VariableInfo>(sqlEquipmentMapping);

                            //List<VariableInfo> parkingEquipmentMapping = new List<VariableInfo>();
                            //VariableInfo variableInfo_1 = new VariableInfo();
                            //variableInfo_1.System_key = "APS-000";
                            //variableInfo_1.system_value = "D3_P_B4F_CAPS_B413";

                            //parkingEquipmentMapping.Add(variableInfo_1);

                            //VariableInfo variableInfo_2 = new VariableInfo();
                            //variableInfo_2.System_key = "APS-001";
                            //variableInfo_2.system_value = "D3_P_B4F_CAPS_B414";
                            //parkingEquipmentMapping.Add(variableInfo_2);

                            if (equipmentResponseResult != null && equipmentResponseResult.Code == "20000")
                            {
                                foreach (var equipment in equipmentResponseResult.Payload)
                                {
                                    //找出對定的設備代碼
                                    var selectedMapping = parkingEquipmentMapping.Where(x => x.System_key == equipment.Name).FirstOrDefault();
                                    if (selectedMapping != null)
                                    {

                                        var tagName = selectedMapping.system_value;
                                        var apiFormat = @"{0}obix/config/Arena/{1}/{2}/{3}/{4}/{5}/ST/set";

                                        var tagNameSplit = tagName.Split("_");

                                        var parames = new List<object>();
                                        parames.Add(parkingConfig.ApiBase);
                                        for (var i = 0; i < tagNameSplit.Length; i++)
                                        {
                                            if (i != tagNameSplit.Length - 1)
                                            {
                                                parames.Add(tagNameSplit[i]);
                                            }
                                            else
                                            {
                                                parames.Add(tagName);
                                            }
                                        }

                                        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(apiFormat, parames.ToArray()));
                                        request.Method = "POST";
                                        request.Headers.Add("Authorization", "Basic " + encoded);
                                        request.PreAuthenticate = true;

                                        var real = $@"<real val='{equipment.Alive.ToString().ToLower()}' />";
                                        byte[] realByteArray = Encoding.UTF8.GetBytes(real);
                                        using (Stream reqStream = request.GetRequestStream())
                                        {
                                            reqStream.Write(realByteArray, 0, realByteArray.Length);
                                        }

                                        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                                        var responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd();

                                        XmlDocument xmlDocument = new XmlDocument();
                                        xmlDocument.LoadXml(responseContent);
                                        string json = JsonConvert.SerializeXmlNode(xmlDocument);
                                        JObject jsonResult = (JObject)JsonConvert.DeserializeObject(json);

                                        if (jsonResult.ContainsKey("err")) //抓取錯誤
                                        {
                                            logger.LogError("【ParkingJob】【設備資訊】");
                                            logger.LogError("【ParkingJob】【設備資訊】[錯誤內容]:{0}", json);
                                        }
                                        else
                                        {
                                            if (jsonResult.ContainsKey("bool")) //表示可以讀取到內容
                                            {
                                                List<Dictionary<string, object>> ontimeRawDatas = new List<Dictionary<string, object>>();

                                                var realList = jsonResult["bool"];
                                                var val = realList["@val"];
                                                if (val != null)
                                                {
                                                    var tempStrSplit = val.ToString();
                                                    if (tempStrSplit != equipment.Alive.ToString().ToLower())
                                                    {
                                                        logger.LogError("【ParkingJob】【設備資訊】[修改失敗]:{0}", val.ToString());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        logger.LogWarning("【ParkingJob】【設備資訊】[查無該名稱對應表]:{0}", equipment.Name);
                                    }
                                }
                            }
                            else
                            {
                                logger.LogWarning("【ParkingJob】【設備資訊】 - [查無資料]");
                            }
                            await task_Detail.InsertWorkTime_End("ParkingJob", "Device", "執行成功設備資訊Job");
                            //logger.LogInformation("【ParkingJob】【執行成功設備資訊Job】");
                        }
                        catch (Exception exception)
                        {
                            await task_Detail.WorkFail("ParkingJob", "Device", exception.ToString());
                            logger.LogInformation("【ParkingJob】【執行失敗設備資訊Job】");
                            logger.LogInformation("【ParkingJob】【執行失敗設備資訊Job】[Exception]:{0}", exception.ToString());
                        }
                    }
                    #endregion

                    await task_Detail.InsertWorkTime_End("ParkingJob", "All", "任務完成");
                }


            }
            catch (Exception exception)
            {
                await task_Detail.WorkFail("ParkingJob", "All", exception.ToString());
                logger.LogError("【ParkingJob】【任務失敗】");
                logger.LogError("【ParkingJob】【任務失敗】[Exception]:{0}", exception.ToString());
            }

        }
    }
}