using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using Microsoft.AspNetCore.Http.HttpResults;

class ApimHelper 
{
    private readonly string _apimBaseUri;
    private readonly string _b2bApiUri;
    private readonly string _padp1201Uri;
    private readonly string _padp1202Uri;
    private readonly string _padp1204Uri;
    private readonly string _padp1205Uri;
    private readonly string _padp1206V2Uri;
    private readonly string _padp1207V2Uri;
    private readonly string _padp1206V3Uri;
    private readonly string _padp1207V3Uri;

    private readonly string _padp1210Uri;
    private readonly string _padp1211Uri;

    private readonly string _b2bApiKey;
    private readonly string _padApiKey;
    private readonly string _clientId;
    private readonly string _clientSecret;
    private CryptoHelper cryptoHelper;
    private readonly HttpClient _httpClient;

    private Dictionary<string, UserAuthInfo> userAuthInfoMap = new Dictionary<string, UserAuthInfo>();

    public ApimHelper(UserProperties userProperties)
    {
        _apimBaseUri = userProperties.Uris.ApimBaseUri;
        _b2bApiUri = userProperties.Uris.B2bApiUri;
        _padp1201Uri = userProperties.Uris.Padp1201Uri;
        _padp1202Uri = userProperties.Uris.Padp1202Uri;
        _padp1204Uri = userProperties.Uris.Padp1204Uri;
        _padp1205Uri = userProperties.Uris.Padp1205Uri;
        _padp1206V2Uri = userProperties.Uris.Padp1206V2Uri;
        _padp1207V2Uri = userProperties.Uris.Padp1207V2Uri;
        _padp1206V3Uri = userProperties.Uris.Padp1206V3Uri;
        _padp1207V3Uri = userProperties.Uris.Padp1207V3Uri;
        _padp1210Uri = userProperties.Uris.Padp1210Uri;
        _padp1211Uri = userProperties.Uris.Padp1211Uri;

        _b2bApiKey = userProperties.Credentials.B2bApiKey;
        _padApiKey = userProperties.Credentials.PadApiKey;
        _clientId = userProperties.Credentials.ClientId;
        _clientSecret = userProperties.Credentials.ClientSecret;

        cryptoHelper = new CryptoHelper(userProperties);

        HttpClientHandler handler = new HttpClientHandler();
        string clientCertPath = Environment.CurrentDirectory + userProperties.Credentials.ClientCertFile;
        Console.WriteLine("Loading client cert from: {0}", clientCertPath);
        handler.ClientCertificates.Add(new X509Certificate2(clientCertPath, userProperties.Credentials.ClientCertPassword));

        _httpClient = new HttpClient(handler);
        _httpClient.BaseAddress = new Uri(_apimBaseUri);
    }

    public async Task<Results<Ok<B2bAccessToken>, JsonHttpResult<ErrorResponse>>> GetB2bAccessToken() 
    {
        var request = new HttpRequestMessage(HttpMethod.Post, _b2bApiUri);
        request.Headers.Add("APIKey", _b2bApiKey);
        request.Headers.Add("Accept", "application/json");
        request.Content = new StringContent($"grant_type=client_credentials&client_id={_clientId}&client_secret={_clientSecret}", Encoding.UTF8, "application/x-www-form-urlencoded");

        Console.WriteLine("Sending request to {0}...", _b2bApiUri);

        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.OK) {
            return TypedResults.Ok(await response.Content.ReadFromJsonAsync<B2bAccessToken>());
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        }
    }

    private string GetB2bAccessTokenAsString()
    {
        var b2bAccessToken = GetB2bAccessToken();
        return ((Ok<B2bAccessToken>)b2bAccessToken.Result.Result).Value.accessToken;
    }

    public IResult GetImageFromBase64(string base64Image) 
    {
        byte[] imageBytes = Convert.FromBase64String(base64Image);
        return TypedResults.File(imageBytes, "image/jpeg");
    }

    public async Task<Results<Created, JsonHttpResult<ErrorResponse>>> CreatePersonalData(string xTat, string email, string? name, string? birthDate, IFormFile? photo) 
    {
        UserAuthInfo userAuthInfo;
        if (userAuthInfoMap.TryGetValue(xTat, out UserAuthInfo? authInfo) && authInfo.encryptedEphemeralKey != null) {
            userAuthInfo = authInfo;
        } else {
            var errorResponse = new ErrorResponse("No ephemeral key found for xTAT " + xTat + " - generate and validate OTP first using API 1210!", 400);
            return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
        }

        var b2bAccessToken = await GetB2bAccessToken();

        var requestUri = _padp1201Uri.Replace("{xtat}", xTat);
        var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
        request.Headers.Add("Authorization", "Bearer " + ((Ok<B2bAccessToken>)b2bAccessToken.Result).Value.accessToken);
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");

        var createPersonalDataRequest = new CreatePersonalDataRequest();
        createPersonalDataRequest.metadata.email = email;
        createPersonalDataRequest.metadata.ephemeralKeyAlias = userAuthInfo.ephemeralKeyAlias;

        var decryptedEphemeralKey = cryptoHelper.DecryptEphemeralKey(userAuthInfo.encryptedEphemeralKey);

        if (name != null) {
            createPersonalDataRequest.data.personalAccountData.name = cryptoHelper.EncryptText(decryptedEphemeralKey, name);
        }
        if (birthDate != null) {
            createPersonalDataRequest.data.personalAccountData.birthdate = cryptoHelper.EncryptText(decryptedEphemeralKey, birthDate);;
        }
        if (photo != null) {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                photo.CopyTo(memoryStream);
                byte[] photoBytes = memoryStream.ToArray();
                createPersonalDataRequest.data.personalAccountData.photo = cryptoHelper.EncryptPhoto(decryptedEphemeralKey, photoBytes);
            }
        }
        request.Content = JsonContent.Create(createPersonalDataRequest);

        Console.WriteLine("Sending request to {0} with request body {1}...", requestUri, request.Content.ReadAsStringAsync().Result);
        
        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.Created) {
            return TypedResults.Created();            
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        }
    }
    
    public async Task<Results<Created, JsonHttpResult<ErrorResponse>>> CreatePersonalDataWithEmailVerification(string xTat, string email, string? userAccessToken, string? name, string? birthDate, IFormFile? photo) 
    {
        UserAuthInfo userAuthInfo;
        if (userAuthInfoMap.TryGetValue(xTat, out UserAuthInfo? authInfo)) {
            if (authInfo.encryptedEphemeralKey == null) {
                var errorResponse = new ErrorResponse("No ephemeral key found for xTAT " + xTat + " - generate an ephemeral key first, using API 1210!", 400);
                return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
            } else if (authInfo.UserAccessToken == null && userAccessToken == null) {
                var errorResponse = new ErrorResponse("No User Access Token found for xTAT " + xTat + " - provide one in the request, or generate and validate OTP using API 1206 and 1207!", 400);
                return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
            }
            userAuthInfo = authInfo;
        } else {
            var errorResponse = new ErrorResponse("No ephemeral key found for xTAT " + xTat + " - generate an ephemeral key first, using API 1210!", 400);
            return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
        }

        var requestUri = _padp1201Uri.Replace("{xtat}", xTat);
        var request = new HttpRequestMessage(HttpMethod.Post, requestUri);

        if (userAccessToken != null) {
            Console.WriteLine("Using provided User Access Token {0}...", userAccessToken);
            request.Headers.Add("Authorization", "Bearer " + userAccessToken);
        } else {
            Console.WriteLine("Using stored User Access Token {0}...", userAuthInfo.UserAccessToken);
            request.Headers.Add("Authorization", "Bearer " + userAuthInfo.UserAccessToken);
        }
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");

        var createPersonalDataRequest = new CreatePersonalDataRequest();
        createPersonalDataRequest.metadata.email = email;
        createPersonalDataRequest.metadata.ephemeralKeyAlias = userAuthInfo.ephemeralKeyAlias;

        var decryptedEphemeralKey = cryptoHelper.DecryptEphemeralKey(userAuthInfo.encryptedEphemeralKey);

        if (name != null) {
            createPersonalDataRequest.data.personalAccountData.name = cryptoHelper.EncryptText(decryptedEphemeralKey, name);
        }
        if (birthDate != null) {
            createPersonalDataRequest.data.personalAccountData.birthdate = cryptoHelper.EncryptText(decryptedEphemeralKey, birthDate);;
        }
        if (photo != null) {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                photo.CopyTo(memoryStream);
                byte[] photoBytes = memoryStream.ToArray();
                createPersonalDataRequest.data.personalAccountData.photo = cryptoHelper.EncryptPhoto(decryptedEphemeralKey, photoBytes);
            }
        }
        request.Content = JsonContent.Create(createPersonalDataRequest);

        Console.WriteLine("Sending request to {0} with request body {1}...", requestUri, request.Content.ReadAsStringAsync().Result);
        
        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.Created) {
            return TypedResults.Created();            
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        }
    }

    public async Task<Results<Ok<PersonalData>, JsonHttpResult<ErrorResponse>>> GetPersonalData(string xTat, string? userAccessToken)
    {
        UserAuthInfo? userAuthInfo = null;
        if (userAuthInfoMap.TryGetValue(xTat, out UserAuthInfo? authInfo) && authInfo.UserAccessToken != null)
        {
            userAuthInfo = authInfo;
        } else if (userAccessToken == null) {
            var errorResponse = new ErrorResponse("No User Access Token found for xTAT " + xTat + " - provide one in the request, or generate and validate OTP using API 1206 and 1207!", 400);
            return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
        }

        var requestUri = _padp1202Uri.Replace("{xtat}", xTat) + "?pemRsaPublicKey=" + cryptoHelper.GetPublicKeyRsa();
        var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        
        if (userAccessToken != null)
        {
            Console.WriteLine("Using provided User Access Token {0}...", userAccessToken);
            request.Headers.Add("Authorization", "Bearer " + userAccessToken);
        } else {
            Console.WriteLine("Using stored User Access Token {0}...", userAuthInfo.UserAccessToken);
            request.Headers.Add("Authorization", "Bearer " + userAuthInfo.UserAccessToken);
        }
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");

        Console.WriteLine("Sending request to {0}...", requestUri);

        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            return TypedResults.Ok(await response.Content.ReadFromJsonAsync<PersonalData>());
        }
        else
        {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        }
    }

    public async Task<Results<Ok<DecryptedPersonalData>, JsonHttpResult<ErrorResponse>>> GetDecryptedPersonalData(string xTat, string? userAccessToken) 
    {
        var personalData = await GetPersonalData(xTat, userAccessToken);
        if (personalData.Result.GetType() == typeof(JsonHttpResult<ErrorResponse>)) { 
            return (JsonHttpResult<ErrorResponse>)personalData.Result;
        } else {
            var decryptedPersonalData = await DecryptPersonalData(((Ok<PersonalData>)personalData.Result).Value);
            return TypedResults.Ok(decryptedPersonalData);
        }       
    }

    public async Task<Results<Ok<DeletePersonalDataResponse>, JsonHttpResult<ErrorResponse>>> DeletePersonalData(string xTat, string? userAccessToken) 
    {
        UserAuthInfo? userAuthInfo = null;
        if (userAuthInfoMap.TryGetValue(xTat, out UserAuthInfo? authInfo) && authInfo.UserAccessToken != null)
        {
            userAuthInfo = authInfo;
        } else if (userAccessToken == null) {
            var errorResponse = new ErrorResponse("No User Access Token found for xTAT " + xTat + " - provide one in the request, or generate and validate OTP using API 1206 and 1207!", 400);
            return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
        }

        var requestUri = _padp1204Uri.Replace("{xtat}", xTat);
        var request = new HttpRequestMessage(HttpMethod.Delete, requestUri);
        
        if (userAccessToken != null) {
            Console.WriteLine("Using provided User Access Token {0}...", userAccessToken);
            request.Headers.Add("Authorization", "Bearer " + userAccessToken);
        } else {
            Console.WriteLine("Using stored User Access Token {0}...", userAuthInfo.UserAccessToken);
            request.Headers.Add("Authorization", "Bearer " + userAuthInfo.UserAccessToken);
        }
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");

        Console.WriteLine("Sending request to {0}...", requestUri);
        
        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.OK) {
            return TypedResults.Ok(await response.Content.ReadFromJsonAsync<DeletePersonalDataResponse>());
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        }
    }

    public async Task<Results<Ok, JsonHttpResult<ErrorResponse>>> UpdatePersonalData(string xTat, string? userAccessToken, bool skipUpdateCounter, string? name, string? birthDate, IFormFile? photo) 
    {
        UserAuthInfo userAuthInfo;
        if (userAuthInfoMap.TryGetValue(xTat, out UserAuthInfo? authInfo)) {
            if (authInfo.encryptedEphemeralKey == null) {
                var errorResponse = new ErrorResponse("No ephemeral key found for xTAT " + xTat + " - generate an ephemeral key first, using API 1210!", 400);
                return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
            } else if (authInfo.UserAccessToken == null && userAccessToken == null) {
                var errorResponse = new ErrorResponse("No User Access Token found for xTAT " + xTat + " - provide one in the request, or generate and validate OTP using API 1206 and 1207!", 400);
                return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
            }
            userAuthInfo = authInfo;
        } else {
            var errorResponse = new ErrorResponse("No User Access Token found for xTAT " + xTat + " - provide one in the request, or generate and validate OTP using API 1206 and 1207!", 400);
            return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
        }

        var requestUri = _padp1205Uri.Replace("{xtat}", xTat);
        var request = new HttpRequestMessage(HttpMethod.Put, requestUri);
        
        if (userAccessToken != null) {
            Console.WriteLine("Using provided User Access Token {0}...", userAccessToken);
            request.Headers.Add("Authorization", "Bearer " + userAccessToken);
        } else {
            Console.WriteLine("Using stored User Access Token {0}...", userAuthInfo.UserAccessToken);
            request.Headers.Add("Authorization", "Bearer " + userAuthInfo.UserAccessToken);
        }
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");

        var updatePersonalDataRequest = new UpdatePersonalDataRequest();
        updatePersonalDataRequest.metadata.skipUpdateCounter = skipUpdateCounter;
        updatePersonalDataRequest.metadata.ephemeralKeyAlias = userAuthInfo.ephemeralKeyAlias;

        var decryptedEphemeralKey = cryptoHelper.DecryptEphemeralKey(userAuthInfo.encryptedEphemeralKey);

        if (name != null) {
            updatePersonalDataRequest.data.personalAccountData.name = cryptoHelper.EncryptText(decryptedEphemeralKey, name);
        }
        if (birthDate != null) {
            updatePersonalDataRequest.data.personalAccountData.birthdate = cryptoHelper.EncryptText(decryptedEphemeralKey, birthDate);;
        }
        if (photo != null) {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                photo.CopyTo(memoryStream);
                byte[] photoBytes = memoryStream.ToArray();
                updatePersonalDataRequest.data.personalAccountData.photo = cryptoHelper.EncryptPhoto(decryptedEphemeralKey, photoBytes);
            }
        }
        request.Content = JsonContent.Create(updatePersonalDataRequest);

        Console.WriteLine("Sending request to {0} with request body {1}...", requestUri, request.Content.ReadAsStringAsync().Result);
        
        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.OK) {
            return TypedResults.Ok();
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        } 
    }

    public async Task<Results<JsonHttpResult<OtpResponse>, JsonHttpResult<ErrorResponse>>> GenerateOtpV2(string xTat)
    {
        var requestUri = _padp1206V2Uri.Replace("{xtat}", xTat);
        var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
        request.Headers.Add("Authorization", "Bearer " + GetB2bAccessTokenAsString());
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");
        request.Content = new StringContent("{\"channel\": \"EMAIL\"}", Encoding.UTF8, "application/json");

        Console.WriteLine("Sending request to {0}...", requestUri);
        
        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.Accepted) {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<OtpResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        }
    }

    public async Task<Results<Ok<UserAccessToken>, JsonHttpResult<ErrorResponse>>> ValidateOtpV2(string xTat, string otp)
    {
        var requestUri = _padp1207V2Uri.Replace("{xtat}", xTat);
        var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
        request.Headers.Add("Authorization", "Bearer " + GetB2bAccessTokenAsString());
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");
        request.Content = new StringContent("{\"otp\": \"" + otp + "\"}", Encoding.UTF8, "application/json");

        Console.WriteLine("Sending request to {0}...", requestUri);
        
        var response = await _httpClient.SendAsync(request);
        
        if (response.StatusCode == System.Net.HttpStatusCode.OK) {
            var content = await response.Content.ReadFromJsonAsync<UserAccessToken>();
            Console.WriteLine("Successfully retrieved User Access Token for xTAT {0} - storing for use in following calls...", xTat);

            if (!userAuthInfoMap.ContainsKey(xTat)) {
                userAuthInfoMap.Add(xTat, new UserAuthInfo());
            }
            UserAuthInfo userAuthInfo = userAuthInfoMap[xTat];
            userAuthInfo.UserAccessToken = content.accessToken;

            return TypedResults.Ok(content);
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode); 
        }
    }
    
    public async Task<Results<JsonHttpResult<OtpResponse>, JsonHttpResult<ErrorResponse>>> GenerateOtpV3(string? email, string? xTat)
    {
        var requestUri = _padp1206V3Uri;
        var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
        request.Headers.Add("Authorization", "Bearer " + GetB2bAccessTokenAsString());
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");

        var generateOtpRequest = new GenerateOtpRequestV3();

        generateOtpRequest.channel = "EMAIL";
        if (email != null) {
            generateOtpRequest.source = "EMAIL";
            generateOtpRequest.recipient = email;
        } else if (xTat != null) {
            generateOtpRequest.source = "XTAT";
            generateOtpRequest.recipient = xTat;
        } else if (email != null && xTat != null){
            var errorResponse = new ErrorResponse("Both e-mail and xTAT filled, only one should be filled in the request!", 400);
            return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
        }

        request.Content = JsonContent.Create(generateOtpRequest);

        Console.WriteLine("Sending request to {0}...", requestUri);
        
        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.Accepted) {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<OtpResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        }
    }

    public async Task<Results<Ok<UserAccessToken>, JsonHttpResult<ErrorResponse>>> ValidateOtpV3(string? email, string? xTat, string otp)
    {
        var requestUri = _padp1207V3Uri;
        var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
        request.Headers.Add("Authorization", "Bearer " + GetB2bAccessTokenAsString());
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");

        var validateOtpRequest = new ValidateOtpRequestV3();

        validateOtpRequest.otp = otp;
        if (email != null)
        {
            validateOtpRequest.source = "EMAIL";
            validateOtpRequest.recipient = email;
        }
        else if (xTat != null)
        {
            validateOtpRequest.source = "XTAT";
            validateOtpRequest.recipient = xTat;
        }
        else if (email != null && xTat != null)
        {
            var errorResponse = new ErrorResponse("Both e-mail and xTAT filled, only one should be filled in the request!", 400);
            return TypedResults.Json(errorResponse, JsonSerializerOptions.Default, "application/json", 400);
        }

        request.Content = JsonContent.Create(validateOtpRequest);

        Console.WriteLine("Sending request to {0}...", requestUri);
        
        var response = await _httpClient.SendAsync(request);
        
        if (response.StatusCode == System.Net.HttpStatusCode.OK) {
            var content = await response.Content.ReadFromJsonAsync<UserAccessToken>();

            if (xTat != null) {
                Console.WriteLine("Successfully retrieved User Access Token for xTAT {0} - storing for use in following calls...", xTat);

                if (!userAuthInfoMap.ContainsKey(xTat))
                {
                    userAuthInfoMap.Add(xTat, new UserAuthInfo());
                }
                UserAuthInfo userAuthInfo = userAuthInfoMap[xTat];
                userAuthInfo.UserAccessToken = content.accessToken;
            }
            else {
                Console.WriteLine("Successfully retrieved User Access Token for e-mail {0} - we don't know the xTAT (yet), so we don't cannot the user access token for later use...");
            }
            

            return TypedResults.Ok(content);
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode); 
        }
    }

    public async Task<Results<Ok<AdministrativeData>, JsonHttpResult<ErrorResponse>>> GetAdministrativeData(string xTat)
    {
        var requestUri = _padp1211Uri.Replace("{xtat}", xTat);
        var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        request.Headers.Add("Authorization", "Bearer " + GetB2bAccessTokenAsString());
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");

        Console.WriteLine("Sending request to {0}...", requestUri);

        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            return TypedResults.Ok(await response.Content.ReadFromJsonAsync<AdministrativeData>());
        }
        else
        {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        }
    }

    public string EncryptDecryptPoc(string textToEncrypt, string encryptedEphemeralKey) 
    {
        Console.WriteLine("Encrypting, then decrypting text {0} using encrypted ephemeral key {1}...", textToEncrypt, encryptedEphemeralKey);
        var decryptedEphemeralKey = cryptoHelper.DecryptEphemeralKey(encryptedEphemeralKey);
        var encryptedText = cryptoHelper.EncryptText(decryptedEphemeralKey, textToEncrypt);
        Console.WriteLine($"Encrypted text: {encryptedText}");

        Console.WriteLine("Decrypting text {0}...", encryptedText);
        var decryptedText = cryptoHelper.DecryptTextAsync(decryptedEphemeralKey, encryptedText);
        Console.WriteLine($"Decrypted text: {decryptedText.Result}");

        return "Encrypted text: " + encryptedText + "\nDecrypted text: " + decryptedText.Result;
    }

    public string EncryptPoc(string textToEncrypt, string encryptedEphemeralKey) 
    {
        Console.WriteLine("Encrypting text {0} using encrypted ephemeral key {1}...", textToEncrypt, encryptedEphemeralKey);
        var decryptedEphemeralKey = cryptoHelper.DecryptEphemeralKey(encryptedEphemeralKey);
        var encryptedText = cryptoHelper.EncryptText(decryptedEphemeralKey, textToEncrypt);
        Console.WriteLine($"Encrypted text: {encryptedText}");

        return "Encrypted text: " + encryptedText;
    }

    public string DecryptPoc(string textToDecrypt, string encryptedEphemeralKey) 
    {
        Console.WriteLine("Decrypting text {0} using ephemeral key {1}...", textToDecrypt, encryptedEphemeralKey);
        var decryptedEphemeralKey = cryptoHelper.DecryptEphemeralKey(encryptedEphemeralKey);
        var decryptedText = cryptoHelper.DecryptTextAsync(decryptedEphemeralKey, textToDecrypt);
        Console.WriteLine($"Decrypted text: {decryptedText.Result}");

        return "Decrypted text: " + decryptedText.Result;
    }

    public async Task<Results<Ok<EphemeralKey>, JsonHttpResult<ErrorResponse>>> CreateEphemeralKey(string xTat) 
    {
        var requestUri = _padp1210Uri + "?pemRsaPublicKey=" + cryptoHelper.GetPublicKeyRsa();
        var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
        request.Headers.Add("Authorization", "Bearer " + GetB2bAccessTokenAsString());
        request.Headers.Add("APIKey", _padApiKey);
        request.Headers.Add("requestId", Guid.NewGuid().ToString());
        request.Headers.Add("Accept", "application/json");

        Console.WriteLine("Sending request to {0}...", requestUri);
        
        var response = await _httpClient.SendAsync(request);

        if (response.StatusCode == System.Net.HttpStatusCode.Created) {
            var content = await response.Content.ReadFromJsonAsync<EphemeralKey>();
            Console.WriteLine("Successfully created Ephemeral Key for xTAT {0} - storing for use in following calls...", xTat);
            if (!userAuthInfoMap.ContainsKey(xTat)) {
                userAuthInfoMap.Add(xTat, new UserAuthInfo());
            }
            UserAuthInfo userAuthInfo = userAuthInfoMap[xTat];
            userAuthInfo.ephemeralKeyAlias = content.ephemeralKeyAlias;
            userAuthInfo.encryptedEphemeralKey = content.encryptedEphemeralKey;

            return TypedResults.Ok(content);
        } else {
            return TypedResults.Json(await response.Content.ReadFromJsonAsync<ErrorResponse>(), JsonSerializerOptions.Default, "application/json", (int)response.StatusCode);
        }
    }

    private async Task<DecryptedPersonalData> DecryptPersonalData(PersonalData personalData)
    {
        var decryptedEphemeralKey = cryptoHelper.DecryptEphemeralKey(personalData.metadata.encryptedEphemeralKey);
        Console.WriteLine("Decrypted encryptedEphemeralKey: {0}", Convert.ToBase64String(decryptedEphemeralKey));

        var decryptedData = new DecryptedPersonalData.DecryptedData();

        if (personalData.data.name != null) {
            decryptedData.decryptedName = await cryptoHelper.DecryptTextAsync(decryptedEphemeralKey, personalData.data.name);
            Console.WriteLine($"Decrypted name: {decryptedData.decryptedName}");
        }
        
        if (personalData.data.birthdate != null) {
            decryptedData.decryptedBirthdate = await cryptoHelper.DecryptTextAsync(decryptedEphemeralKey, personalData.data.birthdate);
            Console.WriteLine($"Decrypted birthDate: {decryptedData.decryptedBirthdate}");
        }

        if (personalData.data.photo != null) {
            decryptedData.decryptedPhoto = await cryptoHelper.DecryptPhotoAsync(decryptedEphemeralKey, personalData.data.photo);
            Console.WriteLine($"Decrypted photo: {decryptedData.decryptedPhoto}");
        }

        return new DecryptedPersonalData(decryptedData, personalData);
    }
}