using Azure.Security.KeyVault.Secrets;
using VaultSharp;
using VaultSharp.V1.AuthMethods.Token;
using Core.Blueprint.KeyVault.Configuration;
using Microsoft.Extensions.Configuration;
using System.Net.Http.Json;
using VaultSharp.Core;
namespace Core.Blueprint.KeyVault;
/// 
/// Provides operations for managing secrets in Azure Key Vault or HashiCorp Vault transparently based on the environment.
/// 
public sealed class KeyVaultProvider : IKeyVaultProvider
{
    private readonly string environment;
    private readonly SecretClient? azureClient;
    private readonly IVaultClient? hashiClient;
    private readonly VaultOptions? hashiOptions;
    public KeyVaultProvider(IConfiguration configuration)
    {
        environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
        if (environment == "Local")
        {
            hashiOptions = configuration.GetSection("Vault").Get();
            hashiClient = new VaultClient(new VaultClientSettings(
                hashiOptions?.Address,
                new TokenAuthMethodInfo(hashiOptions?.Token)
            ));
        }
        else
        {
            var keyVaultUri = new Uri(configuration["ConnectionStrings:KeyVaultDAL"]!);
            azureClient = new SecretClient(keyVaultUri, new Azure.Identity.DefaultAzureCredential());
        }
    }
    /// 
    /// Creates a new secret in Azure Key Vault or HashiCorp Vault.
    /// 
    public async ValueTask CreateSecretAsync(KeyVaultRequest keyVaultRequest, CancellationToken cancellationToken)
    {
        if (environment == "Local")
        {
            await hashiClient!.V1.Secrets.KeyValue.V2.WriteSecretAsync(
                path: keyVaultRequest.Name,
                data: new Dictionary { { "value", keyVaultRequest.Value } },
                mountPoint: hashiOptions!.SecretMount
            );
            return new KeyVaultResponse { Name = keyVaultRequest.Name, Value = keyVaultRequest.Value };
        }
        KeyVaultSecret azureResponse = await azureClient!.SetSecretAsync(
            new KeyVaultSecret(keyVaultRequest.Name, keyVaultRequest.Value), cancellationToken
        );
        return new KeyVaultResponse { Name = azureResponse.Name, Value = azureResponse.Value };
    }
    /// 
    /// Permanently deletes a secret from Azure Key Vault or HashiCorp Vault (hard delete for Vault).
    /// 
    /// The name of the secret to delete.
    /// The cancellation token to cancel the operation.
    /// 
    /// A  containing a status message and a boolean indicating whether the secret was successfully deleted.
    /// 
    public async ValueTask> DeleteSecretAsync(string secretName, CancellationToken cancellationToken)
    {
        if (environment == "Local")
        {
            await DestroyAllSecretVersionsAsync(secretName, cancellationToken);
        }
        var existingSecret = await this.GetSecretAsync(secretName, cancellationToken);
        if (existingSecret.Item2 == string.Empty)
        {
            await azureClient!.StartDeleteSecretAsync(secretName, cancellationToken);
            return new("Key Deleted", true);
        }
        return new("Key Not Found", false);
    }
    /// 
    /// Retrieves a secret from Azure Key Vault or HashiCorp Vault.
    /// 
    public async ValueTask> GetSecretAsync(string secretName, CancellationToken cancellationToken)
    {
        if (environment == "Local")
        {
            try
            {
                var secret = await hashiClient!.V1.Secrets.KeyValue.V2.ReadSecretAsync(
                    path: secretName,
                    mountPoint: hashiOptions!.SecretMount
                );
                if (secret.Data.Data.TryGetValue("value", out var value))
                {
                    return new(new KeyVaultResponse { Name = secretName, Value = value?.ToString() ?? "" }, string.Empty);
                }
                return new(new KeyVaultResponse(), "Key Not Found");
            }
            catch (VaultSharp.Core.VaultApiException ex) when (ex.HttpStatusCode == System.Net.HttpStatusCode.NotFound)
            {
                return new(new KeyVaultResponse(), "Key Not Found");
            }
        }
        try
        {
            KeyVaultSecret azureResponse = await azureClient!.GetSecretAsync(secretName, cancellationToken: cancellationToken);
            return new(new KeyVaultResponse { Name = secretName, Value = azureResponse.Value }, string.Empty);
        }
        catch (Azure.RequestFailedException ex) when (ex.Status == 404)
        {
            return new(new KeyVaultResponse(), "Key Not Found");
        }
    }
    /// 
    /// Updates an existing secret in Azure Key Vault or HashiCorp Vault. If the secret does not exist, an error is returned.
    /// 
    public async ValueTask> UpdateSecretAsync(KeyVaultRequest newSecret, CancellationToken cancellationToken)
    {
        var existingSecret = await this.GetSecretAsync(newSecret.Name, cancellationToken);
        if (!string.IsNullOrEmpty(existingSecret.Item2))
        {
            return new(new KeyVaultResponse(), "Key Not Found");
        }
        var updated = await CreateSecretAsync(newSecret, cancellationToken);
        return new(updated, string.Empty);
    }
    /// 
    /// Permanently deletes all versions of a given secret in HashiCorp Vault.
    /// Returns a tuple indicating the result status and a message.
    /// 
    /// The secret name/path.
    /// A cancellation token.
    /// 
    /// A tuple:
    /// - bool?: true if deleted, false if no versions, null if not found.
    /// - string: message explaining the result.
    /// 
    private async Task<(bool? WasDeleted, string Message)> DestroyAllSecretVersionsAsync(string secretName, CancellationToken cancellationToken)
    {
        Dictionary versions;
        try
        {
            var metadata = await hashiClient!.V1.Secrets.KeyValue.V2.ReadSecretMetadataAsync(
                path: secretName,
                mountPoint: hashiOptions!.SecretMount
            );
            versions = metadata.Data.Versions.Keys.ToDictionary(k => k, _ => (object)0);
            if (versions.Count == 0)
                return (false, "Key exists but contains no versions.");
        }
        catch (VaultApiException ex) when (ex.HttpStatusCode == System.Net.HttpStatusCode.NotFound)
        {
            return (null, "Key Not Found.");
        }
        using var httpClient = new HttpClient { BaseAddress = new Uri(hashiOptions.Address) };
        var request = new HttpRequestMessage(HttpMethod.Post, $"/v1/{hashiOptions.SecretMount}/destroy/{secretName}")
        {
            Content = JsonContent.Create(new { versions = versions.Keys.ToArray() })
        };
        request.Headers.Add("X-Vault-Token", hashiOptions.Token);
        var response = await httpClient.SendAsync(request, cancellationToken);
        response.EnsureSuccessStatusCode();
        await hashiClient.V1.Secrets.KeyValue.V2.DeleteMetadataAsync(
            path: secretName,
            mountPoint: hashiOptions.SecretMount
        );
        return (true, "Key Permanently Deleted.");
    }
}