using FluentScheduler;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Performance.DtoModels;
using Performance.DtoModels.AppSettings;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Services;
using Performance.Services.ExtractExcelService;
using Performance.Services.Queues;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Performance.Api
{
    public class BackgroundJob : IJob
    {
        private readonly IServiceScopeFactory _serviceScopeFactory;
        private readonly ILogger<BackgroundJob> _logger;
        private readonly BackgroundSetting[] _settings;

        public BackgroundJob(
            ILogger<BackgroundJob> logger,
            IServiceScopeFactory serviceScopeFactory)
        {
            _serviceScopeFactory = serviceScopeFactory;
            _logger = logger;

            _settings = new BackgroundSetting[]
            {
                new BackgroundSetting { JobType = Background.JobType.提取数据, MaxThread = 1, Timeout = 90 },
                new BackgroundSetting { JobType = Background.JobType.生成测算表, MaxThread = 2, Timeout = 20 },
                new BackgroundSetting { JobType = Background.JobType.报表, MaxThread = 5, Timeout = 20 },
                new BackgroundSetting { JobType = Background.JobType.自定义抽取, MaxThread = 1, Timeout = 90 },
            };
        }


        public void Execute()
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var service = scope.ServiceProvider.GetService<TaskService>();

                var tasks = service.GetTasks();
                if (tasks == null || tasks.Count == 0)
                    return;

                tasks = tasks.OrderBy(w => w.ID).ToList();

                Timeout(service, tasks);
                Repeat(service, tasks);

                foreach (var jobType in EnumHelper.GetItems<Background.JobType>().Select(w => w.Value))
                {
                    var sett = _settings.FirstOrDefault(w => (int)w.JobType == jobType);
                    if (sett != null && tasks.Count(w => w.JobType == jobType && (int)Background.Status.执行中 == w.Status) >= sett.MaxThread)
                    {
                        continue;
                    }

                    #region 相同参数同时只允许一个执行
                    bg_task task = null;

                    foreach (var item in tasks.Where(w => w.JobType == jobType && w.Status == (int)Background.Status.等待))
                    {
                        if (!tasks.Any(w => w.JobType == jobType && (int)Background.Status.执行中 == w.Status && w.Argument == item.Argument))
                        {
                            task = item;
                            break;
                        }
                    }
                    if (task == null)
                    {
                        continue;
                    } 
                    #endregion

                    switch (task.JobType)
                    {
                        case (int)Background.JobType.生成测算表:
                            Execute_Allot_Generate(service, task);
                            break;
                        case (int)Background.JobType.提取数据:
                            Execute_Allot_ExtractData(service, task);
                            break;
                        case (int)Background.JobType.报表:
                            Execute_Allot_Generate_Report(service, task);
                            break;
                        case (int)Background.JobType.自定义抽取:
                            Execute_Allot_CustomExtract(service, task);
                            break;
                        default:
                            service.Update(task.ID, Background.Status.无效);
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// 生成测算表
        /// </summary>
        /// <param name="service"></param>
        /// <param name="task"></param>
        private void Execute_Allot_Generate(TaskService service, bg_task task)
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var allotService = scope.ServiceProvider.GetService<AllotService>();

                if (int.TryParse(task.Argument, out int allotId))
                {
                    var allot = allotService.GetAllot(allotId);
                    if (allot == null)
                    {
                        service.Update(task.ID, Background.Status.无效);
                        return;
                    }
                    try
                    {
                        service.Update(task.ID, Background.Status.执行中);

                        Stopwatch stopwatch = Stopwatch.StartNew();
                        allotService.Generate(allot);
                        stopwatch.Stop();

                        service.Update(task.ID, Background.Status.完成, seconds: stopwatch.Elapsed.TotalSeconds);
                    }
                    catch (Exception ex)
                    {
                        service.Update(task.ID, Background.Status.失败, ex.ToString());
                    }
                    allotService.GenerateReport(allot);
                }

            }
        }
        /// <summary>
        /// 生成报表
        /// </summary>
        /// <param name="service"></param>
        /// <param name="task"></param>
        private void Execute_Allot_Generate_Report(TaskService service, bg_task task)
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var allotService = scope.ServiceProvider.GetService<AllotService>();
                try
                {
                    if (int.TryParse(task.Argument, out int allotId))
                    {

                        var allot = allotService.GetAllot(allotId);
                        if (allot == null)
                        {
                            service.Update(task.ID, Background.Status.无效);
                            return;
                        }

                        service.Update(task.ID, Background.Status.执行中);

                        Stopwatch stopwatch = Stopwatch.StartNew();
                        allotService.GenerateReport(allot);
                        stopwatch.Stop();

                        service.Update(task.ID, Background.Status.完成, seconds: stopwatch.Elapsed.TotalSeconds);
                    }
                }
                catch (Exception ex)
                {
                    service.Update(task.ID, Background.Status.失败, ex.ToString());
                }
            }
        }

        /// <summary>
        /// 提取绩效数据
        /// </summary>
        /// <param name="service"></param>
        /// <param name="task"></param>
        private void Execute_Allot_CustomExtract(TaskService service, bg_task task)
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var scopedServices = scope.ServiceProvider.GetRequiredService<CustomExtractService>();
                var allotService = scope.ServiceProvider.GetRequiredService<AllotService>();
                var scopedQueue = scope.ServiceProvider.GetRequiredService<IHubNotificationQueue>();

                try
                {
                    if (!string.IsNullOrEmpty(task.Argument))
                    {
                        var argument = JsonHelper.Deserialize<Dictionary<string, object>>(task.Argument);
                        var allotId = argument.GetValue<int>("allotid");
                        var userId = argument.GetValue<int>("userid");

                        var allot = allotService.GetAllot(allotId);
                        if (allot == null)
                        {
                            service.Update(task.ID, Background.Status.无效);
                            return;
                        }

                        service.Update(task.ID, Background.Status.执行中);

                        Stopwatch stopwatch = Stopwatch.StartNew();

                        if (scopedServices.ExtractData(userId, allotId, out string resultFilePath))
                        {
                            allotService.UpdateAllotCustomExtractPath(allotId, resultFilePath);
                            scopedQueue.Send(new Notification(allotId, "CustomDowoload", new CustomDownloadContent("自定义数据提取数据成功，是否立即下载", allotId)));
                        }
                        else
                        {
                            scopedQueue.Send(new Notification(allotId, "ReceiveMessage", new TextContent("自定义数据提取数据失败", NotificationLevel.ERR)));
                        }

                        stopwatch.Stop();

                        service.Update(task.ID, Background.Status.完成, seconds: stopwatch.Elapsed.TotalSeconds);

                    }
                }
                catch (Exception ex)
                {
                    service.Update(task.ID, Background.Status.失败, ex.ToString());
                }
            }
        }

        private void Execute_Allot_ExtractData(TaskService service, bg_task task)
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var allotService = scope.ServiceProvider.GetService<AllotService>();
                var scopedServices = scope.ServiceProvider.GetRequiredService<ExtractService>();
                var url = scope.ServiceProvider.GetRequiredService<IOptions<WebapiUrl>>();
                try
                {
                    if (!string.IsNullOrEmpty(task.Argument))
                    {
                        var argument = JsonHelper.Deserialize<Dictionary<string, object>>(task.Argument);
                        var allotId = argument.GetValue<int>("allotid");
                        var hospitalId = argument.GetValue<int>("hospitalid");
                        var isSingle = argument.GetValue<bool>("issingle");
                        var filePath = argument.GetValue<string>("filepath");

                        var allot = allotService.GetAllot(allotId);
                        if (allot == null)
                        {
                            service.Update(task.ID, Background.Status.无效);
                            return;
                        }

                        service.Update(task.ID, Background.Status.执行中);

                        Stopwatch stopwatch = Stopwatch.StartNew();

                        try
                        {
                            allot.IsExtracting = 1;
                            allot.ExtractTime = DateTime.Now;
                            allotService.Update(allot);

                            if (isSingle)
                            {
                                _logger.LogInformation("同一项目中进行提取");
                                _logger.LogInformation("提取绩效数据参数:" + JsonHelper.Serialize(new { allotId = allot.ID, hospitalId = allot.HospitalId }));
                                scopedServices.Main(allot.ID, allot.HospitalId, "", allot.ID.ToString(), filePath, isSingle);
                            }
                            else
                            {
                                var http = new RestSharpHelper();
                                var extractUrl = http.SetUrl(url.Value.HttpPost, "extract/extract");

                                var obj = new ExtractRequest
                                {
                                    AllotId = allotId,
                                    HospitalId = hospitalId,
                                    Email = ""
                                };

                                string json = JsonHelper.Serialize(obj);
                                _logger.LogInformation("提取绩效数据参数:" + json);
                                var parameter = JsonHelper.Deserialize<Dictionary<string, object>>(json);
                                var restRequest = string.IsNullOrEmpty(filePath) ? http.CreatePostRequest(json) : http.CreateFileRequest(new string[] { filePath }, parameter);
                                http.GetResponse(extractUrl, restRequest);

                            }
                        }
                        catch (Exception ex)
                        {
                            if (allot != null)
                            {
                                allot.IsExtracting = 3;
                                allotService.Update(allot);
                            }
                            _logger.LogError("提取绩效数据失败:" + ex.ToString());
                        }


                        stopwatch.Stop();

                        service.Update(task.ID, Background.Status.完成, seconds: stopwatch.Elapsed.TotalSeconds);
                    }
                }
                catch (Exception ex)
                {
                    service.Update(task.ID, Background.Status.失败, ex.ToString());
                }
            }
        }
        /// <summary>
        /// 超时关闭
        /// </summary>
        /// <param name="service"></param>
        /// <param name="tasks"></param>
        private void Timeout(TaskService service, List<bg_task> tasks)
        {
            foreach (var task in tasks)
            {
                var sett = _settings.FirstOrDefault(w => (int)w.JobType == task.JobType);
                if (task.Status == (int)Background.Status.执行中 && task.CreateTime.AddMinutes(sett.Timeout) < DateTime.Now)
                    service.Update(task.ID, Background.Status.超时);
            }
        }
        /// <summary>
        /// 重复任务仅执行最后异常
        /// </summary>
        /// <param name="service"></param>
        /// <param name="tasks"></param>
        private void Repeat(TaskService service, List<bg_task> tasks)
        {
            var gps = tasks
                .Where(w => w.Status == (int)Background.Status.等待)
                .GroupBy(w => new { w.JobType, w.Argument });

            foreach (var item in gps)
            {
                foreach (var task in item)
                {
                    if (task.Status == (int)Background.Status.等待 && task.ID != item.Max(w => w.ID))
                        service.Update(task.ID, Background.Status.无效, "任务重复");
                }
            }
        }

        class BackgroundSetting
        {
            public Background.JobType JobType { get; internal set; }
            public int MaxThread { get; internal set; }
            public int Timeout { get; internal set; }
        }
    }
}
