@namespace Lumeo
@inject Lumeo.Services.IComponentInteropService Interop
@implements IAsyncDisposable

@if (ShouldRender_)
{
    <div role="tabpanel"
         id="@PanelId"
         aria-labelledby="@TabId"
         tabindex="0"
         hidden="@(IsActive ? null : "hidden")"
         class="@CssClass"
         @attributes="AdditionalAttributes">
        @ChildContent
    </div>
}

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

    /// <summary>
    /// When true, this panel is always rendered into the DOM (hidden when
    /// inactive), regardless of the parent <see cref="Tabs.RenderMode"/>.
    /// Use for panels whose state must survive every tab switch — e.g. a
    /// half-filled form — while leaving the rest of the tab set on the
    /// lighter default mode.
    /// </summary>
    [Parameter] public bool ForceMount { get; set; }

    [Parameter] public string? Class { get; set; }
    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }

    private bool IsActive => Context.ActiveValue == Value;
    private bool _swipeAttached;

    // Decide whether this panel's <div> exists in the DOM at all.
    //   Active mode  → only when this tab is the active one
    //   Lazy mode    → once this tab has ever been activated (or ForceMount)
    //   Eager mode   → always (or ForceMount)
    private bool ShouldRender_
    {
        get
        {
            if (ForceMount) return true;
            return Context.RenderMode switch
            {
                Tabs.TabsRenderMode.Eager => true,
                Tabs.TabsRenderMode.Lazy  => IsActive || Context.ActivatedValues.Contains(Value),
                _                          => IsActive
            };
        }
    }

    private string PanelId => Context.TabIds.TryGetValue(Value, out var ids) ? ids.PanelId : $"tabpanel-{Value}";
    private string TabId => Context.TabIds.TryGetValue(Value, out var ids) ? ids.TabId : $"tab-{Value}";

    private string CssClass => Context.SwipeEnabled
        ? $"mt-2 focus-visible:outline-none touch-pan-y {Class}".Trim()
        : $"mt-2 focus-visible:outline-none {Class}".Trim();

    protected override void OnInitialized()
    {
        // Register this panel's value in the parent's ordered list.
        Context.RegisterContent(Value);
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (!Context.SwipeEnabled) return;

        // Re-attach whenever this panel transitions from inactive → active.
        // In Active render mode the <div> is removed from the DOM when the tab
        // becomes inactive, so every time it reappears it is a fresh element
        // with no JS listeners — we must re-register each time.
        var shouldAttach = IsActive;
        if (shouldAttach && !_swipeAttached)
        {
            _swipeAttached = true;
            try
            {
                await Interop.RegisterTabSwipe(PanelId, Context.SwipeWrap, async direction =>
                {
                    // Navigate via the parent Tabs component's method through the context callback.
                    // The context carries a delegate that resolves to Tabs.NavigateBySwipe.
                    await Context.SwipeNavigate(direction);
                });
            }
            catch (JSDisconnectedException) { _swipeAttached = false; }
            catch (InvalidOperationException) { _swipeAttached = false; } // JS unavailable during prerender
        }
        else if (!shouldAttach && _swipeAttached)
        {
            _swipeAttached = false;
            try { await Interop.UnregisterTabSwipe(PanelId); }
            catch (JSDisconnectedException) { }
        }
    }

    public async ValueTask DisposeAsync()
    {
        if (!_swipeAttached) return;
        try
        {
            await Interop.UnregisterTabSwipe(PanelId);
        }
        catch (JSDisconnectedException) { }
    }
}
