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

namespace BackendWorkerService.Quartz.Jobs
{
    [DisallowConcurrentExecution]
    class DataDeliveryJob : IJob
    {
        private readonly ILogger<DataDeliveryJob> logger;
        private readonly IBackgroundServiceRepository backgroundServiceRepository;

        public DataDeliveryJob(
            ILogger<DataDeliveryJob> logger,
            IBackgroundServiceRepository backgroundServiceRepository)
        {
            this.logger = logger;
            this.backgroundServiceRepository = backgroundServiceRepository;
        }

        public async Task Execute(IJobExecutionContext context)
        {
            Dictionary<string, object> insertLog = new Dictionary<string, object>()
            {
                { "@task_id", 0 },
                { "@log_level", "" },
                { "@log_content", "" }
            };

            try
            {
                logger.LogInformation("【DataDeliveryJob】【任務開始】");
                insertLog["@log_level"] = $@"INFO";
                insertLog["@log_content"] = $@"【DataDeliveryJob】任務開始";
                await backgroundServiceRepository.AddOneByCustomTable(insertLog, "background_service_task_log");

                //找出所有要派送的資料
                string sWhere = @"is_complete = 0 AND task_type = @Task_type AND repeat_times < 10";
                var backgroundServiceTasks = await backgroundServiceRepository.GetAllAsync<BackgroundServiceTask>("background_service_task", sWhere, new { Task_type = BackgroundServiceTaskType.data_delivery });

                if (backgroundServiceTasks.Count == 0)
                {
                    logger.LogInformation("【DataDeliveryJob】【查無任務列表】");

                    insertLog["@log_level"] = $@"INFO";
                    insertLog["@log_content"] = $@"【DataDeliveryJob】查無任務列表";
                    await backgroundServiceRepository.AddOneByCustomTable(insertLog, "background_service_task_log");
                }
                else
                {
                    List<Dictionary<string, object>> updateObjs = new List<Dictionary<string, object>>();
                    foreach (var task in backgroundServiceTasks)
                    {
                        var DateTimeNow = DateTime.Now;
                        Dictionary<string, object> updateObj = new Dictionary<string, object>()
                        {
                            { "Id", task.Id },
                            { "@repeat_times", 0 },
                            { "@is_complete", 0 },
                            { "@fail_reason", null },
                            { "@complete_at", null },
                            { "@updated_at", DateTimeNow }
                        };

                        insertLog["@task_id"] = task.Id;

                        //var parameters = JsonSerializer.Deserialize<Dictionary<string, object>>(task.Target_data);
                        try
                        {
                            logger.LogInformation("【DataDeliveryJob】【開始派送】[棟別IP]:{0}", task.Target_ip);
                            insertLog["@log_level"] = $@"INFO";
                            insertLog["@log_content"] = $@"開始派送";
                            await backgroundServiceRepository.AddOneByCustomTable(insertLog, "background_service_task_log");

                            var boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

                            var boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
                            var endBoundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--");

                            HttpWebRequest Postrequest = (HttpWebRequest)WebRequest.Create($"http://{task.Target_ip}/api/ReceiveDataDelivery/GetData");
                            Postrequest.ContentType = "multipart/form-data; boundary=" + boundary;
                            Postrequest.Method = "POST";

                            if (!string.IsNullOrEmpty(task.Target_table))
                            {
                                using (Stream requestStream = Postrequest.GetRequestStream())
                                {
                                    //Id
                                    requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                                    string task_id = "Content-Disposition: form-data; name=\"" + "Id" + "\"\r\n\r\n" + task.Id;
                                    byte[] task_id_bytes = System.Text.Encoding.UTF8.GetBytes(task_id);
                                    requestStream.Write(task_id_bytes, 0, task_id_bytes.Length);

                                    //Target Table
                                    requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                                    string target_table = "Content-Disposition: form-data; name=\"" + "TargetTable" + "\"\r\n\r\n" + task.Target_table;
                                    byte[] target_table_bytes = System.Text.Encoding.UTF8.GetBytes(target_table);
                                    requestStream.Write(target_table_bytes, 0, target_table_bytes.Length);

                                    //mode
                                    requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                                    string target_mode = "Content-Disposition: form-data; name=\"" + "mode" + "\"\r\n\r\n" + task.Mode;
                                    byte[] target_mode_bytes = System.Text.Encoding.UTF8.GetBytes(target_mode);
                                    requestStream.Write(target_mode_bytes, 0, target_mode_bytes.Length);

                                    //Target data
                                    requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                                    string target_data = "Content-Disposition: form-data; name=\"" + "TargetData" + "\"\r\n\r\n" + task.Target_data;
                                    byte[] target_data_bytes = System.Text.Encoding.UTF8.GetBytes(target_data);
                                    requestStream.Write(target_data_bytes, 0, target_data_bytes.Length);

                                    //解析Files
                                    if (task.Target_files != null)
                                    {
                                        var target_files = JsonSerializer.Deserialize<List<Backend.Models.FileInfo>>(task.Target_files);
                                        var file_index = 0;

                                        foreach (var file in target_files)
                                        {
                                            //file Folder
                                            requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                                            string file_folder = "Content-Disposition: form-data; name=\"" + $"FileInfos[{file_index}].Folder" + "\"\r\n\r\n" + file.Folder;
                                            byte[] file_folder_bytes = System.Text.Encoding.UTF8.GetBytes(file_folder);
                                            requestStream.Write(file_folder_bytes, 0, file_folder_bytes.Length);

                                            //file OriginalFileName
                                            requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                                            string orig_file_name = "Content-Disposition: form-data; name=\"" + $"FileInfos[{file_index}].OriginalFileName" + "\"\r\n\r\n" + file.OriginalFileName;
                                            byte[] orig_file_name_bytes = System.Text.Encoding.UTF8.GetBytes(orig_file_name);
                                            requestStream.Write(orig_file_name_bytes, 0, orig_file_name_bytes.Length);

                                            //file FileName
                                            requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                                            string file_name = "Content-Disposition: form-data; name=\"" + $"FileInfos[{file_index}].FileName" + "\"\r\n\r\n" + file.FileName;
                                            byte[] file_name_bytes = System.Text.Encoding.UTF8.GetBytes(file_name);
                                            requestStream.Write(file_name_bytes, 0, file_name_bytes.Length);

                                            //取得Content-Type
                                            var content_type = string.Empty;
                                            string ext = Path.GetExtension(file.File);
                                            using (Microsoft.Win32.RegistryKey registryKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext))
                                            {
                                                if (registryKey != null)
                                                {
                                                    var value = registryKey.GetValue("Content Type");
                                                    if (value != null)
                                                    {
                                                        content_type = value.ToString();
                                                    }
                                                }
                                            }

                                            if (file.File != null)
                                            {
                                                string file_header = "Content-Disposition: form-data; name=\"" + $"FileInfos[{file_index}].File" + "\"; filename=\"" + file.FileName + "\"\r\nContent-Type: " + content_type + "\r\n\r\n";
                                                byte[] file_header_bytes = System.Text.Encoding.UTF8.GetBytes(file_header);

                                                requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                                                requestStream.Write(file_header_bytes, 0, file_header_bytes.Length);
                                                byte[] buffer = new byte[32768];
                                                int bytesRead;

                                                // upload from file
                                                using (FileStream fileStream = File.OpenRead(file.File))
                                                {
                                                    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                                                        requestStream.Write(buffer, 0, bytesRead);
                                                    fileStream.Close();
                                                }
                                            }
                                        }
                                    }

                                    requestStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);
                                    requestStream.Close();
                                }
                            }

                            HttpWebResponse response = (HttpWebResponse)Postrequest.GetResponse();
                            var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
                            var statusNumber = (int)response.StatusCode;
                            if (statusNumber != 200)
                            {
                                logger.LogError("【DataDeliveryJob】【派送失敗】[棟別IP]:{0}", task.Target_ip);
                                logger.LogError("【DataDeliveryJob】【派送失敗】[response]:{0}", responseString);
                                updateObj["@repeat_times"] = task.Repeat_times + 1;
                                updateObj["@fail_reason"] = responseString;

                                insertLog["@log_level"] = $@"ERR";
                                insertLog["@log_content"] = $@"派送失敗 - [失敗原因]:{responseString}";
                                await backgroundServiceRepository.AddOneByCustomTable(insertLog, "background_service_task_log");
                            }
                            else
                            {
                                //解析回傳內容
                                var final = JObject.Parse(responseString);
                                var code = final["code"].ToString();
                                if (code == "0000")
                                {
                                    logger.LogInformation("【DataDeliveryJob】【派送成功】[棟別IP]:{0}", task.Target_ip);
                                    updateObj["@repeat_times"] = task.Repeat_times;
                                    updateObj["@is_complete"] = 1;
                                    updateObj["@complete_at"] = DateTime.Now;

                                    insertLog["@log_level"] = $@"INFO";
                                    insertLog["@log_content"] = $@"派送成功";
                                    await backgroundServiceRepository.AddOneByCustomTable(insertLog, "background_service_task_log");
                                }
                                else
                                {
                                    logger.LogError("【DataDeliveryJob】【派送失敗】[棟別IP]:{0}", task.Target_ip);
                                    logger.LogError("【DataDeliveryJob】【派送失敗】[response]:{0}", responseString);
                                    updateObj["@repeat_times"] = task.Repeat_times + 1;
                                    updateObj["@fail_reason"] = responseString;

                                    insertLog["@log_level"] = $@"ERR";
                                    insertLog["@log_content"] = $@"派送失敗 - [失敗原因]:{responseString}";
                                    await backgroundServiceRepository.AddOneByCustomTable(insertLog, "background_service_task_log");
                                }
                            }
                        }
                        catch (Exception exception)
                        {
                            logger.LogError("【DataDeliveryJob】【派送失敗】[棟別IP]:{0}", task.Target_ip);
                            logger.LogError("【DataDeliveryJob】【派送失敗】[Exception]:{0}", exception.ToString());
                            updateObj["@repeat_times"] = task.Repeat_times + 1;
                            updateObj["@fail_reason"] = exception.ToString();

                            insertLog["@log_level"] = $@"ERR";
                            insertLog["@log_content"] = $@"派送失敗 - [失敗原因(Exception)]:{exception.ToString()}";
                            await backgroundServiceRepository.AddOneByCustomTable(insertLog, "background_service_task_log");
                        }

                        updateObjs.Add(updateObj);
                    }

                    await backgroundServiceRepository.UpdateListByCustomTable(updateObjs, "background_service_task", "id = @Id");
                    logger.LogInformation("【DataDeliveryJob】【任務完成】");

                    insertLog["@task_id"] = 0;
                    insertLog["@log_level"] = $@"INFO";
                    insertLog["@log_content"] = $@"任務完成";
                    await backgroundServiceRepository.AddOneByCustomTable(insertLog, "background_service_task_log");
                }
            }
            catch (Exception exception)
            {
                logger.LogError("【DataDeliveryJob】【任務失敗】");
                logger.LogError("【DataDeliveryJob】【任務失敗】[Exception]:{0}", exception.ToString());

                insertLog["@task_id"] = 0;
                insertLog["@log_level"] = $@"ERR";
                insertLog["@log_content"] = $@"任務失敗";
                await backgroundServiceRepository.AddOneByCustomTable(insertLog, "background_service_task_log");
            }

        }
    }
}