@namespace Lumeo
@implements IAsyncDisposable

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

@inject ComponentInteropService Interop

@code {
    [CascadingParameter] public Popover.PopoverContext Context { get; set; } = default!;
    [Parameter] public RenderFragment? ChildContent { get; set; }
    [Parameter] public PopoverAlign Align { get; set; } = PopoverAlign.Center;
    [Parameter] public PopoverSide Side { get; set; } = PopoverSide.Bottom;
    [Parameter] public bool ShowArrow { get; set; }
    [Parameter] public string? Class { get; set; }
    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }

    private readonly string _contentId = $"popover-content-{Guid.NewGuid():N}";
    private bool _registered;

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

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

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

    // Rotated square so the arrow inherits both the popover surface colour AND a matching border
    // on the two sides that face out — looks sharper than the hollow-triangle trick and lines the
    // arrow up with the popover corner radius visually.
    private string ArrowClasses => Side switch
    {
        PopoverSide.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",
        PopoverSide.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",
        PopoverSide.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",
        PopoverSide.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"
    };

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

    private async Task HandleKeyDown(KeyboardEventArgs e)
    {
        if (e.Key == "Escape") await Context.SetOpen.InvokeAsync(false);
    }

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

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

    public enum PopoverAlign { Start, Center, End }
    public enum PopoverSide { Top, Bottom, Left, Right }
}
