@namespace Lumeo
@inject Lumeo.Services.Localization.ILumeoLocalizer L

<button type="button"
        id="@_tabId"
        role="tab"
        aria-selected="@IsActive.ToString().ToLower()"
        aria-disabled="@(Disabled ? "true" : null)"
        aria-controls="@_panelId"
        tabindex="@(Disabled ? -1 : IsActive ? 0 : -1)"
        class="@CssClass"
        @onclick="Activate"
        @onkeydown="HandleKeyDown"
        @onkeydown:preventDefault="@_preventKey"
        @attributes="AdditionalAttributes">
    @if (IconContent is not null)
    {
        <span class="inline-flex shrink-0">@IconContent</span>
    }
    @ChildContent
    @if (TrailingContent is not null)
    {
        <span class="inline-flex shrink-0">@TrailingContent</span>
    }
    @if (IsClosable)
    {
        <span class="ml-1 inline-flex items-center justify-center rounded-sm cursor-pointer opacity-70 hover:opacity-100 transition-opacity h-11 w-11 -my-2 sm:h-4 sm:w-4 sm:my-0 focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring focus-visible:ring-offset-1"
              @onclick="HandleClose" @onclick:stopPropagation="true"
              role="button" aria-label="@L["Tabs.CloseTab"]">
            <Blazicon Svg="Lucide.X" class="h-3 w-3" />
        </span>
    }
</button>

@inject ComponentInteropService Interop
@inject ThemeService Theme

@code {
    [CascadingParameter] public Tabs.TabsContext Context { get; set; } = default!;
    [Parameter] public RenderFragment? ChildContent { get; set; }

    /// <summary>Icon slot. Named <c>IconContent</c> to avoid shadowing the <c>&lt;Icon&gt;</c> component.</summary>
    [Parameter] public RenderFragment? IconContent { get; set; }

    [Parameter] public RenderFragment? TrailingContent { get; set; }
    [Parameter, EditorRequired] public string Value { get; set; } = "";
    [Parameter] public bool IsClosable { get; set; }
    [Parameter] public bool Disabled { get; set; }
    [Parameter] public EventCallback OnClose { get; set; }
    [Parameter] public string? Class { get; set; }
    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }

    private readonly string _tabId = $"tab-{Guid.NewGuid():N}";
    private readonly string _panelId = $"tabpanel-{Guid.NewGuid():N}";
    private bool _preventKey;

    private bool IsActive => Context.ActiveValue == Value;
    private Tabs.TabsVariant CurrentVariant => Context.Variant;

    protected override void OnInitialized()
    {
        Context.TabIds[Value] = (_tabId, _panelId);
    }

    private string CssClass
    {
        get
        {
            var disabledClasses = Disabled ? "opacity-50 cursor-not-allowed pointer-events-none" : "";
            var variantClasses = CurrentVariant switch
            {
                Tabs.TabsVariant.Card => IsActive
                    ? "rounded-t-lg border border-b-0 border-border/40 bg-card text-foreground"
                    : "text-muted-foreground hover:text-foreground",
                Tabs.TabsVariant.Pill => IsActive
                    ? "rounded-full bg-primary text-primary-foreground"
                    : "text-muted-foreground hover:bg-muted/50 rounded-full",
                _ => IsActive
                    ? "bg-background text-foreground shadow"
                    : ""
            };
            return $"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium cursor-pointer transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed {variantClasses} {disabledClasses} {Class}".Trim();
        }
    }

    private async Task Activate()
    {
        if (Disabled) return;
        await Context.SetValue.InvokeAsync(Value);
    }

    private async Task HandleClose()
    {
        await OnClose.InvokeAsync();
    }

    private async Task HandleKeyDown(KeyboardEventArgs e)
    {
        if (Disabled) return;

        var isHorizontal = Context.Orientation == Tabs.TabsOrientation.Horizontal;
        var isRtl = Theme.CurrentDirection == LayoutDirection.Rtl;
        var keys = Context.TabIds.Keys.ToList();
        var idx = keys.IndexOf(Value);

        // In RTL, ArrowLeft/Right are swapped for horizontal tabs so they follow visual flow.
        var (hBack, hForward) = (isHorizontal && isRtl) ? ("ArrowRight", "ArrowLeft") : ("ArrowLeft", "ArrowRight");

        string? nextValue = null;
        if (isHorizontal)
        {
            if (e.Key == hForward) nextValue = FindNextEnabledTab(keys, idx, 1);
            else if (e.Key == hBack) nextValue = FindNextEnabledTab(keys, idx, -1);
        }
        else
        {
            if (e.Key == "ArrowDown") nextValue = FindNextEnabledTab(keys, idx, 1);
            else if (e.Key == "ArrowUp") nextValue = FindNextEnabledTab(keys, idx, -1);
        }
        if (e.Key == "Home") nextValue = keys.Count > 0 ? keys[0] : null;
        else if (e.Key == "End") nextValue = keys.Count > 0 ? keys[^1] : null;

        _preventKey = e.Key is "ArrowRight" or "ArrowLeft" or "ArrowDown" or "ArrowUp" or "Home" or "End";

        if (nextValue is not null && Context.TabIds.TryGetValue(nextValue, out var ids))
        {
            await Context.SetValue.InvokeAsync(nextValue);
            await Interop.FocusElement(ids.TabId);
        }
    }

    private string? FindNextEnabledTab(List<string> keys, int currentIdx, int direction)
    {
        if (keys.Count == 0) return null;
        var nextIdx = currentIdx + direction;
        if (nextIdx < 0) nextIdx = keys.Count - 1;
        else if (nextIdx >= keys.Count) nextIdx = 0;
        return keys[nextIdx];
    }
}
