using Baya.Application.Contracts.Common;
using Baya.Infrastructure.Persistence.Services.Notifications;

namespace Baya.Test.Foundation.Marketplace;

public sealed class NotificationServiceTests
{
    [Fact]
    public async Task Dispatch_ThenList_UnreadFirst_Count_MarkRead()
    {
        using var host = new OpsTestHost();
        var userId = await host.AddUserAsync("family");
        var dispatcher = new InAppNotificationDispatcher(host.Db, host.Clock);
        var service = new NotificationService(host.Db, host.Clock);

        await dispatcher.DispatchAsync(new Notification(userId, "booking_confirmed", "Booking confirmed", DataJson: "{\"booking_id\":1}"));

        var page = await service.ListMineAsync(userId, 1, 20);
        var notification = Assert.Single(page.Items);
        Assert.False(notification.IsRead);
        Assert.Equal("booking_confirmed", notification.Type);
        Assert.Equal(1, await service.GetUnreadCountAsync(userId));

        Assert.True(await service.MarkReadAsync(userId, notification.Id));
        Assert.Equal(0, await service.GetUnreadCountAsync(userId));
    }

    [Fact]
    public async Task Notifications_AreTenantScoped()
    {
        using var host = new OpsTestHost();
        var owner = await host.AddUserAsync("owner");
        var other = await host.AddUserAsync("other");
        var dispatcher = new InAppNotificationDispatcher(host.Db, host.Clock);
        var service = new NotificationService(host.Db, host.Clock);

        await dispatcher.DispatchAsync(new Notification(owner, "booking_confirmed", "For owner"));

        Assert.Empty((await service.ListMineAsync(other, 1, 20)).Items);
        Assert.Equal(0, await service.GetUnreadCountAsync(other));

        // A different user cannot mark another user's notification read.
        var ownerNotificationId = (await service.ListMineAsync(owner, 1, 20)).Items[0].Id;
        Assert.False(await service.MarkReadAsync(other, ownerNotificationId));
    }

    [Fact]
    public async Task PurgeOldRead_RemovesOnlyReadOlderThanWindow()
    {
        using var host = new OpsTestHost();
        var userId = await host.AddUserAsync("family");
        var dispatcher = new InAppNotificationDispatcher(host.Db, host.Clock);
        var service = new NotificationService(host.Db, host.Clock);

        var reference = host.Clock.UtcNow;

        // Old + read → should be purged.
        host.Clock.UtcNow = reference.AddDays(-100);
        await dispatcher.DispatchAsync(new Notification(userId, "t", "old read"));
        var oldReadId = (await service.ListMineAsync(userId, 1, 20)).Items[0].Id;
        await service.MarkReadAsync(userId, oldReadId);

        // Old + unread → must survive.
        await dispatcher.DispatchAsync(new Notification(userId, "t", "old unread"));

        // Recent + read → must survive.
        host.Clock.UtcNow = reference;
        await dispatcher.DispatchAsync(new Notification(userId, "t", "recent read"));
        var recentReadId = (await service.ListMineAsync(userId, 1, 20)).Items.First(n => n.Title == "recent read").Id;
        await service.MarkReadAsync(userId, recentReadId);

        var removed = await service.PurgeOldReadAsync(90);

        Assert.Equal(1, removed);
        var remaining = (await service.ListMineAsync(userId, 1, 20)).Items;
        Assert.Equal(2, remaining.Count);
        Assert.DoesNotContain(remaining, n => n.Title == "old read");
    }
}
