using e_suite.API.Common.exceptions;
using e_suite.Database.Core.Models;
using e_suite.Database.Core.Tables.UserManager;
using eSuite.Core.MailService;
using Moq;
using NUnit.Framework;
using UserManager.UnitTests.Helpers;

namespace UserManager.UnitTests.UserManager;

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

    [Test]
    public Task ForgotPassword_UserNotFound_ReturnsFailed()
    {
        //Arrange
        var email = "fred@bloggs.com"; //User does not exist in fake database.

        //Act & Assert
        Assert.ThrowsAsync<NotFoundException>(async () => { await UserManager.ForgotPassword(email); });
        return Task.CompletedTask;
    }

    [Test]
    public async Task ForgotPassword_User_ReturnsSuccess()
    {
        //Arrange
        var existingUser = new User
        {
            Id = 9,
            Email = "testuser@sun-strategy.com",
            EmailConfirmed = true,
            Active = true,
            FirstName = "Test",
            LastName = "User"
        };

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

        MailRequest actualMailRequest = null!;

        MailServiceMock.Setup(x => x.RequestEMailAsync(It.IsAny<MailRequest>(), It.IsAny<CancellationToken>()))
            .Callback<MailRequest, CancellationToken>((mailRequest, cancellationToken) => { actualMailRequest = mailRequest; });

        //Act
        await UserManager.ForgotPassword(existingUser.Email);

        //Assert that the new row was added to the database correctly.
        var emailUserAction = UserManagerRepository.EmailUserActions.SingleOrDefault(x => x.User.Email == existingUser.Email);

        Assert.That(emailUserAction, Is.Not.Null);
        Assert.That(emailUserAction?.EmailActionType, Is.EqualTo(EmailUserActionType.PasswordReset));
        Assert.That(emailUserAction?.User.Email, Is.EqualTo(existingUser.Email));
        Assert.That(emailUserAction?.Token, Is.Not.Empty);

        //Assert that the e-mail request was sent.
        MailServiceMock.Verify(x => x.RequestEMailAsync(It.IsAny<MailRequest>(), It.IsAny<CancellationToken>()), Times.Once);
        Assert.That(actualMailRequest, Is.Not.Null);
        Assert.That(actualMailRequest.EmailType, Is.EqualTo(MailType.PasswordReset));
        Assert.That(actualMailRequest.To.Count, Is.EqualTo(1));
        Assert.That(actualMailRequest.To[0].DisplayName, Is.EqualTo(existingUser.DisplayName));
        Assert.That(actualMailRequest.To[0].Email, Is.EqualTo(existingUser.Email));
        Assert.That(actualMailRequest.Parameters.Count, Is.EqualTo(1));
        Assert.That(actualMailRequest.Parameters["url"], Is.Not.Empty);
    }

    [Test]
    public async Task ForgotPassword_User_EmailNotConfirmedReturnsException()
    {
        //Arrange
        var existingUser = new User
        {
            Id = 9,
            Email = "testuser@sun-strategy.com",
            EmailConfirmed = false,
            Active = true,
            FirstName = "Test",
            LastName = "User"
        };

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

        //Act
        Assert.ThrowsAsync<EmailNotConfirmedException>(async () => { await UserManager.ForgotPassword(existingUser.Email); });
    }


    [Test]
    public async Task ForgotPassword_DeactivatedUser_ReturnsFailed()
    {
        //Arrange
        var existingUser = new User
        {
            Id = 9,
            Email = "testuser@sun-strategy.com",
            EmailConfirmed = true,
            Active = false,
            FirstName = "Test",
            LastName = "User"
        };

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

        MailRequest actualMailRequest = null!;

        MailServiceMock.Setup(x => x.RequestEMailAsync(It.IsAny<MailRequest>(), It.IsAny<CancellationToken>()))
            .Callback<MailRequest, CancellationToken>((mailRequest, cancellationToken) => { actualMailRequest = mailRequest; });

        //Assert
        Assert.ThrowsAsync<NotFoundException>(async () =>
        {
            //Act
            await UserManager.ForgotPassword(existingUser.Email);
        });

    }
}