using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using PARR.BLL.Services.Interfaces;
using PARR.Common.Domain;
using PARR.Constants;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces;
using PARR.DAL.Services.Interfaces.Job;
using PARR.DAL.TransformServices;
using PARR.NextRun.Settings;

namespace PARR.NextRun
{
    internal class NextRunManager : INextRunManager
    {
        private readonly WorkerSettings workerSettings;
        private readonly ILogger<NextRunManager> logger;
        private readonly IIntervalService intervalService;
        private readonly IServiceProvider serviceProvider;

        public NextRunManager(
            WorkerSettings workerSettings,
            ILogger<NextRunManager> logger,
            IIntervalService intervalService,
            IServiceProvider serviceProvider
            )
        {
            this.workerSettings = workerSettings;
            this.logger = logger;
            this.intervalService = intervalService;
            this.serviceProvider = serviceProvider;
        }

        public async Task StartAsync()
        {
            logger.LogInformation("Запуск сервиса рассчета времени следующего создания наряда (nextRun)");

            await intervalService.IntervalInitAsync(async () =>
            {
                using (var scope = serviceProvider.CreateScope())
                {
                    //var appInWorkService = scope.ServiceProvider.GetService<IApplicationsInWorkService>();
                    var esppSchService = scope.ServiceProvider.GetService<IEsppScheduleTransformService>();
                    var templateService = scope.ServiceProvider.GetService<ITemplateService>();
                    var jobGroupService = scope.ServiceProvider.GetService<IJobGroupService>();

                    if (templateService == null || esppSchService == null || jobGroupService == null)
                        throw new Exception($"Не смог получить серивс {nameof(ITemplateService)} или {nameof(IEsppScheduleTransformService)} или {nameof(IJobGroupService)}");

                    await HandlerAsync(templateService, esppSchService, jobGroupService);
                }
            }, workerSettings.RepeatEvery);
        }


        /// <summary>
        /// Рассчет следующей даты срабатываения
        /// </summary>
        /// <returns></returns>
        private async Task HandlerAsync(ITemplateService templateService, IEsppScheduleTransformService esppScheduleTransformService, IJobGroupService jobGroupService)
        {
            #region логика для групп у которых не включено автораспределение

            // выбираем шаблоны которые не учавствуют в автораспределении
            // и у которых IsUnuser==false
            // затем группируем по GroupId, так как nextRun и расписание настраивается для группы, то для всех дочерних шаблонов, nextRun будет одинаковым


            var groups = await jobGroupService.Get()
                                    .Where(t => t.IsAutoDistributionEnabled == false)
                                    .Select(t => new { t.Id, t.ReferenceDate })
                                    .ToListAsync();

            logger.LogInformation("Найдено {GroupCount} групп для обработки.", groups.Count);

            foreach (var group in groups)
            {
                //получаем по каждой группе следующий nextRun и сравниваем с существующим, если не равны, то обновляем
                var nextRun = await esppScheduleTransformService.GetNextDateAsync(group.Id, group.ReferenceDate);


                // Загружаем только шаблоны текущей группы где nextRun в БД не равен расчетному
                var templatesToUpdate = await templateService.Get()
                                        .Where(t => t.StatusTypeId == TemplateStatusTypeEnum.Used && t.Job!.GroupId == group.Id && t.NextRun != nextRun)
                                        .ToListAsync();

                if (!templatesToUpdate.Any())
                    continue;

                // Обновляем
                foreach (var template in templatesToUpdate)
                {
                    template.LastRun = template.NextRun;
                    template.NextRun = nextRun;
                }

                // Коммитим изменения для этой группы
                if (await templateService.CommitAsync(new HistoryInitiator { InitiatorComment = "Обновлён NextRun", InitiatorParrComponentId = Constants.ParrComponentsEnum.NextRun }))
                    logger.LogInformation($"Обновлены значения полей NextRun для шаблонов группы: {group.Id}, " +
                        $"refDate: {group.ReferenceDate}. Обновлен nextRun: {nextRun} для шаблонов {templatesToUpdate.Count} шт.");
                else
                {
                    logger.LogError($"Ошибка при обновлении значений полей NextRun: {nextRun}, для шаблонов {templatesToUpdate.Count} шт., группы {group.Id}, refDate: {group.ReferenceDate} ");
                }
            }

            #endregion

            //todo: когда будет автораспределение, для него придумать логику как считать




            #region old logic
            //var templates = await templateService.Get()
            //                        .Include(t => t.Job)
            //                            .ThenInclude(t => t.Group)
            //                        .Where(t => t.NextRun < DateTimeOffset.UtcNow).ToListAsync();

            //logger.LogDebug($"Шаблонов для обновления: {templates.Count()} шт.");

            //int errorsCount = 0;

            //foreach (var template in templates)
            //{
            //    var nextRun = await esppScheduleTransformService.GetNextDateAsync(template.Job!.GroupId, template.NextRun);
            //    template.LastRun = template.NextRun;
            //    template.NextRun = nextRun;

            //    if (await templateService.CommitAsync(new HistoryInitiator { InitiatorComment = "Обновлён NextRun", InitiatorParrComponentId = Constants.ParrComponentsEnum.NextRun }))
            //        logger.LogDebug($"Обновлены значения полей NextRun: {template.NextRun}, LastRun: {template.LastRun} для шаблона {template.Name}, {template.Id}");
            //    else
            //    {
            //        logger.LogError($"Ошибка при обновлении значений полей NextRun: {template.NextRun}, LastRun: {template.LastRun} для шаблона {template.Name}, {template.Id}");
            //        errorsCount++;
            //    }
            //}

            //logger.LogInformation($"Завершено обновление полей NextRun. Полей в задании: {templates.Count()}, обновлено: {templates.Count() - errorsCount}, ошибок: {errorsCount}");

            #endregion

            logger.LogInformation($"Завершено обновление полей NextRun.");
        }
    }
}
