@page "/users"
@using AliasVault.RazorComponents.Tables
@inherits MainBase

<LayoutPageTitle>Users</LayoutPageTitle>

<PageHeader
    BreadcrumbItems="@BreadcrumbItems"
    Title="@(TotalRecords > 0 ? $"Users ({TotalRecords:N0})" : "Users")"
    Description="This page shows an overview of all registered users and the associated vaults.">
    <CustomActions>
        <RefreshButton OnClick="RefreshData" ButtonText="Refresh" />
    </CustomActions>
</PageHeader>

@if (IsInitialized)
{
    <div class="px-4">
        <Paginator CurrentPage="CurrentPage" PageSize="PageSize" TotalRecords="TotalRecords" OnPageChanged="HandlePageChanged" />
        <div class="mb-3">
            <div class="relative">
                <SearchIcon />
                <input type="text" @bind-value="SearchTerm" @bind-value:event="oninput" id="search" placeholder="Search users..." class="w-full px-4 ps-10 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white">
            </div>
        </div>
    </div>
}

@if (IsLoading)
{
    <LoadingIndicator />
}
else
{
    <div class="px-4">
        <SortableTable Columns="@_tableColumns" SortColumn="@SortColumn" SortDirection="@SortDirection" OnSortChanged="HandleSortChanged">
            @foreach (var user in UserList)
            {
                <SortableTableRow>
                    <SortableTableColumn IsPrimary="true">@user.CreatedAt.ToString("yyyy-MM-dd HH:mm")</SortableTableColumn>
                    <SortableTableColumn>@user.UserName</SortableTableColumn>
                    <SortableTableColumn>@user.VaultCount</SortableTableColumn>
                    <SortableTableColumn>@user.CredentialCount</SortableTableColumn>
                    <SortableTableColumn>@user.EmailClaimCount</SortableTableColumn>
                    <SortableTableColumn>@user.ReceivedEmailCount</SortableTableColumn>
                    <SortableTableColumn>@Math.Round((double)user.VaultStorageInKb / 1024, 1) MB</SortableTableColumn>
                    <SortableTableColumn>@user.LastVaultUpdate.ToString("yyyy-MM-dd HH:mm")</SortableTableColumn>
                    <SortableTableColumn>
                        @if (user.Blocked)
                        {
                            <StatusPill Enabled="false" TextFalse="Blocked" />
                        }
                        @if (user.TwoFactorEnabled)
                        {
                            <StatusPill Enabled="true" TextTrue="2FA enabled" />
                        }
                    </SortableTableColumn>
                    <SortableTableColumn>
                        <LinkButton Color="primary" Href="@($"users/{user.Id}")" Text="View" />
                    </SortableTableColumn>
                </SortableTableRow>
            }
        </SortableTable>
    </div>
}

@code {
    private readonly List<TableColumn> _tableColumns = [
        new TableColumn { Title = "Registered", PropertyName = "CreatedAt" },
        new TableColumn { Title = "Username", PropertyName = "UserName" },
        new TableColumn { Title = "# Vaults", PropertyName = "VaultCount" },
        new TableColumn { Title = "# Credentials", PropertyName = "CredentialCount" },
        new TableColumn { Title = "# Email claims", PropertyName = "EmailClaimCount" },
        new TableColumn { Title = "# Received emails", PropertyName = "ReceivedEmailCount" },
        new TableColumn { Title = "Storage", PropertyName = "VaultStorageInKb" },
        new TableColumn { Title = "LastVaultUpdate", PropertyName = "LastVaultUpdate" },
        new TableColumn { Title = "Status", Sortable = false },
        new TableColumn { Title = "Actions", Sortable = false},
    ];

    private List<UserViewModel> UserList { get; set; } = [];
    private bool IsInitialized { get; set; } = false;
    private bool IsLoading { get; set; } = true;
    private int CurrentPage { get; set; } = 1;
    private int PageSize { get; set; } = 50;
    private int TotalRecords { get; set; }

    private string _searchTerm = string.Empty;

    /// <summary>
    /// The last search term.
    /// </summary>
    private string _lastSearchTerm = string.Empty;

    private string SearchTerm
    {
        get => _searchTerm;
        set
        {
            if (_searchTerm != value)
            {
                _searchTerm = value;
                _ = RefreshData();
            }
        }
    }

    private string SortColumn { get; set; } = "CreatedAt";
    private SortDirection SortDirection { get; set; } = SortDirection.Descending;

    private async Task HandleSortChanged((string column, SortDirection direction) sort)
    {
        SortColumn = sort.column;
        SortDirection = sort.direction;
        await RefreshData();
    }

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

    /// <inheritdoc />
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await RefreshData();
        }
    }

    private void HandlePageChanged(int newPage)
    {
        CurrentPage = newPage;
        _ = RefreshData();
    }

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

        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        IQueryable<AliasVaultUser> query = dbContext.AliasVaultUsers;

        query = ApplySearchFilter(query);
        query = ApplySort(query, dbContext);

        TotalRecords = await query.CountAsync();
        var users = await query
            .Skip((CurrentPage - 1) * PageSize)
            .Take(PageSize)
            .Select(u => new
            {
                u.Id,
                u.UserName,
                u.CreatedAt,
                u.TwoFactorEnabled,
                u.Blocked,
                Vaults = u.Vaults.Select(v => new
                {
                    v.FileSize,
                    v.CreatedAt,
                    v.RevisionNumber,
                    CredentialCount = v.CredentialsCount,
                }),
                EmailClaims = u.EmailClaims.Select(ec => new
                {
                    ec.CreatedAt,
                    ec.Address
                }),
                ReceivedEmails = u.EmailClaims.SelectMany(ec => dbContext.Emails.Where(e => e.To == ec.Address)).Count(),
            })
            .ToListAsync();

        UserList = users.Select(user => new UserViewModel
        {
            Id = user.Id,
            UserName = user.UserName?.ToLower() ?? "N/A",
            TwoFactorEnabled = user.TwoFactorEnabled,
            Blocked = user.Blocked,
            CreatedAt = user.CreatedAt,
            VaultCount = user.Vaults.Count(),
            CredentialCount = user.Vaults.OrderByDescending(x => x.RevisionNumber).First().CredentialCount,
            EmailClaimCount = user.EmailClaims.Count(),
            ReceivedEmailCount = user.ReceivedEmails,
            VaultStorageInKb = user.Vaults.Sum(x => x.FileSize),
            LastVaultUpdate = user.Vaults.Any() ? user.Vaults.Max(x => x.CreatedAt) : user.CreatedAt,
        }).ToList();

        IsLoading = false;
        IsInitialized = true;
        StateHasChanged();
    }

    /// <summary>
    /// Apply search filter to the query.
    /// </summary>
    private IQueryable<AliasVaultUser> ApplySearchFilter(IQueryable<AliasVaultUser> query)
    {
        if (SearchTerm.Length > 0)
        {
            // Reset page number back to 1 if the search term has changed.
            if (SearchTerm != _lastSearchTerm && CurrentPage != 1)
            {
                CurrentPage = 1;
            }
            _lastSearchTerm = SearchTerm;

            var searchTerm = SearchTerm.Trim().ToLower();
            query = query.Where(x => EF.Functions.Like(x.UserName!.ToLower(), "%" + searchTerm + "%"));
        }

        return query;
    }

    /// <summary>
    /// Apply sort to the query.
    /// </summary>
    private IQueryable<AliasVaultUser> ApplySort(IQueryable<AliasVaultUser> query, AliasServerDbContext dbContext)
    {
        // Apply sort.
        switch (SortColumn)
        {
            case "Id":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Id)
                    : query.OrderByDescending(x => x.Id);
                break;
            case "CreatedAt":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.CreatedAt)
                    : query.OrderByDescending(x => x.CreatedAt);
                break;
            case "UserName":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.UserName)
                    : query.OrderByDescending(x => x.UserName);
                break;
            case "VaultCount":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Vaults.Count)
                    : query.OrderByDescending(x => x.Vaults.Count);
                break;
            case "CredentialCount":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Vaults.OrderByDescending(x => x.RevisionNumber).First().CredentialsCount)
                    : query.OrderByDescending(x => x.Vaults.OrderByDescending(x => x.RevisionNumber).First().CredentialsCount);
                break;
            case "EmailClaimCount":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.EmailClaims.Count)
                    : query.OrderByDescending(x => x.EmailClaims.Count);
                break;
            case "ReceivedEmailCount":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.EmailClaims.SelectMany(ec => dbContext.Emails.Where(e => e.To == ec.Address)).Count())
                    : query.OrderByDescending(x => x.EmailClaims.SelectMany(ec => dbContext.Emails.Where(e => e.To == ec.Address)).Count());
                break;
            case "VaultStorageInKb":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Vaults.Sum(v => v.FileSize))
                    : query.OrderByDescending(x => x.Vaults.Sum(v => v.FileSize));
                break;
            case "LastVaultUpdate":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Vaults.Max(v => v.CreatedAt))
                    : query.OrderByDescending(x => x.Vaults.Max(v => v.CreatedAt));
                break;
            default:
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Id)
                    : query.OrderByDescending(x => x.Id);
                break;
        }

        return query;
    }

}
