using System.Globalization;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using HellionChat.Branding;
using HellionChat.Code;
using HellionChat.Privacy;
using HellionChat.Resources;
using HellionChat.Themes;
using HellionChat.Util;

namespace HellionChat.Ui;

// Multi-step first-run wizard. public sealed because Plugin.cs has a
// public-typed property on this class — narrowing to internal would
// be a build break across the assembly boundary. State lives in a
// nested WizardState record; every step writes nullable Pending*
// fields, and CommitPending() applies only the non-null ones so
// users who skip a step never get their existing config overwritten.
public sealed class FirstRunWizard : Window
{
    // Forge-Bronze (#C2410C). The same constant lives in ThemeRegistry
    // and the forge-announce workflow; pinning it locally keeps the
    // wizard render path free of registry lookups during draw.
    private static readonly Vector4 ForgeBronze = new(0xC2 / 255f, 0x41 / 255f, 0x0C / 255f, 1f);
    private static readonly Vector4 ForgeBronzeDim = new(
        0xC2 / 255f,
        0x41 / 255f,
        0x0C / 255f,
        0.3f
    );

    private const int TotalSteps = 4;

    private readonly Plugin Plugin;
    private readonly WizardState _state = new();

    internal FirstRunWizard(Plugin plugin)
        : base($"{HellionStrings.Wizard_Title}###hellion-firstrun")
    {
        Plugin = plugin;

        Flags = ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoDocking;
        SizeCondition = ImGuiCond.Appearing;
        Size = new Vector2(720, 480);
        SizeConstraints = new WindowSizeConstraints
        {
            MinimumSize = new Vector2(600, 400),
            MaximumSize = new Vector2(float.MaxValue, float.MaxValue),
        };
    }

    public override void OnClose()
    {
        // OnClose fires on explicit X-click and on plugin dispose. We never
        // implicitly accept the defaults here — both the explicit "Decide
        // later" footer link and a successful "Finish ✓" set FirstRunCompleted
        // = true, so the wizard does not reopen on the next plugin load
        // regardless of which path the user took.
    }

    public override void Draw()
    {
        DrawPagination();
        ImGui.Spacing();
        ImGui.Separator();
        ImGui.Spacing();

        switch (_state.CurrentStep)
        {
            case 1:
                DrawStepWelcome();
                break;
            case 2:
                DrawStepPrivacy();
                break;
            case 3:
                DrawStepPowerSettings();
                break;
            case 4:
                DrawStepDone();
                break;
            default:
                _state.CurrentStep = 1;
                DrawStepWelcome();
                break;
        }
    }

    private void DrawPagination()
    {
        var draw = ImGui.GetWindowDrawList();
        var avail = ImGui.GetContentRegionAvail();
        var cursor = ImGui.GetCursorScreenPos();
        const float radius = 5f;
        const float spacing = 16f;
        var totalWidth = (TotalSteps - 1) * spacing;
        var startX = cursor.X + avail.X - totalWidth - radius;

        for (var i = 0; i < TotalSteps; i++)
        {
            var color = (i + 1) == _state.CurrentStep ? ForgeBronze : ForgeBronzeDim;
            var packed = ImGui.GetColorU32(color);
            draw.AddCircleFilled(
                new Vector2(startX + i * spacing, cursor.Y + radius),
                radius,
                packed
            );
        }

        // Reserve vertical space the circles consumed so the next widget starts below them.
        ImGui.Dummy(new Vector2(0, radius * 2));
    }

    private void DrawFooter(bool showBack, bool showSkip, string primaryLabel, Action onPrimary)
    {
        var spacing = ImGui.GetStyle().ItemSpacing.Y;
        var primaryWidth =
            ImGui.CalcTextSize(primaryLabel).X + ImGui.GetStyle().FramePadding.X * 2 + 16f;
        var avail = ImGui.GetContentRegionAvail();

        // Push the footer to the bottom of the window so step contents
        // above can size themselves with GetContentRegionAvail().
        var lineHeight = ImGui.GetFrameHeightWithSpacing();
        var pushDown = avail.Y - lineHeight - spacing;
        if (pushDown > 0)
            ImGui.Dummy(new Vector2(0, pushDown));

        ImGui.Separator();
        ImGui.Spacing();

        if (showBack)
        {
            if (ImGui.Button(HellionStrings.Wizard_Nav_Back))
                _state.CurrentStep = Math.Max(1, _state.CurrentStep - 1);
            ImGui.SameLine();
        }

        if (showSkip)
        {
            if (ImGui.Button(HellionStrings.Wizard_Step1_Skip_Label))
            {
                // Skip path = matches today's Cancel path: mark first-run
                // complete, save, close. No CommitPending — the user said
                // 'decide later', so existing config stays as-is.
                Plugin.Config.FirstRunCompleted = true;
                Plugin.SaveConfig();
                IsOpen = false;
            }
            if (ImGui.IsItemHovered())
                ImGuiUtil.Tooltip(HellionStrings.Wizard_Step1_Skip_Tooltip);
        }

        // Right-align the primary action button.
        var rightX = ImGui.GetCursorPosX() + ImGui.GetContentRegionAvail().X - primaryWidth;
        if (rightX > ImGui.GetCursorPosX())
            ImGui.SameLine(rightX);

        using (ImRaii.PushColor(ImGuiCol.Button, ForgeBronze))
        using (ImRaii.PushColor(ImGuiCol.ButtonHovered, ForgeBronze))
        using (ImRaii.PushColor(ImGuiCol.ButtonActive, ForgeBronze))
        {
            if (ImGui.Button($"{primaryLabel}##wizard-primary"))
                onPrimary();
        }
    }

    private void DrawStepWelcome()
    {
        ImGui.TextUnformatted(HellionStrings.Wizard_Step1_Title);
        ImGui.Spacing();

        // Fox-banner image: the embedded Hellion Forge fox artwork. The card
        // behind the image gives the dark fox enough contrast against the
        // plugin's dark UI so the logo reads clearly at a glance.
        var banner = FoxBannerTexture.Shared.GetWrapOrDefault();
        if (banner is not null)
        {
            const uint CardColor = 0xFFE8E8E8; // off-white fill so the dark fox pops
            var imgHeight = 170f * ImGuiHelpers.GlobalScale;
            var imgWidth = imgHeight * banner.Size.X / banner.Size.Y;
            var pad = 14f * ImGuiHelpers.GlobalScale;
            var cardWidth = imgWidth + pad * 2f;
            var cardHeight = imgHeight + pad * 2f;
            var rounding = 8f * ImGuiHelpers.GlobalScale;

            // Centre the card in the content region. Clamp to zero so the card
            // never shifts left of the window edge on very narrow windows.
            var offsetX = Math.Max(0f, (ImGui.GetContentRegionAvail().X - cardWidth) * 0.5f);
            var cardOrigin = ImGui.GetCursorScreenPos() + new Vector2(offsetX, 0f);

            // Draw the rounded card behind the image, then place the image on top.
            ImGui
                .GetWindowDrawList()
                .AddRectFilled(
                    cardOrigin,
                    cardOrigin + new Vector2(cardWidth, cardHeight),
                    CardColor,
                    rounding
                );
            ImGui.SetCursorScreenPos(cardOrigin + new Vector2(pad, pad));
            ImGui.Image(banner.Handle, new Vector2(imgWidth, imgHeight));

            // Advance the layout cursor past the full card so the content below
            // starts at the right position and does not overlap the card.
            ImGui.SetCursorScreenPos(cardOrigin);
            ImGui.Dummy(new Vector2(cardWidth, cardHeight));
        }

        ImGui.Spacing();
        ImGui.TextWrapped(HellionStrings.Wizard_Step1_Subtitle);
        ImGui.Spacing();
        ImGui.TextWrapped(HellionStrings.Wizard_Step1_Footer_Hint);

        DrawFooter(
            showBack: false,
            showSkip: true,
            HellionStrings.Wizard_Nav_Next,
            () => _state.CurrentStep = 2
        );
    }

    private void DrawStepPrivacy()
    {
        ImGui.TextUnformatted(HellionStrings.Wizard_Step2_Title);
        ImGui.Spacing();

        // Reserve footer height (separator + spacing + button row) so the
        // 2x2 grid uses the rest of the window.
        var footerReserve =
            ImGui.GetFrameHeightWithSpacing()
            + ImGui.GetStyle().ItemSpacing.Y * 3
            + ImGui.GetTextLineHeight();
        var grid = ImGui.GetContentRegionAvail();
        var cardWidth = (grid.X - ImGui.GetStyle().ItemSpacing.X) / 2f;
        var cardHeight = (grid.Y - footerReserve - ImGui.GetStyle().ItemSpacing.Y) / 2f;

        // Top row.
        DrawProfileCard(
            PrivacyProfile.PrivacyFirst,
            "🔒",
            HellionStrings.Wizard_Profile_PrivacyFirst_Heading,
            HellionStrings.Wizard_Profile_PrivacyFirst_Description,
            recommended: false,
            cardWidth,
            cardHeight
        );
        ImGui.SameLine();
        DrawProfileCard(
            PrivacyProfile.Casual,
            "💬",
            HellionStrings.Wizard_Profile_Casual_Heading,
            HellionStrings.Wizard_Profile_Casual_Description,
            recommended: true,
            cardWidth,
            cardHeight
        );

        // Bottom row.
        DrawProfileCard(
            PrivacyProfile.Roleplay,
            "🎭",
            HellionStrings.Wizard_Profile_Roleplay_Heading,
            HellionStrings.Wizard_Profile_Roleplay_Description,
            recommended: false,
            cardWidth,
            cardHeight
        );
        ImGui.SameLine();
        DrawProfileCard(
            PrivacyProfile.FullHistory,
            "📚",
            HellionStrings.Wizard_Profile_FullHistory_Heading,
            HellionStrings.Wizard_Profile_FullHistory_Description,
            recommended: false,
            cardWidth,
            cardHeight
        );

        ImGui.Spacing();
        ImGui.TextDisabled(HellionStrings.Wizard_Step2_RecommendedFooter);

        DrawFooter(
            showBack: true,
            showSkip: true,
            HellionStrings.Wizard_Nav_Next,
            () => _state.CurrentStep = 3
        );
    }

    private void DrawProfileCard(
        PrivacyProfile profile,
        string emoji,
        string heading,
        string description,
        bool recommended,
        float width,
        float height
    )
    {
        var isSelected = _state.PendingProfile == profile;
        // GetStyleColorVec4 returns a pointer to the live style entry in
        // Dalamud.Bindings.ImGui, which would require unsafe. Use the U32
        // packed-colour overload of PushColor for the default branch so we
        // can stay in safe code while still matching the current border.
        var borderColor = isSelected
            ? ImGui.GetColorU32(ForgeBronze)
            : ImGui.GetColorU32(ImGuiCol.Border);

        using var _border = ImRaii.PushColor(ImGuiCol.Border, borderColor);
        using var child = ImRaii.Child(
            $"##profile-card-{profile}",
            new Vector2(width, height),
            true
        );
        if (!child.Success)
            return;

        // InvisibleButton over the full card area, then SetCursorScreenPos
        // back to draw the heading/description content on top. Selectable
        // would be semantically wrong here — the card is a standalone
        // choice tile, not a list-item inside a list/menu. The button
        // takes the click for the entire card area, and IsItemHovered()
        // on it (if we wire one up later) would naturally cover the full
        // tile. Visual feedback comes from the border colour above.
        var startPos = ImGui.GetCursorScreenPos();
        var cardArea = ImGui.GetContentRegionAvail();
        if (ImGui.InvisibleButton($"##profile-hit-{profile}", cardArea))
            _state.PendingProfile = profile;

        ImGui.SetCursorScreenPos(startPos);

        ImGui.TextUnformatted($"{emoji}  {heading}{(recommended ? "  ★" : string.Empty)}");
        ImGui.Spacing();
        ImGui.Separator();
        ImGui.Spacing();
        ImGui.TextWrapped(description);
    }

    private void DrawStepPowerSettings()
    {
        // Seed only the two recommendation fields here. Other fields remain
        // null until the user touches the corresponding control.
        // Spec FR-4: the wizard explicitly recommends LoadPreviousSession =
        // true and FilterIncludePreviousSessions = true (Config defaults are
        // false). The other four fields (AutoTellTabsHistoryPreload,
        // UseCompactDensity, PrettierTimestamps, Theme) follow the generic
        // null-semantics from Spec Z.176: a null pending means the user did
        // not touch that control, so CommitPending must not write back. They
        // are read live from Plugin.Config below for the ImGui ref-binding
        // but never seeded into Pending* without a user gesture.
        _state.PendingLoadPreviousSession ??= true;
        _state.PendingFilterIncludePreviousSessions ??= true;

        ImGui.TextUnformatted(HellionStrings.Wizard_Step3_Title);
        ImGui.Spacing();

        // History section.
        using (ImRaii.PushColor(ImGuiCol.Text, ForgeBronze))
            ImGui.TextUnformatted(HellionStrings.Wizard_Step3_Section_History);

        var loadPrev = _state.PendingLoadPreviousSession ?? true;
        if (ImGui.Checkbox(HellionStrings.Wizard_Step3_LoadPreviousSession_Label, ref loadPrev))
        {
            _state.PendingLoadPreviousSession = loadPrev;
            // Mirror the DataAndPrivacy coupling: turning load-previous on
            // also turns filter-include on (otherwise old messages bypass
            // the filter chain), and turning filter-include off forces
            // load-previous off. Same idiom as Ui/SettingsTabs/DataAndPrivacy.cs.
            if (loadPrev)
                _state.PendingFilterIncludePreviousSessions = true;
        }

        var filterPrev = _state.PendingFilterIncludePreviousSessions ?? true;
        if (
            ImGui.Checkbox(
                HellionStrings.Wizard_Step3_FilterIncludePreviousSessions_Label,
                ref filterPrev
            )
        )
        {
            _state.PendingFilterIncludePreviousSessions = filterPrev;
            if (!filterPrev)
                _state.PendingLoadPreviousSession = false;
        }

        ImGui.Spacing();

        // Tell-Tabs section.
        using (ImRaii.PushColor(ImGuiCol.Text, ForgeBronze))
            ImGui.TextUnformatted(HellionStrings.Wizard_Step3_Section_TellTabs);

        var preload =
            _state.PendingAutoTellTabsHistoryPreload ?? Plugin.Config.AutoTellTabsHistoryPreload;
        if (
            ImGui.SliderInt(
                HellionStrings.Wizard_Step3_AutoTellTabsHistoryPreload_Label,
                ref preload,
                0,
                100
            )
        )
            _state.PendingAutoTellTabsHistoryPreload = preload;

        ImGui.Spacing();

        // Visual section.
        using (ImRaii.PushColor(ImGuiCol.Text, ForgeBronze))
            ImGui.TextUnformatted(HellionStrings.Wizard_Step3_Section_Visual);

        var compact = _state.PendingUseCompactDensity ?? Plugin.Config.UseCompactDensity;
        if (ImGui.Checkbox(HellionStrings.Wizard_Step3_UseCompactDensity_Label, ref compact))
            _state.PendingUseCompactDensity = compact;

        var pretty = _state.PendingPrettierTimestamps ?? Plugin.Config.PrettierTimestamps;
        if (ImGui.Checkbox(HellionStrings.Wizard_Step3_PrettierTimestamps_Label, ref pretty))
            _state.PendingPrettierTimestamps = pretty;

        // Theme dropdown — built-ins only. Custom themes are power-user
        // territory and would clutter the first-run flow.
        var currentSlug = _state.PendingTheme ?? Plugin.Config.Theme;
        var builtIns = Plugin.ThemeRegistry.AllBuiltIns().ToList();
        var currentIndex = builtIns.FindIndex(t =>
            string.Equals(t.Slug, currentSlug, StringComparison.OrdinalIgnoreCase)
        );
        if (currentIndex < 0)
            currentIndex = 0;

        using (
            var combo = ImRaii.Combo(
                HellionStrings.Wizard_Step3_Theme_Label,
                builtIns[currentIndex].Name
            )
        )
        {
            if (combo.Success)
            {
                for (var i = 0; i < builtIns.Count; i++)
                {
                    var isSelected = i == currentIndex;
                    if (ImGui.Selectable(builtIns[i].Name, isSelected))
                        _state.PendingTheme = builtIns[i].Slug;
                    if (isSelected)
                        ImGui.SetItemDefaultFocus();
                }
            }
        }

        DrawFooter(
            showBack: true,
            showSkip: true,
            HellionStrings.Wizard_Nav_Next,
            () => _state.CurrentStep = 4
        );
    }

    private void DrawStepDone()
    {
        ImGui.TextUnformatted(HellionStrings.Wizard_Step4_Title);
        ImGui.Spacing();

        // ✓ symbol, centred-ish via dummy padding.
        var checkmark = "✓";
        var checkSize = ImGui.CalcTextSize(checkmark);
        var avail = ImGui.GetContentRegionAvail();
        ImGui.Dummy(new Vector2((avail.X - checkSize.X) * 0.5f, 0));
        ImGui.SameLine();
        using (ImRaii.PushColor(ImGuiCol.Text, ForgeBronze))
            ImGui.TextUnformatted(checkmark);

        ImGui.Spacing();
        ImGui.Separator();
        ImGui.Spacing();

        // Summary card.
        using (var summary = ImRaii.Child("##wizard-summary", new Vector2(-1, 130), true))
        {
            if (summary.Success)
            {
                ImGui.TextUnformatted(HellionStrings.Wizard_Step4_SummaryHeading);
                ImGui.Spacing();
                ImGui.Separator();
                ImGui.Spacing();

                var profileLabel = _state.PendingProfile switch
                {
                    PrivacyProfile.PrivacyFirst =>
                        HellionStrings.Wizard_Profile_PrivacyFirst_Heading,
                    PrivacyProfile.Casual => HellionStrings.Wizard_Profile_Casual_Heading,
                    PrivacyProfile.Roleplay => HellionStrings.Wizard_Profile_Roleplay_Heading,
                    PrivacyProfile.FullHistory => HellionStrings.Wizard_Profile_FullHistory_Heading,
                    _ => HellionStrings.Wizard_Step4_Summary_Unchanged,
                };
                ImGui.TextWrapped(
                    string.Format(HellionStrings.Wizard_Step4_Summary_Profile, profileLabel)
                );

                var historyLabel =
                    (_state.PendingLoadPreviousSession ?? false)
                        ? HellionStrings.Wizard_Step3_LoadPreviousSession_Label
                        : HellionStrings.Wizard_Step4_Summary_Unchanged;
                ImGui.TextWrapped(
                    string.Format(HellionStrings.Wizard_Step4_Summary_History, historyLabel)
                );

                var preloadValue =
                    _state.PendingAutoTellTabsHistoryPreload
                    ?? Plugin.Config.AutoTellTabsHistoryPreload;
                ImGui.TextWrapped(
                    string.Format(HellionStrings.Wizard_Step4_Summary_TellTabs, preloadValue)
                );

                var compact = _state.PendingUseCompactDensity ?? Plugin.Config.UseCompactDensity;
                var pretty = _state.PendingPrettierTimestamps ?? Plugin.Config.PrettierTimestamps;
                var themeSlug = _state.PendingTheme ?? Plugin.Config.Theme;
                var themeName = Plugin.ThemeRegistry.Get(themeSlug).Name;
                var visualParts = new List<string>();
                if (compact)
                    visualParts.Add(HellionStrings.Wizard_Step3_UseCompactDensity_Label);
                if (pretty)
                    visualParts.Add(HellionStrings.Wizard_Step3_PrettierTimestamps_Label);
                visualParts.Add(themeName);
                ImGui.TextWrapped(
                    string.Format(
                        HellionStrings.Wizard_Step4_Summary_Visual,
                        string.Join(", ", visualParts)
                    )
                );
            }
        }

        ImGui.Spacing();

        // Inline FR-3 hint with placeholder for preload count.
        var preloadForHint =
            _state.PendingAutoTellTabsHistoryPreload ?? Plugin.Config.AutoTellTabsHistoryPreload;
        using (ImRaii.PushColor(ImGuiCol.Text, ForgeBronze))
            ImGui.TextWrapped(string.Format(HellionStrings.Wizard_Step4_TestHint, preloadForHint));

        ImGui.Spacing();
        ImGui.TextDisabled(HellionStrings.Wizard_Step4_SettingsHint);

        DrawFooter(
            showBack: true,
            showSkip: false,
            HellionStrings.Wizard_Nav_Finish,
            () =>
            {
                CommitPending();
                Plugin.Config.FirstRunCompleted = true;
                Plugin.SaveConfig();
                IsOpen = false;
            }
        );
    }

    // Writes only non-null pending values back to Config. A null pending
    // means the user did not touch that step's control, so the existing
    // Config value is preserved. Theme switch goes through ThemeRegistry
    // so the active palette updates live for the rest of the session.
    internal void CommitPending()
    {
        switch (_state.PendingProfile)
        {
            case PrivacyProfile.PrivacyFirst:
                ApplyPrivacyFirst();
                break;
            case PrivacyProfile.Casual:
                ApplyCasual();
                break;
            case PrivacyProfile.Roleplay:
                ApplyRoleplay();
                break;
            case PrivacyProfile.FullHistory:
                ApplyFullHistory();
                break;
        }

        if (_state.PendingLoadPreviousSession.HasValue)
            Plugin.Config.LoadPreviousSession = _state.PendingLoadPreviousSession.Value;

        if (_state.PendingFilterIncludePreviousSessions.HasValue)
            Plugin.Config.FilterIncludePreviousSessions = _state
                .PendingFilterIncludePreviousSessions
                .Value;

        if (_state.PendingAutoTellTabsHistoryPreload.HasValue)
            Plugin.Config.AutoTellTabsHistoryPreload = _state
                .PendingAutoTellTabsHistoryPreload
                .Value;

        if (_state.PendingUseCompactDensity.HasValue)
            Plugin.Config.UseCompactDensity = _state.PendingUseCompactDensity.Value;

        if (_state.PendingPrettierTimestamps.HasValue)
            Plugin.Config.PrettierTimestamps = _state.PendingPrettierTimestamps.Value;

        if (!string.IsNullOrWhiteSpace(_state.PendingTheme))
        {
            Plugin.Config.Theme = _state.PendingTheme;
            Plugin.ThemeRegistry.Switch(_state.PendingTheme);
        }
    }

    private void ApplyPrivacyFirst()
    {
        Plugin.Config.PrivacyFilterEnabled = true;
        Plugin.Config.PrivacyPersistChannels = [.. PrivacyDefaults.PrivacyFirstWhitelist];
        Plugin.Config.PrivacyPersistUnknownChannels = false;

        Plugin.Config.RetentionEnabled = true;
        Plugin.Config.RetentionDefaultDays = 30;
        Plugin.Config.RetentionPerChannelDays = PrivacyDefaults.DefaultRetentionDays.ToDictionary(
            p => p.Key,
            p => p.Value
        );
    }

    private void ApplyCasual()
    {
        Plugin.Config.PrivacyFilterEnabled = true;
        Plugin.Config.PrivacyPersistChannels = [.. PrivacyDefaults.CasualWhitelist];
        Plugin.Config.PrivacyPersistUnknownChannels = false;

        Plugin.Config.RetentionEnabled = true;
        Plugin.Config.RetentionDefaultDays = 30;
        var policy = PrivacyDefaults.DefaultRetentionDays.ToDictionary(p => p.Key, p => p.Value);
        foreach (var (type, days) in PrivacyDefaults.CasualRetentionOverrides)
            policy[type] = days;
        Plugin.Config.RetentionPerChannelDays = policy;
    }

    private void ApplyRoleplay()
    {
        Plugin.Config.PrivacyFilterEnabled = true;
        Plugin.Config.PrivacyPersistChannels = [.. PrivacyDefaults.RoleplayWhitelist];
        Plugin.Config.PrivacyPersistUnknownChannels = false;

        Plugin.Config.RetentionEnabled = true;
        Plugin.Config.RetentionDefaultDays = 30;
        var policy = PrivacyDefaults.DefaultRetentionDays.ToDictionary(p => p.Key, p => p.Value);
        foreach (var (type, days) in PrivacyDefaults.RoleplayRetentionOverrides)
            policy[type] = days;
        Plugin.Config.RetentionPerChannelDays = policy;
    }

    private void ApplyFullHistory()
    {
        // Full history = upstream Chat 2 behavior. Filter off, retention off,
        // everything (except battle messages, which Chat 2 itself controls)
        // accumulates indefinitely.
        Plugin.Config.PrivacyFilterEnabled = false;
        Plugin.Config.PrivacyPersistUnknownChannels = true;

        Plugin.Config.RetentionEnabled = false;
        Plugin.Config.RetentionPerChannelDays.Clear();
    }

    // Test-only entry point so SelfTests/WizardStateSmokeStep can advance
    // the state machine without spawning ImGui input events.
    internal void TestOnly_AdvanceTo(int step) =>
        _state.CurrentStep = Math.Clamp(step, 1, TotalSteps);

    // Test-only setter so the smoke-test can pin a profile selection
    // without driving the ImGui card-click path.
    internal void TestOnly_SetPendingProfile(PrivacyProfile profile) =>
        _state.PendingProfile = profile;

    internal enum PrivacyProfile
    {
        PrivacyFirst,
        Casual,
        Roleplay,
        FullHistory,
    }

    private sealed class WizardState
    {
        public int CurrentStep { get; set; } = 1;
        public PrivacyProfile? PendingProfile { get; set; }
        public bool? PendingLoadPreviousSession { get; set; }
        public bool? PendingFilterIncludePreviousSessions { get; set; }
        public int? PendingAutoTellTabsHistoryPreload { get; set; }
        public bool? PendingUseCompactDensity { get; set; }
        public bool? PendingPrettierTimestamps { get; set; }
        public string? PendingTheme { get; set; }
    }
}
