using OF_DL.Models.Downloads;
using OF_DL.Services;
using Spectre.Console;

namespace OF_DL.CLI;

/// <summary>
///     Spectre.Console implementation of IDownloadEventHandler.
///     Handles all CLI-specific display logic for downloads.
/// </summary>
public class SpectreDownloadEventHandler : IDownloadEventHandler
{
    public async Task<T> WithStatusAsync<T>(string statusMessage, Func<IStatusReporter, Task<T>> work)
    {
        TaskCompletionSource<T> tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);

        await AnsiConsole.Status()
            .StartAsync($"[red]{Markup.Escape(statusMessage)}[/]",
                async ctx =>
                {
                    try
                    {
                        SpectreStatusReporter reporter = new(ctx);
                        T result = await work(reporter);
                        tcs.TrySetResult(result);
                    }
                    catch (Exception ex)
                    {
                        tcs.TrySetException(ex);
                    }
                });

        return await tcs.Task;
    }

    public async Task<T> WithProgressAsync<T>(string description, long maxValue, bool showSize,
        Func<IProgressReporter, Task<T>> work)
    {
        TaskCompletionSource<T> tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);

        await AnsiConsole.Progress()
            .Columns(GetProgressColumns(showSize))
            .StartAsync(async ctx =>
            {
                try
                {
                    ProgressTask task = ctx.AddTask($"[red]{Markup.Escape(description)}[/]", false);
                    task.MaxValue = maxValue;
                    task.StartTask();

                    SpectreProgressReporter progressReporter = new(task);
                    T result = await work(progressReporter);
                    tcs.TrySetResult(result);

                    task.StopTask();
                }
                catch (Exception ex)
                {
                    tcs.TrySetException(ex);
                }
            });

        return await tcs.Task;
    }

    public void OnContentFound(string contentType, int mediaCount, int objectCount) =>
        AnsiConsole.Markup($"[red]Found {mediaCount} Media from {objectCount} {Markup.Escape(contentType)}\n[/]");

    public void OnNoContentFound(string contentType) =>
        AnsiConsole.Markup($"[red]Found 0 {Markup.Escape(contentType)}\n[/]");

    public void OnDownloadComplete(string contentType, DownloadResult result) =>
        AnsiConsole.Markup(
            $"[red]{Markup.Escape(contentType)} Already Downloaded: {result.ExistingDownloads} New {Markup.Escape(contentType)} Downloaded: {result.NewDownloads}[/]\n");

    public void OnUserStarting(string username) =>
        AnsiConsole.Markup($"[red]\nScraping Data for {Markup.Escape(username)}\n[/]");

    public void OnUserComplete(string username, CreatorDownloadResult result)
    {
        AnsiConsole.Markup("\n");
        AnsiConsole.Write(new BreakdownChart()
            .FullSize()
            .AddItem("Paid Posts", result.PaidPostCount, Color.Red)
            .AddItem("Posts", result.PostCount, Color.Blue)
            .AddItem("Archived", result.ArchivedCount, Color.Green)
            .AddItem("Streams", result.StreamsCount, Color.Purple)
            .AddItem("Stories", result.StoriesCount, Color.Yellow)
            .AddItem("Highlights", result.HighlightsCount, Color.Orange1)
            .AddItem("Messages", result.MessagesCount, Color.LightGreen)
            .AddItem("Paid Messages", result.PaidMessagesCount, Color.Aqua));
        AnsiConsole.Markup("\n");
    }

    public void OnPurchasedTabUserComplete(string username, int paidPostCount, int paidMessagesCount)
    {
        AnsiConsole.Markup("\n");
        AnsiConsole.Write(new BreakdownChart()
            .FullSize()
            .AddItem("Paid Posts", paidPostCount, Color.Red)
            .AddItem("Paid Messages", paidMessagesCount, Color.Aqua));
        AnsiConsole.Markup("\n");
    }

    public void OnScrapeComplete(TimeSpan elapsed) =>
        AnsiConsole.Markup($"[green]Scrape Completed in {elapsed.TotalMinutes:0.00} minutes\n[/]");

    public void OnMessage(string message) => AnsiConsole.Markup($"[red]{Markup.Escape(message)}\n[/]");

    private static ProgressColumn[] GetProgressColumns(bool showScrapeSize)
    {
        List<ProgressColumn> progressColumns;
        if (showScrapeSize)
        {
            progressColumns =
            [
                new TaskDescriptionColumn(),
                new ProgressBarColumn(),
                new PercentageColumn(),
                new DownloadedColumn(),
                new RemainingTimeColumn()
            ];
        }
        else
        {
            progressColumns =
            [
                new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()
            ];
        }

        return progressColumns.ToArray();
    }
}
