using e_suite.API.Common.exceptions;
using e_suite.Database.Core.Tables.Domain;
using e_suite.Database.Core.Tables.UserManager;
using e_suite.Modules.RoleManager.UnitTests.Helpers;
using eSuite.Core.Miscellaneous;
using eSuite.Core.Security;
using NUnit.Framework;

namespace e_suite.Modules.RoleManager.UnitTests;

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

    [Test]
    public void CheckHasDomainAccess_DomainSuppliedButNotFound_ThrowsNotFoundException()
    {
        //Arrange
        var domainToCheck = new GeneralIdRef
        {
            Guid = new Guid("adfc267b-6dce-4f93-b1e1-33a15861107c")
        };

        //Assert
        var actualResult = Assert.ThrowsAsync<NotFoundException>(async () =>
        {
            //Act
            await RoleManager.CheckHasDomainAccess(123, domainToCheck, SecurityAccess.AddUser, CancellationToken.None);
        });
        Assert.That(actualResult!.Message, Is.EqualTo("Domain Not Found"));
    }

    [Test]
    public void CheckHasDomainAccess_DomainNotSuppliedAndNoUserDomainExists_ThrowsNotFoundException()
    {
        //Arrange
        var user = new User
        {
            Guid = new Guid("52b71abe-5544-4ec1-a3a1-6dfac219a085"),
            Id = 234095788,
            Domain = new Domain
            {
                Guid = new Guid("5f4b802c-67dc-4cc6-a34b-c5a9c3388799"),
                Id = 2352,
                Name = "Does not exist"
            }
        };
        UserManagerRepository.Users.Add(user);

        GeneralIdRef? domainToCheck = null;
        
        //Assert
        var actualResult = Assert.ThrowsAsync<NotFoundException>(async () =>
        {
            //Act
            await RoleManager.CheckHasDomainAccess(user.Id, domainToCheck, SecurityAccess.AddUser, CancellationToken.None);
        });
        Assert.That(actualResult!.Message, Is.EqualTo("Domain Not Found"));
    }

    [Test]
    public void CheckHasDomainAccess_DomainFound_ThrowsUnauthorizedAccessException()
    {
        //Arrange
        var domain = new Domain
        {
            Guid = new Guid("097b14d4-d4d4-4142-b0a6-04af95ba6df4"),
            Id = 324,
            Name = "Existing Domain"
        };
        DomainRepository.Domains.Add(domain);

        var domainToCheck = new GeneralIdRef
        {
            Guid = domain.Guid
        };

        //Assert
        var actualResult = Assert.ThrowsAsync<UnauthorizedAccessException>(async () =>
        {
            //Act
            await RoleManager.CheckHasDomainAccess(123, domainToCheck, SecurityAccess.AddUser, CancellationToken.None);
        });
        Assert.That(actualResult!.Message, Is.EqualTo("Attempted to perform an unauthorized operation."));
    }

    [Test]
    public void CheckHasDomainAccess_DomainFoundViaUser_ThrowsUnauthorizedAccessException()
    {
        //Arrange
        var domain = new Domain
        {
            Guid = new Guid("5f4b802c-67dc-4cc6-a34b-c5a9c3388799"),
            Id = 2352,
            Name = "Does not exist"
        };
        DomainRepository.Domains.Add(domain);

        var user = new User
        {
            Guid = new Guid("52b71abe-5544-4ec1-a3a1-6dfac219a085"),
            Id = 234095788,
            Domain = domain
        };
        UserManagerRepository.Users.Add(user);

        GeneralIdRef? domainToCheck = null;

        //Assert
        var actualResult = Assert.ThrowsAsync<UnauthorizedAccessException>(async () =>
        {
            //Act
            await RoleManager.CheckHasDomainAccess(user.Id, domainToCheck, SecurityAccess.AddUser, CancellationToken.None);
        });
        Assert.That(actualResult!.Message, Is.EqualTo("Attempted to perform an unauthorized operation."));
    }

    [Test]
    public void CheckHasDomainAccess_UserIsAdmin_DoesNotThrowException()
    {
        //Arrange
        var domain = new Domain
        {
            Guid = new Guid("5f4b802c-67dc-4cc6-a34b-c5a9c3388799"),
            Id = 2352,
            Name = "Does not exist"
        };
        DomainRepository.Domains.Add(domain);

        var user = new User
        {
            Guid = new Guid("52b71abe-5544-4ec1-a3a1-6dfac219a085"),
            Id = 234095788,
            Domain = domain
        };
        UserManagerRepository.Users.Add(user);

        var role = new Role
        {
            Guid = new Guid("875fdb45-4f25-4176-9b74-6aedcc57f746"),
            Id = 2342365,
            Name = "Administrator",
            IsAdministrator = true,
            Domain = domain,
            DomainId = domain.Id
        };
        RoleManagerRepository.Roles.Add(role);

        var userRole = new UserRole
        {
            User = user,
            UserId = user.Id,
            Role = role
        };
        RoleManagerRepository.RoleUsers.Add(userRole);

        GeneralIdRef? domainToCheck = null;
        
        //Assert
        Assert.DoesNotThrowAsync(async () =>
        {
            //Act
            await RoleManager.CheckHasDomainAccess(user.Id, domainToCheck, SecurityAccess.AddUser, CancellationToken.None);
        });
    }

    [Test]
    public void CheckHasDomainAccess_HasUserAccessViaRoleMemebership_DoesNotThrow()
    {
        //Arrange
        var domain = new Domain
        {
            Guid = new Guid("5f4b802c-67dc-4cc6-a34b-c5a9c3388799"),
            Id = 2352,
            Name = "Does not exist"
        };
        DomainRepository.Domains.Add(domain);

        var user = new User
        {
            Guid = new Guid("52b71abe-5544-4ec1-a3a1-6dfac219a085"),
            Id = 234095788,
            Domain = domain
        };
        UserManagerRepository.Users.Add(user);

        var role = new Role
        {
            Guid = new Guid("875fdb45-4f25-4176-9b74-6aedcc57f746"),
            Id = 2342365,
            Name = "NotAdmin",
            IsAdministrator = false,
            Domain = domain,
            DomainId = domain.Id
        };
        RoleManagerRepository.Roles.Add(role);

        RoleManagerRepository.UserAccess.Add( new UserAccess
        {
            Domain = domain,
            DomainId = domain.Id,
            User = user,
            UserId = user.Id,
            AccessKey = (int)SecurityAccess.AddUser,
        });
        
        var userRole = new UserRole
        {
            User = user,
            UserId = user.Id,
            Role = role
        };
        RoleManagerRepository.RoleUsers.Add(userRole);

        GeneralIdRef? domainToCheck = null;

        //Assert
        Assert.DoesNotThrowAsync(async () =>
        {
            //Act
            await RoleManager.CheckHasDomainAccess(user.Id, domainToCheck, SecurityAccess.AddUser, CancellationToken.None);
        });
    }
}