using e_suite.API.Common.exceptions;
using e_suite.Database.Core.Tables.Mail;
using e_suite.Database.Core.Tables.UserManager;
using eSuite.Core.MailService;
using Moq;
using NUnit.Framework;
using System.Net.Mail;
using e_suite.Service.Mail.Facade;

namespace e_suite.Service.Mail.UnitTests.MailService;

[TestFixture]
public class RequestEMailAsyncUnitTests : MailServiceTestBase
{
    [SetUp]
    public override async Task Setup()
    {
        await base.Setup();
    }

    [Test]
    public async Task RequestEMailAsync_WhenNoDestination_DoesNotSendAnyMails()
    {
        //Arrange
        var mailRequest = new MailRequest();
        
        //Act
        await mailService.RequestEMailAsync(mailRequest, CancellationToken.None);

        //Assert
        MailClientFactoryMock.Verify( x => x.CreateSmtpClient( It.IsAny<string?>(), It.IsAny<int>()), Times.Never);
    }

    [Test]
    public void RequestEMailAsync_EmailDestinationNotFound_ThrowsExpectedNotFoundException()
    {
        //Arrange
        var mailRequest = new MailRequest();
        var user = new User
        {
            Email = "test@test.test",
            FirstName = "Testy",
            LastName = "McTester"
        };

        mailRequest.To.Add(user);
        mailRequest.EmailType = MailType.ConfirmEmailAddress;
        mailRequest.Parameters.Add("url", "http://test.com/emailsstuff/testing");

        //Assert
        var result = Assert.ThrowsAsync<NotFoundException>(async () =>
        {
            //Act
            await mailService.RequestEMailAsync(mailRequest, CancellationToken.None);
        });
        Assert.That(result!.Message, Is.EqualTo($"Unable to find user for e-mail {user.Email}"));
    }

    [Test]
    public void RequestEMailAsync_EmailDestinationFound_AttemptsToPrepareMessageBody()
    {
        //Arrange
        var mailRequest = new MailRequest();
        var user = new User
        {
            Email = "test@test.test",
            FirstName = "Testy",
            LastName = "McTester",
            DomainId = 1
        };

        mailRequest.To.Add(user);
        mailRequest.EmailType = MailType.ConfirmEmailAddress;
        mailRequest.Parameters.Add("url", "http://test.com/emailsstuff/testing");

        UserManagerRepositoryMock.Setup(x => x.GetUserByEmail(user.Email, It.IsAny<CancellationToken>())).ReturnsAsync(user);

        //Assert
        var result = Assert.ThrowsAsync<NotImplementedException>(async () =>
        {
            //Act
            await mailService.RequestEMailAsync(mailRequest, CancellationToken.None);
        });
        MailServiceRepositoryMock.Verify( x => x.GetMailTemplate(It.IsAny<long>(), It.IsAny<MailType>(), It.IsAny<CancellationToken>()), Times.AtLeastOnce);
    }

    [Test]
    public async Task RequestEMailAsync_CanCreateEmail_SendsEmailSuccessfully()
    {
        //Arrange
        var mailRequest = new MailRequest();
        var user = new User
        {
            Email = "test@test.test",
            FirstName = "Testy",
            LastName = "McTester",
            DomainId = 1
        };

        mailRequest.To.Add(user);
        mailRequest.EmailType = MailType.ConfirmEmailAddress;
        mailRequest.Parameters.Add("url", "http://test.com/emailsstuff/testing");

        UserManagerRepositoryMock.Setup(x => x.GetUserByEmail(user.Email, It.IsAny<CancellationToken>())).ReturnsAsync(user);

        var mailTemplate = new MailTemplate();

        MailServiceRepositoryMock.Setup(x => x.GetMailTemplate(It.IsAny<long>(), mailRequest.EmailType, It.IsAny<CancellationToken>())).ReturnsAsync(mailTemplate);

        var smtpClientMock = new Mock<ISmtpClient>();

        MailClientFactoryMock.Setup(x => x.CreateSmtpClient(It.IsAny<string?>(), It.IsAny<int>())).Returns(smtpClientMock.Object);

        //Act
        await mailService.RequestEMailAsync(mailRequest, CancellationToken.None);

        //Assert
        smtpClientMock.Verify( x=> x.SendMailAsync(It.IsAny<MailMessage>(), It.IsAny<CancellationToken>()), Times.Once);
    }

    [Test]
    public void RequestEMailAsync_WhenEmailClientFailsToSend_ThrowsExceptionFromMailClient()
    {
        //Arrange
        var mailRequest = new MailRequest();
        var user = new User
        {
            Email = "test@test.test",
            FirstName = "Testy",
            LastName = "McTester",
            DomainId = 1
        };

        mailRequest.To.Add(user);
        mailRequest.EmailType = MailType.ConfirmEmailAddress;
        mailRequest.Parameters.Add("url", "http://test.com/emailsstuff/testing");

        UserManagerRepositoryMock.Setup(x => x.GetUserByEmail(user.Email, It.IsAny<CancellationToken>())).ReturnsAsync(user);

        var mailTemplate = new MailTemplate();

        MailServiceRepositoryMock.Setup(x => x.GetMailTemplate(It.IsAny<long>(), mailRequest.EmailType, It.IsAny<CancellationToken>())).ReturnsAsync(mailTemplate);

        var smtpClientMock = new Mock<ISmtpClient>();

        MailClientFactoryMock.Setup(x => x.CreateSmtpClient(It.IsAny<string?>(), It.IsAny<int>())).Returns(smtpClientMock.Object);

        smtpClientMock.Setup(x => x.SendMailAsync(It.IsAny<MailMessage>(), It.IsAny<CancellationToken>()))
            .Throws(new Exception("Test error"));

        //Assert
        var result = Assert.ThrowsAsync<Exception>(async () =>
        {
            //Act
            await mailService.RequestEMailAsync(mailRequest, CancellationToken.None);
        });

        Assert.That(result!.Message, Is.EqualTo("Test error"));
    }
}