@namespace Lumeo.Docs.Shared
@using Blazicons
@using Lumeo.Theming
@inject IJSRuntime JS
@inject IconService _iconService
@inject Lumeo.Services.ThemeService ThemeService
@inject Lumeo.Services.KeyboardShortcutService Shortcuts
@implements IAsyncDisposable


<!-- Click-outside backdrop for popover -->
@if (_expandedSection != "")
{
    <div class="fixed inset-0 z-30" @onclick='() => _expandedSection = ""'></div>
}

<!-- Customizer Sidebar -->
@if (IsOpen)
{
<aside class="@(IsMobile ? "relative flex flex-col w-full flex-1 overflow-y-auto" : "relative w-[260px] shrink-0 m-3 rounded-[var(--radius-xl)] border border-border/60 shadow-lg bg-card sticky top-[84px] overflow-visible z-40 flex flex-col")" style="@(IsMobile ? "" : "max-height: calc(100vh - 100px)")">

    <!-- Popover menu (rendered at aside level, outside scroll container) -->
    @if (_expandedSection != "")
    {
        <div class="absolute right-[calc(100%+6px)] rounded-[var(--radius-lg)] border border-border bg-card shadow-lg overflow-y-auto z-50 @GetPopoverWidthClass()"
             style="top: @(GetSectionTopPx())px; max-height: calc(100vh - 3.5rem - @(GetSectionTopPx())px - 16px)">
            <div class="px-3 py-2 border-b border-border">
                <Lumeo.Text Size="xs" Weight="semibold">@GetSectionTitle()</Lumeo.Text>
            </div>
            <div class="p-1.5">
                @switch (_expandedSection)
                {
                    case "preset":
                        @foreach (var preset in _presets)
                        {
                            <Button Variant="Button.ButtonVariant.Ghost" Class="w-full flex items-center gap-2.5 rounded-md px-2 py-2 text-left hover:bg-accent/50 transition-colors"
                                    OnClick="() => ApplyPreset(preset)">
                                <Lumeo.Text As="span" Class="flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-muted-foreground/30">
                                    @if (preset.Name == GetActivePresetName())
                                    {
                                        <Lumeo.Text As="span" Class="h-2 w-2 rounded-full bg-foreground"></Lumeo.Text>
                                    }
                                </Lumeo.Text>
                                <div class="flex-1 min-w-0">
                                    <Lumeo.Text Class="text-[13px] font-medium">@preset.Name</Lumeo.Text>
                                    <Lumeo.Text Class="text-[11px] text-muted-foreground leading-tight">@preset.Description</Lumeo.Text>
                                </div>
                                @if (preset.Name == GetActivePresetName())
                                {
                                    <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5 shrink-0 text-foreground" />
                                }
                            </Button>
                        }
                        break;

                    case "style":
                        @foreach (var style in _styles)
                        {
                            <Button Variant="Button.ButtonVariant.Ghost" Class="w-full flex items-center gap-2.5 rounded-md px-2 py-2 text-left hover:bg-accent/50 transition-colors"
                                    OnClick="() => SetStyle(style.Value)">
                                <Lumeo.Text As="span" Class="flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-muted-foreground/30">
                                    @if (style.Value == _activeStyle)
                                    {
                                        <Lumeo.Text As="span" Class="h-2 w-2 rounded-full bg-foreground"></Lumeo.Text>
                                    }
                                </Lumeo.Text>
                                <div class="flex-1 min-w-0">
                                    <Lumeo.Text Class="text-[13px] font-medium">@style.Name</Lumeo.Text>
                                    <Lumeo.Text Class="text-[11px] text-muted-foreground leading-tight">@style.Description</Lumeo.Text>
                                </div>
                                @if (style.Value == _activeStyle)
                                {
                                    <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5 shrink-0 text-foreground" />
                                }
                            </Button>
                        }
                        break;

                    case "baseColor":
                        @foreach (var color in _baseColors)
                        {
                            <Button Variant="Button.ButtonVariant.Ghost" Class="w-full flex items-center gap-2.5 rounded-md px-2 py-1.5 text-left hover:bg-accent/50 transition-colors"
                                    OnClick="() => SetBaseColor(color.Value)">
                                <span class="h-3 w-3 rounded-full shrink-0" style="background: @color.Preview"></span>
                                <Lumeo.Text As="span" Class="text-[13px] text-foreground flex-1">@color.Name</Lumeo.Text>
                                @if (color.Value == _activeBaseColor)
                                {
                                    <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5 shrink-0 text-foreground" />
                                }
                            </Button>
                        }
                        break;

                    case "theme":
                        @foreach (var theme in _themes)
                        {
                            <Button Variant="Button.ButtonVariant.Ghost" Class="w-full flex items-center gap-2.5 rounded-md px-2 py-1.5 text-left hover:bg-accent/50 transition-colors"
                                    OnClick="() => SetTheme(theme.Value)">
                                <span class="h-3 w-3 rounded-full shrink-0" style="background: @theme.Color"></span>
                                <Lumeo.Text As="span" Class="text-[13px] text-foreground flex-1">@theme.Name</Lumeo.Text>
                                @if (theme.Value == _activeTheme)
                                {
                                    <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5 shrink-0 text-foreground" />
                                }
                            </Button>
                        }
                        break;

                    case "font":
                        @foreach (var font in _fonts)
                        {
                            <Button Variant="Button.ButtonVariant.Ghost" Class="w-full flex items-center gap-2.5 rounded-md px-2 py-1.5 text-left hover:bg-accent/50 transition-colors"
                                    OnClick="() => SetFont(font.Value)">
                                <span class="text-[13px] text-foreground flex-1" style="font-family: @font.Family">@font.Name</span>
                                <Lumeo.Text As="span" Class="text-[11px] text-muted-foreground">@font.Label</Lumeo.Text>
                                @if (font.Value == _activeFont)
                                {
                                    <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5 shrink-0 text-foreground" />
                                }
                            </Button>
                        }
                        break;

                    case "icons":
                        @foreach (var lib in _iconLibraries)
                        {
                            <button class="w-full rounded-md px-2 py-2 text-left hover:bg-accent/50 transition-colors @(lib.Value == _activeIconLibrary ? "bg-accent/30" : "")"
                                    @onclick="() => SetIconLibrary(lib.Value)">
                                <Flex Class="items-center justify-between mb-1.5">
                                    <div class="min-w-0">
                                        <Lumeo.Text As="span" Class="text-[13px] font-medium text-foreground">@lib.Name</Lumeo.Text>
                                        <Lumeo.Text As="span" Class="text-[10px] text-muted-foreground ml-1.5">@lib.Package</Lumeo.Text>
                                    </div>
                                    @if (lib.Value == _activeIconLibrary)
                                    {
                                        <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5 shrink-0 text-foreground" />
                                    }
                                </Flex>
                                <IconPreview Library="@lib.Value" Count="12" />
                            </button>
                        }
                        break;

                    case "radius":
                        @foreach (var radius in _radiiOptions)
                        {
                            <Button Variant="Button.ButtonVariant.Ghost" Class="w-full flex items-center gap-2.5 rounded-md px-2 py-1.5 text-left hover:bg-accent/50 transition-colors"
                                    OnClick="() => SetRadius(radius.Value)">
                                <Lumeo.Text As="span" Class="inline-flex items-center justify-center h-3.5 w-3.5 shrink-0">
                                    <span class="h-3 w-3 border-t-2 border-l-2 border-muted-foreground/40" style="border-radius: @(radius.Value)rem 0 0 0"></span>
                                </Lumeo.Text>
                                <Lumeo.Text As="span" Class="text-[13px] text-foreground flex-1">@radius.Name</Lumeo.Text>
                                <Lumeo.Text As="span" Class="text-[11px] text-muted-foreground">@(radius.Value)rem</Lumeo.Text>
                                @if (radius.Value == _activeRadius)
                                {
                                    <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5 shrink-0 text-foreground" />
                                }
                            </Button>
                        }
                        break;

                    case "menuColor":
                        @foreach (var mc in _menuColors)
                        {
                            <Button Variant="Button.ButtonVariant.Ghost" Class="w-full flex items-center gap-2.5 rounded-md px-2 py-1.5 text-left hover:bg-accent/50 transition-colors"
                                    OnClick="() => SetMenuColor(mc.Value)">
                                <Lumeo.Text As="span" Class="text-[13px] text-foreground flex-1">@mc.Name</Lumeo.Text>
                                @if (mc.Value == _activeMenuColor)
                                {
                                    <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5 shrink-0 text-foreground" />
                                }
                            </Button>
                        }
                        break;

                    case "menuAccent":
                        @foreach (var ma in _menuAccents)
                        {
                            <Button Variant="Button.ButtonVariant.Ghost" Class="w-full flex items-center gap-2.5 rounded-md px-2 py-1.5 text-left hover:bg-accent/50 transition-colors"
                                    OnClick="() => SetMenuAccent(ma.Value)">
                                <div class="flex-1 min-w-0">
                                    <Lumeo.Text Class="text-[13px] font-medium">@ma.Name</Lumeo.Text>
                                    <Lumeo.Text Class="text-[11px] text-muted-foreground leading-tight">@ma.Description</Lumeo.Text>
                                </div>
                                @if (ma.Value == _activeMenuAccent)
                                {
                                    <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5 shrink-0 text-foreground" />
                                }
                            </Button>
                        }
                        break;
                }
            </div>
        </div>
    }

    <!-- Header -->
    <div class="px-4 py-3 border-b border-border bg-card">
        <Flex Class="items-center justify-between">
            <Flex Class="items-center gap-2">
                <svg width="16" height="16" viewBox="0 0 32 32" fill="none" class="text-primary">
                    <circle cx="16" cy="16" r="9" fill="none" stroke="currentColor" stroke-width="2" opacity="0.55"/>
                    <circle cx="16" cy="16" r="3" fill="currentColor"/>
                </svg>
                <Lumeo.Text As="span" Size="sm" Weight="semibold">/ create</Lumeo.Text>
            </Flex>
            <button class="inline-flex items-center justify-center h-6 w-6 rounded text-muted-foreground/60 hover:text-foreground hover:bg-accent transition-colors"
                    title="Close customizer"
                    @onclick="() => IsOpenChanged.InvokeAsync(false)">
                <Blazicon Svg="Lucide.X" class="h-3.5 w-3.5" />
            </button>
        </Flex>
        <Lumeo.Text Size="xs" Color="muted" Class="mt-0.5">Adjust Lumeo styles</Lumeo.Text>
    </div>

    <!-- Settings List -->
    <div class="flex-1 overflow-y-auto bg-card">

        <!-- Preset -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left @(_expandedSection == "preset" ? "bg-accent/40" : "")"
                @onclick='() => ToggleSection("preset")'>
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Preset</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@GetActivePresetName()</Lumeo.Text>
            </Stack>
        </button>

        <!-- Style -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left @(_expandedSection == "style" ? "bg-accent/40" : "")"
                @onclick='() => ToggleSection("style")'>
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Style</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@(_activeStyle == "new-york" ? "New York" : "Default")</Lumeo.Text>
            </Stack>
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" class="text-muted-foreground/50">
                <circle cx="8" cy="8" r="5" stroke="currentColor" stroke-width="1.5"/>
            </svg>
        </button>

        <!-- Base Color -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left @(_expandedSection == "baseColor" ? "bg-accent/40" : "")"
                @onclick='() => ToggleSection("baseColor")'>
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Base Color</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@GetActiveBaseColorName()</Lumeo.Text>
            </Stack>
            <span class="h-4 w-4 rounded-full" style="background: @GetActiveBaseColorPreview()"></span>
        </button>

        <!-- Theme -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left @(_expandedSection == "theme" ? "bg-accent/40" : "")"
                @onclick='() => ToggleSection("theme")'>
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Theme</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@GetActiveThemeName()</Lumeo.Text>
            </Stack>
            <span class="h-4 w-4 rounded-full" style="background: @GetActiveThemePreview()"></span>
        </button>

        <!-- Font -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left @(_expandedSection == "font" ? "bg-accent/40" : "")"
                @onclick='() => ToggleSection("font")'>
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Font</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@GetActiveFontName()</Lumeo.Text>
            </Stack>
            <Lumeo.Text As="span" Class="text-[11px] font-bold text-muted-foreground/50 leading-none">Aa</Lumeo.Text>
        </button>

        <!-- Icon Library -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left @(_expandedSection == "icons" ? "bg-accent/40" : "")"
                @onclick='() => ToggleSection("icons")'>
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Icon Library</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@GetActiveIconLibraryName()</Lumeo.Text>
            </Stack>
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" class="text-muted-foreground/50">
                <rect x="2" y="2" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/>
                <rect x="9" y="2" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/>
                <rect x="2" y="9" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/>
                <rect x="9" y="9" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/>
            </svg>
        </button>

        <!-- Radius -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left @(_expandedSection == "radius" ? "bg-accent/40" : "")"
                @onclick='() => ToggleSection("radius")'>
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Radius</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@GetActiveRadiusName()</Lumeo.Text>
            </Stack>
            <Lumeo.Text As="span" Class="inline-flex items-center justify-center h-4 w-4">
                <span class="h-3.5 w-3.5 border-t-2 border-l-2 border-muted-foreground/50" style="border-radius: @(_activeRadius)rem 0 0 0"></span>
            </Lumeo.Text>
        </button>

        <!-- Menu Color -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left @(_expandedSection == "menuColor" ? "bg-accent/40" : "")"
                @onclick='() => ToggleSection("menuColor")'>
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Menu Color</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@(_activeMenuColor == "dark" ? "Dark" : _activeMenuColor == "light" ? "Light" : "Default")</Lumeo.Text>
            </Stack>
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" class="text-muted-foreground/50">
                <rect x="2" y="2" width="4" height="12" rx="1" fill="currentColor" opacity="0.4"/>
                <rect x="7" y="2" width="7" height="12" rx="1" stroke="currentColor" stroke-width="1.5"/>
            </svg>
        </button>

        <!-- Menu Accent -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left @(_expandedSection == "menuAccent" ? "bg-accent/40" : "")"
                @onclick='() => ToggleSection("menuAccent")'>
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Menu Accent</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@(_activeMenuAccent == "bold" ? "Bold" : _activeMenuAccent == "outline" ? "Outline" : "Subtle")</Lumeo.Text>
            </Stack>
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" class="text-muted-foreground/50">
                <path d="M3 8a5 5 0 0 1 10 0" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
                <circle cx="8" cy="11" r="2" fill="currentColor" opacity="0.5"/>
            </svg>
        </button>

        <!-- Mode -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left"
                @onclick="ToggleDarkMode">
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Mode</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">@(_darkMode ? "Dark" : "Light")</Lumeo.Text>
            </Stack>
            @if (_darkMode)
            {
                <Blazicon Svg="Lucide.Moon" class="h-4 w-4 text-muted-foreground/50" />
            }
            else
            {
                <Blazicon Svg="Lucide.Sun" class="h-4 w-4 text-muted-foreground/50" />
            }
        </button>

        <!-- Spacer before actions -->
        <div class="my-1 border-t border-border/40"></div>

        <!-- Shuffle -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left"
                @onclick="ShuffleSettings">
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Shuffle</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">Try Random</Lumeo.Text>
            </Stack>
            <Kbd>R</Kbd>
        </button>

        <!-- Reset -->
        <button class="w-full flex items-center justify-between px-4 py-2.5 hover:bg-accent/40 transition-colors text-left"
                @onclick="ResetSettings">
            <Stack Gap="0">
                <Lumeo.Text Class="text-[11px] text-muted-foreground leading-none">Reset</Lumeo.Text>
                <Lumeo.Text Class="text-[13px] font-semibold mt-0.5">Start Over</Lumeo.Text>
            </Stack>
            <Blazicon Svg="Lucide.RotateCcw" class="h-4 w-4 text-muted-foreground/50" />
        </button>
    </div>

    <!-- Single share action — copies `lumeo apply --preset <code>` to the clipboard.
         The 6-char code encodes every customizer selection deterministically, so the
         same configuration always shares the same code. -->
    <div class="px-4 py-3 border-t border-border bg-card">
        <Button Class="w-full flex items-center justify-center gap-2 rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
                OnClick="SharePreset">
            @if (_shareCopied)
            {
                <Blazicon Svg="Lucide.Check" class="h-3.5 w-3.5" />
                <Lumeo.Text As="span">Copied — <span class="font-mono text-xs opacity-80">@GetPresetCode()</span></Lumeo.Text>
            }
            else
            {
                <Blazicon Svg="Lucide.Share2" class="h-3.5 w-3.5" />
                <Lumeo.Text As="span">Share preset <span class="font-mono text-xs opacity-70">(@GetPresetCode())</span></Lumeo.Text>
            }
        </Button>
    </div>
</aside>
}

@code {
    [Parameter] public bool IsOpen { get; set; } = true;
    [Parameter] public EventCallback<bool> IsOpenChanged { get; set; }
    [Parameter] public bool IsMobile { get; set; }

    private readonly List<IAsyncDisposable> _kbdShortcuts = new();
    private string _expandedSection = "";
    private bool _darkMode;
    private bool _shareCopied;

    private string _activeTheme = "";
    private string _activeRadius = "0.75";
    private string _activeStyle = "default";
    private string _activeBaseColor = "slate";
    private string _activeFont = "system";
    private string _activeIconLibrary = "lucide";
    private string _activeMenuColor = "default";
    private string _activeMenuAccent = "subtle";

    private record ThemeOption(string Name, string Value, string Color);
    private record StyleOption(string Name, string Value, string Description);
    private record BaseColorOption(string Name, string Value, string Preview);
    private record RadiusOption(string Name, string Value);
    private record FontOption(string Name, string Value, string Family, string Label);
    private record IconLibraryOption(string Name, string Value, string Package);
    private record MenuColorOption(string Name, string Value);
    private record MenuAccentOption(string Name, string Value, string Description);
    private record PresetOption(string Name, string ThemeValue, string StyleValue, string BaseColorValue, string RadiusValue, string FontValue, string MenuColorValue, string MenuAccentValue, string Preview, string Description);

    private readonly ThemeOption[] _themes =
    [
        new("Default", "", "hsl(240 4% 46%)"),
        new("Blue", "blue", "hsl(221 83% 53%)"),
        new("Orange", "orange", "hsl(14 70% 50%)"),
        new("Green", "green", "hsl(142 71% 45%)"),
        new("Rose", "rose", "hsl(346 77% 50%)"),
        new("Zinc", "zinc", "hsl(240 6% 10%)"),
        new("Violet", "violet", "hsl(262 83% 58%)"),
        new("Amber", "amber", "hsl(38 92% 50%)"),
        new("Teal", "teal", "hsl(173 58% 39%)"),
    ];

    private readonly StyleOption[] _styles =
    [
        new("Default", "default", "The classic shadcn/ui look. Clean, neutral, and familiar."),
        new("New York", "new-york", "Reduced padding and margins for compact layouts."),
    ];

    private readonly BaseColorOption[] _baseColors =
    [
        new("Slate", "slate", "hsl(215 16% 47%)"),
        new("Gray", "gray", "hsl(220 9% 46%)"),
        new("Zinc", "zinc", "hsl(240 4% 46%)"),
        new("Neutral", "neutral", "hsl(0 0% 45%)"),
        new("Stone", "stone", "hsl(25 5% 45%)"),
    ];

    private readonly RadiusOption[] _radiiOptions =
    [
        new("None", "0"),
        new("Small", "0.25"),
        new("Default", "0.5"),
        new("Medium", "0.75"),
        new("Large", "1"),
    ];

    private readonly FontOption[] _fonts =
    [
        new("System", "system", "system-ui, -apple-system, sans-serif", "default"),
        new("Inter", "inter", "'Inter', sans-serif", "sans"),
        new("Geist", "geist", "'Geist', sans-serif", "sans"),
        new("IBM Plex Sans", "ibm-plex-sans", "'IBM Plex Sans', sans-serif", "sans"),
        new("JetBrains Mono", "jetbrains-mono", "'JetBrains Mono', monospace", "mono"),
        new("Fira Code", "fira-code", "'Fira Code', monospace", "mono"),
    ];

    private readonly IconLibraryOption[] _iconLibraries =
    [
        new("Lucide", "lucide", "Blazicons.Lucide"),
        new("Bootstrap", "bootstrap", "Blazicons.Bootstrap"),
        new("Fluent UI", "fluentui", "Blazicons.FluentUI"),
        new("Font Awesome", "font-awesome", "Blazicons.FontAwesome"),
        new("Google Material", "google-material", "Blazicons.GoogleMaterialDesign"),
        new("Material Design", "material-design", "Blazicons.MaterialDesignIcons"),
        new("Ionicons", "ionicons", "Blazicons.Ionicons"),
        new("Devicon", "devicon", "Blazicons.Devicon"),
        new("Flag Icons", "flag-icons", "Blazicons.FlagIcons"),
    ];

    private readonly MenuColorOption[] _menuColors =
    [
        new("Default", "default"),
        new("Dark", "dark"),
        new("Light", "light"),
    ];

    private readonly MenuAccentOption[] _menuAccents =
    [
        new("Subtle", "subtle", "Soft tinted active state"),
        new("Bold", "bold", "Primary color active state"),
        new("Outline", "outline", "Bordered transparent active state"),
    ];

    private readonly PresetOption[] _presets =
    [
        new("Vega", "", "default", "slate", "0.75", "system", "default", "subtle", "hsl(240 4% 46%)", "The classic shadcn/ui look. Clean, neutral, and familiar."),
        new("Nova", "blue", "new-york", "slate", "0.5", "inter", "default", "subtle", "hsl(221 83% 53%)", "Sleek and compact. Blue with tight spacing."),
        new("Maia", "rose", "default", "stone", "1", "system", "default", "subtle", "hsl(346 77% 50%)", "Soft and rounded, with generous spacing."),
        new("Lyra", "zinc", "default", "zinc", "0", "jetbrains-mono", "default", "subtle", "hsl(240 6% 10%)", "Boxy and sharp. Pairs well with mono fonts."),
        new("Mira", "green", "new-york", "gray", "0.25", "system", "default", "bold", "hsl(142 71% 45%)", "Compact. Made for dense interfaces."),
    ];

    // Section order matches the sidebar layout — index * row height + header height
    private static readonly string[] _sectionOrder = ["preset", "style", "baseColor", "theme", "font", "icons", "radius", "menuColor", "menuAccent"];
    private const int HeaderHeight = 57; // px: header area (px-4 py-3 + border)
    private const int RowHeight = 40;    // px: each button row (py-2.5)

    private int GetSectionTopPx()
    {
        var index = Array.IndexOf(_sectionOrder, _expandedSection);
        if (index < 0) index = 0;
        return HeaderHeight + (index * RowHeight);
    }

    private string GetSectionTitle() => _expandedSection switch
    {
        "preset" => "Preset",
        "style" => "Style",
        "baseColor" => "Base Color",
        "theme" => "Theme",
        "font" => "Font",
        "icons" => "Icon Library",
        "radius" => "Radius",
        "menuColor" => "Menu Color",
        "menuAccent" => "Menu Accent",
        _ => ""
    };

    private string GetPopoverWidthClass() => _expandedSection switch
    {
        "icons" => "w-[300px]",
        "preset" or "style" or "menuAccent" => "w-[260px]",
        "font" => "w-[240px]",
        _ => "w-[220px]"
    };

    private string GetActivePresetName()
    {
        foreach (var p in _presets)
            if (p.ThemeValue == _activeTheme && p.StyleValue == _activeStyle && p.BaseColorValue == _activeBaseColor && p.RadiusValue == _activeRadius)
                return p.Name;
        return "Custom";
    }

    private string GetActiveThemeName() => _themes.FirstOrDefault(t => t.Value == _activeTheme)?.Name ?? "Default";
    private string GetActiveThemePreview() => _themes.FirstOrDefault(t => t.Value == _activeTheme)?.Color ?? "hsl(14 70% 50%)";
    private string GetActiveBaseColorName() => _baseColors.FirstOrDefault(c => c.Value == _activeBaseColor)?.Name ?? "Slate";
    private string GetActiveBaseColorPreview() => _baseColors.FirstOrDefault(c => c.Value == _activeBaseColor)?.Preview ?? "hsl(215 16% 47%)";
    private string GetActiveRadiusName() => _radiiOptions.FirstOrDefault(r => r.Value == _activeRadius)?.Name ?? _activeRadius + "rem";
    private string GetActiveFontName() => _fonts.FirstOrDefault(f => f.Value == _activeFont)?.Name ?? "System";
    private string GetActiveIconLibraryName() => _iconLibraries.FirstOrDefault(l => l.Value == _activeIconLibrary)?.Name ?? "Lucide";

    private void ToggleSection(string section) => _expandedSection = _expandedSection == section ? "" : section;

    private async Task ApplyPreset(PresetOption preset)
    {
        await SetTheme(preset.ThemeValue);
        await SetStyle(preset.StyleValue);
        await SetBaseColor(preset.BaseColorValue);
        await SetRadius(preset.RadiusValue);
        await SetFont(preset.FontValue);
        await SetMenuColor(preset.MenuColorValue);
        await SetMenuAccent(preset.MenuAccentValue);
        _expandedSection = "";
    }

    private async Task SetStyle(string style)
    {
        _activeStyle = style;
        await JS.InvokeVoidAsync("themeManager.setStyle", style);
    }

    private async Task SetTheme(string theme)
    {
        _activeTheme = theme;
        await JS.InvokeVoidAsync("themeManager.setScheme", string.IsNullOrEmpty(theme) ? "zinc" : theme);
    }

    private async Task SetRadius(string radius)
    {
        _activeRadius = radius;
        await JS.InvokeVoidAsync("themeManager.setRadius", radius);
    }

    private async Task SetBaseColor(string baseColor)
    {
        _activeBaseColor = baseColor;
        await JS.InvokeVoidAsync("themeManager.setBaseColor", baseColor);
    }

    private async Task SetFont(string font)
    {
        _activeFont = font;
        var fontOption = _fonts.FirstOrDefault(f => f.Value == font);
        var family = fontOption?.Family ?? "system-ui, -apple-system, sans-serif";
        var css = font == "system"
            ? ""
            : $"body, body * {{ font-family: {family} !important; }} pre, pre *, code, code *, .font-mono, .font-mono * {{ font-family: revert !important; }}";
        await JS.InvokeVoidAsync("themeManager.setFont", font, css);
    }

    private Task SetIconLibrary(string library)
    {
        _activeIconLibrary = library;
        _iconService.SetLibrary(library);
        return Task.CompletedTask;
    }

    private async Task SetMenuColor(string menuColor)
    {
        _activeMenuColor = menuColor;
        await JS.InvokeVoidAsync("themeManager.setMenuColor", menuColor);
    }

    private async Task SetMenuAccent(string accent)
    {
        _activeMenuAccent = accent;
        await JS.InvokeVoidAsync("themeManager.setMenuAccent", accent);
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await ThemeService.InitializeAsync();
            _darkMode = ThemeService.IsDark;
            ThemeService.OnThemeChanged += OnThemeServiceChanged;

            // Sync all C# state from what themeManager restored from localStorage
            try
            {
                var scheme = await JS.InvokeAsync<string>("themeManager.getScheme");
                _activeTheme = (scheme == "zinc" || string.IsNullOrEmpty(scheme)) ? "" : scheme;

                var radius = await JS.InvokeAsync<string>("themeManager.getRadius");
                if (!string.IsNullOrEmpty(radius)) _activeRadius = radius;

                var style = await JS.InvokeAsync<string>("themeManager.getStyle");
                if (!string.IsNullOrEmpty(style)) _activeStyle = style;

                var baseColor = await JS.InvokeAsync<string>("themeManager.getBaseColor");
                if (!string.IsNullOrEmpty(baseColor)) _activeBaseColor = baseColor;

                var font = await JS.InvokeAsync<string>("themeManager.getFont");
                if (!string.IsNullOrEmpty(font)) _activeFont = font;

                var menuColor = await JS.InvokeAsync<string>("themeManager.getMenuColor");
                if (!string.IsNullOrEmpty(menuColor)) _activeMenuColor = menuColor;

                var menuAccent = await JS.InvokeAsync<string>("themeManager.getMenuAccent");
                if (!string.IsNullOrEmpty(menuAccent)) _activeMenuAccent = menuAccent;
            }
            catch (Exception ex) { Console.Error.WriteLine($"[Lumeo Docs] Failed to load theme settings: {ex.Message}"); }

            // Register keyboard shortcuts
            _kbdShortcuts.Add(await Shortcuts.RegisterAsync("r", async () =>
            {
                await ShuffleSettings();
                await InvokeAsync(StateHasChanged);
            }));
            _kbdShortcuts.Add(await Shortcuts.RegisterAsync("escape", () =>
            {
                if (_expandedSection != "")
                {
                    _expandedSection = "";
                    InvokeAsync(StateHasChanged);
                }
                return Task.CompletedTask;
            }, preventDefault: false));

            StateHasChanged();
        }
    }

    private void OnThemeServiceChanged()
    {
        _darkMode = ThemeService.IsDark;
        InvokeAsync(StateHasChanged);
    }

    public async ValueTask DisposeAsync()
    {
        ThemeService.OnThemeChanged -= OnThemeServiceChanged;
        foreach (var s in _kbdShortcuts) await s.DisposeAsync();
    }

    private async Task ToggleDarkMode()
    {
        await ThemeService.ToggleModeAsync();
        _darkMode = ThemeService.IsDark;
    }

    private async Task ShuffleSettings()
    {
        var random = new Random();
        await SetTheme(_themes[random.Next(_themes.Length)].Value);
        await SetRadius(_radiiOptions[random.Next(_radiiOptions.Length)].Value);
        await SetStyle(_styles[random.Next(_styles.Length)].Value);
        await SetBaseColor(_baseColors[random.Next(_baseColors.Length)].Value);
        await SetFont(_fonts[random.Next(_fonts.Length)].Value);
        await SetMenuColor(_menuColors[random.Next(_menuColors.Length)].Value);
        await SetMenuAccent(_menuAccents[random.Next(_menuAccents.Length)].Value);
        if (random.Next(2) == 1 != _darkMode) await ToggleDarkMode();
        _expandedSection = "";
    }

    private async Task ResetSettings()
    {
        _activeTheme = "";
        _activeRadius = "0.75";
        _activeStyle = "default";
        _activeBaseColor = "slate";
        _activeFont = "system";
        _activeMenuColor = "default";
        _activeMenuAccent = "subtle";
        await JS.InvokeVoidAsync("themeManager.setScheme", "zinc");
        await JS.InvokeVoidAsync("themeManager.resetAll");
        if (_darkMode) await ToggleDarkMode();
        _expandedSection = "";
    }

    private string GetPresetCode()
    {
        var preset = new LumeoPreset(
            Theme: LumeoPresetOptions.IndexOf(LumeoPresetOptions.Themes, _activeTheme),
            Style: LumeoPresetOptions.IndexOf(LumeoPresetOptions.Styles, _activeStyle),
            BaseColor: LumeoPresetOptions.IndexOf(LumeoPresetOptions.BaseColors, _activeBaseColor),
            Radius: LumeoPresetOptions.IndexOf(LumeoPresetOptions.Radii, _activeRadius),
            Font: LumeoPresetOptions.IndexOf(LumeoPresetOptions.Fonts, _activeFont),
            IconLibrary: LumeoPresetOptions.IndexOf(LumeoPresetOptions.IconLibraries, _activeIconLibrary),
            MenuColor: LumeoPresetOptions.IndexOf(LumeoPresetOptions.MenuColors, _activeMenuColor),
            MenuAccent: LumeoPresetOptions.IndexOf(LumeoPresetOptions.MenuAccents, _activeMenuAccent),
            Dark: _darkMode ? 1 : 0
        );
        return LumeoPresetCodec.Encode(preset);
    }

    // One-shot share: copies `lumeo apply --preset <code>` to the clipboard. The 6-char
    // client-side bitfield fully encodes every customizer selection, so the same config
    // deterministically produces the same code — safe to share in issues, docs, or chats.
    // The Worker at api.lumeo.nativ.sh stays deployed for future configs that exceed the
    // bitfield (custom HSL, user-authored fonts); the CLI handles both paths transparently.
    private async Task SharePreset()
    {
        var code = GetPresetCode();
        var cmd = $"lumeo apply --preset {code}";
        try { await JS.InvokeVoidAsync("themeManager.copyText", cmd); } catch (JSException) { }
        _shareCopied = true;
        StateHasChanged();
        await Task.Delay(2000);
        _shareCopied = false;
        StateHasChanged();
    }

}
