using AyCode.Core.Benchmarks.Workloads.Scenarios;
using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Tests.TestModels;

namespace AyCode.Core.Serializers.Console;

/// <summary>
/// Interactive console menu for the benchmark application. Shown when the user runs without CLI args:
/// top-level layer/mode selection + nested Settings sub-menus (iteration counts, wire mode, charset).
/// All settings mutate <see cref="Configuration"/> in place; the menu loop returns control to the
/// caller (<c>Program.Main</c>) once the user picks a benchmark layer or quits.
/// </summary>
internal static class Menu
{
    /// <summary>
    /// Interactive menu shown when no CLI args. Returns the (layer, serializerMode) tuple, or null on Quit.
    /// Loops on settings-changes ([S]) — user is returned to this menu after modifying iteration counts.
    /// </summary>
    internal static (BenchmarkLayer layer, SerializerSelectionMode serializerMode)? ShowInteractiveMenu()
    {
        while (true)
        {
            System.Console.WriteLine();
            System.Console.WriteLine("╔══════════════════════════════════════════════════════════╗");
            System.Console.WriteLine("║          AcBinary Benchmark Suite                        ║");
            System.Console.WriteLine("╚══════════════════════════════════════════════════════════╝");
            System.Console.WriteLine();
            System.Console.WriteLine("Select benchmark layer:");
            System.Console.WriteLine();
            System.Console.WriteLine("  [1] Core           — daily iteration");
            System.Console.WriteLine("  [2] Comprehensive  — release validation");
            System.Console.WriteLine("  [3] Edge cases     — refactor verification");
            System.Console.WriteLine("  [A] All layers");
            System.Console.WriteLine("  [F] FastestByte    — AcBinary FastMode Byte[] vs MemoryPack Byte[] only (tight optimization loop)");
            System.Console.WriteLine("  [P] AsyncPipe      — streaming I/O isolation (only AsyncPipe, all test data)");
            System.Console.WriteLine($"  [S] Settings       — Iteration / WireMode (current: {Configuration.SelectedWireMode})");
            System.Console.WriteLine("  [Q] Quit");
            System.Console.Write("\nSelection: ");

            var key = System.Console.ReadKey(intercept: false).KeyChar;
            System.Console.WriteLine();

            switch (char.ToLower(key))
            {
                case '1': return (BenchmarkLayer.Core, SerializerSelectionMode.Standard);
                case '2': return (BenchmarkLayer.Comprehensive, SerializerSelectionMode.Standard);
                case '3': return (BenchmarkLayer.Edge, SerializerSelectionMode.Standard);
                case 'a': return (BenchmarkLayer.All, SerializerSelectionMode.Standard);
                case 'f': return (BenchmarkLayer.All, SerializerSelectionMode.FastestByte);
                case 'p': return (BenchmarkLayer.All, SerializerSelectionMode.AsyncPipe);
                case 's':
                    ShowSettingsMenu();
                    continue;  // re-display the main menu after settings update
                case 'q': return null;
                default: return (BenchmarkLayer.All, SerializerSelectionMode.Standard);
            }
        }
    }

    /// <summary>
    /// Settings sub-menu — dispatches to per-area sub-menus (iteration counts, wire mode, charset).
    /// Returns to the caller (which re-displays the main menu) when [B]ack is pressed.
    /// </summary>
    private static void ShowSettingsMenu()
    {
        while (true)
        {
            System.Console.WriteLine();
            System.Console.WriteLine("─────────────────────────────────────────────");
            System.Console.WriteLine("Settings");
            System.Console.WriteLine("─────────────────────────────────────────────");
            System.Console.WriteLine("  [1] Iteration  — Warmup / Iterations / Samples");
            System.Console.WriteLine($"  [2] WireMode   — current: {Configuration.SelectedWireMode}");
            System.Console.WriteLine($"  [3] Charset    — current: {Configuration.GetCurrentCharsetName()}");
            System.Console.WriteLine("  [B] Back");
            System.Console.Write("\nSelection: ");

            var key = System.Console.ReadKey(intercept: false).KeyChar;
            System.Console.WriteLine();

            switch (char.ToLower(key))
            {
                case '1':
                    ShowIterationSettingsMenu();
                    break;
                case '2':
                    ShowWireModeSettingsMenu();
                    break;
                case '3':
                    ShowCharsetSettingsMenu();
                    break;
                case 'b':
                    return;
                default:
                    continue;
            }
        }
    }

    private static void ShowCharsetSettingsMenu()
    {
        while (true)
        {
            System.Console.WriteLine();
            System.Console.WriteLine("─────────────────────────────────────────────");
            System.Console.WriteLine("Charset settings — long-string suffix profile");
            System.Console.WriteLine("─────────────────────────────────────────────");
            System.Console.WriteLine($"Current: {Configuration.GetCurrentCharsetName()}");
            System.Console.WriteLine();
            System.Console.WriteLine("  All *Short = 40 char, all *Long = 280 char (= Short × 7) — length-consistent across charsets.");
            System.Console.WriteLine();
            System.Console.WriteLine("  [1] AsciiFix        — empty suffix; baseline-only short values → FixStrAscii tier");
            System.Console.WriteLine("  [2] AsciiShort      — 40 char pure ASCII (quic × 8) → StringAscii tier");
            System.Console.WriteLine("  [3] AsciiLong       — 280 char pure ASCII → StringAscii tier");
            System.Console.WriteLine("  [4] Latin1Fix       — 5 char Hungarian (árví) → FixStr-lean tier");
            System.Console.WriteLine("  [5] Latin1Short     — 40 char Hungarian (árví × 8) → StringSmall tier");
            System.Console.WriteLine("  [6] Latin1Long      — 280 char Hungarian (default) → StringMedium tier");
            System.Console.WriteLine("  [7] CjkBmpShort     — 40 char CJK BMP (3-byte runs) → StringSmall tier");
            System.Console.WriteLine("  [8] CjkBmpLong      — 280 char CJK BMP → StringMedium tier");
            System.Console.WriteLine("  [9] CyrillicShort   — 40 char Cyrillic (2-byte runs) → StringSmall tier");
            System.Console.WriteLine("  [0] CyrillicLong    — 280 char Cyrillic → StringMedium tier");
            System.Console.WriteLine("  [A] MixedShort      — 40 char multi-codepage → StringSmall tier");
            System.Console.WriteLine("  [C] MixedLong       — 280 char multi-codepage → StringMedium tier");
            System.Console.WriteLine("  [B] Back");
            System.Console.Write("\nSelection: ");

            var key = System.Console.ReadKey(intercept: false).KeyChar;
            System.Console.WriteLine();

            switch (char.ToLower(key))
            {
                case '1':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.AsciiFix;
                    System.Console.WriteLine("✓ Charset set to AsciiFix");
                    return;
                case '2':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.AsciiShort;
                    System.Console.WriteLine("✓ Charset set to AsciiShort");
                    return;
                case '3':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.AsciiLong;
                    System.Console.WriteLine("✓ Charset set to AsciiLong");
                    return;
                case '4':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.Latin1Fix;
                    System.Console.WriteLine("✓ Charset set to Latin1Fix");
                    return;
                case '5':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.Latin1Short;
                    System.Console.WriteLine("✓ Charset set to Latin1Short");
                    return;
                case '6':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.Latin1Long;
                    System.Console.WriteLine("✓ Charset set to Latin1Long");
                    return;
                case '7':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.CjkBmpShort;
                    System.Console.WriteLine("✓ Charset set to CjkBmpShort");
                    return;
                case '8':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.CjkBmpLong;
                    System.Console.WriteLine("✓ Charset set to CjkBmpLong");
                    return;
                case '9':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.CyrillicShort;
                    System.Console.WriteLine("✓ Charset set to CyrillicShort");
                    return;
                case '0':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.CyrillicLong;
                    System.Console.WriteLine("✓ Charset set to CyrillicLong");
                    return;
                case 'a':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.MixedShort;
                    System.Console.WriteLine("✓ Charset set to MixedShort");
                    return;
                case 'c':
                    BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.MixedLong;
                    System.Console.WriteLine("✓ Charset set to MixedLong");
                    return;
                case 'b':
                    return;
                default:
                    continue;
            }
        }
    }

    private static void ShowIterationSettingsMenu()
    {
        System.Console.WriteLine();
        System.Console.WriteLine("─────────────────────────────────────────────");
        System.Console.WriteLine("Iteration settings — press Enter to keep current value");
        System.Console.WriteLine("─────────────────────────────────────────────");
        System.Console.WriteLine();

        Configuration.WarmupIterations = PromptInt("WarmupIterations", Configuration.WarmupIterations, min: 0);
        Configuration.TestIterations = PromptInt("TestIterations  ", Configuration.TestIterations, min: 1);
        Configuration.BenchmarkSamples = PromptInt("BenchmarkSamples", Configuration.BenchmarkSamples, min: 1);

        System.Console.WriteLine();
        System.Console.WriteLine($"✓ Iteration settings updated: Warmup={Configuration.WarmupIterations} | Iterations={Configuration.TestIterations} | Samples={Configuration.BenchmarkSamples}");
    }

    private static void ShowWireModeSettingsMenu()
    {
        while (true)
        {
            System.Console.WriteLine();
            System.Console.WriteLine("─────────────────────────────────────────────");
            System.Console.WriteLine("WireMode settings");
            System.Console.WriteLine("─────────────────────────────────────────────");
            System.Console.WriteLine($"Current: {Configuration.SelectedWireMode}");
            System.Console.WriteLine("  [1] Compact");
            System.Console.WriteLine("  [2] Fast");
            System.Console.WriteLine("  [B] Back");
            System.Console.Write("\nSelection: ");

            var key = System.Console.ReadKey(intercept: false).KeyChar;
            System.Console.WriteLine();

            switch (char.ToLower(key))
            {
                case '1':
                    Configuration.SelectedWireMode = WireMode.Compact;
                    System.Console.WriteLine("✓ WireMode set to Compact");
                    return;
                case '2':
                    Configuration.SelectedWireMode = WireMode.Fast;
                    System.Console.WriteLine("✓ WireMode set to Fast");
                    return;
                case 'b':
                    return;
                default:
                    continue;
            }
        }
    }

    /// <summary>
    /// Prompts the user for an integer with a default (current value). Returns the current value if
    /// the user presses Enter on empty input or if parsing fails / value is below the minimum.
    /// </summary>
    private static int PromptInt(string name, int currentValue, int min)
    {
        System.Console.Write($"  {name} [{currentValue}]: ");

        var input = System.Console.ReadLine()?.Trim() ?? "";
        if (input.Length == 0) return currentValue;

        if (int.TryParse(input, out var newValue) && newValue >= min) return newValue;

        System.Console.WriteLine($"    ! Invalid value (need int ≥ {min}) — keeping {currentValue}");
        return currentValue;
    }
}
