using e_suite.API.Common;
using e_suite.API.Common.models;
using e_suite.Database.Core.Extensions;
using e_suite.Database.Core.Tables.Domain;
using e_suite.Database.Core.Tables.UserManager;
using eSuite.Core.Miscellaneous;
using Moq;
using NUnit.Framework;
using UserManager.UnitTests.Helpers;

namespace UserManager.UnitTests.UserManager;

[TestFixture]
public class PatchUserUnitTests : UserManagerTestBase<PatchUser>
{
    [SetUp]
    public override async Task Setup()
    {
        await base.Setup();
    }

    [Test]
    public async Task PatchUser_AppliesPatchToUser()
    {
        // Arrange
        var existingUser = new User
        {
            Id = 123,
            FirstName = "Original",
            LastName = "User",
            Active = true
        };

        // Seed the fake repository
        await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default);

        var dto = new PatchUser
        {
            FirstName = "Updated"
        };

        // Configure the patch mock to mutate the user
        PatchMock
            .Setup(p => p.ApplyTo(It.IsAny<User>()))
            .Callback<object>(target =>
            {
                var user = (User)target;
                user.FirstName = "Updated";
            });

        // Act
        await UserManager.PatchUser(AuditUserDetails, existingUser.ToGeneralIdRef()!, dto, CancellationToken.None);

        // Assert: factory was used
        PatchFactoryMock.Verify(f => f.Create(dto), Times.Once);

        // Assert: patch was applied
        PatchMock.Verify(p => p.ApplyTo(It.IsAny<User>()), Times.Once);

        // Assert: user was updated
        Assert.That(existingUser.FirstName, Is.EqualTo("Updated"));
    }

    [Test]
    public async Task PatchUser_UpdatesDomain_WhenDomainRefProvided()
    {
        // Arrange
        var existingDomain = new Domain
        {
            Id = 10,
            Guid = Guid.NewGuid(),
            Name = "Original Domain"
        };

        var newDomain = new Domain
        {
            Id = 20,
            Guid = Guid.NewGuid(),
            Name = "New Domain"
        };

        // Seed both domains into the fake repository
        DomainRepository.Domains.Add(existingDomain);
        DomainRepository.Domains.Add(newDomain);

        var existingUser = new User
        {
            Id = 123,
            FirstName = "Original",
            Active = true,
            DomainId = existingDomain.Id,
            Domain = existingDomain
        };
        UserManagerRepository.Users.Add(existingUser);

        var dto = new PatchUser
        {
            Domain = new GeneralIdRef { Id = newDomain.Id }
        };

        var userId = new GeneralIdRef { Id = 123 };

        // Patch engine should not touch Domain (nested object)
        PatchMock
            .Setup(p => p.ApplyTo(It.IsAny<User>()))
            .Callback<object>(target =>
            {
                ((User)target).DomainId = newDomain.Id;
                ((User)target).Domain = newDomain;
            });

        PatchFactoryMock
            .Setup(f => f.Create(dto))
            .Returns(PatchMock.Object);

        // Act
        await UserManager.PatchUser(AuditUserDetails, userId, dto, CancellationToken.None);

        // Assert: factory was used
        PatchFactoryMock.Verify(f => f.Create(dto), Times.Once);

        // Assert: patch engine was invoked
        PatchMock.Verify(p => p.ApplyTo(It.IsAny<User>()), Times.Once);

        // Assert: domain was updated
        Assert.That(existingUser.DomainId, Is.EqualTo(newDomain.Id));
        Assert.That(existingUser.Domain, Is.EqualTo(newDomain));
    }
}