@page "/users/{Id}"
@using AliasVault.Admin.Main.Pages.Users.View.Components
@using AliasVault.Admin.Main.Models
@using AliasVault.Admin.Services
@using AliasVault.Auth
@using Microsoft.EntityFrameworkCore
@inherits MainBase
@inject StatisticsService StatisticsService
@inject ILogger<Index> Logger

<LayoutPageTitle>User</LayoutPageTitle>

@if (IsLoading || User == null)
{
    <LoadingIndicator/>
}
else
{
    <PageHeader
        BreadcrumbItems="@BreadcrumbItems"
        Title="View user"
        Description="View details of the user below.">
        <CustomActions>
            <RefreshButton OnClick="RefreshData" ButtonText="Refresh" />
            <LinkButton Color="danger" Href="@($"users/{Id}/delete")" Text="Delete user" />
        </CustomActions>
    </PageHeader>

    <div class="grid grid-cols-2 px-4 pt-6 md:grid-cols-3 lg:gap-4 dark:bg-gray-900">
        <div class="col-span-full">
            <div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
                <div class="items-center xl:block sm:space-x-4 xl:space-x-0 2xl:space-x-4">
                    <div>
                        <div class="mb-4 border-b border-gray-200 dark:border-gray-700 pb-2">
                            <div class="flex items-center gap-2">
                                <h3 class="text-2xl font-bold text-gray-900 dark:text-white">@User.UserName</h3>
                                @if (!IsEditingUsername)
                                {
                                    <button type="button" id="edit-username-button" @onclick="StartEditingUsername"
                                            class="inline-flex items-center justify-center px-3 py-2 text-sm font-medium text-white rounded-lg focus:outline-none focus:ring-4 bg-gray-700 hover:bg-gray-800 focus:ring-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 dark:focus:ring-gray-800"
                                            title="Change username">
                                        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                            <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"></path>
                                        </svg>
                                    </button>
                                }
                            </div>
                        </div>

                        @if (IsEditingUsername)
                        {
                            <div class="mb-4 space-y-3 p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
                                <div class="mb-3">
                                    <div class="flex items-center mb-2">
                                        <svg class="w-5 h-5 text-orange-600 dark:text-orange-400 mr-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                                            <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
                                        </svg>
                                        <h4 class="text-lg font-semibold text-gray-900 dark:text-white">Change Username</h4>
                                    </div>
                                    <p class="text-sm text-gray-600 dark:text-gray-300 mb-3">
                                        Changing a username is permanent and may affect the user's ability to log in.
                                        This is typically used for account archival or when a user specifically requests a username change.
                                    </p>
                                </div>
                                <div class="space-y-3">
                                    <div>
                                        <label class="block mb-1 text-sm font-medium text-gray-900 dark:text-white">New Username</label>
                                        <input type="text" @bind="NewUsername" placeholder="Enter new username"
                                               class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full max-w-md p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" />
                                        @if (!string.IsNullOrEmpty(UsernameValidationError))
                                        {
                                            <p class="text-sm text-red-600 dark:text-red-400 mt-1">@UsernameValidationError</p>
                                        }
                                    </div>
                                    <div class="flex space-x-2 pt-2">
                                        <Button Color="danger" OnClick="ChangeUsername">Change Username</Button>
                                        <Button Color="secondary" OnClick="CancelEditingUsername">Cancel</Button>
                                    </div>
                                </div>
                            </div>
                        }

                        <!-- Usage Statistics Section -->
                        @if (UserUsageStats != null)
                        {
                            <div class="mb-6 bg-gray-100 dark:bg-gray-700 rounded-lg p-4">
                                <h4 class="text-lg font-semibold text-gray-900 dark:text-white text-center mb-3">Usage Statistics</h4>
                                <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
                                    <div class="text-left">
                                        <div class="text-xl text-center font-semibold text-gray-900 dark:text-white">@UserUsageStats.TotalCredentials.ToString("N0")</div>
                                        <div class="text-sm text-center text-gray-600 dark:text-gray-400">Total Credentials</div>
                                        @if (UserUsageStats.RecentCredentials72h > 0)
                                        {
                                            <div class="text-xs text-center text-green-500 dark:text-green-400">(+@UserUsageStats.RecentCredentials72h.ToString("N0") in last 72h)</div>
                                        }
                                    </div>
                                    <div class="text-left">
                                        <div class="text-xl text-center font-semibold text-gray-900 dark:text-white">@UserUsageStats.TotalActiveEmailClaims.ToString("N0")</div>
                                        <div class="text-sm text-center text-gray-600 dark:text-gray-400">Active Email Claims</div>
                                        @if (UserUsageStats.RecentEmailClaims72h > 0)
                                        {
                                            <div class="text-xs text-center text-green-500 dark:text-green-400">(+@UserUsageStats.RecentEmailClaims72h.ToString("N0") in last 72h)</div>
                                        }
                                        @if (UserUsageStats.TotalDisabledEmailClaims > 0)
                                        {
                                            <div class="text-xs text-center text-red-400 dark:text-red-300">(@UserUsageStats.TotalDisabledEmailClaims.ToString("N0") disabled)</div>
                                        }
                                    </div>
                                    <div class="text-left">
                                        <div class="text-xl text-center font-semibold text-gray-900 dark:text-white">@UserUsageStats.TotalReceivedEmails.ToString("N0")</div>
                                        <div class="text-sm text-center text-gray-600 dark:text-gray-400">Total Received Emails</div>
                                        @if (UserUsageStats.RecentReceivedEmails72h > 0)
                                        {
                                            <div class="text-xs text-center text-green-500 dark:text-green-400">(+@UserUsageStats.RecentReceivedEmails72h.ToString("N0") in last 72h)</div>
                                        }
                                        @if (UserUsageStats.TotalEmailsReceivedPersistent > UserUsageStats.TotalReceivedEmails)
                                        {
                                            <div class="text-xs text-center text-orange-500 dark:text-orange-400" title="Persistent counter (never decremented, even when emails deleted)">(@UserUsageStats.TotalEmailsReceivedPersistent.ToString("N0") all-time)</div>
                                        }
                                    </div>
                                    <div class="text-left">
                                        <div class="text-xl text-center font-semibold text-gray-900 dark:text-white">@UserUsageStats.TotalEmailAttachments.ToString("N0")</div>
                                        <div class="text-sm text-center text-gray-600 dark:text-gray-400">Email Attachments</div>
                                        @if (UserUsageStats.TotalEmailAttachmentStorageMB > 0)
                                        {
                                            <div class="text-xs text-center text-blue-500 dark:text-blue-400">@UserUsageStats.TotalEmailAttachmentStorageMB.ToString("N2") MB</div>
                                        }
                                    </div>
                                </div>
                            </div>
                        }

                        <div class="w-full mb-4 overflow-x-auto">
                            <table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
                                <tbody>
                                    <tr class="">
                                        <th scope="row" class="px-4 py-3 font-medium text-gray-900 whitespace-nowrap dark:text-white">Registered at</th>
                                        <td class="px-4 py-3">@User.CreatedAt.ToString("yyyy-MM-dd HH:mm")</td>
                                    </tr>
                                    <tr>
                                        <th scope="row" class="px-4 py-3 font-medium text-gray-900 whitespace-nowrap dark:text-white">2FA Status</th>
                                        <td class="px-4 py-3">
                                            <div class="flex items-center space-x-2">
                                                <StatusPill Enabled="@User.TwoFactorEnabled"/>
                                                <span class="text-gray-700 dark:text-gray-300">Authenticator key(s) active: @TwoFactorKeysCount</span>
                                                @if (User.TwoFactorEnabled)
                                                {
                                                    <Button Color="danger" OnClick="DisableTwoFactor">Disable 2FA</Button>
                                                }
                                                else
                                                {
                                                    if (TwoFactorKeysCount > 0)
                                                    {
                                                        <Button Color="success" OnClick="EnableTwoFactor">Enable 2FA</Button>
                                                        <Button Color="danger" OnClick="ResetTwoFactor">Remove 2FA keys</Button>
                                                    }
                                                }
                                            </div>
                                        </td>
                                    </tr>
                                    <tr>
                                        <th scope="row" class="px-4 py-3 font-medium text-gray-900 whitespace-nowrap dark:text-white">Account Status</th>
                                        <td class="px-4 py-3">
                                            <div class="flex items-center space-x-2">
                                                <StatusPill Enabled="@(!User.Blocked)" TextTrue="Active" TextFalse="Blocked" />
                                                @if (User.Blocked && User.BlockedAt is not null)
                                                {
                                                    <span class="text-sm text-gray-500 dark:text-gray-400">since @User.BlockedAt.Value.ToString("yyyy-MM-dd HH:mm") UTC</span>
                                                }
                                                <Button Color="@(User.Blocked ? "success" : "danger")" OnClick="ToggleBlockStatus">
                                                    @(User.Blocked ? "Unblock User" : "Block User")
                                                </Button>
                                                <span class="text-sm text-gray-500 dark:text-gray-400">
                                                    <i class="fas fa-info-circle mr-1"></i>
                                                    Blocking a user prevents them from logging in or accessing AliasVault
                                                </span>
                                            </div>
                                        </td>
                                    </tr>
                                    <tr>
                                        <th scope="row" class="px-4 py-3 font-medium text-gray-900 whitespace-nowrap dark:text-white">Email Limits</th>
                                        <td class="px-4 py-3">
                                            @if (!IsEditingEmailLimits)
                                            {
                                                <div class="flex flex-col space-y-2">
                                                    <div class="flex items-center space-x-2">
                                                        <span class="text-gray-700 dark:text-gray-300">
                                                            <strong>Max Emails:</strong> @(User.MaxEmails == 0 ? "Unlimited" : User.MaxEmails.ToString("N0"))
                                                        </span>
                                                    </div>
                                                    <div class="flex items-center space-x-2">
                                                        <span class="text-gray-700 dark:text-gray-300">
                                                            <strong>Max Age:</strong> @(User.MaxEmailAgeDays == 0 ? "Unlimited" : User.MaxEmailAgeDays + " days")
                                                        </span>
                                                    </div>
                                                    <div class="mt-2">
                                                        <Button Color="primary" OnClick="@(() => StartEditingEmailLimits())">Edit Limits</Button>
                                                    </div>
                                                </div>
                                            }
                                            else
                                            {
                                                <div class="space-y-3 p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
                                                    <div>
                                                        <label class="block mb-1 text-sm font-medium text-gray-900 dark:text-white">Max Emails</label>
                                                        <div class="flex items-center space-x-2">
                                                            <input type="number" min="0" @bind="EditMaxEmails" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-32 p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" />
                                                            <span class="text-sm text-gray-500 dark:text-gray-400">(0 = unlimited)</span>
                                                        </div>
                                                    </div>
                                                    <div>
                                                        <label class="block mb-1 text-sm font-medium text-gray-900 dark:text-white">Max Email Age</label>
                                                        <div class="flex items-center space-x-2">
                                                            <input type="number" min="0" @bind="EditMaxEmailAgeDays" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-32 p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" />
                                                            <span class="text-sm text-gray-500 dark:text-gray-400">days (0 = unlimited)</span>
                                                        </div>
                                                    </div>
                                                    <div class="flex space-x-2 pt-2">
                                                        <Button Color="success" OnClick="SaveEmailLimits">Save</Button>
                                                        <Button Color="secondary" OnClick="CancelEditingEmailLimits">Cancel</Button>
                                                    </div>
                                                </div>
                                            }
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>

            <div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
                <div class="items-center">
                    <div>
                        <h3 class="mb-1 text-xl font-bold text-gray-900 dark:text-white">Vaults (@VaultList.Count)</h3>

                        <VaultTable VaultList="@VaultList" OnMakeCurrent="@MakeCurrentAsync" />
                    </div>
                </div>
            </div>

            <div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
                <div class="items-center">
                    <div>
                        <h3 class="mb-1 text-xl font-bold text-gray-900 dark:text-white">Logged in devices (@RefreshTokenList.Count)</h3>

                        <RefreshTokenTable RefreshTokenList="@RefreshTokenList" OnRevokeToken="@RevokeRefreshToken" OnRevokeAllTokens="@RevokeAllTokens" />
                    </div>
                </div>
            </div>

            <div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
                <div class="items-center">
                    <div>
                        <h3 class="mb-1 text-xl font-bold text-gray-900 dark:text-white">Recent authentication logs (@AuthLogList.Count)</h3>
                        <p class="text-sm text-gray-500 dark:text-gray-400">
                            Shows the last 5 authentication attempts for this user. Click "View all auth logs" to see complete history.
                        </p>
                        <AuthLogTable Username="@(User.UserName ?? string.Empty)" AuthLogList="@AuthLogList" />
                    </div>
                </div>
            </div>

            <div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
                <div class="items-center">
                    <div>
                        <h3 class="mb-1 text-xl font-bold text-gray-900 dark:text-white">Related users (@RelatedUsersCount)</h3>
                        <p class="text-sm text-gray-500 dark:text-gray-400">
                            Other user accounts that have authenticated from the same IP addresses or IP ranges as this user.
                            This can help identify potential duplicate accounts or related users.
                        </p>
                        <RelatedUsersTable UserId="@Id" @ref="RelatedUsersTableRef" OnCountChanged="HandleCountChanged" />
                    </div>
                </div>
            </div>

            <div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
                <div class="items-center">
                    <div>
                        <h3 class="mb-1 text-xl font-bold text-gray-900 dark:text-white">Email claims (@EmailClaimsCount)</h3>
                        <p class="text-sm text-gray-500 dark:text-gray-400">
                            Email claims represent the email addresses that the user has (historically) used. Whenever a user deletes an email alias
                            the claim gets disabled and the server will reject all emails sent to that alias. A user can always re-enable
                            the claim by using it again. Email claims are permanently tied to a user and cannot be transferred to another user.
                        </p>
                        <EmailClaimTable User="@User" @ref="EmailClaimTableRef" OnCountChanged="HandleCountChanged" />
                    </div>
                </div>
            </div>
        </div>
    </div>
}

@code {
    /// <summary>
    /// Gets or sets the user ID.
    /// </summary>
    [Parameter]
    public string Id { get; set; } = string.Empty;

    private bool IsLoading { get; set; } = true;
    private AliasVaultUser? User { get; set; } = new();
    private int TwoFactorKeysCount { get; set; }
    private List<AliasVaultUserRefreshToken> RefreshTokenList { get; set; } = [];
    private List<Vault> VaultList { get; set; } = [];
    private List<AuthLog> AuthLogList { get; set; } = [];
    private UserUsageStatistics? UserUsageStats { get; set; }
    private bool IsEditingEmailLimits { get; set; }
    private int EditMaxEmails { get; set; }
    private int EditMaxEmailAgeDays { get; set; }
    private bool IsEditingUsername { get; set; }
    private string NewUsername { get; set; } = string.Empty;
    private string UsernameValidationError { get; set; } = string.Empty;
    private RelatedUsersTable? RelatedUsersTableRef { get; set; }
    private EmailClaimTable? EmailClaimTableRef { get; set; }

    private int RelatedUsersCount => RelatedUsersTableRef?.GetRelatedUsersCount() ?? 0;
    private int EmailClaimsCount => EmailClaimTableRef?.GetTotalEmailClaimsCount() ?? 0;

    /// <inheritdoc />
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Users", Url = "users" });
        BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "View user" });
    }

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

        if (firstRender)
        {
            await RefreshData();
        }
    }

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

        // Refresh data when navigating to a different user (e.g., clicking related user links)
        if (!IsLoading && User?.Id != Id)
        {
            await RefreshData();
        }
    }

    private async Task RefreshData()
    {
        IsLoading = true;
        StateHasChanged();

        // Load the aliases from the webapi via AliasService.
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        User = await dbContext.AliasVaultUsers.FindAsync(Id);

        // Get count of user authenticator tokens.
        TwoFactorKeysCount = await dbContext.UserTokens.CountAsync(x => x.UserId == User!.Id && x.Name == "AuthenticatorKey");

        if (User is null)
        {
            // Error loading user.
            GlobalNotificationService.AddErrorMessage("This user does not exist (anymore). Please try again.");
            NavigationService.RedirectTo("users");
            return;
        }

        // Load all active refresh tokens for this user to show which devices are logged in.
        RefreshTokenList = await dbContext.AliasVaultUserRefreshTokens.Where(x => x.UserId == User.Id).Select(x => new AliasVaultUserRefreshToken()
            {
                Id = x.Id,
                DeviceIdentifier = x.DeviceIdentifier,
                IpAddress = x.IpAddress,
                ExpireDate = x.ExpireDate,
                CreatedAt = x.CreatedAt,
            })
            .Where(x => x.ExpireDate > DateTime.UtcNow)
            .OrderBy(x => x.CreatedAt)
            .ToListAsync();

        // Load all vaults for this user (do not load the actual file content for performance reasons).
        VaultList = await dbContext.Vaults.Where(x => x.UserId == User.Id).Select(x => new Vault
            {
                Id = x.Id,
                Version = x.Version,
                RevisionNumber = x.RevisionNumber,
                FileSize = x.FileSize,
                CreatedAt = x.CreatedAt,
                UpdatedAt = x.UpdatedAt,
                Salt = x.Salt,
                Verifier = x.Verifier,
                VaultBlob = string.Empty,
                EncryptionType = x.EncryptionType,
                EncryptionSettings = x.EncryptionSettings,
                CredentialsCount = x.CredentialsCount,
                EmailClaimsCount = x.EmailClaimsCount,
                Client = x.Client,
            })
            .OrderBy(x => x.UpdatedAt)
            .ToListAsync();

        // Load recent auth logs for this user (last 3 records).
        AuthLogList = await dbContext.AuthLogs
            .Where(x => x.Username == User.UserName)
            .OrderByDescending(x => x.Timestamp)
            .Take(5)
            .ToListAsync();

        // Load user usage statistics
        try
        {
            UserUsageStats = await StatisticsService.GetUserUsageStatisticsAsync(User.Id);
        }
        catch (Exception ex)
        {
            GlobalNotificationService.AddErrorMessage($"Error loading user usage statistics: {ex.Message}");
            UserUsageStats = null;
        }

        IsLoading = false;
        StateHasChanged();
    }

    /// <summary>
    /// This method will revoke a refresh token for the user which will log out the device.
    /// </summary>
    private async Task RevokeRefreshToken(AliasVaultUserRefreshToken entry)
    {
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        var token = await dbContext.AliasVaultUserRefreshTokens.FindAsync(entry.Id);

        if (token != null)
        {
            dbContext.AliasVaultUserRefreshTokens.Remove(token);
            await dbContext.SaveChangesAsync();
            await RefreshData();
        }
    }

    /// <summary>
    /// This method enables two-factor authentication for the user based on existing keys. If no keys are present
    /// then 2FA will not work. The user will need to manually set up a new authenticator device.
    /// </summary>
    private async Task EnableTwoFactor()
    {
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        User = await dbContext.AliasVaultUsers.FindAsync(Id);

        if (User != null)
        {
            User.TwoFactorEnabled = true;
            await dbContext.SaveChangesAsync();
            await RefreshData();
        }
    }

    /// <summary>
    /// This method disables two-factor authentication for the user. This will NOT remove the authenticator keys.
    /// This means the admin can re-enable 2FA for the user without the user having to set up a new authenticator
    /// keys.
    /// </summary>
    private async Task DisableTwoFactor()
    {
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        User = await dbContext.AliasVaultUsers.FindAsync(Id);

        if (User != null)
        {
            User.TwoFactorEnabled = false;
            await dbContext.SaveChangesAsync();
            await RefreshData();
        }
    }

    /// <summary>
    /// This method resets the two-factor authentication for the user which will remove all authenticator keys. The
    /// next time the user enables two-factor authentication new keys will be generated. When keys are removed it
    /// also means 2FA cannot be re-enabled until the user manually sets up a new authenticator device.
    /// </summary>
    private async Task ResetTwoFactor()
    {
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        User = await dbContext.AliasVaultUsers.FindAsync(Id);

        if (User != null)
        {
            // Remove all authenticator keys and recovery codes.
            await dbContext.UserTokens
                .Where(x => x.UserId == User.Id && (x.Name == "AuthenticatorKey" || x.Name == "RecoveryCodes"))
                .ForEachAsync(x => dbContext.UserTokens.Remove(x));

            await dbContext.SaveChangesAsync();
            await RefreshData();
        }
    }

    /// <summary>
    /// Handles the action of making a vault the current one.
    /// </summary>
    /// <param name="vault">The vault to make current.</param>
    private async Task MakeCurrentAsync(Vault vault)
    {
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        if (await ConfirmModalService.ShowConfirmation(
                title: "Confirm Vault Restoration",
                message: @"Are you sure you want to restore this specific vault and make it the active one?

Important notes:
• The next time the user logs in, they will load this vault version.
• If the user has changed their password recently, their password will be reverted to the one associated with this vault.
• All active sessions will be revoked and the user will need to login again on all devices.

Do you want to proceed with the restoration?")) {
            // Load vault
            var currentVault = await dbContext.Vaults.FindAsync(vault.Id);
            if (currentVault == null)
            {
                return;
            }

            // Update time to make this the current vault.
            currentVault.RevisionNumber = VaultList.MaxBy(x => x.RevisionNumber)!.RevisionNumber + 1;

            // Save it.
            await dbContext.SaveChangesAsync();

            // Revoke all user sessions to force re-authentication with potentially reverted password
            await RevokeAllTokens();
            Logger.LogInformation("Revoked all sessions for user {UserName} ({UserId}) after vault restoration to revision {RevisionNumber}.",
                User!.UserName, User.Id, currentVault.RevisionNumber);

            // Reload the page.
            await RefreshData();
        }
    }

    /// <summary>
    /// Toggles the blocked status of the user.
    /// </summary>
    private async Task ToggleBlockStatus()
    {
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        User = await dbContext.AliasVaultUsers.FindAsync(Id);

        if (User != null)
        {
            User.Blocked = !User.Blocked;

            // Record when the block was activated; clear the trace again when the user is unblocked.
            if (User.Blocked)
            {
                User.BlockedAt = DateTime.UtcNow;
            }
            else
            {
                User.BlockedAt = null;
            }

            // If user is unblocked by the admin, also reset any lockout status, which can be
            // automatically triggered by the system when user has entered an incorrect password too many times.
            if (!User.Blocked) {
                User.AccessFailedCount = 0;
                User.LockoutEnd = null;
            }

            await dbContext.SaveChangesAsync();

            // Add log entry for block/unblock action
            var action = User.Blocked ? "Blocked" : "Unblocked";
            Logger.LogInformation("{Action} user {UserName} ({UserId}).", action, User.UserName, User.Id);

            await RefreshData();
        }
    }

    /// <summary>
    /// This method will revoke all refresh tokens for the user which will log out all their devices.
    /// </summary>
    private async Task RevokeAllTokens()
    {
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        var tokens = await dbContext.AliasVaultUserRefreshTokens.Where(x => x.UserId == User!.Id).ToListAsync();

        if (tokens.Any())
        {
            dbContext.AliasVaultUserRefreshTokens.RemoveRange(tokens);
            await dbContext.SaveChangesAsync();
            await RefreshData();
        }
    }

    /// <summary>
    /// Starts editing email limits for the user.
    /// </summary>
    private void StartEditingEmailLimits()
    {
        IsEditingEmailLimits = true;
        EditMaxEmails = User!.MaxEmails;
        EditMaxEmailAgeDays = User.MaxEmailAgeDays;
    }

    /// <summary>
    /// Cancels editing email limits.
    /// </summary>
    private void CancelEditingEmailLimits()
    {
        IsEditingEmailLimits = false;
        EditMaxEmails = 0;
        EditMaxEmailAgeDays = 0;
    }

    /// <summary>
    /// Saves the email limits for the user.
    /// </summary>
    private async Task SaveEmailLimits()
    {
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        User = await dbContext.AliasVaultUsers.FindAsync(Id);

        if (User != null)
        {
            User.MaxEmails = EditMaxEmails;
            User.MaxEmailAgeDays = EditMaxEmailAgeDays;
            await dbContext.SaveChangesAsync();
            IsEditingEmailLimits = false;
            await RefreshData();
            GlobalNotificationService.AddSuccessMessage("Email limits updated successfully.", true);
        }
    }

    /// <summary>
    /// Starts editing username for the user.
    /// </summary>
    private void StartEditingUsername()
    {
        IsEditingUsername = true;
        NewUsername = User!.UserName ?? string.Empty;
        UsernameValidationError = string.Empty;
    }

    /// <summary>
    /// Cancels editing username.
    /// </summary>
    private void CancelEditingUsername()
    {
        IsEditingUsername = false;
        NewUsername = string.Empty;
        UsernameValidationError = string.Empty;
    }

    /// <summary>
    /// Changes the username for the user with validation.
    /// </summary>
    private async Task ChangeUsername()
    {
        // Normalize to lowercase and trim to match how usernames are stored at registration.
        NewUsername = UsernameHelper.NormalizeUsername(NewUsername);

        if (string.IsNullOrWhiteSpace(NewUsername))
        {
            UsernameValidationError = "Username cannot be empty.";
            return;
        }

        if (NewUsername.Length < 3)
        {
            UsernameValidationError = "Username must be at least 3 characters long.";
            return;
        }

        if (NewUsername.Length > 256)
        {
            UsernameValidationError = "Username cannot be longer than 256 characters.";
            return;
        }

        if (NewUsername == User!.UserName)
        {
            UsernameValidationError = "New username must be different from current username.";
            return;
        }

        await using var dbContext = await DbContextFactory.CreateDbContextAsync();

        // Check if username already exists
        var existingUser = await dbContext.AliasVaultUsers.FirstOrDefaultAsync(x => x.UserName == NewUsername);
        if (existingUser != null)
        {
            UsernameValidationError = "This username is already in use by another user.";
            return;
        }

        // Reload user to ensure we have the latest data
        User = await dbContext.AliasVaultUsers.FindAsync(Id);
        if (User != null)
        {
            var oldUsername = User.UserName;
            User.UserName = NewUsername;
            User.NormalizedUserName = NewUsername.ToUpperInvariant();
            User.Email = NewUsername;
            User.NormalizedEmail = NewUsername.ToUpperInvariant();
            User.UpdatedAt = DateTime.UtcNow;

            try
            {
                await dbContext.SaveChangesAsync();

                // Add log entry for username change
                Logger.LogInformation("Changed username for user {OldUsername} ({UserId}) to {NewUsername}.", oldUsername, User.Id, NewUsername);

                IsEditingUsername = false;
                UsernameValidationError = string.Empty;
                await RefreshData();
                GlobalNotificationService.AddSuccessMessage($"Username changed from '{oldUsername}' to '{NewUsername}' successfully.", true);
            }
            catch (Exception ex)
            {
                UsernameValidationError = $"Error updating username: {ex.Message}";
            }
        }
    }

    /// <summary>
    /// Handle count changes from child components to trigger re-render.
    /// </summary>
    private void HandleCountChanged()
    {
        StateHasChanged();
    }
}
