using System.Net;
using System.Net.Sockets;
using e_suite.UnitTestCore;
using eSuite.API.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Moq;
using NUnit.Framework;

namespace eSuite.API.UnitTests.HealthChecks;

[TestFixture]
public class SmtpHealthCheckUnitTests : TestBase
{
    private Mock<ISocketFactory> _socketFactoryMock = null!;
    private Mock<ISocketFacade> _socketFacadeMock = null!;

    [SetUp]
    public override async Task Setup()
    {
        await base.Setup();

        _socketFacadeMock = new Mock<ISocketFacade>();
        _socketFacadeMock.Setup(x => x.Connect(It.IsAny<IPEndPoint>())).Callback(() => {});

        _socketFactoryMock = new Mock<ISocketFactory>();
        _socketFactoryMock
            .Setup(x => x.CreateSocket(It.IsAny<AddressFamily>(), It.IsAny<SocketType>(), It.IsAny<ProtocolType>()))
            .Returns(_socketFacadeMock.Object);
    }

    [Test]
    public void SmtpHealthCheck_Healthy_WhenEverythingWorking_ReturnsHealthy()
    {
        //Arrange
        var socketAvailable = 50;
        _socketFacadeMock.SetupGet(x => x.Available).Returns(() => socketAvailable);

        var sendCounter = 0;

        _socketFacadeMock
            .Setup(x => x.Send(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<SocketFlags>()))
            .Callback<byte[], int, int, SocketFlags>(
                (data, offset, size, flags) =>
                {
                    sendCounter++;
                });

        _socketFacadeMock
            .Setup(x => x.Receive(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<SocketFlags>()))
            .Callback<byte[],int,int,SocketFlags>(
                (data, offset, size, flags) =>
                {
                    if (sendCounter == 0)
                    {
                        data[0] = 50; //2
                        data[1] = 50; //2
                        data[2] = 48; //0
                        data[3] = 0; //terminator
                    }
                    if (sendCounter == 1)
                    {
                        data[0] = 50; //2
                        data[1] = 53; //5
                        data[2] = 48; //0
                        data[3] = 0; //terminator
                    }
                });
        
        //Act
        var result = SmtpHealthCheck.Healthy(_configuration, _socketFactoryMock.Object);

        //Assert
        Assert.That(result, Is.Not.Null);
        Assert.That(result.Status, Is.EqualTo(HealthStatus.Healthy));
    }

    [Test]
    public void SmtpHealthCheck_Healthy_WhenServerRepondsIncorrectlyOnInitialConnection_ReturnsUnHealthy()
    {
        //Arrange
        var socketAvailable = 50;
        _socketFacadeMock.SetupGet(x => x.Available).Returns(() => socketAvailable);

        var sendCounter = 0;

        _socketFacadeMock
            .Setup(x => x.Send(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<SocketFlags>()))
            .Callback<byte[], int, int, SocketFlags>(
                (data, offset, size, flags) =>
                {
                    sendCounter++;
                });

        _socketFacadeMock
            .Setup(x => x.Receive(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<SocketFlags>()))
            .Callback<byte[], int, int, SocketFlags>(
                (data, offset, size, flags) =>
                {
                    if (sendCounter == 0)
                    {
                        data[0] = 50; //2
                        data[1] = 53; //5 -- should be a 2, for healthy
                        data[2] = 48; //0
                        data[3] = 0; //terminator
                    }
                    if (sendCounter == 1)
                    {
                        data[0] = 50; //2
                        data[1] = 53; //5
                        data[2] = 48; //0
                        data[3] = 0; //terminator
                    }
                });

        //Act
        var result = SmtpHealthCheck.Healthy(_configuration, _socketFactoryMock.Object);

        //Assert
        Assert.That(result, Is.Not.Null);
        Assert.That(result.Status, Is.EqualTo(HealthStatus.Unhealthy));
    }

    [Test]
    public void SmtpHealthCheck_Healthy_WhenServerRepondsToHeloCheck_ReturnsUnHealthy()
    {
        //Arrange
        var socketAvailable = 50;
        _socketFacadeMock.SetupGet(x => x.Available).Returns(() => socketAvailable);

        var sendCounter = 0;

        _socketFacadeMock
            .Setup(x => x.Send(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<SocketFlags>()))
            .Callback<byte[], int, int, SocketFlags>(
                (data, offset, size, flags) =>
                {
                    sendCounter++;
                });

        _socketFacadeMock
            .Setup(x => x.Receive(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<SocketFlags>()))
            .Callback<byte[], int, int, SocketFlags>(
                (data, offset, size, flags) =>
                {
                    if (sendCounter == 0)
                    {
                        data[0] = 50; //2
                        data[1] = 50; //2
                        data[2] = 48; //0
                        data[3] = 0; //terminator
                    }
                    if (sendCounter == 1)
                    {
                        data[0] = 50; //2
                        data[1] = 48; //0 -- Should be a 5 for healthy
                        data[2] = 48; //0
                        data[3] = 0; //terminator
                    }
                });

        //Act
        var result = SmtpHealthCheck.Healthy(_configuration, _socketFactoryMock.Object);

        //Assert
        Assert.That(result, Is.Not.Null);
        Assert.That(result.Status, Is.EqualTo(HealthStatus.Unhealthy));
    }
}