using e_suite.API.Common.exceptions;
using e_suite.API.Common.models;
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 SetAuthenticationUnitTests : UserManagerTestBase<object>
{
    [SetUp]
    public override async Task Setup()
    {
        await base.Setup();
    }

    [Test]
    public void SetAuthentication_UserNotFound_ThrowsException()
    {
        //Arrange
        var userAuthenticationDetails = new UserAuthenticationDetails()
        {
            Id = new GeneralIdRef
            {
                Id = 100
            }

        };

        //Act & Assert
        Assert.ThrowsAsync<NotFoundException>(async () =>
        {
            await UserManager.SetAuthentication(AuditUserDetails, userAuthenticationDetails, true, CancellationToken.None);
        });
    }

    [Test]
    public async Task SetAuthentication_PasswordSet_HashesPassword()
    {
        //Arrange
        const string existingEmail = "testuser@sun-strategy.com";
        var existingUser = new User
        {
            Id = 12,
            Email = existingEmail,
            EmailConfirmed = false
        };
        await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default);


        var userAuthenticationDetails = new UserAuthenticationDetails()
        {
            Id = new GeneralIdRef
            {
                Id = 12
            },
            Password = "This is my new password",

        };

        var hashedPassword = "owekjhrtlkerjthbwerlkjrthbw3";
        CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny<User>(), It.IsAny<string>()))
            .Returns(hashedPassword);

        //Act & Assert
        await UserManager.SetAuthentication(AuditUserDetails, userAuthenticationDetails, false, CancellationToken.None);

        //Assert
        var alteredProfile = UserManagerRepository.Users.SingleOrDefault(x => x.Id == existingUser.Id);
        Assert.That(alteredProfile?.Password, Is.EqualTo(hashedPassword));
    }

    [Test]
    public async Task SetAuthentication_ConfirmEmail_SetsEmailConfirmedTrue()
    {
        //Arrange
        const string existingEmail = "testuser@sun-strategy.com";
        var existingUser = new User
        {
            Id = 12,
            Email = existingEmail,
            EmailConfirmed = false
        };
        await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default);


        var userAuthenticationDetails = new UserAuthenticationDetails()
        {
            Id = new GeneralIdRef
            {
                Id = 12
            }
        };

        //Act & Assert
        await UserManager.SetAuthentication(AuditUserDetails, userAuthenticationDetails, true, CancellationToken.None);

        //Assert
        var alteredProfile = UserManagerRepository.Users.SingleOrDefault(x => x.Id == existingUser.Id);
        Assert.That(alteredProfile?.EmailConfirmed, Is.True);
    }

    [Test]
    public async Task SetAuthentication_EnablingTFANoSecurityCode_DoesNotEnableTFA()
    {
        //Arrange
        var existingUser = new User
        {
            Id = 12,
            Email = "testuser@sun-strategy.com",
            UsingTwoFactorAuthentication = false,
            TwoFactorAuthenticationKey = "FirstKey"
        };

        await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default);


        var userAuthenticationDetails = new UserAuthenticationDetails()
        {
            Id = new GeneralIdRef
            {
                Id = 12
            },
            UsingTwoFactorAuthentication = true
        };

        //Act
        await UserManager.SetAuthentication(AuditUserDetails, userAuthenticationDetails, true, CancellationToken.None);


        //Assert
        var alteredProfile = UserManagerRepository.Users.SingleOrDefault(x => x.Id == existingUser.Id);
        Assert.That(alteredProfile?.UsingTwoFactorAuthentication, Is.False);

        TwoFactorAuthenticatorMock.Verify(x => x.ValidateTwoFactorPIN(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()), Times.Never);
    }

    [Test]
    public async Task SetAuthentication_EnablingTFAIncorrectSecurityCode_DoesNotEnableTFA()
    {
        //Arrange
        var existingUser = new User
        {
            Id = 12,
            Email = "testuser@sun-strategy.com",
            UsingTwoFactorAuthentication = false,
            TwoFactorAuthenticationKey = "FirstKey"
        };

        await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default);

        var userAuthenticationDetails = new UserAuthenticationDetails()
        {
            Id = new GeneralIdRef
            {
                Id = 12
            },
            UsingTwoFactorAuthentication = true,
            SecurityCode = "12345"
        };

        TwoFactorAuthenticatorMock.Setup(x => x.ValidateTwoFactorPIN(It.IsAny<string>(), userAuthenticationDetails.SecurityCode, It.IsAny<bool>())).Returns(false);

        //Act
        await UserManager.SetAuthentication(AuditUserDetails, userAuthenticationDetails, true, CancellationToken.None);


        //Assert
        var alteredProfile = UserManagerRepository.Users.SingleOrDefault(x => x.Id == existingUser.Id);
        Assert.That(alteredProfile?.UsingTwoFactorAuthentication, Is.False);

        TwoFactorAuthenticatorMock.Verify(x => x.ValidateTwoFactorPIN(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()), Times.Once);
    }

    [Test]
    public async Task SetAuthentication_EnablingTFACorrectSecurityCode_EnablesTFA()
    {
        //Arrange
        var existingUser = new User
        {
            Id = 12,
            Email = "testuser@sun-strategy.com",
            UsingTwoFactorAuthentication = false,
            TwoFactorAuthenticationKey = "FirstKey"
        };

        await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default);

        var userAuthenticationDetails = new UserAuthenticationDetails()
        {
            Id = new GeneralIdRef
            {
                Id = 12
            },
            UsingTwoFactorAuthentication = true,
            SecurityCode = "12345"
        };

        TwoFactorAuthenticatorMock.Setup(x => x.ValidateTwoFactorPIN(It.IsAny<string>(), userAuthenticationDetails.SecurityCode, It.IsAny<bool>())).Returns(true);

        //Act
        await UserManager.SetAuthentication(AuditUserDetails, userAuthenticationDetails, true, CancellationToken.None);

        //Assert
        var alteredProfile = UserManagerRepository.Users.SingleOrDefault(x => x.Id == existingUser.Id);
        Assert.That(alteredProfile?.UsingTwoFactorAuthentication, Is.True);

        TwoFactorAuthenticatorMock.Verify(x => x.ValidateTwoFactorPIN(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()), Times.Once);
    }
}