using e_suite.API.Common.exceptions;
using e_suite.Database.Core.Tables.UserManager;
using eSuite.API.Middleware;
using eSuite.API.SingleSignOn;
using Microsoft.IdentityModel.Tokens;
using Moq;
using NUnit.Framework;

namespace eSuite.API.UnitTests.SingleSignOn.SingleSignOn;

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

    [Test]
    public void ExchangeAuthorisationToken_WhenSsoProviderNotFound_ThrowsNotFoundException()
    {
        //Arrange
        var ssoProviderId = 123;
        var code = "21kj345ghl2jk13b4";

        //Act & Assert
        Assert.ThrowsAsync<NotFoundException>(async () =>
        {
            await _singleSignOn.ExchangeAuthorisationToken(ssoProviderId, code, CancellationToken.None);
        });
    }

    [Test]
    public void ExchangeAuthorisationToken_WhenSsoProviderReturnsInvalidOpenIdResponse_ThrowsNullReferenceException()
    {
        //Arrange
        var ssoProviderId = 123;
        var code = "21kj345ghl2jk13b4";

        var ssoProvider = new SsoProvider
        {
            Id = ssoProviderId,
            AuthorizationEndpoint = "https://validIssuer.com/authorisationEndPoint",
            ClientId = "clientId",
            ClientSecret = "clientSecret",
            Deleted = false,
            IsPublic = true,
            Name = "MyTestIdentityProvider",
            TokenEndpoint = "https://validIssuer.com/TokenEndpoint",
            ValidIssuer = "https://validIssuer.com/"
        };

        _userManagerMock.Setup(x => x.GetSsoProviderById(ssoProviderId, It.IsAny<CancellationToken>()))
            .ReturnsAsync( () => ssoProvider);

        //Act & Assert
        Assert.ThrowsAsync<NullReferenceException>(async () =>
        {
            await _singleSignOn.ExchangeAuthorisationToken(ssoProviderId, code, CancellationToken.None);
        });
    }

    [Test]
    public void ExchangeAuthorisationToken_WhenOpenIdConfigurationReturnsEmptyString_ThrowsNullReferenceException()
    {
        //Arrange
        var ssoProviderId = 123;
        var code = "21kj345ghl2jk13b4";

        var ssoProvider = new SsoProvider
        {
            Id = ssoProviderId,
            AuthorizationEndpoint = "https://validIssuer.com/authorisationEndPoint",
            ClientId = "clientId",
            ClientSecret = "clientSecret",
            Deleted = false,
            IsPublic = true,
            Name = "MyTestIdentityProvider",
            TokenEndpoint = "https://validIssuer.com/TokenEndpoint",
            ValidIssuer = "https://validIssuer.com/"
        };

        _userManagerMock.Setup(x => x.GetSsoProviderById(ssoProviderId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => ssoProvider);

        var httpResponseMessage = new HttpResponseMessage
        {
            Content = new StringContent("{\"id_token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"}")
        };

        _httpClientFacadeMock.Setup(x => x.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => httpResponseMessage);
        _httpClientFacadeMock.Setup(x => x.GetStringAsync(It.IsAny<Uri>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => "");

        //Act & Assert
        Assert.ThrowsAsync<NullReferenceException>(async () =>
        {
            await _singleSignOn.ExchangeAuthorisationToken(ssoProviderId, code, CancellationToken.None);
        });
    }
    
    [Test]
    public void ExchangeAuthorisationToken_WhenKeyIsInCorrect_ThrowsSignatureNotFoundExcpetion()
    {
        //Arrange
        var ssoProviderId = 123;
        var code = "21kj345ghl2jk13b4";

        var ssoProvider = new SsoProvider
        {
            Id = ssoProviderId,
            AuthorizationEndpoint = "https://validIssuer.com/authorisationEndPoint",
            ClientId = "clientId",
            ClientSecret = "clientSecret",
            Deleted = false,
            IsPublic = true,
            Name = "MyTestIdentityProvider",
            TokenEndpoint = "https://validIssuer.com/TokenEndpoint",
            ValidIssuer = "https://validIssuer.com/"
        };

        _userManagerMock.Setup(x => x.GetSsoProviderById(ssoProviderId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => ssoProvider);

        var httpResponseMessage = new HttpResponseMessage
        {
            //note: This JWT will expire 2130-07-05T08:43:33.330Z.
            Content = new StringContent("{\"id_token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3ZhbGlkSXNzdWVyLmNvbS8iLCJpYXQiOjE3MjAxNjkwMTMsImV4cCI6NTA2NTE0NTAxMywiYXVkIjoiY2xpZW50SWQiLCJzdWIiOiI2OTg0NzMyNDExIn0.YSzsnhkhZEyz1SfRQxNIQDv_7tzS-NxXqVKqvs3R308\"}")
        };

        _httpClientFacadeMock.Setup(x => x.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => httpResponseMessage);

        var openIdConfiguration = new OpenIdConfiguration
        {
            JwksUri = "https://test.uri/jwks"
        }.ToJson();

        _httpClientFacadeMock.Setup(x => x.GetStringAsync(It.Is<Uri>(uri => uri.AbsoluteUri.ToString().Equals("https://validissuer.com/.well-known/openid-configuration")), It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => openIdConfiguration);

        var openIdKeys = "{}";

        _httpClientFacadeMock.Setup(x => x.GetStringAsync(It.Is<Uri>(uri => uri.AbsoluteUri.ToString().Equals("https://test.uri/jwks")), It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => openIdKeys);

        _singleSignOn.ManualKey = "IncorrectKey";

        //Act & Assert
        Assert.ThrowsAsync<SecurityTokenSignatureKeyNotFoundException>( async () => { var result = await _singleSignOn.ExchangeAuthorisationToken(ssoProviderId, code, CancellationToken.None); });
    }

    [Test]
    public async Task ExchangeAuthorisationToken_WhenValidJwt_ReturnsSubjectIdAsSsoUserId()
    {
        //Arrange
        var ssoProviderId = 123;
        var code = "21kj345ghl2jk13b4";

        var ssoProvider = new SsoProvider
        {
            Id = ssoProviderId,
            AuthorizationEndpoint = "https://validIssuer.com/authorisationEndPoint",
            ClientId = "clientId",
            ClientSecret = "clientSecret",
            Deleted = false,
            IsPublic = true,
            Name = "MyTestIdentityProvider",
            TokenEndpoint = "https://validIssuer.com/TokenEndpoint",
            ValidIssuer = "https://validIssuer.com/"
        };

        _userManagerMock.Setup(x => x.GetSsoProviderById(ssoProviderId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => ssoProvider);

        var httpResponseMessage = new HttpResponseMessage
        {
            //note: This JWT will expire 2130-07-05T08:43:33.330Z.
            Content = new StringContent("{\"id_token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3ZhbGlkSXNzdWVyLmNvbS8iLCJpYXQiOjE3MjAxNjkwMTMsImV4cCI6NTA2NTE0NTAxMywiYXVkIjoiY2xpZW50SWQiLCJzdWIiOiI2OTg0NzMyNDExIn0.YSzsnhkhZEyz1SfRQxNIQDv_7tzS-NxXqVKqvs3R308\"}")
        };

        _httpClientFacadeMock.Setup(x => x.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => httpResponseMessage);

        var openIdConfiguration = new OpenIdConfiguration
        {
            JwksUri = "https://test.uri/jwks"
        }.ToJson();

        _httpClientFacadeMock.Setup(x => x.GetStringAsync(It.Is<Uri>(uri => uri.AbsoluteUri.ToString().Equals("https://validissuer.com/.well-known/openid-configuration")), It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => openIdConfiguration);

        var openIdKeys = "{}";

        _httpClientFacadeMock.Setup(x => x.GetStringAsync(It.Is<Uri>(uri => uri.AbsoluteUri.ToString().Equals("https://test.uri/jwks")), It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => openIdKeys);

        _singleSignOn.ManualKey = "qwertyuiopasdfghjklzxcvbnm123456";

        //Act
        var result = await _singleSignOn.ExchangeAuthorisationToken(ssoProviderId, code, CancellationToken.None);

        //Assert
        Assert.That(result, Is.EqualTo("6984732411"));
    }
}