@inject NavConfigService NavConfig
@inject NavigationManager Nav
@inject IJSRuntime JS
@implements IDisposable
@using Lumeo.Docs.Services

@* Active-link highlight is applied to NavLink's auto-generated `.active` class via an
   arbitrary Tailwind variant on the <nav>. Active-item scroll-into-view happens on
   every render once we have the nav ref (see OnAfterRenderAsync). *@

@{
    RenderFragment navContent =
    @<text>
        @if (_config is null)
        {
            <Stack Gap="1" Class="px-2 py-4">
                <Skeleton Height="16" Width="60%" />
                <Skeleton Height="14" Width="80%" />
                <Skeleton Height="14" Width="70%" />
            </Stack>
        }
        else
        {
            @foreach (var group in _config.Groups)
            {
                <NavGroupSection Group="@group" Collapsible="@Collapsible"
                                 IsExpanded="@IsGroupExpanded(group)"
                                 OnToggle="@(() => ToggleGroup(group))" />
            }
        }
    </text>;
}

@if (Embedded)
{
    <nav class="lumeo-sidebar-nav p-4 space-y-6 text-sm" @ref="_navRef">@navContent</nav>
}
else if (IsMobile)
{
    <nav class="lumeo-sidebar-nav p-4 space-y-6 text-sm" @ref="_navRef">@navContent</nav>
}
else
{
    <aside class="hidden lg:block w-64 shrink-0 m-3 rounded-[var(--radius-xl)] border border-border/60 bg-card shadow-lg overflow-y-auto sticky top-[84px]" style="max-height: calc(100vh - 100px)">
        <nav class="lumeo-sidebar-nav p-4 space-y-6 text-sm" @ref="_navRef">@navContent</nav>
    </aside>
}

@code {
    [Parameter] public bool IsMobile { get; set; }
    /// <summary>Filter the nav to a single section (docs/components/patterns). Null = show all.</summary>
    [Parameter] public string? Section { get; set; }
    [Parameter] public bool Embedded { get; set; }
    /// <summary>Render group headers as toggleable buttons; the group containing the current URL
    /// auto-expands. Independent of Section — a sidebar can show all groups AND collapse them.</summary>
    [Parameter] public bool Collapsible { get; set; }

    private NavConfig? _config;
    private ElementReference _navRef;
    private string? _lastLocation;
    private readonly Dictionary<string, bool> _expanded = new();

    protected override async Task OnInitializedAsync()
    {
        Nav.LocationChanged += OnLocationChanged;
        _config = Section is null
            ? await NavConfig.GetAsync()
            : await NavConfig.GetForSectionAsync(Section);

        if (Collapsible && _config is not null)
        {
            ExpandActiveGroup();
        }
    }

    private void ExpandActiveGroup()
    {
        if (_config is null) return;
        var currentPath = Nav.ToBaseRelativePath(Nav.Uri).TrimEnd('/').ToLowerInvariant();
        foreach (var group in _config.Groups)
        {
            if (GroupContainsPath(group, currentPath))
            {
                _expanded[group.Id] = true;
                return;
            }
        }
        // If nothing matched, expand first group as fallback
        if (_config.Groups.Count > 0)
            _expanded[_config.Groups[0].Id] = true;
    }

    private static bool GroupContainsPath(NavGroup group, string path)
    {
        if (group.Items is not null)
        {
            if (group.Items.Any(i => i.Href.Equals(path, StringComparison.OrdinalIgnoreCase)))
                return true;
        }
        if (group.Subgroups is not null)
        {
            foreach (var sub in group.Subgroups)
            {
                if (sub.Items.Any(i => i.Href.Equals(path, StringComparison.OrdinalIgnoreCase)))
                    return true;
            }
        }
        return false;
    }

    private bool IsGroupExpanded(NavGroup group)
    {
        if (!Collapsible) return true; // non-collapsible: always expanded
        return _expanded.TryGetValue(group.Id, out var v) && v;
    }

    private void ToggleGroup(NavGroup group)
    {
        var current = _expanded.TryGetValue(group.Id, out var v) && v;
        _expanded[group.Id] = !current;
    }

    private void OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
    {
        if (Collapsible && _config is not null)
        {
            ExpandActiveGroup();
        }
        InvokeAsync(StateHasChanged);
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // Scroll the active nav item into view after each location change.
        if (Nav.Uri != _lastLocation)
        {
            _lastLocation = Nav.Uri;
            try
            {
                await JS.InvokeVoidAsync("lumeoNavScrollActiveIntoView", _navRef);
            }
            catch (JSDisconnectedException) { }
            catch (Microsoft.JSInterop.JSException) { /* nav ref not ready yet */ }
        }
    }

    public void Dispose()
    {
        Nav.LocationChanged -= OnLocationChanged;
    }
}
