using CredentialManager.Data; using CredentialManager.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace CredentialManager.Services; public class ProfileScheduleService : IProfileScheduleService { private readonly CredentialDbContext _context; private readonly ILogger _logger; public ProfileScheduleService(CredentialDbContext context, ILogger logger) { _context = context ?? throw new ArgumentNullException(nameof(context)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task> GetAllSchedulesAsync() { try { return await _context.ProfileSchedules .Include(s => s.Profile) .OrderBy(s => s.Name) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero di tutte le schedulazioni"); throw; } } public async Task GetScheduleByIdAsync(int id) { try { return await _context.ProfileSchedules .Include(s => s.Profile) .FirstOrDefaultAsync(s => s.Id == id); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero della schedulazione con ID {Id}", id); throw; } } public async Task CreateScheduleAsync(ProfileSchedule schedule) { try { schedule.CreatedAt = DateTime.UtcNow; schedule.UpdatedAt = DateTime.UtcNow; schedule.CreatedBy = Environment.UserName; // Calcola la prossima esecuzione schedule.NextExecutionTime = schedule.CalculateNextExecution(); _context.ProfileSchedules.Add(schedule); await _context.SaveChangesAsync(); _logger.LogInformation("Schedulazione creata: {Name} per il profilo {ProfileId}", schedule.Name, schedule.ProfileId); return schedule; } catch (Exception ex) { _logger.LogError(ex, "Errore nella creazione della schedulazione {Name}", schedule.Name); throw; } } public async Task UpdateScheduleAsync(ProfileSchedule schedule) { try { var existingSchedule = await _context.ProfileSchedules.FindAsync(schedule.Id); if (existingSchedule == null) throw new InvalidOperationException($"Schedulazione con ID {schedule.Id} non trovata"); // Aggiorna i campi existingSchedule.Name = schedule.Name; existingSchedule.Description = schedule.Description; existingSchedule.ProfileId = schedule.ProfileId; existingSchedule.IsEnabled = schedule.IsEnabled; existingSchedule.ScheduleType = schedule.ScheduleType; existingSchedule.ScheduledDateTime = schedule.ScheduledDateTime; existingSchedule.DailyTime = schedule.DailyTime; existingSchedule.DayOfWeek = schedule.DayOfWeek; existingSchedule.DayOfMonth = schedule.DayOfMonth; existingSchedule.IntervalValue = schedule.IntervalValue; existingSchedule.IntervalUnit = schedule.IntervalUnit; existingSchedule.IsActive = schedule.IsActive; existingSchedule.EnableDeletionSync = schedule.EnableDeletionSync; existingSchedule.SourceDatabaseOverride = schedule.SourceDatabaseOverride; existingSchedule.DestinationDatabaseOverride = schedule.DestinationDatabaseOverride; existingSchedule.UpdatedAt = DateTime.UtcNow; // Ricalcola la prossima esecuzione existingSchedule.NextExecutionTime = existingSchedule.CalculateNextExecution(); await _context.SaveChangesAsync(); _logger.LogInformation("Schedulazione aggiornata: {Name}", existingSchedule.Name); return existingSchedule; } catch (Exception ex) { _logger.LogError(ex, "Errore nell'aggiornamento della schedulazione con ID {Id}", schedule.Id); throw; } } public async Task DeleteScheduleAsync(int id) { try { var schedule = await _context.ProfileSchedules.FindAsync(id); if (schedule == null) return false; _context.ProfileSchedules.Remove(schedule); await _context.SaveChangesAsync(); _logger.LogInformation("Schedulazione eliminata: {Name} (ID: {Id})", schedule.Name, id); return true; } catch (Exception ex) { _logger.LogError(ex, "Errore nell'eliminazione della schedulazione con ID {Id}", id); throw; } } public async Task> GetActiveSchedulesAsync() { try { return await _context.ProfileSchedules .Include(s => s.Profile) .Where(s => s.IsActive && s.IsEnabled) .OrderBy(s => s.NextExecutionTime) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero delle schedulazioni attive"); throw; } } public async Task> GetPendingExecutionsAsync() { try { var now = DateTime.Now; return await _context.ProfileSchedules .Include(s => s.Profile) .Where(s => s.IsActive && s.IsEnabled && s.NextExecutionTime.HasValue && s.NextExecutionTime <= now && s.LastExecutionStatus != "running") .OrderBy(s => s.NextExecutionTime) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero delle schedulazioni in attesa di esecuzione"); throw; } } public async Task UpdateExecutionStatusAsync(int scheduleId, string status, string? message = null, int? recordCount = null) { try { var schedule = await _context.ProfileSchedules.FindAsync(scheduleId); if (schedule == null) return false; schedule.LastExecutionStatus = status; schedule.LastExecutionMessage = message; schedule.LastExecutionTime = DateTime.Now; if (recordCount.HasValue) schedule.LastExecutionRecordCount = recordCount; if (status == "success" || status == "failed") { schedule.ExecutionCount++; // Calcola la prossima esecuzione solo se completata (successo o errore) schedule.NextExecutionTime = schedule.CalculateNextExecution(); } await _context.SaveChangesAsync(); _logger.LogInformation("Status esecuzione aggiornato per schedulazione {ScheduleId}: {Status}", scheduleId, status); return true; } catch (Exception ex) { _logger.LogError(ex, "Errore nell'aggiornamento dello status per schedulazione {ScheduleId}", scheduleId); throw; } } public async Task UpdateNextExecutionTimeAsync(int scheduleId) { try { var schedule = await _context.ProfileSchedules.FindAsync(scheduleId); if (schedule == null) return; // Per schedulazioni a intervallo, calcola dalla ultima esecuzione if (schedule.ScheduleType == "interval") { schedule.NextExecutionTime = schedule.CalculateNextExecutionFromLast(); } else { schedule.NextExecutionTime = schedule.CalculateNextExecution(); } await _context.SaveChangesAsync(); _logger.LogDebug("Prossima esecuzione aggiornata per schedulazione {ScheduleId}: {NextExecution} (tipo: {Type})", scheduleId, schedule.NextExecutionTime, schedule.ScheduleType); } catch (Exception ex) { _logger.LogError(ex, "Errore nell'aggiornamento della prossima esecuzione per schedulazione {ScheduleId}", scheduleId); throw; } } public async Task> GetAvailableProfilesAsync() { try { return await _context.DataCouplerProfiles .Where(p => p.IsActive) .OrderBy(p => p.Name) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero dei profili disponibili"); throw; } } // Implementazione metodi per lo storico delle esecuzioni public async Task CreateExecutionHistoryAsync(ScheduleExecutionHistory history) { try { history.CreatedAt = DateTime.UtcNow; _context.ScheduleExecutionHistories.Add(history); await _context.SaveChangesAsync(); _logger.LogInformation("Storico esecuzione creato: ID {HistoryId} per schedulazione {ScheduleId}", history.Id, history.ScheduleId); return history; } catch (Exception ex) { _logger.LogError(ex, "Errore nella creazione dello storico esecuzione per schedulazione {ScheduleId}", history.ScheduleId); throw; } } public async Task UpdateExecutionHistoryAsync(ScheduleExecutionHistory history) { try { _context.ScheduleExecutionHistories.Update(history); await _context.SaveChangesAsync(); _logger.LogDebug("Storico esecuzione aggiornato: ID {HistoryId}", history.Id); return history; } catch (Exception ex) { _logger.LogError(ex, "Errore nell'aggiornamento dello storico esecuzione ID {HistoryId}", history.Id); throw; } } public async Task> GetExecutionHistoryAsync(int scheduleId, int? limit = null) { try { var query = _context.ScheduleExecutionHistories .Where(h => h.ScheduleId == scheduleId) .OrderByDescending(h => h.StartTime); if (limit.HasValue) { query = (IOrderedQueryable)query.Take(limit.Value); } return await query.ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero dello storico per schedulazione {ScheduleId}", scheduleId); throw; } } public async Task> GetRecentExecutionsAsync(int limit = 50) { try { return await _context.ScheduleExecutionHistories .Include(h => h.Schedule) .OrderByDescending(h => h.StartTime) .Take(limit) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero delle esecuzioni recenti"); throw; } } public async Task GetExecutionByIdAsync(int executionId) { try { return await _context.ScheduleExecutionHistories .Include(h => h.Schedule) .FirstOrDefaultAsync(h => h.Id == executionId); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero dell'esecuzione ID {ExecutionId}", executionId); throw; } } }