@page "/items"
@page "/items/folder/{FolderId:guid}"
@inherits MainBase
@inject ItemService ItemService
@inject FolderService FolderService
@using AliasVault.RazorComponents.Tables
@using AliasVault.Client.Main.Models
@using AliasVault.Client.Main.Components.Items
@using AliasClientDb.Models
@using AliasVault.Client.Main.Components.Folders
@using Microsoft.Extensions.Localization
@implements IAsyncDisposable

<LayoutPageTitle>Home</LayoutPageTitle>

<PageHeader
    BreadcrumbItems="@BreadcrumbItems"
    Title="@GetFilterTitle()"
    Description="@Localizer["PageDescription"]">
    <TitleActions>
        <div class="relative flex items-center gap-2">
            @if (IsInFolder || HasRootItems)
            {
                <button @onclick="ToggleFilterDropdown" id="filterButton" class="flex items-center gap-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none">
                    <h1 class="flex items-baseline gap-1.5 text-xl font-semibold tracking-tight text-gray-900 dark:text-white sm:text-2xl">
                        <span>@GetFilterTitle()</span>
                        <span class="text-base text-gray-500 dark:text-gray-400">(@TotalFilteredItems)</span>
                    </h1>
                    <svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <polyline points="6 9 12 15 18 9" />
                    </svg>
                </button>
            }
            else
            {
                <h1 class="text-xl font-semibold tracking-tight text-gray-900 dark:text-white sm:text-2xl">
                    @GetFilterTitle()
                </h1>
            }
            @if (IsInFolder)
            {
                <button @onclick="ShowEditFolderModal" title="@Localizer["EditFolder"]" class="p-1.5 text-gray-400 hover:text-orange-500 dark:text-gray-500 dark:hover:text-orange-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors">
                    <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
                    </svg>
                </button>
                <button @onclick="ShowDeleteFolderModal" title="@Localizer["DeleteFolder"]" class="p-1.5 text-gray-400 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors">
                    <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <polyline points="3 6 5 6 21 6" />
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
                    </svg>
                </button>
            }

            @if (ShowFilterDropdown)
            {
                <ClickOutsideHandler OnClose="CloseFilterDropdown" ContentId="filterDropdown,filterButton">
                    <div id="filterDropdown" class="absolute left-0 top-full z-10 mt-2 w-56 origin-top-left rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-700">
                        <div class="py-1">
                            @* All items filter *@
                            <button @onclick="() => SetFilter(ItemFilterType.All)" class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 @(FilterType == ItemFilterType.All ? "bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400" : "text-gray-700 dark:text-gray-300")">
                                @Localizer["FilterAllOption"]
                            </button>
                            <div class="border-t border-gray-200 dark:border-gray-600 my-1"></div>

                            @* Item type filters with icons *@
                            <button @onclick="() => SetFilter(ItemFilterType.Login)" class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 flex items-center gap-2 @(FilterType == ItemFilterType.Login ? "bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400" : "text-gray-700 dark:text-gray-300")">
                                <svg class="w-5 h-5 @(FilterType == ItemFilterType.Login ? "text-orange-500 dark:text-orange-400" : "text-gray-400 dark:text-gray-500")" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
                                </svg>
                                @ItemTypeSelectorLocalizer["TypeLogin"]
                            </button>
                            <button @onclick="() => SetFilter(ItemFilterType.Alias)" class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 flex items-center gap-2 @(FilterType == ItemFilterType.Alias ? "bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400" : "text-gray-700 dark:text-gray-300")">
                                <svg class="w-5 h-5 @(FilterType == ItemFilterType.Alias ? "text-orange-500 dark:text-orange-400" : "text-gray-400 dark:text-gray-500")" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
                                </svg>
                                @ItemTypeSelectorLocalizer["TypeAlias"]
                            </button>
                            <button @onclick="() => SetFilter(ItemFilterType.CreditCard)" class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 flex items-center gap-2 @(FilterType == ItemFilterType.CreditCard ? "bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400" : "text-gray-700 dark:text-gray-300")">
                                <svg class="w-5 h-5 @(FilterType == ItemFilterType.CreditCard ? "text-orange-500 dark:text-orange-400" : "text-gray-400 dark:text-gray-500")" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
                                </svg>
                                @ItemTypeSelectorLocalizer["TypeCreditCard"]
                            </button>
                            <button @onclick="() => SetFilter(ItemFilterType.Note)" class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 flex items-center gap-2 @(FilterType == ItemFilterType.Note ? "bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400" : "text-gray-700 dark:text-gray-300")">
                                <svg class="w-5 h-5 @(FilterType == ItemFilterType.Note ? "text-orange-500 dark:text-orange-400" : "text-gray-400 dark:text-gray-500")" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
                                </svg>
                                @ItemTypeSelectorLocalizer["TypeNote"]
                            </button>

                            <div class="border-t border-gray-200 dark:border-gray-600 my-1"></div>

                            @* Special filters *@
                            <button @onclick="() => SetFilter(ItemFilterType.Passkeys)" class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 @(FilterType == ItemFilterType.Passkeys ? "bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400" : "text-gray-700 dark:text-gray-300")">
                                @Localizer["FilterPasskeysOption"]
                            </button>
                            <button @onclick="() => SetFilter(ItemFilterType.Attachments)" class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 @(FilterType == ItemFilterType.Attachments ? "bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400" : "text-gray-700 dark:text-gray-300")">
                                @Localizer["FilterAttachmentsOption"]
                            </button>

                            <div class="border-t border-gray-200 dark:border-gray-600 my-1"></div>

                            @* Recently deleted link *@
                            <button @onclick="NavigateToRecentlyDeleted" class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 flex items-center justify-between">
                                <span>@Localizer["FilterRecentlyDeletedOption"]</span>
                                @if (RecentlyDeletedCount > 0)
                                {
                                    <span class="text-gray-400 dark:text-gray-500">@RecentlyDeletedCount</span>
                                }
                            </button>
                        </div>
                    </div>
                </ClickOutsideHandler>
            }
        </div>
    </TitleActions>
    <CustomActions>
        <div class="relative">
            <button @onclick="ToggleSettingsDropdown" id="settingsButton" class="p-2 text-gray-500 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-700">
                <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                    <path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"></path>
                </svg>
            </button>

            @if (ShowSettingsDropdown)
            {
                <ClickOutsideHandler OnClose="CloseSettingsPopup" ContentId="settingsDropdown,settingsButton">
                    <div id="settingsDropdown" class="absolute right-0 z-10 mt-2 min-w-[220px] origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-700">
                        <div class="p-4">
                            <div class="mb-4">
                                <label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["ViewModeLabel"]</label>
                                <select @bind="ViewMode" @bind:after="CloseSettingsPopup" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
                                    <option value="grid">@Localizer["GridViewOption"]</option>
                                    <option value="table">@Localizer["TableViewOption"]</option>
                                </select>
                            </div>
                            <div class="mb-4">
                                <label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["SortOrderLabel"]</label>
                                <select @bind="SortOrder" @bind:after="CloseSettingsPopup" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
                                    <option value="@CredentialSortOrder.OldestFirst">@Localizer["OldestFirstOption"]</option>
                                    <option value="@CredentialSortOrder.NewestFirst">@Localizer["NewestFirstOption"]</option>
                                    <option value="@CredentialSortOrder.Alphabetical">@Localizer["AlphabeticalOption"]</option>
                                </select>
                            </div>
                        </div>
                    </div>
                </ClickOutsideHandler>
            }
        </div>
        <RefreshButton OnClick="LoadItemsAsync" ButtonText="@SharedLocalizer["Refresh"]" />
    </CustomActions>
</PageHeader>

@if (IsLoading)
{
    <LoadingIndicator />
}
else
{
    <div class="px-4 mb-4 min-h-[250px]">
        @* Active filter indicator - show persistently when a filter is active *@
        @if (FilterType != ItemFilterType.All)
        {
            <div class="flex flex-wrap items-center gap-2 mb-4">
                <span class="text-sm text-gray-500 dark:text-gray-400">@Localizer["FilteringBy"]</span>
                <button @onclick="ClearFilter" class="inline-flex items-center gap-1 px-3 py-1.5 text-sm bg-orange-100 dark:bg-orange-900/30 hover:bg-orange-200 dark:hover:bg-orange-900/50 text-orange-700 dark:text-orange-300 rounded-lg transition-colors">
                    <span>@GetFilterTitle()</span>
                    <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <line x1="18" y1="6" x2="6" y2="18" />
                        <line x1="6" y1="6" x2="18" y2="18" />
                    </svg>
                </button>
            </div>
        }

        @* Folders section - only show at root level *@
        @if (!IsInFolder && !IsSearching)
        {
            <div class="flex flex-wrap items-center gap-2 mb-4">
                @foreach (var folder in Folders)
                {
                    <FolderPill Folder="@folder" OnClick="() => NavigateToFolder(folder.Id)" />
                }
                <button @onclick="ShowCreateFolderModal" class="@GetAddFolderButtonClass()">
                    <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
                    </svg>
                    <svg class="w-3.5 h-3.5 -ml-0.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                        <line x1="12" y1="5" x2="12" y2="19" />
                        <line x1="5" y1="12" x2="19" y2="12" />
                    </svg>
                    @if (Folders.Count == 0)
                    {
                        <span>@Localizer["NewFolder"]</span>
                    }
                </button>
            </div>
        }

        @if (DbService.Settings.CredentialsViewMode == "table")
        {
            <ItemsTable Credentials="@FilteredAndSortedItems.ToList()" SortOrder="@SortOrder" />
        }
        else
        {
            <div class="grid gap-4 md:grid-cols-4 xl:grid-cols-6">
                @if (Items.Count == 0 && !IsInFolder)
                {
                    <div class="credential-card col-span-full p-4 space-y-2 bg-amber-50 border border-primary-500 rounded-lg shadow-sm dark:border-primary-700 dark:bg-gray-800">
                        <div class="px-4 py-6 text-gray-700 dark:text-gray-200 rounded text-center flex flex-col items-center">
                            <p class="mb-2 text-lg font-semibold text-primary-700 dark:text-primary-400">@Localizer["NoItemsTitle"]</p>

                            <div class="max-w-md mx-auto">
                                <div class="mb-6">
                                    <p class="text-sm mb-2">@Localizer["CreateFirstItemText"] <span class="hidden md:inline">@Localizer["NewAliasButtonText"]</span><span class="md:hidden">@Localizer["NewAliasButtonTextMobile"]</span> @Localizer["ButtonLocationText"]</p>
                                </div>

                                <div class="flex items-center my-6">
                                    <div class="flex-1 h-px bg-gray-300 dark:bg-gray-600"></div>
                                    <span class="px-4 text-sm text-gray-500 dark:text-gray-400">@Localizer["OrText"]</span>
                                    <div class="flex-1 h-px bg-gray-300 dark:bg-gray-600"></div>
                                </div>

                                <div>
                                    <p class="text-sm mb-2">@Localizer["ImportItemsText"]</p>
                                    <a href="/settings/import-export" class="inline-block text-sm px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors dark:bg-primary-700 dark:hover:bg-primary-600">
                                        @Localizer["ImportButtonText"]
                                    </a>
                                </div>
                            </div>
                        </div>
                    </div>
                }
                else if (!FilteredAndSortedItems.Any())
                {
                    <div class="credential-card col-span-full p-4 space-y-2 bg-amber-50 border border-primary-500 rounded-lg shadow-sm dark:border-primary-700 dark:bg-gray-800">
                        <div class="px-4 py-6 text-gray-700 dark:text-gray-200 rounded text-center">
                            @if (FilterType == ItemFilterType.Passkeys)
                            {
                                <p>@Localizer["NoPasskeysFound"]</p>
                            }
                            else if (FilterType == ItemFilterType.Attachments)
                            {
                                <p>@Localizer["NoAttachmentsFound"]</p>
                            }
                            else if (IsInFolder)
                            {
                                <p>@Localizer["EmptyFolderMessage"]</p>
                            }
                            else if (HasItemsInFoldersOnly)
                            {
                                <p>@Localizer["AllItemsInFoldersMessage"]</p>
                            }
                            else
                            {
                                <p>@Localizer["NoItemsFound"]</p>
                            }
                        </div>
                    </div>
                }
                @foreach (var item in FilteredAndSortedItems)
                {
                    <ItemCard Obj="@item" ShowFolderPath="@IsSearching" />
                }
            </div>
        }

        @* Infinite scroll sentinel - triggers loading more items when visible *@
        @if (HasMoreItems)
        {
            <div @ref="_scrollSentinel" class="flex justify-center py-4 min-h-[40px]">
                @if (IsLoadingMore)
                {
                    <div class="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400">
                        <svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
                            <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                            <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
                        </svg>
                        <span>@Localizer["LoadingMore"]</span>
                    </div>
                }
            </div>
        }
    </div>
}

@* Create Folder Modal *@
<FolderModal
    IsOpen="@ShowFolderModal"
    OnClose="CloseFolderModal"
    OnSave="CreateFolderAsync"
    Mode="create" />

@* Edit Folder Modal *@
<FolderModal
    IsOpen="@ShowEditFolderModalVisible"
    OnClose="CloseEditFolderModal"
    OnSave="EditFolderAsync"
    InitialName="@CurrentFolderName"
    Mode="edit" />

@* Delete Folder Modal *@
<DeleteFolderModal
    IsOpen="@ShowDeleteFolderModalVisible"
    OnClose="CloseDeleteFolderModal"
    OnDeleteFolderOnly="DeleteFolderOnlyAsync"
    OnDeleteFolderAndContents="DeleteFolderAndContentsAsync"
    FolderName="@CurrentFolderName"
    ItemCount="@FilteredAndSortedItems.Count()" />

@code {
    private IStringLocalizer Localizer => LocalizerFactory.Create("Pages.Main.Items.Home", "AliasVault.Client");
    private IStringLocalizer ItemTypeSelectorLocalizer => LocalizerFactory.Create("Components.Main.Items.ItemTypeSelector", "AliasVault.Client");

    /// <summary>
    /// Gets or sets the folder ID from the URL.
    /// </summary>
    [Parameter]
    public Guid? FolderId { get; set; }

    /// <summary>
    /// Gets or sets whether the items are being loaded.
    /// </summary>
    private bool IsLoading { get; set; } = true;

    /// <summary>
    /// Gets or sets the items.
    /// </summary>
    private List<ItemListEntry> Items { get; set; } = new();

    /// <summary>
    /// Gets or sets the folders.
    /// </summary>
    private List<FolderWithCount> Folders { get; set; } = new();

    /// <summary>
    /// Gets or sets the current folder name (when in folder view).
    /// </summary>
    private string CurrentFolderName { get; set; } = string.Empty;

    /// <summary>
    /// Gets or sets whether the settings dropdown is shown.
    /// </summary>
    private bool ShowSettingsDropdown { get; set; }

    /// <summary>
    /// Gets or sets whether the filter dropdown is shown.
    /// </summary>
    private bool ShowFilterDropdown { get; set; }

    /// <summary>
    /// Gets or sets whether the folder modal is shown.
    /// </summary>
    private bool ShowFolderModal { get; set; }

    /// <summary>
    /// Gets or sets whether the delete folder modal is shown.
    /// </summary>
    private bool ShowDeleteFolderModalVisible { get; set; }

    /// <summary>
    /// Gets or sets whether the edit folder modal is shown.
    /// </summary>
    private bool ShowEditFolderModalVisible { get; set; }

    /// <summary>
    /// Gets or sets the count of recently deleted items.
    /// </summary>
    private int RecentlyDeletedCount { get; set; }

    /// <summary>
    /// Gets or sets the search term.
    /// </summary>
    private string SearchTerm { get; set; } = string.Empty;

    /// <summary>
    /// Gets or sets the filter type for the items.
    /// </summary>
    private ItemFilterType FilterType { get; set; } = ItemFilterType.All;

    /// <summary>
    /// Gets or sets the number of visible items for infinite scroll.
    /// </summary>
    private int VisibleItemCount { get; set; } = 200;

    /// <summary>
    /// Gets or sets the batch size for loading more items.
    /// </summary>
    private const int BatchSize = 200;

    /// <summary>
    /// Gets or sets whether more items are currently being loaded.
    /// </summary>
    private bool IsLoadingMore { get; set; }

    /// <summary>
    /// Reference for the infinite scroll sentinel element.
    /// </summary>
    private ElementReference _scrollSentinel;

    /// <summary>
    /// Reference to the .NET object for JS interop callbacks.
    /// </summary>
    private DotNetObjectReference<Home>? _dotNetRef;

    /// <summary>
    /// Gets or sets the view mode for the items.
    /// </summary>
    private string ViewMode
    {
        get => DbService.Settings.CredentialsViewMode;
        set => DbService.Settings.SetCredentialsViewMode(value);
    }

    /// <summary>
    /// Gets or sets the sort order for the items.
    /// </summary>
    private CredentialSortOrder SortOrder
    {
        get => DbService.Settings.CredentialsSortOrder;
        set => DbService.Settings.SetCredentialsSortOrder(value);
    }

    /// <summary>
    /// Gets whether we're currently in a folder view.
    /// </summary>
    private bool IsInFolder => FolderId.HasValue;

    /// <summary>
    /// Gets whether we're currently searching.
    /// </summary>
    private bool IsSearching => !string.IsNullOrEmpty(SearchTerm);

    /// <summary>
    /// Gets whether all items are in folders (no items at root level but items exist in folders).
    /// </summary>
    private bool HasItemsInFoldersOnly => Items.Count > 0 && Items.All(x => x.FolderId != null);

    /// <summary>
    /// Gets whether there are items at the root level (not in any folder).
    /// </summary>
    private bool HasRootItems => Items.Any(x => x.FolderId == null);

    /// <summary>
    /// Gets the items filtered and sorted according to the current filter and sort order (all items, not paginated).
    /// </summary>
    private IEnumerable<ItemListEntry> AllFilteredAndSortedItems
    {
        get
        {
            // First filter by folder
            var filtered = Items.AsEnumerable();

            if (IsInFolder)
            {
                // Show only items in this folder
                filtered = filtered.Where(x => x.FolderId == FolderId);
            }
            else if (!IsSearching)
            {
                // At root level, exclude items that are in folders
                filtered = filtered.Where(x => x.FolderId == null);
            }

            // Then apply type/feature filter
            filtered = FilterType switch
            {
                ItemFilterType.Passkeys => filtered.Where(x => x.HasPasskey),
                ItemFilterType.Attachments => filtered.Where(x => x.HasAttachment),
                ItemFilterType.Login => filtered.Where(x => x.ItemType == ItemType.Login),
                ItemFilterType.Alias => filtered.Where(x => x.ItemType == ItemType.Alias),
                ItemFilterType.CreditCard => filtered.Where(x => x.ItemType == ItemType.CreditCard),
                ItemFilterType.Note => filtered.Where(x => x.ItemType == ItemType.Note),
                _ => filtered, // All
            };

            // Then apply sort
            return SortOrder switch
            {
                CredentialSortOrder.NewestFirst => filtered.OrderByDescending(x => x.CreatedAt),
                CredentialSortOrder.Alphabetical => filtered.OrderBy(x => x.Service ?? string.Empty),
                _ => filtered.OrderBy(x => x.CreatedAt), // OldestFirst (default)
            };
        }
    }

    /// <summary>
    /// Gets the total number of filtered items.
    /// </summary>
    private int TotalFilteredItems => AllFilteredAndSortedItems.Count();

    /// <summary>
    /// Gets whether there are more items to load.
    /// </summary>
    private bool HasMoreItems => VisibleItemCount < TotalFilteredItems;

    /// <summary>
    /// Gets the visible items (limited by VisibleItemCount for infinite scroll).
    /// </summary>
    private IEnumerable<ItemListEntry> FilteredAndSortedItems =>
        AllFilteredAndSortedItems.Take(VisibleItemCount);

    /// <summary>
    /// Gets the title based on the active filter and folder.
    /// </summary>
    private string GetFilterTitle()
    {
        if (IsInFolder && !string.IsNullOrEmpty(CurrentFolderName))
        {
            return CurrentFolderName;
        }

        return FilterType switch
        {
            ItemFilterType.Passkeys => Localizer["FilterPasskeysOption"],
            ItemFilterType.Attachments => Localizer["FilterAttachmentsOption"],
            ItemFilterType.Login => ItemTypeSelectorLocalizer["TypeLogin"],
            ItemFilterType.Alias => ItemTypeSelectorLocalizer["TypeAlias"],
            ItemFilterType.CreditCard => ItemTypeSelectorLocalizer["TypeCreditCard"],
            ItemFilterType.Note => ItemTypeSelectorLocalizer["TypeNote"],
            _ => Localizer["PageTitle"],
        };
    }

    /// <summary>
    /// Gets the CSS class for the add folder button.
    /// </summary>
    private string GetAddFolderButtonClass()
    {
        if (Folders.Count > 0)
        {
            return "inline-flex items-center gap-1.5 px-5 py-2.5 text-sm rounded-lg transition-colors focus:outline-none text-gray-500 dark:text-gray-400 hover:text-orange-600 dark:hover:text-orange-400 hover:bg-gray-100 dark:hover:bg-gray-700/50";
        }

        return "inline-flex items-center gap-1.5 px-5 py-2.5 text-sm rounded-lg transition-colors focus:outline-none text-gray-400 dark:text-gray-500 border border-dashed border-gray-300 dark:border-gray-600 hover:border-orange-400 dark:hover:border-orange-500 hover:text-orange-600 dark:hover:text-orange-400";
    }

    /// <summary>
    /// Toggles the settings dropdown.
    /// </summary>
    private void ToggleSettingsDropdown()
    {
        ShowSettingsDropdown = !ShowSettingsDropdown;
        StateHasChanged();
    }

    /// <summary>
    /// Toggles the filter dropdown.
    /// </summary>
    private void ToggleFilterDropdown()
    {
        ShowFilterDropdown = !ShowFilterDropdown;
        StateHasChanged();
    }

    /// <summary>
    /// Sets the filter type and closes the dropdown.
    /// </summary>
    private void SetFilter(ItemFilterType filterType)
    {
        FilterType = filterType;
        VisibleItemCount = BatchSize; // Reset visible items when filter changes
        ShowFilterDropdown = false;
        StateHasChanged();
    }

    /// <summary>
    /// Clears the active filter and resets to showing all items.
    /// </summary>
    private void ClearFilter()
    {
        FilterType = ItemFilterType.All;
        VisibleItemCount = BatchSize; // Reset visible items when filter changes
        StateHasChanged();
    }

    /// <summary>
    /// Loads more items for infinite scroll. Called from JavaScript via IntersectionObserver.
    /// </summary>
    [JSInvokable]
    public async Task LoadMoreItems()
    {
        if (HasMoreItems && !IsLoadingMore)
        {
            IsLoadingMore = true;
            StateHasChanged();

            // Brief delay to show loading indicator
            await Task.Delay(300);

            VisibleItemCount += BatchSize;
            IsLoadingMore = false;
            StateHasChanged();
        }
    }

    /// <summary>
    /// Closes the filter dropdown.
    /// </summary>
    private void CloseFilterDropdown()
    {
        ShowFilterDropdown = false;
        StateHasChanged();
    }

    /// <summary>
    /// Closes the settings dropdown.
    /// </summary>
    private async Task CloseSettingsPopup()
    {
        ShowSettingsDropdown = false;

        // Reload the items in case the settings were changed.
        await LoadItemsAsync();
    }

    /// <summary>
    /// Navigate to a folder.
    /// </summary>
    private void NavigateToFolder(Guid folderId)
    {
        VisibleItemCount = BatchSize; // Reset visible items when navigating to folder
        NavigationManager.NavigateTo($"/items/folder/{folderId}");
    }

    /// <summary>
    /// Navigate back to root.
    /// </summary>
    private void NavigateToRoot()
    {
        VisibleItemCount = BatchSize; // Reset visible items when navigating to root
        NavigationManager.NavigateTo("/items");
    }

    /// <summary>
    /// Navigate to the recently deleted page.
    /// </summary>
    private void NavigateToRecentlyDeleted()
    {
        ShowFilterDropdown = false;
        NavigationManager.NavigateTo("/items/recently-deleted");
    }

    /// <summary>
    /// Show the create folder modal.
    /// </summary>
    private void ShowCreateFolderModal()
    {
        ShowFolderModal = true;
        StateHasChanged();
    }

    /// <summary>
    /// Close the folder modal.
    /// </summary>
    private void CloseFolderModal()
    {
        ShowFolderModal = false;
        StateHasChanged();
    }

    /// <summary>
    /// Create a new folder.
    /// </summary>
    private async Task CreateFolderAsync(string folderName)
    {
        var folderId = await FolderService.CreateAsync(folderName);
        if (folderId != Guid.Empty)
        {
            // Reload folders
            Folders = await FolderService.GetAllWithCountsAsync();
            StateHasChanged();
        }
        else
        {
            GlobalNotificationService.AddErrorMessage(Localizer["FailedToCreateFolder"], true);
        }
    }

    /// <summary>
    /// Show the edit folder modal.
    /// </summary>
    private void ShowEditFolderModal()
    {
        ShowEditFolderModalVisible = true;
        StateHasChanged();
    }

    /// <summary>
    /// Close the edit folder modal.
    /// </summary>
    private void CloseEditFolderModal()
    {
        ShowEditFolderModalVisible = false;
        StateHasChanged();
    }

    /// <summary>
    /// Edit the current folder's name.
    /// </summary>
    private async Task EditFolderAsync(string newName)
    {
        if (FolderId.HasValue)
        {
            var success = await FolderService.UpdateAsync(FolderId.Value, newName);
            if (success)
            {
                // Update the current folder name to reflect the change
                CurrentFolderName = newName;

                // Reload folders to update the name in the list
                Folders = await FolderService.GetAllWithCountsAsync();
                StateHasChanged();
            }
            else
            {
                GlobalNotificationService.AddErrorMessage(SharedLocalizer["ErrorGeneric"], true);
            }
        }
    }

    /// <summary>
    /// Show the delete folder modal.
    /// </summary>
    private void ShowDeleteFolderModal()
    {
        ShowDeleteFolderModalVisible = true;
        StateHasChanged();
    }

    /// <summary>
    /// Close the delete folder modal.
    /// </summary>
    private void CloseDeleteFolderModal()
    {
        ShowDeleteFolderModalVisible = false;
        StateHasChanged();
    }

    /// <summary>
    /// Delete the current folder only (move items to root).
    /// </summary>
    private async Task DeleteFolderOnlyAsync()
    {
        if (FolderId.HasValue)
        {
            var success = await FolderService.DeleteAsync(FolderId.Value);
            if (success)
            {
                NavigationManager.NavigateTo("/items");
            }
            else
            {
                GlobalNotificationService.AddErrorMessage(Localizer["FailedToDeleteFolder"], true);
            }
        }
    }

    /// <summary>
    /// Delete the current folder and all its contents.
    /// </summary>
    private async Task DeleteFolderAndContentsAsync()
    {
        if (FolderId.HasValue)
        {
            var success = await FolderService.DeleteWithContentsAsync(FolderId.Value);
            if (success)
            {
                NavigationManager.NavigateTo("/items");
            }
            else
            {
                GlobalNotificationService.AddErrorMessage(Localizer["FailedToDeleteFolder"], true);
            }
        }
    }

    /// <inheritdoc />
    protected override async Task OnParametersSetAsync()
    {
        await base.OnParametersSetAsync();

        // Load folder name if in folder view
        if (FolderId.HasValue)
        {
            var folder = await FolderService.GetByIdAsync(FolderId.Value);
            CurrentFolderName = folder?.Name ?? string.Empty;
        }
        else
        {
            CurrentFolderName = string.Empty;
        }

        await LoadItemsAsync();
    }

    /// <inheritdoc />
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (firstRender)
        {
            await LoadItemsAsync();

            // Set up IntersectionObserver for infinite scroll (only if there are more items to load).
            // The sentinel element only exists when HasMoreItems is true.
            _dotNetRef = DotNetObjectReference.Create(this);
            if (HasMoreItems)
            {
                await JsInteropService.SetupInfiniteScroll(_scrollSentinel, _dotNetRef);
            }
        }
    }

    /// <inheritdoc />
    public async ValueTask DisposeAsync()
    {
        if (_dotNetRef != null)
        {
            await JsInteropService.TeardownInfiniteScroll(_scrollSentinel);
            _dotNetRef.Dispose();
            _dotNetRef = null;
        }
    }

    /// <summary>
    /// Loads and/or refreshes the items.
    /// </summary>
    private async Task LoadItemsAsync()
    {
        IsLoading = true;
        VisibleItemCount = BatchSize; // Reset visible items when loading
        StateHasChanged();

        // Load the items from the database via ItemService.
        var itemListEntries = await ItemService.GetListAsync();
        if (itemListEntries is null)
        {
            // Error loading items.
            GlobalNotificationService.AddErrorMessage(Localizer["FailedToLoadItemsMessage"], true);
            return;
        }

        if (itemListEntries.Count == 0 && !DbService.Settings.TutorialDone && !IsInFolder)
        {
            // Redirect to the welcome page.
            NavigationManager.NavigateTo("/welcome");
            return;
        }

        // Load folders
        Folders = await FolderService.GetAllWithCountsAsync();

        // Load recently deleted count
        var recentlyDeleted = await ItemService.GetRecentlyDeletedAsync();
        RecentlyDeletedCount = recentlyDeleted.Count;

        // Pass unsorted list to the view - sorting will be handled by the property
        Items = itemListEntries;
        IsLoading = false;
        StateHasChanged();
    }
}
