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 behavior:
///   First Q  -> warning shown in log
///   Second Q (within 3s) -> sends cancellation (SIGTERM -> wait 5s -> SIGKILL)
///
/// Post-completion:
///   Stats freeze, log stays.
///   "Returning in 10s... [any key to stay]" countdown.
///   If key pressed: stays until Enter/Esc.
/// </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;

        // Abort state machine
        DateTime? firstQPressTime = null;

        // -----------------------------------------------------------------------
        // Key listener task — runs concurrently with the Live render loop
        // -----------------------------------------------------------------------
        var keyTask = Task.Run(async () =>
        {
            while (!operationDone)
            {
                if (Console.KeyAvailable)
                {
                    var key = Console.ReadKey(intercept: true);
                    if (key.Key == ConsoleKey.Q || key.KeyChar == 'q' || key.KeyChar == 'Q')
                    {
                        if (cts.IsCancellationRequested) break; // already cancelled

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

        // -----------------------------------------------------------------------
        // Main 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(logPanel, stats, startTime, "[yellow]Running...[/]"))
            .AutoClear(false)
            .StartAsync(async ctx =>
            {
                while (!operationDone)
                {
                    var elapsed = DateTime.Now - startTime;
                    ctx.UpdateTarget(
                        BuildLayout(logPanel, stats, startTime, "[yellow]Running...[/]"));
                    await Task.Delay(800, CancellationToken.None);
                }

                // Final render with result
                var elapsed2 = DateTime.Now - startTime;
                var statusMsg = cts.IsCancellationRequested
                    ? "[red]Aborted[/]"
                    : operationSuccess
                        ? "[green]Completed successfully[/]"
                        : "[red]Failed[/]";

                if (cts.IsCancellationRequested)
                    logPanel.Add($"[WARN] Operation aborted after {FormatElapsed(elapsed2)}.");
                else if (operationSuccess)
                    logPanel.Add($"[DONE] Operation completed in {FormatElapsed(elapsed2)}.");
                else
                    logPanel.Add($"[ERR ] Operation failed after {FormatElapsed(elapsed2)}.");

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

        // Clean up
        stats.Dispose();
        await operationTask; // ensure it's fully done
        operationDone = true;
        await keyTask;

        // -----------------------------------------------------------------------
        // Post-completion: countdown or hold
        // -----------------------------------------------------------------------
        await PostCompletionWaitAsync(logPanel, stats, startTime, operationSuccess, cts.IsCancellationRequested);
    }

    // -----------------------------------------------------------------------
    // Post-completion hold screen
    // -----------------------------------------------------------------------

    private static async Task PostCompletionWaitAsync(
        LogPanel logPanel,
        SystemStats stats,
        DateTime startTime,
        bool success,
        bool aborted)
    {
        var statusMsg = aborted ? "[red]Aborted[/]" :
                        success  ? "[green]Completed[/]" : "[red]Failed[/]";

        var countdownCancelled = false;
        var returnNow = false;

        // Start 10-second countdown in background
        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(logPanel, stats, startTime, statusMsg))
            .AutoClear(false)
            .StartAsync(async ctx =>
            {
                var secondsLeft = CountdownSeconds;
                var staying = false;

                while (!returnNow)
                {
                    if (Console.KeyAvailable)
                    {
                        var key = Console.ReadKey(intercept: true);
                        countdownCancelled = true;
                        staying = true;

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

                        // Any other key: cancel countdown, show "hold" message
                        logPanel.Add("[INFO] Countdown cancelled. Press [Enter] or [Esc] to return to menu.");
                    }

                    // Update footer countdown
                    var elapsed = DateTime.Now - startTime;
                    string footer;
                    if (!staying)
                    {
                        secondsLeft = CountdownSeconds -
                            (int)(DateTime.Now - startTime).TotalSeconds; // approximate
                        // recompute properly
                        footer = $"[dim]Returning to menu in {Math.Max(0, secondsLeft)}s... " +
                                 "[any key to stay][/]";
                    }
                    else
                    {
                        footer = "[dim]Press [Enter] or [Esc] to return to menu.[/]";
                    }

                    ctx.UpdateTarget(BuildLayoutWithFooter(logPanel, stats, startTime, statusMsg, footer));
                    await Task.Delay(200, CancellationToken.None);
                }
            });

        await countdownTask;
    }

    // -----------------------------------------------------------------------
    // Layout builders
    // -----------------------------------------------------------------------

    private static IRenderable BuildLayout(
        LogPanel logPanel,
        SystemStats stats,
        DateTime startTime,
        string statusMsg)
    {
        return BuildLayoutWithFooter(logPanel, stats, startTime, statusMsg, footer: null);
    }

    private static IRenderable BuildLayoutWithFooter(
        LogPanel logPanel,
        SystemStats stats,
        DateTime startTime,
        string statusMsg,
        string? footer)
    {
        var elapsed = DateTime.Now - startTime;
        var snap = stats.CurrentSnapshot;

        // Stats panel (left) — fixed width ~36 chars
        var statsPanelWidth = 36;
        var statsRenderable = StatsPanel.Build(snap, elapsed, statsPanelWidth - 4);

        // Log panel (right) — fills remaining space
        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);

        // Wrap in a grid so we can add a footer row
        var outerGrid = new Grid();
        outerGrid.AddColumn(new GridColumn());

        // Title row
        outerGrid.AddRow(new Markup(
            $"[bold dodgerblue1]  Running:[/] [yellow]{Markup.Escape(ExtractTitle(statusMsg))}[/]  " +
            $"Status: {statusMsg}"));

        outerGrid.AddRow(layout);

        if (footer is not null)
            outerGrid.AddRow(new Markup($"\n  {footer}  [dim][[Q]] Abort[/]"));
        else
            outerGrid.AddRow(new Markup("\n  [dim][[Q]] Press once to warn, twice to abort[/]"));

        return outerGrid;
    }

    private static string ExtractTitle(string statusMsg)
    {
        // statusMsg is markup like "[yellow]Running...[/]" — we just return a static title
        return "HANA Operation";
    }

    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";
    }
}
