using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using Microsoft.EntityFrameworkCore;
using Moq;
using Xunit;

namespace JobTrackerApi.Tests;

// Regression guard for the production tenant-read failure: the "local" JwtBearer OnTokenValidated
// resolves the request-scoped JobTrackerContext to run session validation BEFORE the auth middleware
// sets HttpContext.User. When CurrentUserId was captured in the DbContext constructor, that froze it
// to null for the whole request, and every global-query-filtered read returned WHERE FALSE — created
// rows 404'd on read and lists came back empty, while writes (which set OwnerUserId from the
// controller-resolved user) still succeeded.
//
// The fix makes CurrentUserId read ICurrentUserService.UserId live on each access. These tests fail
// if it reverts to a constructor snapshot.
public sealed class CurrentUserIdLiveEvaluationTests
{
    private static (JobTrackerContext db, Mock<ICurrentUserService> user) New(string? initialUserId)
    {
        var options = new DbContextOptionsBuilder<JobTrackerContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
        var user = new Mock<ICurrentUserService>();
        user.SetupGet(s => s.UserId).Returns(initialUserId);
        return (new JobTrackerContext(options, user.Object), user);
    }

    [Fact]
    public void CurrentUserId_reflects_the_user_resolved_after_construction()
    {
        // Constructed while unauthenticated (UserId null), exactly as OnTokenValidated does.
        var (db, user) = New(initialUserId: null);
        Assert.Null(db.CurrentUserId);

        // The auth middleware then assigns the principal; UserId becomes non-null on the SAME instance.
        user.SetupGet(s => s.UserId).Returns("user-1");

        Assert.Equal("user-1", db.CurrentUserId);
    }

    [Fact]
    public async Task A_row_created_while_unauthenticated_context_is_readable_once_the_user_resolves()
    {
        var (db, user) = New(initialUserId: null);

        // Write happens with an explicit owner (mirrors CreateAsync taking the controller-resolved id),
        // while the context was built before the user resolved.
        db.CvVariants.Add(new CvVariant
        {
            OwnerUserId = "user-1",
            Name = "Test",
            PublicSlug = "slug-1",
            SettingsJson = "{}",
            Version = 1,
        });
        await db.SaveChangesAsync();

        // Before the user resolves, the tenant filter denies (deny-on-null preserved).
        Assert.Empty(await db.CvVariants.ToListAsync());

        // Once the middleware sets the user, the same context reads the row — not WHERE FALSE.
        user.SetupGet(s => s.UserId).Returns("user-1");
        var visible = await db.CvVariants.ToListAsync();
        Assert.Single(visible);
        Assert.Equal("user-1", visible[0].OwnerUserId);
    }
}
