using System;
using System.Text;
using System.Threading.Tasks;
using System.IO;

using Amazon;
using Amazon.SecretsManager;
using Amazon.SecretsManager.Model;
using Serilog;

// Possible AWS Regions
// https://docs.aws.amazon.com/general/latest/gr/rande.html

namespace CoinbaseEngine
{
    public class AWSSecretKeys
    {
        public string ApiKey { get; set; }      
        public string ApiSecret { get; set; }
        public string ApiPassPhrase { get; set; }           
    }

    public sealed partial class AWSSecretManager
    {
        private static readonly Lazy<AWSSecretManager> lazy = new Lazy<AWSSecretManager>(() => new AWSSecretManager());
        public static AWSSecretManager Instance { get { return lazy.Value; } }

        private static readonly string AwsAccessKeyId = CoinbaseConfig.AwsAccessKeyId;
        private static readonly string AwsSecretAccessKey = CoinbaseConfig.AwsSecretAccessKey;
        private static readonly string AwsRegionEndPoint = CoinbaseConfig.AwsRegionEndPoint;

        private IAmazonSecretsManager client = null;

        private AWSSecretManager()
        {
            var regionEP = RegionEndpoint.GetBySystemName(AwsRegionEndPoint);

            if (string.IsNullOrEmpty(AwsAccessKeyId))
                client = new AmazonSecretsManagerClient(regionEP);  // AccessKeys assumed set as environment variables
            else
                client = new AmazonSecretsManagerClient(AwsAccessKeyId, AwsSecretAccessKey, regionEP);

            if (client != null)
            {
                Log.Information("AmazonSecretsManagerClient instantiation OK.");
            }
        }

        /// <summary>
        /// The main method initializes the necessary values and then calls
        /// the GetSecretAsync and DecodeString methods to get the decoded
        /// secret value for the secret named in secretName.
        /// </summary>
        public async Task<string> GetSecretKey(string secretName)
        {
            var response = await GetSecretAsync(client, secretName);

            if (response is not null)
            {
                var secret = DecodeString(response);

                if (!string.IsNullOrEmpty(secret))
                    return secret;
            }
            return string.Empty;
        }

        /// <summary>
        /// Retrieves the secret value given the name of the secret to
        /// retrieve.
        /// </summary>
        /// <param name="client">The client object used to retrieve the secret
        /// value for the given secret name.</param>
        /// <param name="secretName">The name of the secret value to retrieve.</param>
        /// <returns>The GetSecretValueReponse object returned by
        /// GetSecretValueAsync.</returns>
        private async Task<GetSecretValueResponse> GetSecretAsync(IAmazonSecretsManager client, string secretName)
        {
            GetSecretValueRequest request = new();
            request.SecretId = secretName;
            request.VersionStage = "AWSCURRENT"; // VersionStage defaults to AWSCURRENT if unspecified.

            GetSecretValueResponse response = null;

            // For the sake of simplicity, this example handles only the most
            // general SecretsManager exception.
            try
            {
                response = await client.GetSecretValueAsync(request);
            }
            catch (AmazonSecretsManagerException e)
            {
                Log.Debug($"AWSSecretManager.GetSecretAsync: {e.Message}");
            }
            return response;
        }

        /// <summary>
        /// Decodes the secret returned by the call to GetSecretValueAsync and
        /// returns it to the calling program.
        /// </summary>
        /// <param name="response">A GetSecretValueResponse object containing
        /// the requested secret value returned by GetSecretValueAsync.</param>
        /// <returns>A string representing the decoded secret value.</returns>
        private string DecodeString(GetSecretValueResponse response)
        {
            try
            {
                // Decrypts secret using the associated AWS Key Management Service
                // Customer Master Key (CMK.) Depending on whether the secret is a
                // string or binary value, one of these fields will be populated.
                MemoryStream memoryStream = new();

                if (response.SecretString is not null)
                {
                    var secret = response.SecretString;
                    return secret;
                }
                else if (response.SecretBinary is not null)
                {
                    memoryStream = response.SecretBinary;
                    StreamReader reader = new StreamReader(memoryStream);
                    string decodedBinarySecret = Encoding.UTF8.GetString(Convert.FromBase64String(reader.ReadToEnd()));
                    return decodedBinarySecret;
                }
            }
            catch (Exception ex)
            {
                Log.Debug($"AWSSecretManager.DecodeString {ex.Message}");
            }
            return string.Empty;
        }
    }
}