using HanaTui.Hana;
using Spectre.Console;
using SysText = System.Text;

namespace HanaTui.Tui;

/// <summary>
/// Startup screen: lists hdbuserstore keys and lets the user pick one.
/// Returns the selected key name, or null if the user chose to exit.
/// </summary>
public static class KeySelectionScreen
{
    // Sentinel values for the non-key choices
    private const string ManualEntry = "__MANUAL__";
    private const string ExitChoice  = "__EXIT__";

    public static string? Run()
    {
        AnsiConsole.Clear();
        AnsiConsole.Write(new FigletText("HANA TUI").Color(Color.DodgerBlue1));
        AnsiConsole.MarkupLine("[dim]SAP HANA Database Manager[/]\n");

        if (!HdbClientLocator.IsAvailable())
        {
            AnsiConsole.MarkupLine("[red][[ERROR]][/] hdbsql not found. " +
                "Ensure the SAP HANA client is installed and on your PATH, " +
                "or present in [dim]/usr/sap/hdbclient[/] or [dim]/usr/sap/NDB/HDB00/exe[/].");
            AnsiConsole.MarkupLine("\nPress any key to exit.");
            Console.ReadKey(intercept: true);
            return null;
        }

        // Show client path for reference
        var clientDir = HdbClientLocator.ClientDirectory;
        if (clientDir is not null)
            AnsiConsole.MarkupLine($"[dim]HDB client: {Markup.Escape(clientDir)}[/]\n");

        // Load keys with a spinner
        List<HdbUserstoreKey> keys = [];

        AnsiConsole.Status()
            .Spinner(Spinner.Known.Dots)
            .SpinnerStyle(Style.Parse("blue"))
            .Start("[blue]Loading hdbuserstore keys...[/]", _ =>
            {
                keys = HdbCliRunner.ListKeys();
            });

        if (keys.Count == 0)
        {
            AnsiConsole.MarkupLine("[yellow][[WARN]][/] No hdbuserstore keys found.");
            AnsiConsole.MarkupLine("You can still enter a key name manually.\n");
        }

        // Build a plain-string list where the value IS the key name (or sentinel).
        // We use SelectionPrompt<string> with a display converter so Spectre renders
        // the label as markup but we always get back the plain key name.
        var choiceNames = new List<string>();
        var displayMap  = new Dictionary<string, string>(); // name -> display label

        foreach (var k in keys)
        {
            choiceNames.Add(k.Name);
            displayMap[k.Name] = BuildKeyDisplay(k);
        }

        choiceNames.Add(ManualEntry);
        displayMap[ManualEntry] = "[ Enter key name manually ]";

        choiceNames.Add(ExitChoice);
        displayMap[ExitChoice] = "[ Exit ]";

        var prompt = new SelectionPrompt<string>()
            .Title("[bold]Select HDBUSERSTORE key:[/]")
            .PageSize(15)
            .HighlightStyle(Style.Parse("bold dodgerblue1"))
            .UseConverter(name => displayMap.TryGetValue(name, out var label)
                ? Markup.Escape(label)
                : Markup.Escape(name))
            .AddChoices(choiceNames);

        var selected = AnsiConsole.Prompt(prompt);

        if (selected == ExitChoice)
            return null;

        if (selected == ManualEntry)
        {
            var manual = AnsiConsole.Ask<string>("[bold]Enter HDBUSERSTORE key name:[/]").Trim();
            return string.IsNullOrWhiteSpace(manual) ? null : manual;
        }

        // selected is already the plain key name
        return selected;
    }

    /// <summary>
    /// Builds a plain-text display label (no markup) for a key.
    /// Spectre's UseConverter renders the returned string as plain text.
    /// </summary>
    private static string BuildKeyDisplay(HdbUserstoreKey k)
    {
        var sb = new SysText.StringBuilder(k.Name);

        if (!string.IsNullOrEmpty(k.Host))
        {
            sb.Append("  ");
            sb.Append(k.Host);
            if (!string.IsNullOrEmpty(k.Port))
            {
                sb.Append(':');
                sb.Append(k.Port);
            }
        }

        if (!string.IsNullOrEmpty(k.Tenant))
        {
            sb.Append('@');
            sb.Append(k.Tenant);
        }

        if (!string.IsNullOrEmpty(k.User))
        {
            sb.Append("  user=");
            sb.Append(k.User);
        }

        return sb.ToString();
    }
}
