using PARR.Core.Common.Interfaces;
using PARR.Core.Services.Snapshots.Interfaces;

namespace PARR.SnapshotWorker
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly IIntervalService _intervalService;
        private readonly IServiceProvider _serviceProvider;

        // Хранилище времени СЛЕДУЮЩЕГО запуска для каждого сервиса
        private readonly Dictionary<string, DateTimeOffset> _nextRunTimers = new();

        // День последней успешной очистки базы данных
        private int _lastCleanupDay = -1;

        // Интервал, должен быть меньше минуты, чтобы попадать во все возможные интервалы
        TimeSpan tickInterval = TimeSpan.FromSeconds(25);

        public Worker(
            ILogger<Worker> logger,
            IIntervalService intervalService,
            IServiceProvider serviceProvider
            )
        {
            _logger = logger;
            _intervalService = intervalService;
            _serviceProvider = serviceProvider;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("Запуск фонового сервиса создания системных снапшотов");

            await _intervalService.IntervalInitAsync(async () =>
            {
                var now = DateTimeOffset.UtcNow;

                using (var scope = _serviceProvider.CreateScope())
                {
                    // Собираем абсолютно все зарегистрированные сервисы снапшотов
                    var providers = scope.ServiceProvider.GetServices<ISnapshotProvider>();

                    #region Параллельный сбор снапшотов

                    var tasksToRun = new List<Task>();

                    foreach (var provider in providers)
                    {
                        var providerKey = provider.GetType().FullName!;

                        // Если сервис видим впервые, планируем его первый старт прямо сейчас
                        if (!_nextRunTimers.ContainsKey(providerKey))
                        {
                            _nextRunTimers[providerKey] = now;
                        }

                        // Если текущее время добежало до запланированного «будильника»
                        if (now >= _nextRunTimers[providerKey])
                        {
                            // СРАЗУ планируем следующий старт, чтобы сетка времени не съезжала 
                            // из-за времени выполнения самого метода
                            _nextRunTimers[providerKey] = now.Add(provider.Interval);

                            // Добавляем таску в список для параллельного выполнения (без await!)
                            tasksToRun.Add(ExecuteSafelyAsync(provider, () => provider.TakeSnapshotAsync(stoppingToken), "Создание снапшота"));
                        }
                    }

                    // Запускаем все готовые снапшоты ОДНОВРЕМЕННО
                    if (tasksToRun.Any())
                    {
                        await Task.WhenAll(tasksToRun);
                    }

                    #endregion

                    #region Ежедневная очистка БД

                    // Если наступили новые сутки в формате UTC — запускаем ротацию старых данных
                    if (now.Day != _lastCleanupDay)
                    {
                        _logger.LogInformation("Наступили новые сутки. Запуск процесса очистки устаревших снапшотов...");

                        foreach (var provider in providers)
                        {
                            await ExecuteSafelyAsync(provider, () => provider.CleanUpOldSnapshotsAsync(stoppingToken), "Очистка старых данных");
                        }

                        // Запоминаем, что за сегодня очистку уже провели успешно
                        _lastCleanupDay = now.Day;
                    }

                    #endregion
                }

            }, tickInterval);
        }

        private async Task ExecuteSafelyAsync(ISnapshotProvider provider, Func<Task> action, string operationName)
        {
            try
            {
                await action();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Критическая ошибка во время операции '{Operation}' в сервисе {ProviderName}", operationName, provider.GetType().Name);
            }
        }

    }
}
