using e_suite.API.Common.models;
using e_suite.Database.Audit;
using e_suite.Database.Core.Tables.UserManager;
using eSuite.API.SingleSignOn;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Moq;
using NUnit.Framework;

namespace eSuite.API.UnitTests.Controllers.AccountControllerUnitTests;

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

    [Test]
    public async Task Auth_WhenCalledInNormalSsoProcess_LogsInCreatesSessionCookiesAndRedirectsToRoot()
    {
        //Arrange
        var ssoId = 1;
        var code = "code";
        var scope = "scope";
        var authUser = "authUser";
        var prompt = "prompt";

        var ssoUserId = "ssoUserId123456";
        _singleSignOnMock.Setup(x => x.ExchangeAuthorisationToken(ssoId, code, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => ssoUserId);

        var loginResponse = new LoginResponse
        {
            Result = LoginResult.Success,
            Token = "Json Web Toke goes here"
        };

        _userManagerMock.Setup(x => x.LoginSso(ssoId, ssoUserId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => loginResponse);

        //Act
        var response = await _accountController.Auth(ssoId, code, scope, authUser, prompt, 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);
        _cookieManagerMock.Verify(x => x.CreateSsoIdCookie(It.IsAny<HttpResponse>(), ssoId), Times.Once);
        _sentinelMock.Verify(x => x.LogBadRequest(_accountController, It.IsAny<CancellationToken>()), Times.Never);
    }

    [Test]
    public async Task Auth_WhenLoginFails_DoesNotLoginRedirectsToRootAndLogsBadRequest()
    {
        //Arrange
        var ssoId = 1;
        var code = "code";
        var scope = "scope";
        var authUser = "authUser";
        var prompt = "prompt";

        var ssoUserId = "ssoUserId123456";
        _singleSignOnMock.Setup(x => x.ExchangeAuthorisationToken(ssoId, code, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => ssoUserId);

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

        _userManagerMock.Setup(x => x.LoginSso(ssoId, ssoUserId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => loginResponse);

        //Act
        var response = await _accountController.Auth(ssoId, code, scope, authUser, prompt, 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.Never);
        _cookieManagerMock.Verify(x => x.CreateSsoIdCookie(It.IsAny<HttpResponse>(), ssoId), Times.Never);
        _sentinelMock.Verify( x => x.LogBadRequest(_accountController, It.IsAny<CancellationToken>()), Times.Once);
    }

    [TestCase(LoginResult.EmailNotConfirmed)]
    [TestCase(LoginResult.TwoFactorAuthenticationRemovalRequested)]
    [TestCase(LoginResult.TwoFactorAuthenticationCodeRequired)]
    [TestCase(LoginResult.TwoFactorAuthenticationCodeIncorrect)]
    public async Task Auth_WhenLoginCannotComplete_DoesNotLoginAndRedirectsToRoot(LoginResult loginResult)
    {
        //Arrange
        var ssoId = 1;
        var code = "code";
        var scope = "scope";
        var authUser = "authUser";
        var prompt = "prompt";

        var ssoUserId = "ssoUserId123456";
        _singleSignOnMock.Setup(x => x.ExchangeAuthorisationToken(ssoId, code, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => ssoUserId);

        var loginResponse = new LoginResponse
        {
            Result = loginResult
        };

        _userManagerMock.Setup(x => x.LoginSso(ssoId, ssoUserId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => loginResponse);

        //Act
        var response = await _accountController.Auth(ssoId, code, scope, authUser, prompt, 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.Never);
        _cookieManagerMock.Verify(x => x.CreateSsoIdCookie(It.IsAny<HttpResponse>(), ssoId), Times.Never);
        _sentinelMock.Verify(x => x.LogBadRequest(_accountController, It.IsAny<CancellationToken>()), Times.Never);
    }

    [Test]
    public async Task Auth_WhenProfileLinkCookieDetected_DeletesSingleUserCookieLinksSsoUserIdToAccountAndRedirectsToProfileEditPage()
    {
        //Arrange
        var userId = 99;
        var email = "email@mail.test";
        var displayName = "Testy McTester";
        AddAuthorisedUserToController(userId, email, displayName);

        var ssoId = 1;
        var code = "code";
        var scope = "scope";
        var authUser = "authUser";
        var prompt = "prompt";

        var ssoUserId = "ssoUserId123456";
        _singleSignOnMock.Setup(x => x.ExchangeAuthorisationToken(ssoId, code, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => ssoUserId);

        var user = new User();

        _cookieManagerMock
            .Setup(x => x.GetUserIdFromLinkCookie(It.IsAny<HttpRequest>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => new CookieLink
            {
                User = user, LinkType = LinkType.Profile

            });

        //Act
        var response = await _accountController.Auth(ssoId, code, scope, authUser, prompt, CancellationToken.None);

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

        _cookieManagerMock.Verify(x => x.DeleteLinkCookie(It.IsAny<HttpResponse>()), Times.Once);
        _userManagerMock.Verify( x => x.LinkSsoProfileToUser(It.IsAny<AuditUserDetails>(), It.IsAny<User>(), ssoId, ssoUserId, It.IsAny<bool>(),It.IsAny<CancellationToken>()), Times.Once);
    }
}