using HanaTui.System;
using HanaTui.Tui.Components;
using Spectre.Console;
using Spectre.Console.Rendering;

namespace HanaTui.Tui;

/// <summary>
/// Runs an operation while showing a live split-panel:
///   Left:  System stats (CPU, RAM, Swap, elapsed)
///   Right: Streaming operation log
///
/// Abort: first Q warns, second Q within 3s sends SIGTERM then SIGKILL.
/// Post-completion: 10s countdown, any key cancels and holds; Enter/Esc returns.
/// </summary>
public static class TaskRunnerScreen
{
    private const int    CountdownSeconds    = 10;
    private const double AbortWindowSeconds  = 3.0;

    public static async Task RunAsync(
        string operationTitle,
        Func<Action<string>, CancellationToken, Task<bool>> operation)
    {
        AnsiConsole.Clear();

        var logPanel  = new LogPanel();
        var stats     = new SystemStats();
        var cts       = new CancellationTokenSource();
        var startTime = DateTime.Now;

        var operationDone    = false;
        var operationSuccess = false;
        DateTime? firstQTime = null;

        // Key listener
        var keyTask = Task.Run(async () =>
        {
            while (!operationDone)
            {
                if (Console.KeyAvailable)
                {
                    var key = Console.ReadKey(intercept: true);
                    if (key.Key == ConsoleKey.Q || key.KeyChar is 'q' or 'Q')
                    {
                        if (cts.IsCancellationRequested) break;

                        if (firstQTime is null)
                        {
                            firstQTime = DateTime.Now;
                            logPanel.Add("[WARN] Press Q again within 3 seconds to abort.");
                        }
                        else if ((DateTime.Now - firstQTime.Value).TotalSeconds <= AbortWindowSeconds)
                        {
                            logPanel.Add("[WARN] Aborting operation...");
                            await cts.CancelAsync();
                        }
                        else
                        {
                            firstQTime = DateTime.Now;
                            logPanel.Add("[WARN] Press Q again within 3 seconds to abort.");
                        }
                    }
                }
                await Task.Delay(50, CancellationToken.None);
            }
        }, CancellationToken.None);

        // Operation task
        var operationTask = Task.Run(async () =>
        {
            try
            {
                operationSuccess = await operation(line => logPanel.Add(line), cts.Token);
            }
            catch (Exception ex)
            {
                logPanel.Add($"[ERROR] Unexpected exception: {ex.Message}");
                operationSuccess = false;
            }
            finally
            {
                operationDone = true;
            }
        }, CancellationToken.None);

        // Live render loop
        await AnsiConsole.Live(BuildLayout(operationTitle, logPanel, stats, startTime, RunState.Running))
            .AutoClear(false)
            .StartAsync(async ctx =>
            {
                while (!operationDone)
                {
                    ctx.UpdateTarget(BuildLayout(operationTitle, logPanel, stats, startTime, RunState.Running));
                    await Task.Delay(800, CancellationToken.None);
                }

                var elapsed = DateTime.Now - startTime;
                RunState finalState;
                if (cts.IsCancellationRequested)
                {
                    finalState = RunState.Aborted;
                    logPanel.Add($"[WARN] Operation aborted after {FormatElapsed(elapsed)}.");
                }
                else if (operationSuccess)
                {
                    finalState = RunState.Success;
                    logPanel.Add($"[DONE] Operation completed in {FormatElapsed(elapsed)}.");
                }
                else
                {
                    finalState = RunState.Failed;
                    logPanel.Add($"[ERR ] Operation failed after {FormatElapsed(elapsed)}.");
                }

                ctx.UpdateTarget(BuildLayout(operationTitle, logPanel, stats, startTime, finalState));
            });

        stats.Dispose();
        await operationTask;
        operationDone = true;
        await keyTask;

        await PostCompletionWaitAsync(operationTitle, logPanel, stats, startTime,
            cts.IsCancellationRequested ? RunState.Aborted :
            operationSuccess            ? RunState.Success : RunState.Failed);
    }

    // -----------------------------------------------------------------------

    private enum RunState { Running, Success, Failed, Aborted }

    private static async Task PostCompletionWaitAsync(
        string title, LogPanel logPanel, SystemStats stats, DateTime startTime, RunState state)
    {
        var countdownCancelled = false;
        var returnNow = false;
        var staying   = false;
        var startCountdown = DateTime.Now;

        var countdownTask = Task.Run(async () =>
        {
            for (int i = CountdownSeconds; i > 0 && !countdownCancelled; i--)
                await Task.Delay(1000, CancellationToken.None);
            if (!countdownCancelled) returnNow = true;
        }, CancellationToken.None);

        await AnsiConsole.Live(BuildLayout(title, logPanel, stats, startTime, state))
            .AutoClear(false)
            .StartAsync(async ctx =>
            {
                while (!returnNow)
                {
                    if (Console.KeyAvailable)
                    {
                        var key = Console.ReadKey(intercept: true);
                        countdownCancelled = true;

                        if (key.Key is ConsoleKey.Enter or ConsoleKey.Escape)
                        {
                            returnNow = true;
                            break;
                        }

                        if (!staying)
                        {
                            staying = true;
                            logPanel.Add("[INFO] Countdown cancelled. Press Enter or Esc to return to menu.");
                        }
                    }

                    string footerText;
                    if (!staying)
                    {
                        var secsLeft = Math.Max(0, CountdownSeconds -
                            (int)(DateTime.Now - startCountdown).TotalSeconds);
                        footerText = $"Returning to menu in {secsLeft}s... (any key to stay)";
                    }
                    else
                    {
                        footerText = "Press Enter or Esc to return to menu.";
                    }

                    ctx.UpdateTarget(BuildLayout(title, logPanel, stats, startTime, state, footerText));
                    await Task.Delay(200, CancellationToken.None);
                }
            });

        await countdownTask;
    }

    // -----------------------------------------------------------------------
    // Layout
    // -----------------------------------------------------------------------

    private static IRenderable BuildLayout(
        string title,
        LogPanel logPanel,
        SystemStats stats,
        DateTime startTime,
        RunState state,
        string? footerText = null)
    {
        var elapsed = DateTime.Now - startTime;
        var snap    = stats.CurrentSnapshot;

        const int statsPanelWidth = 40;
        var statsRenderable = StatsPanel.Build(snap, elapsed, statsPanelWidth - 4);
        var logRenderable   = logPanel.Build();

        var layout = new Layout("root").SplitColumns(
            new Layout("stats").Size(statsPanelWidth),
            new Layout("log"));
        layout["stats"].Update(statsRenderable);
        layout["log"].Update(logRenderable);

        var outerGrid = new Grid();
        outerGrid.AddColumn(new GridColumn());

        // Title row — state label uses Text objects, not markup interpolation
        var titleGrid = new Grid();
        titleGrid.AddColumn(new GridColumn().NoWrap());
        titleGrid.AddColumn(new GridColumn().NoWrap());
        titleGrid.AddColumn(new GridColumn().NoWrap());
        titleGrid.AddColumn(new GridColumn().NoWrap());
        titleGrid.AddRow(
            new Markup("[bold dodgerblue1]  Operation:[/]"),
            new Text(" " + title, new Style(Color.Yellow)),
            new Markup("  [bold dodgerblue1]Status:[/]"),
            StateLabel(state)
        );
        outerGrid.AddRow(titleGrid);
        outerGrid.AddRow(layout);

        // Footer row — plain Text, no markup parsing of dynamic content
        var footerGrid = new Grid();
        footerGrid.AddColumn(new GridColumn().NoWrap());
        footerGrid.AddColumn(new GridColumn().NoWrap());

        var footerLeft = footerText is not null
            ? new Text("  " + footerText, new Style(Color.Grey, decoration: Decoration.Dim))
            : (IRenderable)new Text("  Press Q to abort (twice within 3s)", new Style(Color.Grey, decoration: Decoration.Dim));

        var footerRight = new Text("  [Q] abort", new Style(Color.Grey, decoration: Decoration.Dim));

        footerGrid.AddRow(footerLeft, footerRight);
        outerGrid.AddRow(footerGrid);

        return outerGrid;
    }

    private static IRenderable StateLabel(RunState state) => state switch
    {
        RunState.Running => new Markup(" [yellow]Running...[/]"),
        RunState.Success => new Markup(" [green]Completed[/]"),
        RunState.Failed  => new Markup(" [red]Failed[/]"),
        RunState.Aborted => new Markup(" [red]Aborted[/]"),
        _                => new Text(""),
    };

    private static string FormatElapsed(TimeSpan t)
    {
        if (t.TotalHours >= 1)  return $"{(int)t.TotalHours}h {t.Minutes:D2}m {t.Seconds:D2}s";
        if (t.TotalMinutes >= 1) return $"{t.Minutes}m {t.Seconds:D2}s";
        return $"{t.Seconds}s";
    }
}
