@namespace Lumeo
@implements IAsyncDisposable

@if (Context.IsOpen)
{
    <div id="@Context.ContentId"
         role="menu"
         aria-orientation="vertical"
         class="@CssClass"
         @onkeydown="HandleKeyDown"
         tabindex="-1"
         @attributes="AdditionalAttributes">
        @ChildContent
        @if (ShowArrow)
        {
            <div class="@ArrowClasses"></div>
        }
    </div>
}

@inject ComponentInteropService Interop

@code {
    [CascadingParameter] public DropdownMenu.DropdownMenuContext Context { get; set; } = default!;
    [Parameter] public RenderFragment? ChildContent { get; set; }
    [Parameter] public DropdownMenuAlign Align { get; set; } = DropdownMenuAlign.Start;
    [Parameter] public DropdownMenuSide Side { get; set; } = DropdownMenuSide.Bottom;
    [Parameter] public bool ShowArrow { get; set; }
    [Parameter] public string? Class { get; set; }
    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }

    private bool _registered;
    private int _focusedIndex = -1;

    private string CssClass => string.Join(" ", new[] { "fixed z-50 min-w-[8rem] rounded-md border border-border/60 bg-popover p-1 text-popover-foreground shadow-lg animate-fade-in", ShowArrow ? "overflow-visible" : "overflow-hidden", Class }.Where(s => !string.IsNullOrWhiteSpace(s)));

    private string ArrowClasses => Side switch
    {
        DropdownMenuSide.Top => "absolute left-1/2 top-full -translate-x-1/2 -translate-y-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-b border-r border-border/60",
        DropdownMenuSide.Bottom => "absolute left-1/2 bottom-full -translate-x-1/2 translate-y-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-t border-l border-border/60",
        DropdownMenuSide.Left => "absolute top-1/2 left-full -translate-y-1/2 -translate-x-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-t border-r border-border/60",
        DropdownMenuSide.Right => "absolute top-1/2 right-full -translate-y-1/2 translate-x-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-b border-l border-border/60",
        _ => "absolute left-1/2 bottom-full -translate-x-1/2 translate-y-1/2 rotate-45 h-2.5 w-2.5 bg-popover border-t border-l border-border/60"
    };

    private string AlignStr => Align switch
    {
        DropdownMenuAlign.Start => "start",
        DropdownMenuAlign.Center => "center",
        DropdownMenuAlign.End => "end",
        _ => "start"
    };

    private string SideStr => Side switch
    {
        DropdownMenuSide.Top => "top",
        DropdownMenuSide.Left => "left",
        DropdownMenuSide.Right => "right",
        _ => "bottom"
    };

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (Context.IsOpen && !_registered)
        {
            await Interop.PositionFixed(Context.ContentId, Context.WrapperId, AlignStr, false, SideStr);
            await Interop.RegisterClickOutside(Context.ContentId, null, async () =>
            {
                await Context.SetOpen.InvokeAsync(false);
                await InvokeAsync(StateHasChanged);
            });
            _registered = true;
            _focusedIndex = -1;

            // Move focus to the menu container so Escape/ArrowUp/ArrowDown reach
            // the @onkeydown handler. Without this, focus stays on the trigger
            // button and keyboard nav silently breaks (per WAI-ARIA menu pattern).
            try { await Interop.FocusElement(Context.ContentId); } catch (JSDisconnectedException) { }
        }
        else if (!Context.IsOpen && _registered)
        {
            await Cleanup();
        }
    }

    private async Task HandleKeyDown(KeyboardEventArgs e)
    {
        switch (e.Key)
        {
            case "Escape":
                await Context.SetOpen.InvokeAsync(false);
                break;
            case "ArrowDown":
                {
                    var count = await Interop.GetMenuItemCount(Context.ContentId);
                    if (count > 0)
                    {
                        _focusedIndex = (_focusedIndex + 1) % count;
                        await Interop.FocusMenuItemByIndex(Context.ContentId, _focusedIndex);
                    }
                    break;
                }
            case "ArrowUp":
                {
                    var count = await Interop.GetMenuItemCount(Context.ContentId);
                    if (count > 0)
                    {
                        _focusedIndex = _focusedIndex <= 0 ? count - 1 : _focusedIndex - 1;
                        await Interop.FocusMenuItemByIndex(Context.ContentId, _focusedIndex);
                    }
                    break;
                }
            case "Home":
                {
                    var count = await Interop.GetMenuItemCount(Context.ContentId);
                    if (count > 0)
                    {
                        _focusedIndex = 0;
                        await Interop.FocusMenuItemByIndex(Context.ContentId, _focusedIndex);
                    }
                    break;
                }
            case "End":
                {
                    var count = await Interop.GetMenuItemCount(Context.ContentId);
                    if (count > 0)
                    {
                        _focusedIndex = count - 1;
                        await Interop.FocusMenuItemByIndex(Context.ContentId, _focusedIndex);
                    }
                    break;
                }
        }
    }

    private async Task Cleanup()
    {
        try { await Interop.UnpositionFixed(Context.ContentId); } catch (JSDisconnectedException) { }
        try { await Interop.UnregisterClickOutside(Context.ContentId); } catch (JSDisconnectedException) { }
        _registered = false;
    }

    public async ValueTask DisposeAsync()
    {
        if (_registered) await Cleanup();
    }

    public enum DropdownMenuAlign { Start, Center, End }
    public enum DropdownMenuSide { Top, Bottom, Left, Right }
}
