using CredentialManager.Models; using CredentialManager.Services; using Data_Coupler.Services; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Logging; using Microsoft.JSInterop; namespace Data_Coupler.Pages; public partial class Scheduling : ComponentBase { [Inject] private IProfileScheduleService ScheduleService { get; set; } = null!; [Inject] private IDataCouplerProfileService ProfileService { get; set; } = null!; [Inject] private IDataTransferService DataTransferService { get; set; } = null!; [Inject] private IJSRuntime JSRuntime { get; set; } = null!; [Inject] private ILogger Logger { get; set; } = null!; protected List? schedules; protected List? availableProfiles; protected ProfileSchedule? editingSchedule; protected DataCouplerProfile? selectedProfile; protected bool isExecuting = false; protected List? executionHistory; protected override async Task OnInitializedAsync() { await LoadSchedules(); await LoadAvailableProfiles(); } protected async Task LoadSchedules() { try { schedules = await ScheduleService.GetAllSchedulesAsync(); } catch (Exception ex) { Logger.LogError(ex, "Errore nel caricamento delle schedulazioni"); await ShowErrorMessage("Errore nel caricamento delle schedulazioni: " + ex.Message); } } protected async Task LoadAvailableProfiles() { try { availableProfiles = await ScheduleService.GetAvailableProfilesAsync(); } catch (Exception ex) { Logger.LogError(ex, "Errore nel caricamento dei profili disponibili"); } } protected async Task ShowCreateModal() { editingSchedule = new ProfileSchedule { Name = "", IsEnabled = true, ScheduleType = "", DailyTime = "09:00", IntervalValue = 5, IntervalUnit = "minutes" }; selectedProfile = null; await ShowModal(); } protected async Task ShowEditModal(ProfileSchedule schedule) { editingSchedule = new ProfileSchedule { Id = schedule.Id, Name = schedule.Name, Description = schedule.Description, ProfileId = schedule.ProfileId, IsEnabled = schedule.IsEnabled, ScheduleType = schedule.ScheduleType, ScheduledDateTime = schedule.ScheduledDateTime, DailyTime = schedule.DailyTime, DayOfWeek = schedule.DayOfWeek, DayOfMonth = schedule.DayOfMonth, IntervalValue = schedule.IntervalValue, IntervalUnit = schedule.IntervalUnit, SourceDatabaseOverride = schedule.SourceDatabaseOverride, DestinationDatabaseOverride = schedule.DestinationDatabaseOverride, EnableDeletionSync = schedule.EnableDeletionSync }; // Imposta il profilo selezionato per mostrare i campi di override selectedProfile = availableProfiles?.FirstOrDefault(p => p.Id == schedule.ProfileId); await ShowModal(); } protected async Task ShowModal() { StateHasChanged(); await Task.Delay(100); try { // Proviamo prima con l'approccio Bootstrap standard await JSRuntime.InvokeVoidAsync("eval", "if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { " + "var modal = new bootstrap.Modal(document.getElementById('scheduleModal')); " + "modal.show(); " + "} else { " + "document.getElementById('scheduleModal').style.display = 'block'; " + "document.getElementById('scheduleModal').classList.add('show'); " + "}"); } catch (Exception) { // Fallback: mostra il modal manualmente await JSRuntime.InvokeVoidAsync("eval", "var modal = document.getElementById('scheduleModal');" + "modal.style.display = 'block';" + "modal.classList.add('show');" + "document.body.classList.add('modal-open');" + "var backdrop = document.createElement('div');" + "backdrop.className = 'modal-backdrop fade show';" + "document.body.appendChild(backdrop);"); } } protected async Task HideModal() { try { await JSRuntime.InvokeVoidAsync("eval", "if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { " + "var modalElement = document.getElementById('scheduleModal'); " + "var modal = bootstrap.Modal.getInstance(modalElement); " + "if (modal) modal.hide(); " + "} else { " + "document.getElementById('scheduleModal').style.display = 'none'; " + "document.getElementById('scheduleModal').classList.remove('show'); " + "document.body.classList.remove('modal-open'); " + "var backdrop = document.querySelector('.modal-backdrop'); " + "if (backdrop) backdrop.remove(); " + "}"); } catch (Exception) { // Fallback: nascondi il modal manualmente await JSRuntime.InvokeVoidAsync("eval", "var modal = document.getElementById('scheduleModal');" + "modal.style.display = 'none';" + "modal.classList.remove('show');" + "document.body.classList.remove('modal-open');" + "var backdrop = document.querySelector('.modal-backdrop');" + "if (backdrop) backdrop.remove();"); } } protected async Task SaveSchedule() { if (editingSchedule == null) return; try { if (editingSchedule.Id == 0) { await ScheduleService.CreateScheduleAsync(editingSchedule); await ShowSuccessMessage("Schedulazione creata con successo!"); } else { await ScheduleService.UpdateScheduleAsync(editingSchedule); await ShowSuccessMessage("Schedulazione aggiornata con successo!"); } await CloseModal(); await LoadSchedules(); } catch (Exception ex) { Logger.LogError(ex, "Errore nel salvataggio della schedulazione"); await ShowErrorMessage("Errore nel salvataggio: " + ex.Message); } } protected async Task DeleteSchedule(int scheduleId) { var confirmed = await JSRuntime.InvokeAsync("confirm", "Sei sicuro di voler eliminare questa schedulazione?"); if (!confirmed) return; try { var success = await ScheduleService.DeleteScheduleAsync(scheduleId); if (success) { await ShowSuccessMessage("Schedulazione eliminata con successo!"); await LoadSchedules(); } else { await ShowErrorMessage("Schedulazione non trovata."); } } catch (Exception ex) { Logger.LogError(ex, "Errore nell'eliminazione della schedulazione {ScheduleId}", scheduleId); await ShowErrorMessage("Errore nell'eliminazione: " + ex.Message); } } protected async Task ToggleScheduleEnabled(int scheduleId, bool enabled) { try { var schedule = schedules?.FirstOrDefault(s => s.Id == scheduleId); if (schedule != null) { schedule.IsEnabled = enabled; await ScheduleService.UpdateScheduleAsync(schedule); Logger.LogInformation("Schedulazione {ScheduleId} {Status}", scheduleId, enabled ? "attivata" : "disattivata"); } } catch (Exception ex) { Logger.LogError(ex, "Errore nel cambio stato schedulazione {ScheduleId}", scheduleId); await ShowErrorMessage("Errore nel cambio stato: " + ex.Message); await LoadSchedules(); // Ricarica per ripristinare lo stato } } protected async Task ExecuteScheduleManually(int scheduleId) { if (isExecuting) return; var confirmed = await JSRuntime.InvokeAsync("confirm", "Eseguire immediatamente questa schedulazione?"); if (!confirmed) return; isExecuting = true; ScheduleExecutionHistory? executionHistory = null; StateHasChanged(); try { var schedule = schedules?.FirstOrDefault(s => s.Id == scheduleId); if (schedule?.Profile == null) { await ShowErrorMessage("Schedulazione o profilo non trovato."); return; } Logger.LogInformation("Esecuzione manuale schedulazione {ScheduleId} - {ScheduleName}", scheduleId, schedule.Name); // Crea record nello storico executionHistory = new ScheduleExecutionHistory { ScheduleId = scheduleId, ProfileId = schedule.ProfileId, ProfileName = schedule.Profile.Name, StartTime = DateTime.Now, Status = "running", TriggerType = "manual", TriggeredBy = Environment.UserName, SourceType = schedule.Profile.SourceType, DestinationType = schedule.Profile.DestinationType, SourceInfo = schedule.SourceDatabaseOverride != null ? $"Database Override: {schedule.SourceDatabaseOverride}" : null, DestinationInfo = schedule.DestinationDatabaseOverride != null ? $"Database Override: {schedule.DestinationDatabaseOverride}" : null, Message = "Esecuzione manuale avviata" }; executionHistory = await ScheduleService.CreateExecutionHistoryAsync(executionHistory); // Aggiorna lo status della schedulazione await ScheduleService.UpdateExecutionStatusAsync(scheduleId, "running", "Esecuzione manuale avviata"); await LoadSchedules(); // Esegui il trasferimento dati con override database se specificati var result = await DataTransferService.ExecuteProfileAsync( schedule.Profile, schedule.SourceDatabaseOverride, schedule.DestinationDatabaseOverride, schedule.EnableDeletionSync); // Aggiorna lo storico con il risultato executionHistory.EndTime = DateTime.Now; executionHistory.Status = result.IsSuccess ? "success" : "failed"; executionHistory.RecordsProcessed = result.RecordsProcessed; executionHistory.Message = result.IsSuccess ? $"Esecuzione completata con successo. {result.RecordsProcessed} record elaborati in {result.Duration.TotalSeconds:F2} secondi." : $"Esecuzione fallita: {result.ErrorMessage}"; if (!result.IsSuccess) { executionHistory.ErrorDetails = string.Join(Environment.NewLine, result.ErrorDetails); } // Aggiungi informazioni aggiuntive se disponibili if (result.AdditionalInfo.Any()) { executionHistory.AdditionalInfo = System.Text.Json.JsonSerializer.Serialize(result.AdditionalInfo); } await ScheduleService.UpdateExecutionHistoryAsync(executionHistory); // Aggiorna lo status della schedulazione var status = result.IsSuccess ? "success" : "failed"; var message = result.IsSuccess ? $"Esecuzione completata con successo. {result.RecordsProcessed} record elaborati." : $"Esecuzione fallita: {result.ErrorMessage}"; await ScheduleService.UpdateExecutionStatusAsync(scheduleId, status, message, result.RecordsProcessed); // Notifica l'utente (best-effort: la connessione browser potrebbe essere stata interrotta // durante un'esecuzione lunga senza che questo invalidi il risultato già salvato). try { if (result.IsSuccess) await ShowSuccessMessage($"Schedulazione eseguita con successo! {result.RecordsProcessed} record elaborati in {result.Duration.TotalSeconds:F2} secondi."); else await ShowErrorMessage($"Errore durante l'esecuzione: {result.ErrorMessage}"); } catch (OperationCanceledException) { // La connessione Blazor è stata interrotta durante l'esecuzione: il risultato è // già stato salvato correttamente, la notifica non può essere recapitata. Logger.LogWarning("Notifica UI non inviata per la schedulazione {ScheduleId}: connessione browser interrotta durante l'esecuzione", scheduleId); } await LoadSchedules(); } catch (Exception ex) { Logger.LogError(ex, "Errore nell'esecuzione manuale schedulazione {ScheduleId}", scheduleId); // Aggiorna lo storico in caso di eccezione durante l'esecuzione effettiva if (executionHistory != null) { executionHistory.EndTime = DateTime.Now; executionHistory.Status = "failed"; executionHistory.Message = $"Errore durante l'esecuzione: {ex.Message}"; executionHistory.ErrorDetails = ex.ToString(); await ScheduleService.UpdateExecutionHistoryAsync(executionHistory); } await ScheduleService.UpdateExecutionStatusAsync(scheduleId, "failed", $"Errore: {ex.Message}"); try { await ShowErrorMessage("Errore nell'esecuzione: " + ex.Message); } catch (OperationCanceledException) { Logger.LogWarning("Notifica UI non inviata per la schedulazione {ScheduleId}: connessione browser non disponibile", scheduleId); } } finally { isExecuting = false; StateHasChanged(); } } protected void OnProfileSelectionChanged(ChangeEventArgs e) { if (editingSchedule != null && int.TryParse(e.Value?.ToString(), out int profileId)) { editingSchedule.ProfileId = profileId; selectedProfile = availableProfiles?.FirstOrDefault(p => p.Id == profileId); // Reset override database quando cambia profilo editingSchedule.SourceDatabaseOverride = null; editingSchedule.DestinationDatabaseOverride = null; } else { selectedProfile = null; } StateHasChanged(); } protected void OnScheduleTypeChanged(ChangeEventArgs e) { if (editingSchedule != null) { editingSchedule.ScheduleType = e.Value?.ToString() ?? ""; // Reset campi quando cambia il tipo if (editingSchedule.ScheduleType != "once") { editingSchedule.ScheduledDateTime = null; } if (editingSchedule.ScheduleType != "weekly") { editingSchedule.DayOfWeek = null; } if (editingSchedule.ScheduleType != "monthly") { editingSchedule.DayOfMonth = null; } if (editingSchedule.ScheduleType != "interval") { editingSchedule.IntervalValue = null; editingSchedule.IntervalUnit = null; } else { // Imposta valori predefiniti per interval editingSchedule.IntervalValue ??= 5; editingSchedule.IntervalUnit ??= "minutes"; } } StateHasChanged(); } protected string GetIntervalPreview() { if (editingSchedule == null || !editingSchedule.IntervalValue.HasValue || string.IsNullOrEmpty(editingSchedule.IntervalUnit)) { return "Configura intervallo e unità di tempo"; } var value = editingSchedule.IntervalValue.Value; var unit = editingSchedule.IntervalUnit; var unitName = unit switch { "seconds" => value == 1 ? "secondo" : "secondi", "minutes" => value == 1 ? "minuto" : "minuti", "hours" => value == 1 ? "ora" : "ore", "days" => value == 1 ? "giorno" : "giorni", "weeks" => value == 1 ? "settimana" : "settimane", "months" => value == 1 ? "mese" : "mesi", _ => unit }; return $"Esecuzione ogni {value} {unitName}"; } private async Task CloseModal() { await HideModal(); editingSchedule = null; selectedProfile = null; } private async Task ShowSuccessMessage(string message) { await JSRuntime.InvokeVoidAsync("alert", message); } private async Task ShowErrorMessage(string message) { await JSRuntime.InvokeVoidAsync("alert", "Errore: " + message); } }