using OF_DL.Models.Downloads;
using OF_DL.Services;

namespace OF_DL.Gui.Services;

internal sealed class AvaloniaDownloadEventHandler(
    Action<string> activitySink,
    Action<string> progressStatusUpdate,
    Action<string, long, bool> progressStart,
    Action<long> progressIncrement,
    Action progressStop,
    Func<bool> isCancellationRequested,
    CancellationToken cancellationToken) : IDownloadEventHandler
{
    private string _lastProgressDescription = string.Empty;
    private string _activeUsername = string.Empty;
    private DateTime _activeUserStartedAtUtc;
    private readonly Dictionary<string, int> _contentObjectCounts = new(StringComparer.Ordinal);
    private bool _hasPerUserCompletionLogged;

    public CancellationToken CancellationToken { get; } = cancellationToken;

    public async Task<T> WithStatusAsync<T>(string statusMessage, Func<IStatusReporter, Task<T>> work)
    {
        ThrowIfCancellationRequested();
        progressStart(statusMessage, 0, false);
        try
        {
            AvaloniaStatusReporter statusReporter = new(progressStatusUpdate, isCancellationRequested);
            return await work(statusReporter);
        }
        finally
        {
            progressStop();
        }
    }

    public async Task<T> WithProgressAsync<T>(string description, long maxValue, bool showSize,
        Func<IProgressReporter, Task<T>> work)
    {
        ThrowIfCancellationRequested();
        _lastProgressDescription = description;
        progressStart(description, maxValue, showSize);
        try
        {
            AvaloniaProgressReporter reporter = new(progressIncrement, isCancellationRequested, CancellationToken);
            return await work(reporter);
        }
        finally
        {
            progressStop();
        }
    }

    public void OnContentFound(string contentType, int mediaCount, int objectCount)
    {
        ThrowIfCancellationRequested();
        _contentObjectCounts[contentType] = objectCount;
        progressStatusUpdate($"Found {mediaCount} media from {objectCount} {contentType}.");
    }

    public void OnNoContentFound(string contentType)
    {
        ThrowIfCancellationRequested();
        progressStatusUpdate($"Found 0 {contentType}.");
    }

    public void OnDownloadComplete(string contentType, DownloadResult result)
    {
        ThrowIfCancellationRequested();
        if (!string.IsNullOrWhiteSpace(_activeUsername) && result.NewDownloads > 0)
        {
            if (_contentObjectCounts.TryGetValue(contentType, out int objectCount) && objectCount > 0)
            {
                activitySink(
                    $"Downloaded {result.NewDownloads} media from {objectCount} {contentType.ToLowerInvariant()}.");
            }
            else
            {
                activitySink($"Downloaded {result.NewDownloads} media from {contentType.ToLowerInvariant()}.");
            }
        }

        progressStatusUpdate(
            $"{contentType} complete. Existing: {result.ExistingDownloads}, New: {result.NewDownloads}, Total: {result.TotalCount}.");
    }

    public void OnUserStarting(string username)
    {
        ThrowIfCancellationRequested();
        _activeUsername = username;
        _activeUserStartedAtUtc = DateTime.UtcNow;
        _hasPerUserCompletionLogged = false;
        activitySink($"Starting scrape for {username}.");
        progressStatusUpdate($"Scraping data for {username}...");
    }

    public void OnUserComplete(string username, CreatorDownloadResult result)
    {
        ThrowIfCancellationRequested();
        TimeSpan elapsed = DateTime.UtcNow - _activeUserStartedAtUtc;
        activitySink($"Completed {username} in {elapsed.TotalMinutes:0.0} minutes.");
        _activeUsername = string.Empty;
        _hasPerUserCompletionLogged = true;
        _contentObjectCounts.Clear();
    }

    public void OnPurchasedTabUserComplete(string username, int paidPostCount, int paidMessagesCount)
    {
        ThrowIfCancellationRequested();
        TimeSpan elapsed = DateTime.UtcNow - _activeUserStartedAtUtc;
        activitySink($"Completed {username} in {elapsed.TotalMinutes:0.0} minutes.");
        _activeUsername = string.Empty;
        _hasPerUserCompletionLogged = true;
        _contentObjectCounts.Clear();
    }

    public void OnScrapeComplete(TimeSpan elapsed)
    {
        ThrowIfCancellationRequested();
        if (_hasPerUserCompletionLogged)
        {
            return;
        }

        string summary = BuildCompletionSummary(elapsed);
        activitySink(summary);
    }

    public void OnMessage(string message)
    {
        ThrowIfCancellationRequested();
        progressStatusUpdate(message);
    }

    private void ThrowIfCancellationRequested()
    {
        if (isCancellationRequested())
        {
            throw new OperationCanceledException("Operation canceled by user.");
        }
    }

    private string BuildCompletionSummary(TimeSpan elapsed)
    {
        if (string.IsNullOrWhiteSpace(_lastProgressDescription))
        {
            return $"Download completed in {elapsed.TotalMinutes:0.0} minutes.";
        }

        string normalized = _lastProgressDescription.Trim().TrimEnd('.');
        if (normalized.StartsWith("Downloading ", StringComparison.OrdinalIgnoreCase))
        {
            string remainder = normalized["Downloading ".Length..].ToLowerInvariant();
            return $"Downloaded {remainder} in {elapsed.TotalMinutes:0.0} minutes.";
        }

        return $"{normalized} in {elapsed.TotalMinutes:0.0} minutes.";
    }
}
