using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.BLL.Domain.Mq;
using PARR.BLL.Helpers;
using PARR.Constants;
using PARR.DAL.Contracts;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces;
using PARR.TemplateDistributor;

namespace PARR.GeneratorTemplates.Services
{
    internal class TemplateManager : ITemplateManager
    {
        private readonly ITemplateService templateService;
        private readonly IHostService hostService;
        private readonly ILogger<TemplateManager> logger;
        private readonly SettingsFromDb settingsFromDb;
        private readonly IRobotConfigurationService robotConfigurationService;
        private readonly IApplicationsInWorkService applicationsInWorkService;
        private readonly ITemplateDistributor templateDistributor;

        public TemplateManager(
            ITemplateService templateService,
            IHostService hostService,
            ILogger<TemplateManager> logger,
            SettingsFromDb settingsFromDb,
            IRobotConfigurationService robotConfigurationService,
            IApplicationsInWorkService applicationsInWorkService,
            ITemplateDistributor templateDistributor
            )
        {
            this.templateService = templateService;
            this.hostService = hostService;
            this.logger = logger;
            this.settingsFromDb = settingsFromDb;
            this.robotConfigurationService = robotConfigurationService;
            this.applicationsInWorkService = applicationsInWorkService;
            this.templateDistributor = templateDistributor;
        }

        public async Task CreateTemplates(GeneratorTemplateMq query)
        {
            //TODO Migratin to Job
            ////В таблице AppInWork находим РР, и смотрим ее рабочие группы. Затем по ним будем фильтровать хосты
            //var appInWorkWg = await applicationsInWorkService.Get()
            //                            .Include(t => t.WorkGroups)
            //                            .FirstAsync(t => t.ApplicationId == query.ApplicationId && t.WorkId == query.WorkId);

            //if (!appInWorkWg.WorkGroups.Any())
            //{
            //    logger.LogWarning($"Не генерирую шаблоны. Нет рабочих групп у ApplicationInWork с id: {appInWorkWg.Id}");
            //    return;
            //}

            //var workGroups = appInWorkWg.WorkGroups.ToList();

            ////Согласно параметрам, находит все ЭК в таблице Hosts.
            ////Далее проверяет есть ли такие шаблоны в таблице Templates, если нет, то создает, активирует и ставит статус Creating.
            ////Если такие шаблоны уже есть, ничего с ними не делает.

            //string ekPattern = SqlHelpers.RegexToLike(query.Ek);

            //// список хостов для создания шаблонов
            //var queryHosts = hostService.Get()
            //                //.Include(t => t.WorkGroupItem)
            //                .Include(t => t.ApplicationsInHosts)
            //                    .ThenInclude(t => t.Application).ThenInclude(t => t!.ApplicationsInWorks).ThenInclude(w => w.Work)
            //                .Include(t => t.Templates)
            //                    .ThenInclude(w => w.ApplicationsInWork)
            //                .Include(t => t.ResponseArea)
            //                .Where(t =>
            //                    t.ApplicationsInHosts.Any(h =>
            //                            h.ApplicationId == query.ApplicationId
            //                            && h.Application!.ApplicationsInWorks.Any(w => w.WorkId == query.WorkId)
            //                        )
            //                    && EF.Functions.Like(t.Ek.ToLower(), ekPattern)
            //                    // смотрим что нет такого шаблона
            //                    && t.Templates.Any(templ => templ.ApplicationsInWork!.WorkId == query.WorkId && templ.ApplicationsInWork.ApplicationId == query.ApplicationId) == false
            //                    // смотрим что у этих хостов есть нужные группы
            //                    //&& workGroups.Any(x => x.WorkGroupId == t.WorkGroupId)
            //                    );

            //// статус ЭК
            //if (query.StatusEk > 0)
            //    queryHosts = queryHosts.Where(t => t.EkStatusCode == query.StatusEk);

            //var hosts = await queryHosts
            //                    .AsSplitQuery()
            //                    .ToListAsync();

            //// Исключаем хосты у которых нет нужных групп
            //hosts = hosts.Where(t => workGroups.Any(x => x.WorkGroupId == t.WorkGroupId)).ToList();
            //if (!hosts.Any())
            //{
            //    logger.LogInformation($"Не найдено ЭК попадающих под условие: ${JsonSerializer.Serialize(query)}");
            //    return;
            //}


            //// хосты группируем по РГ, чтоб потом распределить/рассчитать NextRun
            //var groupingHostByWg = hosts.GroupBy(t => t.WorkGroupId);

            //foreach (var group in groupingHostByWg)
            //{
            //    var groupingHosts = group.ToList();

            //    var templates = CreateQueryTemplates(groupingHosts, query);

            //    //проверяем, что нет шаблонов с такими именами, исключаем их
            //    templates = await ExcludeExistTemplatesAsync(templates);
            //    if (!templates.Any())
            //        continue;

            //    //заполняем NextRun
            //    templates = await templateDistributor.DistributeTemplateAsync(templates, appInWorkWg.Id, (Guid)group.Key!);

            //    // сохраняем
            //    foreach (var template in templates)
            //    {
            //        if (!await templateService.CreateAsync(template) || !await templateService.CommitAsync(query.HistoryInitiator))
            //        {
            //            logger.LogError($"Ошибка при создании шаблона: Name: {template.Name}, ApplicationInWorkId: {template.ApplicationInWorkId}, HostId: {template.HostId}");
            //        }
            //        else
            //        {
            //            logger.LogInformation($"Создан шаблон: Name: {template.Name}, ApplicationInWorkId: {template.ApplicationInWorkId}, " +
            //                $"HostId: {template.HostId}");
            //        }
            //    }
            //}

            #region создавали шаблоны до того, как стали группировать по РГ
            ////Создаем шаблоны
            //foreach (var host in hosts)
            //{
            //    var appInWork = host.ApplicationsInHosts.FirstOrDefault(t => t.ApplicationId == query.ApplicationId)?.Application?.ApplicationsInWorks.FirstOrDefault(a => a.WorkId == query.WorkId);

            //    if (appInWork == null)
            //    {
            //        // такого не может быть, так как выполнился запрос при получении списка хостов, но мало ли
            //        logger.LogError($"Ошибка при создании шаблона. В таблице ApplicationsInWorks не найден Id согласно запроса WorkId: {query.WorkId}, ApplicationId: {query.ApplicationId}");
            //        return;
            //    }

            //    var template = new Template
            //    {
            //        Id = Guid.NewGuid(),
            //        //Name = TemplateHelpers.GenerateTemplateName("ЗО" ,settingsFromDb.TemplatePrefixName, host.Ek, appInWork.Work!.Name),
            //        //Генерить имя с полным именем работы и подстановкой ЭК
            //        //Name = TemplateHelpers.GenerateTemplateNameWithResponseArea(host.ResponseArea!.Name, settingsFromDb.TemplatePrefixName, host.Ek, appInWork.Work!.Name.ApplyShortcode(Constants.Shortcode.ShortcodeEnum.EK, host.Ek)),
            //        //Вместо полного имени работы, подставляется сокращенный суффикс или полное имя с ЭК если суффикса нет
            //        Name = TemplateHelpers.GenerateTemplateNameWithResponseArea(
            //            host.ResponseArea!.Name,
            //            settingsFromDb.TemplatePrefixName,
            //            host.Ek,
            //            appInWork.Work!.TemplateSuffix ?? appInWork.Work!.Name.ApplyShortcode(Constants.Shortcode.ShortcodeEnum.EK, host.Ek)
            //            ),
            //        IsActiveTemplate = query.IsActiveTemplate ?? false,
            //        IsActiveSchedule = query.IsActiveSchedule ?? false,
            //        ApplicationInWorkId = appInWork.Id,
            //        HostId = host.Id,
            //        //TODO:
            //        //NextRun = ???,
            //        LastRun = null
            //    };

            //    if (!await templateService.CreateAsync(template) || !await templateService.CommitAsync())
            //    {
            //        logger.LogError($"Ошибка при создании шаблона: Name: {template.Name}, ApplicationInWorkId: {template.ApplicationInWorkId}, HostId: {template.HostId}");
            //    }
            //    else
            //    {
            //        logger.LogInformation($"Создан шаблон: Name: {template.Name}, ApplicationInWorkId: {template.ApplicationInWorkId}, " +
            //            $"HostId: {template.HostId}");
            //    }
            //}
            #endregion
        }

        /// <summary>
        /// Исключаем существующие шаблоны из запроса на создание
        /// </summary>
        /// <param name="templates"></param>
        /// <returns></returns>
        private async Task<List<Template>> ExcludeExistTemplatesAsync(List<Template> templates)
        {
            // то ли костыль, то ли нет. Иногда попадают отфильтрованные шаблоны для создания, но они уже есть в БД. На всякий проверяем есть они или нет, и если есть, исключаем их из запроса.

            // ищмем по имени такие шаблоны в БД и исключаем их
            var existTemplates = await templateService.Get().Where(t => templates.Select(x => x.Name).Any(x => x == t.Name)).ToListAsync();

            if (existTemplates.Any())
            {
                existTemplates.ForEach(item => logger.LogWarning($"Исключаем из запроса на создание шаблон, так как он уже есть в БД: {item.Name}"));
                templates = templates.Where(t => !existTemplates.Any(x => x.Name == t.Name)).ToList();
            }

            return templates;
        }

        private List<Template> CreateQueryTemplates(List<Host> hosts, GeneratorTemplateMq query)
        {
            var templates = new List<Template>();

            //foreach (var host in hosts)
            //{
            //    var appInWork = host.ApplicationsInHosts.FirstOrDefault(t => t.ApplicationId == query.ApplicationId)?.Application?.ApplicationsInWorks.FirstOrDefault(a => a.WorkId == query.WorkId);

            //    if (appInWork == null)
            //    {
            //        // такого не может быть, так как выполнился запрос при получении списка хостов, но мало ли
            //        logger.LogError($"Ошибка при создании шаблона. В таблице ApplicationsInWorks не найден Id согласно запроса WorkId: {query.WorkId}, ApplicationId: {query.ApplicationId}");
            //        //return;
            //        return templates;
            //    }

            //    var template = new Template
            //    {
            //        Id = Guid.NewGuid(),
            //        //Name = TemplateHelpers.GenerateTemplateName("ЗО" ,settingsFromDb.TemplatePrefixName, host.Ek, appInWork.Work!.Name),
            //        //Генерить имя с полным именем работы и подстановкой ЭК
            //        //Name = TemplateHelpers.GenerateTemplateNameWithResponseArea(host.ResponseArea!.Name, settingsFromDb.TemplatePrefixName, host.Ek, appInWork.Work!.Name.ApplyShortcode(Constants.Shortcode.ShortcodeEnum.EK, host.Ek)),
            //        //Вместо полного имени работы, подставляется сокращенный суффикс или полное имя с ЭК если суффикса нет
            //        Name = TemplateHelpers.GenerateTemplateNameWithResponseArea(
            //            host.ResponseArea!.Name,
            //            settingsFromDb.TemplatePrefixName,
            //            host.Ek,
            //            appInWork.Work!.TemplateSuffix ?? appInWork.Work!.Name
            //            ),
            //        IsActiveTemplate = query.IsActiveTemplate ?? false,
            //        IsActiveSchedule = query.IsActiveSchedule ?? false,
            //        JobId = appInWork.Id,//TODO Migratin to Job
            //        UnitId = host.Id,//TODO Migratin to Job
            //        // NextRun не задаем значение, его рассчитаем позже
            //        //NextRun = ???,
            //        LastRun = null
            //    };

            //    templates.Add(template);
            //}

            return templates;
        }



        public async Task DeactivateTemplates(GeneratorTemplateMq query)
        {
            //TODO: !!! тут не учитываются поля query.IsActiveTemplate, query.IsActiveSchedule, просто все деактивируется. !!! 

            //В таблице AppInWork находим РР, и смотрим ее рабочие группы. Затем по ним будем фильтровать хосты
            var appInWorkWg = await applicationsInWorkService.Get()
                                        .Include(t => t.WorkGroups)
                                        .FirstAsync(t => t.ApplicationId == query.ApplicationId && t.WorkId == query.WorkId);

            if (!appInWorkWg.WorkGroups.Any())
            {
                logger.LogWarning($"Не деактивируем шаблоны. Нет рабочих групп у ApplicationInWork с id: {appInWorkWg.Id}");
                return;
            }

            var workGroups = appInWorkWg.WorkGroups.ToList();


            //Ищет все активные шаблоны согласно параметрам в таблице Templates, и деактивирует их. Меняем на статус Updating

            // делаем sql формат для like из запроса.
            string ekPattern = SqlHelpers.RegexToLike(query.Ek);

            var queryTemplates = templateService.Get()
                .Include(t => t.Unit)//TODO Migratin to Job
                .Include(t => t.Job)//TODO Migratin to Job
                .Include(t => t.RobotConfigurations)
                .Where(t =>
                        (t.IsActiveTemplate == true || t.IsActiveSchedule == true)
                        //&& t.ApplicationsInWork!.ApplicationId == query.ApplicationId//TODO Migratin to Job
                        //&& t.ApplicationsInWork!.WorkId == query.WorkId//TODO Migratin to Job
                        && EF.Functions.Like(t.Unit!.Name.ToLower(), ekPattern)
                        );

            // статус ЭК
            if (query.StatusEk > 0)
                queryTemplates = queryTemplates;//.Where(t => t.Unit!.BaseFields.Status == query.StatusEk);//TODO Migratin to Job

            var existTemplates = await queryTemplates
                                        .AsSplitQuery()
                                        .ToListAsync();

            // Исключаем хосты у которых нет нужных групп
            existTemplates = existTemplates.Where(t => workGroups.Any(x => x.WorkGroup.Name == t.Unit.BaseFields.WorkGroup)).ToList();

            if (!existTemplates.Any())
            {
                logger.LogInformation("Нет шаблонов для деактивации");
                return;
            }

            existTemplates.ForEach(item =>
            {
                item.DateModified = DateTimeOffset.UtcNow;

                if (item.IsActiveTemplate)
                {
                    item.IsActiveTemplate = false;
                    //необходимо обновить шаблон
                    var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.TemplateOrder, item);
                    robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config);
                }

                if (item.IsActiveSchedule)
                {
                    item.IsActiveSchedule = false;
                    //необходимо обновить расписание
                    var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.ScheduleOrder, item);
                    robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config);
                }
            });

            if (await templateService.CommitAsync(query.HistoryInitiator))
                existTemplates.ForEach(item => logger.LogInformation($"Деактивирован шаблон: {item.Name}"));
            else
                existTemplates.ForEach(item => logger.LogError($"Ошибка при дактивации шаблона: {item.Name}"));
        }
    }
}
