using System; using System.Collections.Generic; using System.Linq; using System.Windows; using AutoBidder.Models; using AutoBidder.Utilities; namespace AutoBidder.Dialogs { /// /// Sceglie cosa considerare incompleto nello storico e lo toglie. /// /// Il conto si aggiorna a ogni spunta, prima di toccare qualunque cosa: una /// pulizia è irreversibile, e l'unico modo di renderla una decisione informata è mostrare /// quante righe cadrebbero e per quale motivo mentre si sceglie. La copia di sicurezza /// prima di scrivere è la seconda rete: le statistiche sono mesi di raccolta che nessun /// pulsante ha il diritto di far sparire per un clic distratto. /// public partial class StatsCleanupDialog : Window { private readonly List _records; public StatsCleanupDialog(List records) { InitializeComponent(); _records = records ?? new List(); Loaded += (_, _) => Refresh(); } /// Righe rimosse, dopo la conferma. 0 se l'utente ha annullato. public int RemovedCount { get; private set; } /// Dove è finita la copia di sicurezza. public string? BackupPath { get; private set; } private StatsMaintenance.CleanupOptions ReadOptions() { var options = new StatsMaintenance.CleanupOptions { RemoveWithoutPrice = ChkNoPrice.IsChecked == true, RemoveInconsistentBids = ChkBadBids.IsChecked == true, RemoveDuplicates = ChkDuplicates.IsChecked == true, RemoveWithoutWinnerBids = ChkNoWinnerBids.IsChecked == true, RemoveWithoutWinner = ChkNoWinner.IsChecked == true, RemoveWithoutValue = ChkNoValue.IsChecked == true }; // Una soglia illeggibile o fuori scala non deve diventare "togli tutto": // il filtro resta spento finché il numero non ha senso. if (ChkCoverage.IsChecked == true && double.TryParse(CoverageBox.Text.Trim().Replace(',', '.'), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var percento) && percento is > 0 and <= 100) { options.MinObservedCoverage = percento / 100.0; } if (ChkOlderThan.IsChecked == true && int.TryParse(DaysBox.Text.Trim(), out var days) && days > 0) { options.OlderThan = DateTime.Now.AddDays(-days); } return options; } private void Option_Changed(object sender, RoutedEventArgs e) => Refresh(); private void Refresh() { if (SummaryText == null) return; var report = StatsMaintenance.Preview(_records, ReadOptions()); SummaryText.Text = report.Removed == 0 ? $"Nessuna delle {report.Total} aste verrebbe tolta." : $"Verrebbero tolte {report.Removed} aste su {report.Total}. " + $"Ne resterebbero {report.Kept}."; DetailText.Text = report.ByReason.Count == 0 ? "" : string.Join("\n", report.ByReason .OrderByDescending(kv => kv.Value) .Select(kv => $"• {kv.Value} {kv.Key}")); CleanButton.IsEnabled = report.Removed > 0; } private void Clean_Click(object sender, RoutedEventArgs e) { var options = ReadOptions(); var report = StatsMaintenance.Preview(_records, options); if (report.Removed == 0) return; var answer = MessageBox.Show(this, $"Tolgo {report.Removed} aste su {report.Total}.\n\n" + "L'operazione non è reversibile, ma prima viene salvata una copia " + "dello storico nella cartella dei backup.\n\nProcedo?", "Pulizia dello storico", MessageBoxButton.YesNo, MessageBoxImage.Warning); if (answer != MessageBoxResult.Yes) return; try { BackupPath = CompletedAuctionsStore.BackupNow(); // La stessa risposta usata per il conto: contare con una regola e // cancellare con un'altra è il modo classico di far sparire dati che // nessuno voleva perdere. var kept = StatsMaintenance.Apply(_records, options); CompletedAuctionsStore.ReplaceAll(kept); RemovedCount = report.Removed; DialogResult = true; } catch (Exception ex) { MessageBox.Show(this, $"Pulizia non riuscita: {ex.Message}", "Pulizia dello storico", MessageBoxButton.OK, MessageBoxImage.Error); } } } }