using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Extensions;
using PARR.Core.Repositories.Interfaces;
using PARR.Core.Repositories.Interfaces.Job;
using PARR.Core.Repositories.Interfaces.Unit;
using PARR.Core.Services.NextRunServices;
using PARR.Domain.Common.Rabbit.Messages;
using PARR.Domain.Entities;
using PARR.Domain.Enums;

namespace PARR.TemplateUpdater.Services
{
    internal class TemplateUpdaterService : ITemplateUpdaterService
    {
        private readonly ILogger<TemplateUpdaterService> logger;
        private readonly ITemplateRepository templateService;
        private readonly IJobRepository jobService;
        private readonly IUnitRepository unitService;
        private readonly IRobotConfigurationRepository robotConfigurationService;
        private readonly INextRunService nextRunService;

        public TemplateUpdaterService(
                ILogger<TemplateUpdaterService> logger,
                ITemplateRepository templateService,
                IJobRepository jobService,
                IUnitRepository unitService,
                IRobotConfigurationRepository robotConfigurationService,
                INextRunService nextRunService
            )
        {
            this.logger = logger;
            this.templateService = templateService;
            this.jobService = jobService;
            this.unitService = unitService;
            this.robotConfigurationService = robotConfigurationService;
            this.nextRunService = nextRunService;
        }


        public async Task UpdateTemplateAsync(TemplateUpdaterMq query)
        {
            var isValid = await IsValidAsync(query);
            if (!isValid)
            {
                logger.LogError("Не валидны входные данные. Конец обработки сообщения {Query}", 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 {TemplateId}", 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.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;
            }

            //Для правильного расчета shortcod'ов в БД должны быть сохранены данные UnitInTemplates, JobId, UnitId и т.д.
            if (templateIsChanged && !await templateService.CommitAsync(query.Initiator))
            {
                logger.LogError("Не удалось сохранить изменения в БД. {QueryType}: {Query}", nameof(TemplateUpdaterMq), query.ToJson());
                return;
            }

            var nextRun = await nextRunService.GetNextRunForTemplateAsync(template.Id, query.IsNew);
            if (!nextRun.HasValue)
            {
                logger.LogError("Ошибка при расчете нового nextRun (вернулся null) для шаблона {templateId}, {templateName}", template.Id, template.Name);
                return;
            }

            if (template.NextRun != nextRun.Value)
            {
                template.LastRun = template.NextRun;
                template.NextRun = nextRun.Value;
                scheduleIsChanged = true;
            }

            // === конец обработки UnitsInTemplate ===

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

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

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

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


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

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


        /// <summary>
        /// Валидация входящего сообщения
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        private async Task<bool> IsValidAsync(TemplateUpdaterMq query)
        {
            var template = await templateService.Get().AsNoTracking()
                                    .Include(t => t.RobotConfigurations)
                                    .FirstOrDefaultAsync(t => t.Id == query.TemplateId);

            if (template == null)
            {
                logger.LogError("Сообщение не валидно. Не найден шаблон с id: {TemplateId}", query.TemplateId);
                return false;
            }

            //if (template.ScheduleEsppId == null)
            //{
            //    logger.LogError("Сообщение не валидно. Номер расписания не может быть null, робот не сможет обновить шаблон: {TemplateName}", template.Name);
            //    return false;
            //}

            var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.TemplateOrder, template);
            if (config == null)
            {
                logger.LogError("Сообщение не валидно. Не создана конфигурация роботов: {TemplateName}", template.Name);
                return false;
            }

            //if (config.TaskStatusCode != (int)TaskStatusEnum.Ok)
            //{
            //    logger.LogError("Сообщение не валидно. Не закончено создание или предыдущее обновление - нельзя начинать новое изменение шаблона: {TemplateName}", template.Name);
            //    return false;
            //}

            if (string.IsNullOrEmpty(query.Name?.Trim()))
            {
                logger.LogError("Сообщение не валидно. Имя шаблона не может быть пустым: {TemplateName}", 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("Сообщение не валидно. Имя шаблона не уникально: {TemplateName}", query.Name);
                return false;
            }

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

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

            return true;
        }
    }
}