using InABox.Core;

namespace InABox.Avalonia.Platform;

public class StaleItemMonitor<T>
{
    private readonly CoreObservableCollection<T> _items;
    private readonly Func<T, DateTime> _timestampSelector;
    private readonly Func<T, string> _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<T> items,
        Func<T, DateTime> timestampSelector,
        Func<T, String> 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.");
            }
        }
    }
}