using HanaTui.Hana;
using Spectre.Console;

namespace HanaTui.Tui;

/// <summary>
/// Guided input forms for each operation.
/// Each method collects all needed parameters, then returns a typed params object.
/// Returns null if the user cancelled.
/// </summary>
public static class OperationForms
{
    // -------------------------------------------------------------------------
    // Shared helpers
    // -------------------------------------------------------------------------

    /// <summary>
    /// Fetches the schema list with a spinner, then shows an arrow-key picker.
    /// Returns the selected schema, or null if cancelled.
    /// </summary>
    private static string? PickSchema(string userKey, string title)
    {
        List<string> schemas = [];
        string? error = null;

        AnsiConsole.Status()
            .Spinner(Spinner.Known.Dots)
            .SpinnerStyle(Style.Parse("blue"))
            .Start("[blue]Fetching schemas...[/]", _ =>
            {
                var (success, list, err) = HdbCliRunner.ListSchemas(userKey);
                if (success)
                    schemas = list;
                else
                    error = err;
            });

        if (error is not null)
            AnsiConsole.MarkupLine($"[yellow][[WARN]] Could not fetch schemas: {Markup.Escape(error)}[/]");

        // Use sentinel strings as values so no markup leaks into choice labels
        const string manualSentinel = "__MANUAL__";
        const string cancelSentinel = "__CANCEL__";

        var choiceValues = new List<string>(schemas) { manualSentinel, cancelSentinel };

        var prompt = new SelectionPrompt<string>()
            .Title($"[bold]{Markup.Escape(title)}[/]")
            .PageSize(15)
            .HighlightStyle(Style.Parse("bold dodgerblue1"))
            .UseConverter(v => v switch
            {
                manualSentinel => "[ Enter manually ]",
                cancelSentinel => "[ Cancel ]",
                _ => Markup.Escape(v),
            })
            .AddChoices(choiceValues);

        var selected = AnsiConsole.Prompt(prompt);

        if (selected == cancelSentinel)
            return null;

        if (selected == manualSentinel)
        {
            var manual = AnsiConsole.Ask<string>("Enter schema name:").Trim();
            return string.IsNullOrWhiteSpace(manual) ? null : manual;
        }

        return selected.Trim();
    }

    private static int PickThreads(string label = "Number of threads")
    {
        var maxThreads = Environment.ProcessorCount;
        var defaultThreads = Math.Max(1, maxThreads / 2);
        return AnsiConsole.Ask($"{label} [dim](default={defaultThreads}, max={maxThreads})[/]:",
            defaultThreads);
    }

    private static bool ConfirmYesNo(string question, bool defaultYes = false)
    {
        var defaultLabel = defaultYes ? "Y/n" : "y/N";
        var answer = AnsiConsole.Ask<string>($"{question} [dim]({defaultLabel})[/]:", defaultYes ? "y" : "n");
        return answer.Trim().Equals("y", StringComparison.OrdinalIgnoreCase) ||
               answer.Trim().Equals("yes", StringComparison.OrdinalIgnoreCase);
    }

    private static void PrintOperationHeader(string title)
    {
        AnsiConsole.Clear();
        var rule = new Rule($"[bold dodgerblue1]{Markup.Escape(title)}[/]").RuleStyle(Style.Parse("dodgerblue1"));
        AnsiConsole.Write(rule);
        AnsiConsole.WriteLine();
    }

    private static bool ShowSummaryAndConfirm(string title, Dictionary<string, string> fields)
    {
        AnsiConsole.WriteLine();
        var table = new Table().BorderColor(Color.DodgerBlue1).Border(TableBorder.Rounded);
        table.AddColumn("[bold]Parameter[/]");
        table.AddColumn("[bold]Value[/]");
        foreach (var (k, v) in fields)
            table.AddRow($"[dim]{Markup.Escape(k)}[/]", $"[yellow]{Markup.Escape(v)}[/]");
        AnsiConsole.MarkupLine($"[bold]{Markup.Escape(title)}[/]");
        AnsiConsole.Write(table);
        AnsiConsole.WriteLine();

        return ConfirmYesNo("Proceed with this operation?", defaultYes: true);
    }

    // -------------------------------------------------------------------------
    // Export
    // -------------------------------------------------------------------------

    public static ExportParams? ExportForm(string userKey)
    {
        PrintOperationHeader("Export Schema");

        var schema = PickSchema(userKey, "Select schema to export:");
        if (schema is null) return null;

        var targetPath = AnsiConsole.Ask<string>("Target directory or file path [dim](.tar.gz for archive)[/]:").Trim();
        if (string.IsNullOrWhiteSpace(targetPath)) return null;

        var threads = PickThreads();
        var compress = ConfirmYesNo("Compress output as .tar.gz?");

        var confirmed = ShowSummaryAndConfirm("Export Summary", new Dictionary<string, string>
        {
            ["Schema"]      = schema,
            ["Target path"] = targetPath,
            ["Threads"]     = threads.ToString(),
            ["Compress"]    = compress ? "Yes" : "No",
        });

        if (!confirmed) return null;

        return new ExportParams
        {
            Schema = schema,
            TargetPath = targetPath,
            Threads = threads,
            Compress = compress,
        };
    }

    // -------------------------------------------------------------------------
    // Import
    // -------------------------------------------------------------------------

    public static ImportParams? ImportForm(string userKey, bool renameMode)
    {
        var title = renameMode ? "Import & Rename Schema" : "Import Schema";
        PrintOperationHeader(title);

        var sourceSchema = AnsiConsole.Ask<string>("Source schema name [dim](as it was exported)[/]:").Trim();
        if (string.IsNullOrWhiteSpace(sourceSchema)) return null;

        string? newSchemaName = null;
        if (renameMode)
        {
            newSchemaName = AnsiConsole.Ask<string>("New target schema name:").Trim();
            if (string.IsNullOrWhiteSpace(newSchemaName)) return null;
        }

        var sourcePath = AnsiConsole.Ask<string>("Source path [dim](directory or .tar.gz)[/]:").Trim();
        if (string.IsNullOrWhiteSpace(sourcePath)) return null;

        var threads = PickThreads();
        var replace = ConfirmYesNo("Replace existing objects?");

        var summary = new Dictionary<string, string>
        {
            ["Source schema"] = sourceSchema,
            ["Source path"]   = sourcePath,
            ["Threads"]       = threads.ToString(),
            ["Replace"]       = replace ? "Yes" : "No (IGNORE EXISTING)",
        };
        if (renameMode && newSchemaName is not null)
            summary["New schema name"] = newSchemaName;

        var confirmed = ShowSummaryAndConfirm($"{title} Summary", summary);
        if (!confirmed) return null;

        return new ImportParams
        {
            SourceSchema  = sourceSchema,
            NewSchemaName = newSchemaName,
            SourcePath    = sourcePath,
            Threads       = threads,
            Replace       = replace,
        };
    }

    // -------------------------------------------------------------------------
    // Copy
    // -------------------------------------------------------------------------

    public static CopyParams? CopyForm(string userKey)
    {
        PrintOperationHeader("Copy Schema");

        var sourceSchema = PickSchema(userKey, "Select source schema:");
        if (sourceSchema is null) return null;

        var targetSchema = AnsiConsole.Ask<string>("Target schema name:").Trim();
        if (string.IsNullOrWhiteSpace(targetSchema)) return null;

        var tempPath = AnsiConsole.Ask<string>("Temporary export directory path:").Trim();
        if (string.IsNullOrWhiteSpace(tempPath)) return null;

        var threads = PickThreads();
        var replace = ConfirmYesNo("Replace existing objects in target schema?");

        var confirmed = ShowSummaryAndConfirm("Copy Schema Summary", new Dictionary<string, string>
        {
            ["Source schema"] = sourceSchema,
            ["Target schema"] = targetSchema,
            ["Temp path"]     = tempPath,
            ["Threads"]       = threads.ToString(),
            ["Replace"]       = replace ? "Yes" : "No",
        });

        if (!confirmed) return null;

        return new CopyParams
        {
            SourceSchema = sourceSchema,
            TargetSchema = targetSchema,
            TempPath     = tempPath,
            Threads      = threads,
            Replace      = replace,
        };
    }

    // -------------------------------------------------------------------------
    // Drop
    // -------------------------------------------------------------------------

    public static DropParams? DropForm(string userKey)
    {
        PrintOperationHeader("Drop Schema");

        var schema = PickSchema(userKey, "Select schema to drop:");
        if (schema is null) return null;

        AnsiConsole.WriteLine();
        AnsiConsole.MarkupLine($"[bold red]WARNING:[/] You are about to [bold red]permanently drop[/] schema [bold yellow]{Markup.Escape(schema)}[/].");
        AnsiConsole.MarkupLine("[red]This cannot be undone.[/]\n");

        var confirm = AnsiConsole.Ask<string>($"Type [bold red]YES[/] to confirm dropping [yellow]{Markup.Escape(schema)}[/]:").Trim();
        if (confirm != "YES")
        {
            AnsiConsole.MarkupLine("[yellow]Operation cancelled.[/]");
            AnsiConsole.WriteLine();
            AnsiConsole.MarkupLine("[dim]Press any key to return...[/]");
            Console.ReadKey(intercept: true);
            return null;
        }

        return new DropParams { Schema = schema };
    }

    // -------------------------------------------------------------------------
    // Rename DB
    // -------------------------------------------------------------------------

    public static RenameDbParams? RenameDbForm(string userKey)
    {
        PrintOperationHeader("Rename Database (Company Name)");

        var schema = PickSchema(userKey, "Select schema:");
        if (schema is null) return null;

        var newName = AnsiConsole.Ask<string>("New company name:").Trim();
        if (string.IsNullOrWhiteSpace(newName)) return null;

        var confirmed = ShowSummaryAndConfirm("Rename DB Summary", new Dictionary<string, string>
        {
            ["Schema"]           = schema,
            ["New company name"] = newName,
            ["Tables updated"]   = "CINF, OADM",
        });

        if (!confirmed) return null;

        return new RenameDbParams { Schema = schema, NewCompanyName = newName };
    }

    // -------------------------------------------------------------------------
    // Backup
    // -------------------------------------------------------------------------

    public static BackupParams? BackupForm(string userKey)
    {
        PrintOperationHeader("Backup Tenant");

        var targetPath = AnsiConsole.Ask<string>("Target directory path:").Trim();
        if (string.IsNullOrWhiteSpace(targetPath)) return null;

        var threads = PickThreads("Number of compression threads");
        var compress = ConfirmYesNo("Compress backup as .tar.gz?");

        var confirmed = ShowSummaryAndConfirm("Backup Summary", new Dictionary<string, string>
        {
            ["Target path"] = targetPath,
            ["Threads"]     = threads.ToString(),
            ["Compress"]    = compress ? "Yes" : "No",
        });

        if (!confirmed) return null;

        return new BackupParams
        {
            TargetPath = targetPath,
            Threads    = threads,
            Compress   = compress,
        };
    }
}
