@page "/emails"
@using System.Net
@using System.Text
@using System.Text.Json
@using AliasVault.Client.Main.Pages.Emails.Models
@using AliasVault.Shared.Models.Spamok
@using AliasVault.Shared.Models.WebApi
@using AliasVault.Shared.Models.WebApi.Email
@inherits MainBase
@inject HttpClient HttpClient
@inject ILogger<Home> Logger

<LayoutPageTitle>Emails</LayoutPageTitle>

@if (EmailModalVisible)
{
    <EmailModal Email="EmailModalEmail" IsSpamOk="false" OnClose="CloseEmailModal" OnEmailDeleted="RefreshData" />
}

<PageHeader
    BreadcrumbItems="@BreadcrumbItems"
    Title="Emails"
    Description="You can view all emails received by your private email addresses below.">
    <CustomActions>
        <RefreshButton OnClick="RefreshData" ButtonText="Refresh" />
    </CustomActions>
</PageHeader>

@if (IsLoading)
{
    <LoadingIndicator/>
}
else if (NoEmailClaims)
{
    <div class="p-4 mx-4 mt-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 dark:bg-gray-800">
        <div class="px-4 py-2 text-gray-400 rounded">
            <p class="text-gray-500 dark:text-gray-400">You are not using any private email addresses (yet). Create a new alias and use a private email address supported by AliasVault. All emails received by these private email addresses will show up here.</p>
        </div>
    </div>
}
else
{
    <div class="overflow-x-auto px-4">
        <Paginator CurrentPage="CurrentPage" PageSize="PageSize" TotalRecords="TotalRecords" OnPageChanged="HandlePageChanged"/>

        <div class="bg-white border rounded-lg dark:bg-gray-800 dark:border-gray-700 overflow-hidden mt-6">
            <div class="px-4 py-2 bg-gray-100 border-b dark:bg-gray-600 dark:border-gray-700">
                <h2 class="font-semibold text-gray-800 dark:text-gray-200">Inbox</h2>
            </div>
            <ul class="divide-y divide-gray-200 dark:divide-gray-600">
                @if (EmailList.Count == 0)
                {
                    <li class="p-4 text-center text-gray-500 dark:text-gray-300">
                        No emails have been received yet.
                    </li>
                }
                else
                {
                    @foreach (var email in EmailList)
                    {
                        <li class="hover:bg-gray-50 dark:hover:bg-gray-600 transition duration-150 ease-in-out">
                            <div @onclick="() => ShowAliasVaultEmailInModal(email.Id)" class="p-4 flex justify-start items-start">
                                <div class="mr-4 flex-shrink-0">
                                    <SenderInitials SenderName="@email.FromName" SenderEmail="@email.FromEmail" />
                                </div>
                                <div class="flex-grow">
                                    <div class="flex items-center justify-between mb-2 mr-4">
                                        <div>
                                            <div class="text-gray-800 dark:text-gray-200 mb-2">
                                                @email.Subject
                                            </div>
                                            <div class="text-sm text-gray-400 dark:text-gray-100 line-clamp-2">
                                                @email.MessagePreview
                                            </div>
                                        </div>
                                        <div class="flex justify-end">
                                            <div @onclick="() => NavigateToCredential(email.CredentialId)" class="text-sm text-gray-700 dark:text-gray-300 cursor-pointer mr-4 hover:underline">@email.CredentialName</div>
                                            <div class="text-sm text-gray-500 dark:text-gray-400">@email.Date.ToString("yyyy-MM-dd")</div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </li>
                    }
                }
            </ul>
        </div>
    </div>
}

@code {
    private List<MailListViewModel> EmailList { get; set; } = [];
    private bool IsLoading { get; set; } = true;
    private int CurrentPage { get; set; } = 1;
    private int PageSize { get; set; } = 50;
    private int TotalRecords { get; set; }
    private bool EmailModalVisible { get; set; }
    private bool NoEmailClaims { get; set; }
    private EmailApiModel EmailModalEmail { get; set; } = new();

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

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

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

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

        var emailClaimList = await DbService.GetEmailClaimListAsync();

        if (emailClaimList.Count == 0)
        {
            IsLoading = false;
            NoEmailClaims = true;
            StateHasChanged();
            return;
        }

        var requestModel = new MailboxBulkRequest
        {
            Page = CurrentPage,
            PageSize = PageSize,
            Addresses = emailClaimList,
        };

        var request = new HttpRequestMessage(HttpMethod.Post, $"v1/EmailBox/bulk");
        request.Content = new StringContent(JsonSerializer.Serialize(requestModel), Encoding.UTF8, "application/json");

        try
        {
            var response = await HttpClient.SendAsync(request);
            if (response.IsSuccessStatusCode)
            {
                var mailbox = await response.Content.ReadFromJsonAsync<MailboxBulkResponse>();
                await UpdateMailboxEmails(mailbox);
            }
            else
            {
                var errorContent = await response.Content.ReadAsStringAsync();
                var errorResponse = JsonSerializer.Deserialize<ApiErrorResponse>(errorContent);
                switch (response.StatusCode)
                {
                    case HttpStatusCode.BadRequest:
                        if (errorResponse != null)
                        {
                            switch (errorResponse.Code)
                            {
                                case "CLAIM_DOES_NOT_EXIST":
                                    GlobalNotificationService.AddErrorMessage("An error occurred while trying to load the emails. Please try to edit and " +
                                                                              "save any credential entry to synchronize the database, then try again.", true);
                                    break;
                                default:
                                    throw new ArgumentException(errorResponse.Message);
                            }
                        }

                        break;
                    case HttpStatusCode.Unauthorized:
                        throw new UnauthorizedAccessException(errorResponse?.Message);
                    default:
                        throw new WebException(errorResponse?.Message);
                }
            }
        }
        catch (Exception ex)
        {
           GlobalNotificationService.AddErrorMessage(ex.Message, true);
           Logger.LogError(ex, "An error occurred while refreshing data");
        }

        IsLoading = false;
        StateHasChanged();
    }

    /// <summary>
    /// Update the local mailbox emails.
    /// </summary>
    private async Task UpdateMailboxEmails(MailboxBulkResponse? model)
    {
        if (model == null)
        {
            EmailList = [];
            TotalRecords = 0;
            return;
        }

        var context = await DbService.GetDbContextAsync();

        // Fetch all credentials in a single query and create a lookup dictionary
        var credentialLookup = await context.Credentials
            .Include(x => x.Service)
            .Include(x => x.Alias)
            .Where(x => x.Alias.Email != null)
            .GroupBy(x => x.Alias.Email!.ToLower())
            .ToDictionaryAsync(
                g => g.Key,
                g => new { Id = g.First().Id, ServiceName = g.First().Service.Name ?? "Unknown" }
            );

        // Convert the email list to view models and add credential info in a single pass
        var decryptedEmailList = await EmailService.DecryptEmailList(model.Mails);
        EmailList = decryptedEmailList.Select(email =>
        {
            var toEmail = email.ToLocal + "@" + email.ToDomain;
            var credentialInfo = credentialLookup.TryGetValue(toEmail.ToLower(), out var info)
                ? info
                : new { Id = Guid.Empty, ServiceName = "Unknown" };

            return new MailListViewModel
            {
                Id = email.Id,
                Date = email.DateSystem,
                FromName = email.FromDisplay,
                FromEmail = email.FromLocal + "@" + email.FromDomain,
                ToEmail = toEmail,
                Subject = email.Subject,
                MessagePreview = email.MessagePreview,
                CredentialId = credentialInfo.Id,
                CredentialName = credentialInfo.ServiceName
            };
        }).ToList();

        CurrentPage = model.CurrentPage;
        PageSize = model.PageSize;
        TotalRecords = model.TotalRecords;
    }

    /// <summary>
    /// Load recent emails from AliasVault.
    /// </summary>
    private async Task ShowAliasVaultEmailInModal(int emailId)
    {
        EmailApiModel? mail = await HttpClient.GetFromJsonAsync<EmailApiModel>($"v1/Email/{emailId}");
        if (mail != null)
        {
            // Decrypt the email content locally.
            var context = await DbService.GetDbContextAsync();
            var privateKey = await context.EncryptionKeys.FirstOrDefaultAsync(x => x.PublicKey == mail.EncryptionKey);
            if (privateKey is not null)
            {
                mail = await EmailService.DecryptEmail(mail);
            }

            EmailModalEmail = mail;
            EmailModalVisible = true;
            StateHasChanged();
        }
    }

    /// <summary>
    /// Close the email modal.
    /// </summary>
    private void CloseEmailModal()
    {
        EmailModalVisible = false;
        StateHasChanged();
    }

    /// <summary>
    /// Navigate to the credential page.
    /// </summary>
    private void NavigateToCredential(Guid credentialId)
    {
        NavigationManager.NavigateTo($"/credentials/{credentialId}");
    }
}
