using InABox.Core; namespace InABox.Avalonia.Platform; public class StaleItemMonitor { private readonly CoreObservableCollection _items; private readonly Func _timestampSelector; private readonly Func _displaySelector; private readonly TimeSpan _staleThreshold; private readonly TimeSpan _pollInterval; private CancellationTokenSource? _cts; private Task? _monitorTask; private readonly object _lock = new object(); public event EventHandler? Changed; public StaleItemMonitor( CoreObservableCollection items, Func timestampSelector, Func displaySelector, TimeSpan staleThreshold, TimeSpan pollInterval) { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Initializing Stale Item monitor for {typeof(T).Name}"); _items = items ?? throw new ArgumentNullException(nameof(items)); _timestampSelector = timestampSelector ?? throw new ArgumentNullException(nameof(timestampSelector)); _displaySelector = displaySelector ?? throw new ArgumentNullException(nameof(displaySelector)); _staleThreshold = staleThreshold; _pollInterval = pollInterval; } public Task StartAsync() { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Starting Stale Item monitor for {typeof(T).Name}"); if (_monitorTask != null && !_monitorTask.IsCompleted) { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item monitor already started"); return Task.CompletedTask; // Already running } _cts = new CancellationTokenSource(); _monitorTask = Task.Run(() => MonitorLoop(_cts.Token)); System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item monitor started successfully"); return Task.CompletedTask; } public async Task StopAsync() { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stopping Stale Item monitor for {typeof(T).Name}"); if (_monitorTask == null) { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item monitor already stopped"); return; } if (_cts == null) { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item Monitor cancellation token is not set"); return; } _cts.Cancel(); try { await _monitorTask; } catch (OperationCanceledException) { // Expected during cancellation } finally { _cts.Dispose(); _cts = null; _monitorTask = null; } System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item monitor stopped successfully"); } private async Task MonitorLoop(CancellationToken token) { while (!token.IsCancellationRequested) { try { RemoveStaleItems(); await Task.Delay(_pollInterval, token); } catch (OperationCanceledException) { // Cancellation requested, exit loop break; } catch (Exception ex) { // Log or handle other exceptions System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Monitor error: {ex.Message}"); } } } private void RemoveStaleItems() { DateTime now = DateTime.UtcNow; lock (_lock) { var stale = _items.Where(item => (now - _timestampSelector(item)) > _staleThreshold).ToArray(); if (stale.Any()) { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Removing Stale Items {string.Join(", ", stale.Select(x=>_displaySelector(x)))}..."); _items.RemoveAll(x => stale.Contains(x)); ; Changed?.Invoke(this, EventArgs.Empty); System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Removed {stale.Length} Stale Items."); } } } }