using e_suite.API.Common.models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Moq;
using NUnit.Framework;

namespace eSuite.API.UnitTests.Controllers.AccountControllerUnitTests;

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

    [Test]
    public async Task LoginPost_WhenLoginPasswordEmptyAndNotSingleSignOn_ReturnsLoginViewWithEmptyModel()
    {
        //Arrange
        var login = new Login
        {
            Email = "Test@test.test"
        };

        //Act
        var response = await _accountController.LoginPost(login, CancellationToken.None);

        //Assert
        Assert.That(response, Is.TypeOf<PartialViewResult>());
        var viewResult = response as PartialViewResult;
        Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
        Assert.That(viewResult?.Model, Is.TypeOf<Login>());

        var actualLogin = viewResult?.Model as Login;
        Assert.That(actualLogin, Is.EqualTo(login));
    }

    [Test]
    public async Task LoginPost_WhenSingleSignOnUserPresentsEmail_ReturnsRedirectsToSingleSignOnUrl()
    {
        //Arrange
        var login = new Login
        {
            Email = "Test@test.test"
        };

        var ssoUrl = "http://test.test/login";

        _singleSignOnMock.Setup(x => x.StartSingleSignOn(login.Email, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => ssoUrl);

        //Act
        var response = await _accountController.LoginPost(login, CancellationToken.None);

        //Assert
        Assert.That(response, Is.TypeOf<RedirectResult>());

        var redirectResult = response as RedirectResult;
        Assert.That(redirectResult?.Url, Is.EqualTo(ssoUrl));
    }

    [Test]
    public async Task LoginPost_WhenForgotPassword_UserManagerForgotPasswordCalled()
    {
        //Arrange
        Login? login = new Login
        {
            Email = "TestUser@Test.test",
            ForgotPassword = true
        };

        //Act
        var response = await _accountController.LoginPost(login, CancellationToken.None);

        //Assert
        Assert.That(response, Is.TypeOf<PartialViewResult>());
        var viewResult = response as PartialViewResult;
        Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
        Assert.That(viewResult?.Model, Is.TypeOf<Login>());

        var actualLogin = viewResult?.Model as Login;
        Assert.That(actualLogin, Is.EqualTo(login));

        _userManagerMock.Verify(x => x.ForgotPassword(login.Email, It.IsAny<CancellationToken>()), Times.Once);
    }

    [Test]
    public async Task LoginPost_WhenForgotPasswordButPasswordHasRubbish_UserManagerForgotPasswordCalled()
    {
        //Arrange
        Login? login = new Login
        {
            Email = "TestUser@Test.test",
            Password = "A",
            ForgotPassword = true
        };

        //Act
        var response = await _accountController.LoginPost(login, CancellationToken.None);

        //Assert
        Assert.That(response, Is.TypeOf<PartialViewResult>());
        var viewResult = response as PartialViewResult;
        Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
        Assert.That(viewResult?.Model, Is.TypeOf<Login>());

        var actualLogin = viewResult?.Model as Login;
        Assert.That(actualLogin, Is.EqualTo(login));

        _userManagerMock.Verify(x => x.ForgotPassword(login.Email, It.IsAny<CancellationToken>()), Times.Once);
    }


    [Test]
    public async Task LoginPost_WhenPasswordPresentAndCorrect_CreatesSessionCookieAndRedirectToRoot()
    {
        //Arrange
        Login? login = new Login
        {
            Email = "TestUser@Test.test",
            Password = "SuperSecret"
        };

        var loginResponse = new LoginResponse
        {
            Result = LoginResult.Success,
            Token = "Valid JSON Web Token"
        };

        _userManagerMock.Setup(x => x.Login(login, It.IsAny<CancellationToken>())).ReturnsAsync(() => loginResponse);

        //Act
        var response = await _accountController.LoginPost(login, CancellationToken.None);

        //Assert
        Assert.That(response, Is.TypeOf<RedirectResult>());
        var redirectResult = response as RedirectResult;
        Assert.That(redirectResult?.Url, Is.EqualTo("/"));

        _cookieManagerMock.Verify( x => x.CreateSessionCookie(It.IsAny<HttpResponse>(), loginResponse), Times.Once);
    }

    [TestCase(LoginResult.EmailNotConfirmed)]
    [TestCase(LoginResult.TwoFactorAuthenticationRemovalRequested)]
    [TestCase(LoginResult.TwoFactorAuthenticationCodeRequired)]
    [TestCase(LoginResult.TwoFactorAuthenticationCodeIncorrect)]
    public async Task LoginPost_WhenNonSsoLoginNotCompleted_ReturnsUpdatedView( LoginResult loginResult)
    {
        //Arrange
        Login? login = new Login
        {
            Email = "TestUser@Test.test",
            Password = "SuperSecret"
        };

        var loginResponse = new LoginResponse
        {
            Result = loginResult
        };

        _userManagerMock.Setup(x => x.Login(login, It.IsAny<CancellationToken>())).ReturnsAsync(() => loginResponse);

        //Act
        var response = await _accountController.LoginPost(login, CancellationToken.None);

        //Assert
        Assert.That(response, Is.TypeOf<PartialViewResult>());
        var viewResult = response as PartialViewResult;
        Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
        Assert.That(viewResult?.Model, Is.TypeOf<Login>());

        var actualLogin = viewResult?.Model as Login;
        Assert.That(actualLogin, Is.EqualTo(login));
    }

    [Test]
    public async Task LoginPost_WhenLoginFails_LogsBadLoginAttemptAndReturnsUpdatedView()
    {
        //Arrange
        Login? login = new Login
        {
            Email = "TestUser@Test.test",
            Password = "SuperSecret"
        };

        var loginResponse = new LoginResponse
        {
            Result = LoginResult.Failed
        };

        _userManagerMock.Setup(x => x.Login(login, It.IsAny<CancellationToken>())).ReturnsAsync(() => loginResponse);

        //Act
        var response = await _accountController.LoginPost(login, CancellationToken.None);

        //Assert
        Assert.That(response, Is.TypeOf<PartialViewResult>());
        var viewResult = response as PartialViewResult;
        Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
        Assert.That(viewResult?.Model, Is.TypeOf<Login>());

        var actualLogin = viewResult?.Model as Login;
        Assert.That(actualLogin, Is.EqualTo(login));

        _sentinelMock.Verify( x => x.LogBadRequest(_accountController, It.IsAny<CancellationToken>()), Times.Once);
    }
}