using e_suite.API.Common.models;
using e_suite.Database.Audit;
using eSuite.Core.Miscellaneous;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Moq;
using NUnit.Framework;
using UserProfile = eSuite.API.Models.UserProfile;

namespace eSuite.API.UnitTests.Controllers.AccountControllerUnitTests;

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

    [Test]
    public async Task ProfilePost_WhenSsoIdNotChanged_UpdatesProfileThenRedirectsToProfileGet()
    {
        //Arrange
        _cookieManagerMock.Setup(x => x.GetSsoIdFromSsoIdCookie(It.IsAny<HttpRequest>())).ReturnsAsync(() => null);

        var userId = 99;
        var email = "email@mail.test";
        var displayName = "Testy McTester";
        AddAuthorisedUserToController(userId, email, displayName);

        var userProfile = new UserProfile
        {
            Email = "Test@test.test",
            FirstName = "Testy",
            MiddleNames = "The tested",
            LastName = "McTester",
            SsoProviderId = 2
        };

        var savedUserProfile = new e_suite.API.Common.models.UserProfile
        {
            Email = "Test@test.test",
            FirstName = "Testy",
            MiddleNames = "The tested",
            LastName = "McTester",
            SsoProviderId = 2
        };

        _userManagerMock.Setup(x => x.GetProfile(email, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => savedUserProfile);

        //Act
        var response = await _accountController.ProfilePost(userProfile, CancellationToken.None);

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

        _userManagerMock.Verify(x => x.TurnOfSsoForUser(It.IsAny<AuditUserDetails>(), It.IsAny<GeneralIdRef>(), It.IsAny<CancellationToken>()), Times.Never);
        _userManagerMock.Verify( x => x.UpdateProfile(It.IsAny<AuditUserDetails>(), email, It.IsAny<UpdatedUserProfile>(), It.IsAny<CancellationToken>()), Times.Once);
    }

    [Test]
    public async Task ProfilePost_WhenSsoIdSetToRemoved_UpdatesProfileRemovesSsoLinkThenRedirectToProfileGet()
    {
        //Arrange
        _cookieManagerMock.Setup(x => x.GetSsoIdFromSsoIdCookie(It.IsAny<HttpRequest>())).ReturnsAsync(() => null);

        var userId = 99;
        var email = "email@mail.test";
        var displayName = "Testy McTester";
        AddAuthorisedUserToController(userId, email, displayName);

        var userProfile = new UserProfile
        {
            Email = "Test@test.test",
            FirstName = "Testy",
            MiddleNames = "The tested",
            LastName = "McTester",
            SsoProviderId = -1
        };

        var savedUserProfile = new e_suite.API.Common.models.UserProfile
        {
            Email = "Test@test.test",
            FirstName = "Testy",
            MiddleNames = "The tested",
            LastName = "McTester",
            SsoProviderId = 2
        };

        _userManagerMock.Setup(x => x.GetProfile(email, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => savedUserProfile);

        //Act
        var response = await _accountController.ProfilePost(userProfile, CancellationToken.None);

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

        _userManagerMock.Verify(x => x.TurnOfSsoForUser(It.IsAny<AuditUserDetails>(), It.IsAny<GeneralIdRef>(), It.IsAny<CancellationToken>()), Times.Once);
        _userManagerMock.Verify(x => x.UpdateProfile(It.IsAny<AuditUserDetails>(), email, It.IsAny<UpdatedUserProfile>(), It.IsAny<CancellationToken>()), Times.Once);
    }

    [Test]
    public async Task ProfilePost_WhenSsoIdChanged_UpdatesProfileThenStartsProcessOfLinkingTheNewSsoProvider()
    {
        //Arrange
        _cookieManagerMock.Setup(x => x.GetSsoIdFromSsoIdCookie(It.IsAny<HttpRequest>())).ReturnsAsync(() => null);

        var userId = 99;
        var email = "email@mail.test";
        var displayName = "Testy McTester";
        AddAuthorisedUserToController(userId, email, displayName);

        var userProfile = new UserProfile
        {
            Email = "Test@test.test",
            FirstName = "Testy",
            MiddleNames = "The tested",
            LastName = "McTester",
            SsoProviderId = 3
        };

        var savedUserProfile = new e_suite.API.Common.models.UserProfile
        {
            Email = "Test@test.test",
            FirstName = "Testy",
            MiddleNames = "The tested",
            LastName = "McTester",
            SsoProviderId = 2
        };

        _userManagerMock.Setup(x => x.GetProfile(email, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => savedUserProfile);

        var ssoUrl = "ssoUrl";

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

        //Act
        var response = await _accountController.ProfilePost(userProfile, CancellationToken.None);

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

        _cookieManagerMock.Verify(
            x => x.CreateProfileLinkCookie(It.IsAny<HttpResponse>(), It.IsAny<AuditUserDetails>(),
                It.IsAny<GeneralIdRef>(), It.IsAny<CancellationToken>()), Times.Once);
        _userManagerMock.Verify(x => x.TurnOfSsoForUser(It.IsAny<AuditUserDetails>(), It.IsAny<GeneralIdRef>(), It.IsAny<CancellationToken>()), Times.Never);
        _userManagerMock.Verify(x => x.UpdateProfile(It.IsAny<AuditUserDetails>(), email, It.IsAny<UpdatedUserProfile>(), It.IsAny<CancellationToken>()), Times.Once);
    }

    [Test]
    public async Task ProfilePost_WhenUpdatedSsoIdIsInvalid_SkipsUpdatingTheSsoLink()
    {
        //Arrange
        _cookieManagerMock.Setup(x => x.GetSsoIdFromSsoIdCookie(It.IsAny<HttpRequest>())).ReturnsAsync(() => null);

        var userId = 99;
        var email = "email@mail.test";
        var displayName = "Testy McTester";
        AddAuthorisedUserToController(userId, email, displayName);

        var userProfile = new UserProfile
        {
            Email = "Test@test.test",
            FirstName = "Testy",
            MiddleNames = "The tested",
            LastName = "McTester",
            SsoProviderId = 3
        };

        var savedUserProfile = new e_suite.API.Common.models.UserProfile
        {
            Email = "Test@test.test",
            FirstName = "Testy",
            MiddleNames = "The tested",
            LastName = "McTester",
            SsoProviderId = 2
        };

        _userManagerMock.Setup(x => x.GetProfile(email, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => savedUserProfile);

        _singleSignOnMock.Setup(x => x.StartSingleSignOn(userProfile.SsoProviderId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => null!);

        //Act
        var response = await _accountController.ProfilePost(userProfile, CancellationToken.None);

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

        _cookieManagerMock.Verify(
            x => x.CreateProfileLinkCookie(It.IsAny<HttpResponse>(), It.IsAny<AuditUserDetails>(),
                It.IsAny<GeneralIdRef>(), It.IsAny<CancellationToken>()), Times.Never);
        _userManagerMock.Verify(x => x.TurnOfSsoForUser(It.IsAny<AuditUserDetails>(), It.IsAny<GeneralIdRef>(), It.IsAny<CancellationToken>()), Times.Never);
        _userManagerMock.Verify(x => x.UpdateProfile(It.IsAny<AuditUserDetails>(), email, It.IsAny<UpdatedUserProfile>(), It.IsAny<CancellationToken>()), Times.Once);
    }
}