using Baya.Application.Contracts.Common;
using Baya.Domain.Common;
using Baya.Infrastructure.Persistence.Interceptors;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using NSubstitute;

namespace Baya.Test.Foundation;

public class AuditFieldInterceptorTests
{
    // A throwaway auditable entity + context so the interceptor is tested in isolation, without the
    // full Identity schema.
    private sealed class Widget : BaseEntity
    {
        public string Name { get; set; } = string.Empty;
    }

    private sealed class AuditTestDbContext(DbContextOptions options) : DbContext(options)
    {
        public DbSet<Widget> Widgets => Set<Widget>();
    }

    private static AuditTestDbContext CreateContext(AuditFieldInterceptor interceptor)
    {
        var connection = new SqliteConnection("DataSource=:memory:");
        connection.Open();

        var options = new DbContextOptionsBuilder<AuditTestDbContext>()
            .UseSqlite(connection)
            .AddInterceptors(interceptor)
            .Options;

        var context = new AuditTestDbContext(options);
        context.Database.EnsureCreated();
        return context;
    }

    [Fact]
    public async Task SavingChanges_OnAdd_StampsCreatedAtAndCreatedById()
    {
        // Arrange
        var fixedNow = new DateTimeOffset(2026, 6, 28, 10, 0, 0, TimeSpan.Zero);

        var clock = Substitute.For<IDateTimeProvider>();
        clock.UtcNow.Returns(fixedNow);

        var currentUser = Substitute.For<ICurrentUser>();
        currentUser.UserId.Returns(42);

        await using var context = CreateContext(new AuditFieldInterceptor(currentUser, clock));
        var widget = new Widget { Name = "test" };
        context.Widgets.Add(widget);

        // Act
        await context.SaveChangesAsync();

        // Assert
        Assert.Equal(fixedNow, widget.CreatedAt);
        Assert.Equal(fixedNow, widget.ModifiedAt);
        Assert.Equal(42, widget.CreatedById);
        Assert.Equal(42, widget.ModifiedById);
    }

    [Fact]
    public async Task SavingChanges_OnUpdate_StampsModifiedButNotCreatedBy()
    {
        // Arrange
        var createNow = new DateTimeOffset(2026, 6, 28, 10, 0, 0, TimeSpan.Zero);
        var updateNow = new DateTimeOffset(2026, 6, 28, 12, 0, 0, TimeSpan.Zero);

        var clock = Substitute.For<IDateTimeProvider>();
        var currentUser = Substitute.For<ICurrentUser>();

        clock.UtcNow.Returns(createNow);
        currentUser.UserId.Returns(7);

        await using var context = CreateContext(new AuditFieldInterceptor(currentUser, clock));
        var widget = new Widget { Name = "before" };
        context.Widgets.Add(widget);
        await context.SaveChangesAsync();

        // Act — a different user edits the row later.
        clock.UtcNow.Returns(updateNow);
        currentUser.UserId.Returns(9);
        widget.Name = "after";
        await context.SaveChangesAsync();

        // Assert
        Assert.Equal(createNow, widget.CreatedAt);
        Assert.Equal(7, widget.CreatedById);
        Assert.Equal(updateNow, widget.ModifiedAt);
        Assert.Equal(9, widget.ModifiedById);
    }
}
