using e_suite.Database.Core.Tables.Sentinel;
using e_suite.UnitTestCore;
using eSuite.Core.Clock;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace e_suite.Service.Sentinel.Tests;

public class SentinelTests : TestBase
{
    private Mock<ISentinelRepository> _sentinelRepository;
    private Mock<ControllerBase> _controllerBaseMock;
    private IClock _clock;
    private Sentinel _sentinel;
    private HttpContext _httpContext;

    [SetUp]
    public void SetUp()
    {
        _sentinelRepository = new Mock<ISentinelRepository>();
        _sentinelRepository.Setup<Task<int>>(x => x.GetAccessAttemptsSince(It.IsAny<string>(), It.IsAny<DateTimeOffset>())).ReturnsAsync(100);
        _controllerBaseMock = new Mock<ControllerBase>();
        _httpContext = new DefaultHttpContext();
        _httpContext.Request.Path = "/Test/3133/TestController/TestAction";
        _httpContext.Connection.RemoteIpAddress = new System.Net.IPAddress(0x1234567);
        var controllerContext = new ControllerContext()
        {
            HttpContext = _httpContext
        };
        _controllerBaseMock.Object.ControllerContext = controllerContext;
        _clock = new UtcClock();
        _controllerBaseMock.Setup(x => x.Unauthorized(It.IsAny<ProblemDetails>())).Returns(new UnauthorizedObjectResult(new ProblemDetails()
        {
            Title = "Access Denied",
            Detail = "Too many access attempts"

        }));
        _sentinel = new Sentinel(_clock, base._configuration, _sentinelRepository.Object);
    }

    [Test]
    public void LogBadRequest_NullController_ThrowsException()
    {
        Assert.ThrowsAsync<NullReferenceException>(() => _sentinel.LogBadRequest(null!));
    }

    [Test]
    public void LogBadRequest_NormalConditian_HitsSaveRepoMethod()
    {
        _controllerBaseMock.SetupAllProperties();

        _sentinel.LogBadRequest(_controllerBaseMock.Object).GetAwaiter().GetResult();

        _sentinelRepository.Verify(r => r.AddFailedAccessAttempt(It.IsAny<FailedAccessAttempt>()));
    }

    [Test]
    public void CheckSecurity_NullController_ThrowsException()
    {
        Assert.ThrowsAsync<NullReferenceException>(() => _sentinel.CheckSecurity(null!));
    }

    [Test]
    public void CheckSecurity_BlockedUsersVlues_Unauthorized()
    {
        AddAtemptConditoins();
        _sentinelRepository.Setup(x => x.GetAccessAttemptsSince(_httpContext.Connection.RemoteIpAddress.ToString(), It.IsAny<DateTimeOffset>())).ReturnsAsync(() => 200);
        _sentinel = new Sentinel(_clock,base._configuration, _sentinelRepository.Object);
        
         var res =  _sentinel.CheckSecurity(_controllerBaseMock.Object).GetAwaiter().GetResult();
        
        Assert.That(res, Is.Not.Null);
        Assert.That(res, Is.TypeOf<UnauthorizedObjectResult>());
    }

    [Test]
    public void CheckSecurity_NormalUserConditions_returnsNull()
    {
        AddAtemptConditoins();
        _sentinelRepository.Setup(x => x.GetAccessAttemptsSince(_httpContext.Connection.RemoteIpAddress.ToString(), It.IsAny<DateTimeOffset>())).ReturnsAsync(() => 1);
        _sentinel = new Sentinel(_clock, base._configuration, _sentinelRepository.Object);

        var res = _sentinel.CheckSecurity(_controllerBaseMock.Object).GetAwaiter().GetResult();

        Assert.That(res, Is.Null);

    }

    [Test]
    public void CheckSecurity_RepoReturnsMinusValue_returnsNull()
    {
        AddAtemptConditoins();
        _sentinelRepository.Setup(x => x.GetAccessAttemptsSince(_httpContext.Connection.RemoteIpAddress.ToString(), It.IsAny<DateTimeOffset>())).ReturnsAsync(() => -51);
        _sentinel = new Sentinel(_clock, base._configuration, _sentinelRepository.Object);

        var res = _sentinel.CheckSecurity(_controllerBaseMock.Object).GetAwaiter().GetResult();

        Assert.That(res, Is.Null);
    }
    [Test]
    public void CheckSecurity_ConfigurationsValuesDontExist_ReturnsNUll()
    {

        _sentinelRepository.Setup(x => x.GetAccessAttemptsSince(_httpContext.Connection.RemoteIpAddress.ToString(), It.IsAny<DateTimeOffset>())).ReturnsAsync(() => -51);
        _sentinel = new Sentinel(_clock, base._configuration, _sentinelRepository.Object);
        var res = _sentinel.CheckSecurity(_controllerBaseMock.Object).GetAwaiter().GetResult();
        Assert.That(res,Is.Null);

    }

    private void AddAtemptConditoins()
    {
        var configurationBuilderSetings = new Dictionary<string, string>()
        {
            {"Sentinel:LoginAttemptTimeoutMinutes","10" },
            {"Sentinel:MaxLoginAttempts","12" }
        };
        base._configuration = new ConfigurationBuilder().AddInMemoryCollection(configurationBuilderSetings).Build();

    }
}