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

namespace PARR.TemplateUpdater.Services
{
    internal class TemplateUpdaterService : ITemplateUpdaterService
    {
        private readonly ILogger<TemplateUpdaterService> logger;
        private readonly ITemplateService templateService;
        private readonly IJobService jobService;
        private readonly IUnitService unitService;
        private readonly IRobotConfigurationService robotConfigurationService;

        public TemplateUpdaterService(
                ILogger<TemplateUpdaterService> logger,
                ITemplateService templateService,
                IJobService jobService,
                IUnitService unitService,
                IRobotConfigurationService robotConfigurationService
            )
        {
            this.logger = logger;
            this.templateService = templateService;
            this.jobService = jobService;
            this.unitService = unitService;
            this.robotConfigurationService = robotConfigurationService;
        }


        public async Task UpdateTemplateAsync(TemplateUpdaterMq query)
        {
            var isValid = await IsValidAsync(query);
            if (!isValid)
            {
                logger.LogError($"Не валидны входные данные. Конец обработки сообщения {query.ToJson()}");
                return;
            }

            var template = await templateService.Get()
                                    .Include(t => t.RobotConfigurations)
                                    .Include(t => t.UnitsInTemplate)
                                    .AsSplitQuery()
                                    .FirstOrDefaultAsync(t => t.Id == query.TemplateId);
            if (template == null)
            {
                logger.LogError($"Не найден шаблон с id {query.TemplateId}");
                return;
            }

            var templateIsChanged = false;
            var scheduleIsChanged = false;

            if (template.Name != query.Name.Trim())
            {
                template.Name = query.Name.Trim();
                templateIsChanged = true;
                scheduleIsChanged = true;
            }

            if (template.JobId != query.JobId)
            {
                template.JobId = query.JobId;
                templateIsChanged = true;
                scheduleIsChanged = true;
            }

            if (template.IsActiveTemplate != query.IsActiveTemplate)
            {
                template.IsActiveTemplate = query.IsActiveTemplate;
                templateIsChanged = true;
            }

            if (template.IsActiveSchedule != query.IsActiveSchedule)
            {
                template.IsActiveSchedule = query.IsActiveSchedule;
                scheduleIsChanged = true;
            }

            if (template.NextRun != query.NextRun)
            {
                template.NextRun = query.NextRun;
                scheduleIsChanged = true;
            }


            if (template.UnitId != query.UnitId)
            {
                template.UnitId = query.UnitId;
                templateIsChanged = true;
                scheduleIsChanged = true;
            }

            //просто обнволяем. не влияет ни на шаблон, ни на расписание
            template.LastRun = query.LastRun;
            template.Index = query.Index;
            template.StatusTypeId = query.StatusTypeId;

            // === Обработка изменения состава UnitsInTemplate ===
            var currentUnitIds = template.UnitsInTemplate.Select(u => u.UnitId).ToHashSet();
            var newUnitIds = query.UnitsInTemplate.ToHashSet();

            if (!currentUnitIds.SetEquals(newUnitIds))
            {
                // Удаляем старые связи
                var toRemove = template.UnitsInTemplate
                    .Where(u => !newUnitIds.Contains(u.UnitId))
                    .ToList();

                foreach (var item in toRemove)
                    template.UnitsInTemplate.Remove(item);

                // Добавляем новые связи
                var toAdd = newUnitIds.Except(currentUnitIds);
                foreach (var unitId in toAdd)
                {
                    template.UnitsInTemplate.Add(new UnitsInTemplate
                    {
                        TemplateId = template.Id,
                        UnitId = unitId,
                        DateCreated = DateTimeOffset.UtcNow
                    });
                }

                // Поскольку коллекция изменилась — шаблон считается изменённым
                templateIsChanged = true;
            }
            // === конец обработки UnitsInTemplate ===

            if (templateIsChanged)
            {
                // ставим задачу на обновление шаблона
                var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.TemplateOrder, template);
                robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config);

                logger.LogDebug($"Для шаблона {nameof(template.Id)}:{template.Id} устанавливаю статус {RobotStatusEnum.Wait.ToString()}");
            }

            if (scheduleIsChanged)
            {
                // ставим задачу на обновление расписания
                var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.ScheduleOrder, template);
                robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config);

                logger.LogDebug($"Для расписания {nameof(template.Id)}:{template.Id} устанавливаю статус {RobotStatusEnum.Wait.ToString()}");
            }


            if (!await templateService.CommitAsync(query.Initiator))
                logger.LogError($"Не удалось сохранить изменения в БД. {nameof(TemplateUpdaterMq)}: {query.ToJson()}.");

            logger.LogInformation($"Выполнено изменение шаблона в БД. Отправлен запрос на синхронизацию шаблона: {templateIsChanged}, расписания: {scheduleIsChanged} .Query {query.ToJson()}");
        }


        /// <summary>
        /// Валидация входящего сообщения
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        private async Task<bool> IsValidAsync(TemplateUpdaterMq query)
        {
            var template = await templateService.Get().AsNoTracking().AnyAsync(t => t.Id == query.TemplateId);
            if (!template)
            {
                logger.LogError($"Сообщение не валидно. Не найден шаблон с id: {query.TemplateId}");
                return false;
            }

            if (string.IsNullOrEmpty(query.Name.Trim()))
            {
                logger.LogError($"Сообщение не валидно. Имя шаблона не может быть пустым: {query.Name}");
                return false;
            }

            var existTemplateName = await templateService.Get().AsNoTracking().AnyAsync(t => 
                                                t.Id != query.TemplateId 
                                                && t.Name.ToUpper() == query.Name.Trim().ToUpper()
                                                && t.Index == query.Index);
            if (existTemplateName)
            {
                logger.LogError($"Сообщение не валидно. Имя шаблона не уникально: {query.Name}");
                return false;
            }

            var job = await jobService.Get().AsNoTracking().AnyAsync(t => t.Id == query.JobId);
            if (!job)
            {
                logger.LogError($"Сообщение не валидно. Не найдена работа с JobId: {query.JobId}");
                return false;
            }

            var unit = await unitService.Get().AsNoTracking().AnyAsync(t => t.Id == query.UnitId);
            if (!unit)
            {
                logger.LogError($"Сообщение не валидно. Не найден unit с UnitId: {query.UnitId}");
                return false;
            }

            return true;
        }
    }
}
