using InfluxDB.Client.Api.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces;
using PARR.DAL.Context;
using PARR.DAL.Repositories.Base;
using PARR.Domain.Entities;
using PARR.Domain.Entities.RobotEntities;
using PARR.Domain.Enums;
using PARR.Domain.Settings;

namespace PARR.DAL.Repositories
{
    internal class RobotConfigurationRepository : BaseRepository<RobotConfiguration>, IRobotConfigurationRepository
    {
        private readonly SettingsFromDb settingsFromDb;

        public RobotConfigurationRepository(DataContext dataContext, ILogger<RobotConfigurationRepository> logger, SettingsFromDb settingsFromDb) : base(logger, dataContext)
        {
            this.settingsFromDb = settingsFromDb;
        }


        public void ChangeTaskStatus(TaskStatusEnum taskStatus, RobotConfiguration configuration)
        {
            configuration.TaskStatusCode = (int)taskStatus;

            switch (taskStatus)
            {
                case TaskStatusEnum.Creating:
                    ChangeRobotStatus(RobotStatusEnum.Wait, configuration);
                    break;
                case TaskStatusEnum.Updating:
                    ChangeRobotStatus(RobotStatusEnum.Wait, configuration);
                    break;
                case TaskStatusEnum.Ok:
                    ChangeRobotStatus(RobotStatusEnum.Complete, configuration);
                    break;
                default:
                    break;
            }
        }


        public bool SetUpdateTaskStatusIfAllow(RobotConfiguration configuration)
        {
            var updatingStatus = TaskStatusEnum.Updating;

            // ставить статус Updating можно только если текущий статус == Ok
            if (configuration.TaskStatusCode != (int)TaskStatusEnum.Ok)
            {
                int taskStatusValue = configuration.TaskStatusCode;
                string taskStatusName = Enum.IsDefined(typeof(TaskStatusEnum), taskStatusValue)
                    ? ((TaskStatusEnum)taskStatusValue).ToString()
                    : $"Unknown ({taskStatusValue})";

                _logger.LogInformation("Нельзя установить статус {newStatus} для конфигурации {configurationId}, templateId: {templateId}, так как текущий статус {currentStatus}",
                    updatingStatus, configuration.Id, configuration.TemplateId, taskStatusName);
                return false;
            }

            // Если хотим поставить Updating у шаблона, можно ставить только в том случае, если у шаблона уже есть ScheduleEsppId
            if (configuration.RobotCode == (int)RobotsEnum.TemplateOrder)
            {
                // есть ли связь у config с templetes, может инклуда нет, мало ли 
                if (configuration.Template == null)
                {
                    _logger.LogWarning("При изменении статуса задания на обновление шаблона, не смог проверить наличае ScheduleEsppId, так как нет Include с Templates. Пропустил эту проверку. configurationId: {configurationId}", configuration.Id);
                }
                else
                {
                    if (configuration.Template.ScheduleEsppId == null)
                    {
                        _logger.LogInformation("Нельзя установить статус {newStatus} для конфигурации {configurationId}, templateId: {templateId}, так как у шаблона отсутсвтует ScheduleEsppId=null",
                            updatingStatus, configuration.Id, configuration.TemplateId);
                        return false;
                    }
                }
            }

            // Статус ОК, можно ставить Updating
            ChangeTaskStatus(updatingStatus, configuration);
            _logger.LogInformation("Установлен статус {newStatus} для конфигурации {configurationId}, templateId: {templateId}", updatingStatus, configuration.Id, configuration.TemplateId);

            return true;
        }


        public void ChangeRobotStatus(RobotStatusEnum robotStatus, RobotConfiguration configuration)
        {
            configuration.RobotStatusCode = (int)robotStatus;

            switch (robotStatus)
            {
                case RobotStatusEnum.InProgress:
                    configuration.AttemptsNumber++;
                    configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
                    break;
                case RobotStatusEnum.Error:
                    break;
                case RobotStatusEnum.Complete:
                    configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
                    break;
                case RobotStatusEnum.Wait:
                    configuration.LastRobotStatusUpdated = null;
                    configuration.AttemptsNumber = 0;
                    break;
                default:
                    break;
            }
        }

        public void SetErrorRobotStatusAndMaxAttempts(RobotConfiguration configuration)
        {
            ChangeRobotStatus(RobotStatusEnum.Error, configuration);

            configuration.AttemptsNumber = settingsFromDb.RobotAttemptsNumber;
            configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
        }


        public async Task<bool> SetInProgressStatusAsync(Guid id)
        {
            // Если задание все еще в статусе Wait (RobotStatusEnum.Wait), установить ему статус "InProgress"

            var endDate = DateTimeOffset.UtcNow.Add(-settingsFromDb.RobotWaitTime);

            // Выполняем атомарный UPDATE напрямую в базе данных
            var affectedRows = await EntitySet.Where(t => t.Id == id
                                            && (
                                                // Задания в статусе ожидание
                                                t.RobotStatusCode == (int)RobotStatusEnum.Wait
                                                || (
                                                    // Задание в статусе в работе, но они зависли
                                                    t.RobotStatusCode == (int)RobotStatusEnum.InProgress
                                                    && t.AttemptsNumber < settingsFromDb.RobotAttemptsNumber
                                                    && t.LastRobotStatusUpdated < endDate
                                                )
                                            )
                                         )
                                        .ExecuteUpdateAsync(s => s
                                            .SetProperty(t => t.RobotStatusCode, (int)RobotStatusEnum.InProgress)
                                            .SetProperty(t => t.LastRobotStatusUpdated, DateTimeOffset.UtcNow)
                                            .SetProperty(t => t.AttemptsNumber, t => t.AttemptsNumber + 1)
                                        );

            return affectedRows > 0;
        }


        public RobotConfiguration GetFromTemplateByRobotCode(RobotsEnum robotsEnum, Template template)
        {
            var config = template.RobotConfigurations.FirstOrDefault(t => t.RobotCode == (int)robotsEnum);

            if (config == null)
            {
                _logger.LogError($"У шаблона нет конфигурации роботов. TemplateId: {template.Id}");
                throw new Exception($"У шаблона нет конфигурации роботов. TemplateId: {template.Id}");
            }

            return config;
        }


        // Поиск невыполненных заданий и установка им статуса ошибки робота
        public async Task MarkExpiredTasksAsFailedAsync(int robotAttemptsNumber, TimeSpan robotWaitTime)
        {
            //Ищем `RobotStatusCode` = 22 и `LastStatusUpdated` истекло и `AttemptsNumber` >= допустимого значения из настроек,
            //ставим всем этим записям `RobotStatusCode`= 33

            var endDate = DateTimeOffset.UtcNow.Add(-robotWaitTime);

            var expiredConfigs = await EntitySet.Where(t =>
                                            t.RobotStatusCode == (int)RobotStatusEnum.InProgress
                                            && t.AttemptsNumber >= robotAttemptsNumber
                                            && t.LastRobotStatusUpdated <= endDate
                                        ).ToListAsync();

            if (!expiredConfigs.Any())
                return;

            foreach (var item in expiredConfigs)
            {
                ChangeRobotStatus(RobotStatusEnum.Error, item);
                _logger.LogInformation("Устанавливаю статус RobotStatus: {RobotStatus} для RobotConfigurationId: {RobotConfigurationId}", RobotStatusEnum.Error, item.Id);
            }

            #region Ищем, есть ли связанные шаблоны, которые должны переименоваться, им тоже нужно установить статус ошибки, но только для Шаблонов

            // Проактивная обработка связанных шаблонов переименования Old->New
            // Если старый шаблон умен, мы должны сразу убить (!!!замочить!!!) и новый (целевой), чтобы он не висел вечно в ожидании.

            var expiredTemplateIds = expiredConfigs
                                            .Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder)
                                            .Select(t => t.TemplateId)
                                            .Distinct()
                                            .ToList();

            if (expiredTemplateIds.Any())
            {
                // Находим OldName для этих шаблонов из таблицы переименований.
                var oldNamesToFail = await EntityContext.Templates
                                                            .Where(t => expiredTemplateIds.Contains(t.Id) && t.TemplateRenamePending != null)
                                                            .Select(t => t.TemplateRenamePending!.OldName)
                                                            .Distinct()
                                                            .ToListAsync();

                // Находим целевые (новые задачи), имена которых совпадают с найденными OldName
                if (oldNamesToFail.Any())
                {
                    var targetConfigs = await EntitySet
                                                .Where(t =>
                                                    t.RobotCode == (int)RobotsEnum.TemplateOrder
                                                    && t.RobotStatusCode != (int)RobotStatusEnum.Error // Не трогаем те, что уже в ошибке
                                                    && oldNamesToFail.Contains(t.Template!.Name)
                                                ).ToListAsync();

                    foreach (var item in targetConfigs)
                    {
                        SetErrorRobotStatusAndMaxAttempts(item);

                        // Пишем в лог роботу
                        var history = new RobotHistory
                        {
                            Id = Guid.NewGuid(),
                            HistoryLevel = (int)RobotStatusEnum.Error,
                            TaskStatusCode = item.TaskStatusCode,
                            RobotConfigurationId = item.Id,
                            RobotIp = null,
                            RobotId = ParrComponentsEnum.Api.ToString(),
                            RobotMessage = "[RobotConfigurationRepository] Установлен статус ошибки, так как не переименован связанный шаблон"
                        };

                        // Синхронный Add работает быстрее и безопаснее внутри цикла
                        EntityContext.RobotHistories.Add(history);

                        _logger.LogInformation("Проактивно установлен статус {Status} для целевого задания RobotConfigurationID: {Id} из-за ошибки старого шаблона.", RobotStatusEnum.Error, item.Id);
                    }
                }
            }
            #endregion

            var result = await CommitAsync();

            if (!result)
                _logger.LogError("Ошибка при сохранении изменений RobotStatus для просроченных заданий. Откат транзакции.");
            //else
            //    logger.LogInformation("Успешно обработано и переведено в статус Ошибки просроченных заданий: {Count} шт.", configObjs.Count + linksCount);

        }


    }
}
