using System.Net;
using Bit.Api.IntegrationTest.Factories;
using Bit.Api.IntegrationTest.Helpers;
using Bit.Api.Models.Response;
using Bit.Api.NotificationCenter.Models.Response;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Billing.Enums;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Api;
using Bit.Core.NotificationCenter.Entities;
using Bit.Core.NotificationCenter.Enums;
using Bit.Core.NotificationCenter.Repositories;
using Bit.Core.Repositories;
using Xunit;

namespace Bit.Api.IntegrationTest.NotificationCenter.Controllers;

public class NotificationsControllerTests : IClassFixture<ApiApplicationFactory>, IAsyncLifetime
{
    private static readonly string _mockEncryptedBody =
        "2.AOs41Hd8OQiCPXjyJKCiDA==|O6OHgt2U2hJGBSNGnimJmg==|iD33s8B69C8JhYYhSa4V1tArjvLr8eEaGqOV7BRo5Jk=";

    private static readonly string _mockEncryptedTitle =
        "2.06CDSJjTZaigYHUuswIq5A==|trxgZl2RCkYrrmCvGE9WNA==|w5p05eI5wsaYeSyWtsAPvBX63vj798kIMxBTfSB0BQg=";

    private static readonly Random _random = new();

    private static TimeSpan OneMinuteTimeSpan => TimeSpan.FromMinutes(1);

    private readonly HttpClient _client;
    private readonly ApiApplicationFactory _factory;
    private readonly LoginHelper _loginHelper;
    private readonly INotificationRepository _notificationRepository;
    private readonly INotificationStatusRepository _notificationStatusRepository;
    private readonly IUserRepository _userRepository;
    private Organization _organization = null!;
    private OrganizationUser _organizationUserOwner = null!;
    private string _ownerEmail = null!;
    private List<(Notification, NotificationStatus?)> _notificationsWithStatuses = null!;

    public NotificationsControllerTests(ApiApplicationFactory factory)
    {
        _factory = factory;
        _client = factory.CreateClient();
        _loginHelper = new LoginHelper(_factory, _client);
        _notificationRepository = _factory.GetService<INotificationRepository>();
        _notificationStatusRepository = _factory.GetService<INotificationStatusRepository>();
        _userRepository = _factory.GetService<IUserRepository>();
    }

    public async Task InitializeAsync()
    {
        // Create the owner account
        _ownerEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com";
        await _factory.LoginWithNewAccount(_ownerEmail);

        // Create the organization
        (_organization, _organizationUserOwner) = await OrganizationTestHelpers.SignUpAsync(_factory,
            plan: PlanType.EnterpriseAnnually, ownerEmail: _ownerEmail, passwordManagerSeats: 10,
            paymentMethod: PaymentMethodType.Card);

        _notificationsWithStatuses = await CreateNotificationsWithStatusesAsync();
    }

    public Task DisposeAsync()
    {
        _client.Dispose();

        foreach (var (notification, _) in _notificationsWithStatuses)
        {
            _notificationRepository.DeleteAsync(notification);
        }

        return Task.CompletedTask;
    }

    [Theory]
    [InlineData("invalid")]
    [InlineData("-1")]
    [InlineData("0")]
    public async Task ListAsync_RequestValidationContinuationInvalidNumber_BadRequest(string continuationToken)
    {
        await _loginHelper.LoginAsync(_ownerEmail);

        var response = await _client.GetAsync($"/notifications?continuationToken={continuationToken}");
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
        var result = await response.Content.ReadFromJsonAsync<ErrorResponseModel>();
        Assert.NotNull(result);
        Assert.Contains("ContinuationToken", result.ValidationErrors);
        Assert.Contains("Continuation token must be a positive, non zero integer.",
            result.ValidationErrors["ContinuationToken"]);
    }

    [Fact]
    public async Task ListAsync_RequestValidationContinuationTokenMaxLengthExceeded_BadRequest()
    {
        await _loginHelper.LoginAsync(_ownerEmail);

        var response = await _client.GetAsync("/notifications?continuationToken=1234567890");
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
        var result = await response.Content.ReadFromJsonAsync<ErrorResponseModel>();
        Assert.NotNull(result);
        Assert.Contains("ContinuationToken", result.ValidationErrors);
        Assert.Contains("The field ContinuationToken must be a string with a maximum length of 9.",
            result.ValidationErrors["ContinuationToken"]);
    }

    [Theory]
    [InlineData("9")]
    [InlineData("1001")]
    public async Task ListAsync_RequestValidationPageSizeInvalidRange_BadRequest(string pageSize)
    {
        await _loginHelper.LoginAsync(_ownerEmail);

        var response = await _client.GetAsync($"/notifications?pageSize={pageSize}");
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
        var result = await response.Content.ReadFromJsonAsync<ErrorResponseModel>();
        Assert.NotNull(result);
        Assert.Contains("PageSize", result.ValidationErrors);
        Assert.Contains("The field PageSize must be between 10 and 1000.",
            result.ValidationErrors["PageSize"]);
    }

    [Fact]
    public async Task ListAsync_NotLoggedIn_Unauthorized()
    {
        var response = await _client.GetAsync("/notifications");
        Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
    }

    [Theory]
    [InlineData(null, null, "2", 10)]
    [InlineData(10, null, "2", 10)]
    [InlineData(10, 2, "3", 10)]
    [InlineData(10, 3, null, 4)]
    [InlineData(24, null, "2", 24)]
    [InlineData(24, 2, null, 0)]
    [InlineData(1000, null, null, 24)]
    public async Task ListAsync_PaginationFilter_ReturnsNextPageOfNotificationsCorrectOrder(
        int? pageSize, int? pageNumber, string? expectedContinuationToken, int expectedCount)
    {
        var pageSizeWithDefault = pageSize ?? 10;

        await _loginHelper.LoginAsync(_ownerEmail);

        var skip = pageNumber == null ? 0 : (pageNumber.Value - 1) * pageSizeWithDefault;

        var notificationsInOrder = _notificationsWithStatuses.OrderByDescending(e => e.Item1.Priority)
            .ThenByDescending(e => e.Item1.CreationDate)
            .Skip(skip)
            .Take(pageSizeWithDefault)
            .ToList();

        var url = "/notifications";
        if (pageNumber != null)
        {
            url += $"?continuationToken={pageNumber}";
        }

        if (pageSize != null)
        {
            url += url.Contains('?') ? "&" : "?";
            url += $"pageSize={pageSize}";
        }

        var response = await _client.GetAsync(url);
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        var result = await response.Content.ReadFromJsonAsync<ListResponseModel<NotificationResponseModel>>();
        Assert.NotNull(result?.Data);
        Assert.InRange(result.Data.Count(), 0, pageSizeWithDefault);
        Assert.Equal(expectedCount, notificationsInOrder.Count);
        Assert.Equal(notificationsInOrder.Count, result.Data.Count());
        AssertNotificationResponseModels(result.Data, notificationsInOrder);

        Assert.Equal(expectedContinuationToken, result.ContinuationToken);
    }

    [Theory]
    [InlineData(null, null)]
    [InlineData(null, false)]
    [InlineData(null, true)]
    [InlineData(false, null)]
    [InlineData(true, null)]
    [InlineData(false, false)]
    [InlineData(false, true)]
    [InlineData(true, false)]
    [InlineData(true, true)]
    public async Task ListAsync_ReadStatusDeletedStatusFilter_ReturnsFilteredNotificationsCorrectOrder(
        bool? readStatusFilter, bool? deletedStatusFilter)
    {
        await _loginHelper.LoginAsync(_ownerEmail);
        var notificationsInOrder = _notificationsWithStatuses.FindAll(e =>
                (readStatusFilter == null || readStatusFilter == (e.Item2?.ReadDate != null)) &&
                (deletedStatusFilter == null || deletedStatusFilter == (e.Item2?.DeletedDate != null)))
            .OrderByDescending(e => e.Item1.Priority)
            .ThenByDescending(e => e.Item1.CreationDate)
            .Take(10)
            .ToList();

        var url = "/notifications";
        if (readStatusFilter != null)
        {
            url += $"?readStatusFilter={readStatusFilter}";
        }

        if (deletedStatusFilter != null)
        {
            url += url.Contains('?') ? "&" : "?";
            url += $"deletedStatusFilter={deletedStatusFilter}";
        }

        var response = await _client.GetAsync(url);
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        var result = await response.Content.ReadFromJsonAsync<ListResponseModel<NotificationResponseModel>>();
        Assert.NotNull(result?.Data);
        Assert.InRange(result.Data.Count(), 0, 10);
        Assert.Equal(notificationsInOrder.Count, result.Data.Count());
        AssertNotificationResponseModels(result.Data, notificationsInOrder);
    }

    [Fact]
    private async void MarkAsDeletedAsync_NotLoggedIn_Unauthorized()
    {
        var url = $"/notifications/{Guid.NewGuid().ToString()}/delete";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
    }

    [Fact]
    private async void MarkAsDeletedAsync_NonExistentNotificationId_NotFound()
    {
        await _loginHelper.LoginAsync(_ownerEmail);

        var url = $"/notifications/{Guid.NewGuid()}/delete";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    private async void MarkAsDeletedAsync_UserIdNotMatching_NotFound()
    {
        var email = $"integration-test{Guid.NewGuid()}@bitwarden.com";
        await _factory.LoginWithNewAccount(email);
        var user = (await _userRepository.GetByEmailAsync(email))!;
        var notifications = await CreateNotificationsAsync(user.Id);

        await _loginHelper.LoginAsync(_ownerEmail);

        var url = $"/notifications/{notifications[0].Id}/delete";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    private async void MarkAsDeletedAsync_OrganizationIdNotMatchingUserNotPartOfOrganization_NotFound()
    {
        var email = $"integration-test{Guid.NewGuid()}@bitwarden.com";
        await _factory.LoginWithNewAccount(email);
        var user = (await _userRepository.GetByEmailAsync(email))!;
        var notifications = await CreateNotificationsAsync(user.Id, _organization.Id);

        await _loginHelper.LoginAsync(email);

        var url = $"/notifications/{notifications[0].Id}/delete";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    private async void MarkAsDeletedAsync_OrganizationIdNotMatchingUserPartOfDifferentOrganization_NotFound()
    {
        var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory,
            plan: PlanType.EnterpriseAnnually, ownerEmail: _ownerEmail, passwordManagerSeats: 10,
            paymentMethod: PaymentMethodType.Card);
        var email = $"integration-test{Guid.NewGuid()}@bitwarden.com";
        await _factory.LoginWithNewAccount(email);
        var user = (await _userRepository.GetByEmailAsync(email))!;
        await OrganizationTestHelpers.CreateUserAsync(_factory, organization.Id, email, OrganizationUserType.User);
        var notifications = await CreateNotificationsAsync(user.Id, _organization.Id);

        await _loginHelper.LoginAsync(email);

        var url = $"/notifications/{notifications[0].Id}/delete";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    private async void MarkAsDeletedAsync_NotificationStatusNotExisting_Created()
    {
        var notifications = await CreateNotificationsAsync(_organizationUserOwner.UserId);

        await _loginHelper.LoginAsync(_ownerEmail);

        var url = $"/notifications/{notifications[0].Id}/delete";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        var notificationStatus = await _notificationStatusRepository.GetByNotificationIdAndUserIdAsync(
            notifications[0].Id, _organizationUserOwner.UserId!.Value);
        Assert.NotNull(notificationStatus);
        Assert.NotNull(notificationStatus.DeletedDate);
        Assert.Equal(DateTime.UtcNow, notificationStatus.DeletedDate.Value, OneMinuteTimeSpan);
        Assert.Null(notificationStatus.ReadDate);
    }

    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    private async void MarkAsDeletedAsync_NotificationStatusExisting_Updated(bool deletedDateNull)
    {
        var notifications = await CreateNotificationsAsync(_organizationUserOwner.UserId);
        await _notificationStatusRepository.CreateAsync(new NotificationStatus
        {
            NotificationId = notifications[0].Id,
            UserId = _organizationUserOwner.UserId!.Value,
            ReadDate = null,
            DeletedDate = deletedDateNull ? null : DateTime.UtcNow - TimeSpan.FromMinutes(_random.Next(3600))
        });

        await _loginHelper.LoginAsync(_ownerEmail);

        var url = $"/notifications/{notifications[0].Id}/delete";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        var notificationStatus = await _notificationStatusRepository.GetByNotificationIdAndUserIdAsync(
            notifications[0].Id, _organizationUserOwner.UserId!.Value);
        Assert.NotNull(notificationStatus);
        Assert.NotNull(notificationStatus.DeletedDate);
        Assert.Equal(DateTime.UtcNow, notificationStatus.DeletedDate.Value, OneMinuteTimeSpan);
        Assert.Null(notificationStatus.ReadDate);
    }

    [Fact]
    private async void MarkAsReadAsync_NotLoggedIn_Unauthorized()
    {
        var url = $"/notifications/{Guid.NewGuid().ToString()}/read";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
    }

    [Fact]
    private async void MarkAsReadAsync_NonExistentNotificationId_NotFound()
    {
        await _loginHelper.LoginAsync(_ownerEmail);

        var url = $"/notifications/{Guid.NewGuid()}/read";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    private async void MarkAsReadAsync_UserIdNotMatching_NotFound()
    {
        var email = $"integration-test{Guid.NewGuid()}@bitwarden.com";
        await _factory.LoginWithNewAccount(email);
        var user = (await _userRepository.GetByEmailAsync(email))!;
        var notifications = await CreateNotificationsAsync(user.Id);

        await _loginHelper.LoginAsync(_ownerEmail);

        var url = $"/notifications/{notifications[0].Id}/read";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    private async void MarkAsReadAsync_OrganizationIdNotMatchingUserNotPartOfOrganization_NotFound()
    {
        var email = $"integration-test{Guid.NewGuid()}@bitwarden.com";
        await _factory.LoginWithNewAccount(email);
        var user = (await _userRepository.GetByEmailAsync(email))!;
        var notifications = await CreateNotificationsAsync(user.Id, _organization.Id);

        await _loginHelper.LoginAsync(email);

        var url = $"/notifications/{notifications[0].Id}/read";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    private async void MarkAsReadAsync_OrganizationIdNotMatchingUserPartOfDifferentOrganization_NotFound()
    {
        var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory,
            plan: PlanType.EnterpriseAnnually, ownerEmail: _ownerEmail, passwordManagerSeats: 10,
            paymentMethod: PaymentMethodType.Card);
        var email = $"integration-test{Guid.NewGuid()}@bitwarden.com";
        await _factory.LoginWithNewAccount(email);
        var user = (await _userRepository.GetByEmailAsync(email))!;
        await OrganizationTestHelpers.CreateUserAsync(_factory, organization.Id, email, OrganizationUserType.User);
        var notifications = await CreateNotificationsAsync(user.Id, _organization.Id);

        await _loginHelper.LoginAsync(email);

        var url = $"/notifications/{notifications[0].Id}/read";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    private async void MarkAsReadAsync_NotificationStatusNotExisting_Created()
    {
        var notifications = await CreateNotificationsAsync(_organizationUserOwner.UserId);

        await _loginHelper.LoginAsync(_ownerEmail);

        var url = $"/notifications/{notifications[0].Id}/read";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        var notificationStatus = await _notificationStatusRepository.GetByNotificationIdAndUserIdAsync(
            notifications[0].Id, _organizationUserOwner.UserId!.Value);
        Assert.NotNull(notificationStatus);
        Assert.NotNull(notificationStatus.ReadDate);
        Assert.Equal(DateTime.UtcNow, notificationStatus.ReadDate.Value, OneMinuteTimeSpan);
        Assert.Null(notificationStatus.DeletedDate);
    }

    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    private async void MarkAsReadAsync_NotificationStatusExisting_Updated(bool readDateNull)
    {
        var notifications = await CreateNotificationsAsync(_organizationUserOwner.UserId);
        await _notificationStatusRepository.CreateAsync(new NotificationStatus
        {
            NotificationId = notifications[0].Id,
            UserId = _organizationUserOwner.UserId!.Value,
            ReadDate = readDateNull ? null : DateTime.UtcNow - TimeSpan.FromMinutes(_random.Next(3600)),
            DeletedDate = null
        });

        await _loginHelper.LoginAsync(_ownerEmail);

        var url = $"/notifications/{notifications[0].Id}/read";
        var response = await _client.PatchAsync(url, new StringContent(""));
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        var notificationStatus = await _notificationStatusRepository.GetByNotificationIdAndUserIdAsync(
            notifications[0].Id, _organizationUserOwner.UserId!.Value);
        Assert.NotNull(notificationStatus);
        Assert.NotNull(notificationStatus.ReadDate);
        Assert.Equal(DateTime.UtcNow, notificationStatus.ReadDate.Value, OneMinuteTimeSpan);
        Assert.Null(notificationStatus.DeletedDate);
    }

    private static void AssertNotificationResponseModels(
        IEnumerable<NotificationResponseModel> notificationResponseModels,
        List<(Notification, NotificationStatus?)> expectedNotificationsWithStatuses)
    {
        var i = 0;
        foreach (var notificationResponseModel in notificationResponseModels)
        {
            Assert.Contains(expectedNotificationsWithStatuses, e => e.Item1.Id == notificationResponseModel.Id);
            var (expectedNotification, expectedNotificationStatus) = expectedNotificationsWithStatuses[i];
            Assert.NotNull(expectedNotification);
            Assert.Equal(expectedNotification.Priority, notificationResponseModel.Priority);
            Assert.Equal(expectedNotification.Title, notificationResponseModel.Title);
            Assert.Equal(expectedNotification.Body, notificationResponseModel.Body);
            Assert.Equal(expectedNotification.RevisionDate, notificationResponseModel.Date);
            if (expectedNotificationStatus != null)
            {
                Assert.Equal(expectedNotificationStatus.ReadDate, notificationResponseModel.ReadDate);
                Assert.Equal(expectedNotificationStatus.DeletedDate, notificationResponseModel.DeletedDate);
            }
            else
            {
                Assert.Null(notificationResponseModel.ReadDate);
                Assert.Null(notificationResponseModel.DeletedDate);
            }

            Assert.Equal("notification", notificationResponseModel.Object);
            i++;
        }
    }

    private async Task<List<(Notification, NotificationStatus?)>> CreateNotificationsWithStatusesAsync()
    {
        var userId = (Guid)_organizationUserOwner.UserId!;

        var globalNotifications = await CreateNotificationsAsync();
        var userWithoutOrganizationNotifications = await CreateNotificationsAsync(userId: userId);
        var organizationWithoutUserNotifications = await CreateNotificationsAsync(organizationId: _organization.Id);
        var userPartOrOrganizationNotifications = await CreateNotificationsAsync(userId: userId,
            organizationId: _organization.Id);

        var globalNotificationWithStatuses = await CreateNotificationStatusesAsync(globalNotifications, userId);
        var userWithoutOrganizationNotificationWithStatuses =
            await CreateNotificationStatusesAsync(userWithoutOrganizationNotifications, userId);
        var organizationWithoutUserNotificationWithStatuses =
            await CreateNotificationStatusesAsync(organizationWithoutUserNotifications, userId);
        var userPartOrOrganizationNotificationWithStatuses =
            await CreateNotificationStatusesAsync(userPartOrOrganizationNotifications, userId);

        return new List<List<(Notification, NotificationStatus?)>>
            {
                globalNotificationWithStatuses,
                userWithoutOrganizationNotificationWithStatuses,
                organizationWithoutUserNotificationWithStatuses,
                userPartOrOrganizationNotificationWithStatuses
            }
            .SelectMany(n => n)
            .Where(n => n.Item1.ClientType is ClientType.All or ClientType.Web)
            .ToList();
    }

    private async Task<List<Notification>> CreateNotificationsAsync(Guid? userId = null, Guid? organizationId = null,
        int numberToCreate = 3)
    {
        var priorities = Enum.GetValues<Priority>();
        var clientTypes = Enum.GetValues<ClientType>();

        var notifications = new List<Notification>();

        foreach (var clientType in clientTypes)
        {
            for (var i = 0; i < numberToCreate; i++)
            {
                var notification = new Notification
                {
                    Global = userId == null && organizationId == null,
                    UserId = userId,
                    OrganizationId = organizationId,
                    Title = _mockEncryptedTitle,
                    Body = _mockEncryptedBody,
                    Priority = (Priority)priorities.GetValue(_random.Next(priorities.Length))!,
                    ClientType = clientType,
                    CreationDate = DateTime.UtcNow - TimeSpan.FromMinutes(_random.Next(3600)),
                    RevisionDate = DateTime.UtcNow - TimeSpan.FromMinutes(_random.Next(3600))
                };

                notification = await _notificationRepository.CreateAsync(notification);

                notifications.Add(notification);
            }
        }

        return notifications;
    }

    private async Task<List<(Notification, NotificationStatus?)>> CreateNotificationStatusesAsync(
        List<Notification> notifications, Guid userId)
    {
        var readDateNotificationStatus = await _notificationStatusRepository.CreateAsync(new NotificationStatus
        {
            NotificationId = notifications[0].Id,
            UserId = userId,
            ReadDate = DateTime.UtcNow - TimeSpan.FromMinutes(_random.Next(3600)),
            DeletedDate = null
        });

        var deletedDateNotificationStatus = await _notificationStatusRepository.CreateAsync(new NotificationStatus
        {
            NotificationId = notifications[1].Id,
            UserId = userId,
            ReadDate = null,
            DeletedDate = DateTime.UtcNow - TimeSpan.FromMinutes(_random.Next(3600))
        });

        var readDateAndDeletedDateNotificationStatus = await _notificationStatusRepository.CreateAsync(
            new NotificationStatus
            {
                NotificationId = notifications[2].Id,
                UserId = userId,
                ReadDate = DateTime.UtcNow - TimeSpan.FromMinutes(_random.Next(3600)),
                DeletedDate = DateTime.UtcNow - TimeSpan.FromMinutes(_random.Next(3600))
            });

        List<NotificationStatus> statuses =
            [readDateNotificationStatus, deletedDateNotificationStatus, readDateAndDeletedDateNotificationStatus];

        return notifications.Select(n => (n, statuses.Find(s => s.NotificationId == n.Id))).ToList();
    }
}