#if STELLAOPS_CRYPTO_SODIUM
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;

namespace StellaOps.Cryptography;

/// <summary>
/// Libsodium-backed crypto provider (ES256) registered when <c>STELLAOPS_CRYPTO_SODIUM</c> is defined.
/// </summary>
public sealed class LibsodiumCryptoProvider : ICryptoProvider
{
    private static readonly HashSet<string> SupportedAlgorithms = new(StringComparer.OrdinalIgnoreCase)
    {
        SignatureAlgorithms.Es256
    };

    private readonly ConcurrentDictionary<string, CryptoSigningKey> signingKeys = new(StringComparer.Ordinal);

    public string Name => "libsodium";

    public bool Supports(CryptoCapability capability, string algorithmId)
    {
        if (string.IsNullOrWhiteSpace(algorithmId))
        {
            return false;
        }

        return capability switch
        {
            CryptoCapability.Signing or CryptoCapability.Verification => SupportedAlgorithms.Contains(algorithmId),
            _ => false
        };
    }

    public IPasswordHasher GetPasswordHasher(string algorithmId)
        => throw new NotSupportedException("Libsodium provider does not expose password hashing capabilities.");

    public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference)
    {
        ArgumentNullException.ThrowIfNull(keyReference);

        EnsureAlgorithmSupported(algorithmId);

        if (!signingKeys.TryGetValue(keyReference.KeyId, out var signingKey))
        {
            throw new KeyNotFoundException($"Signing key '{keyReference.KeyId}' is not registered with provider '{Name}'.");
        }

        if (!string.Equals(signingKey.AlgorithmId, algorithmId, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException(
                $"Signing key '{keyReference.KeyId}' is registered for algorithm '{signingKey.AlgorithmId}', not '{algorithmId}'.");
        }

        return new LibsodiumEcdsaSigner(signingKey);
    }

    public void UpsertSigningKey(CryptoSigningKey signingKey)
    {
        ArgumentNullException.ThrowIfNull(signingKey);
        EnsureAlgorithmSupported(signingKey.AlgorithmId);

        signingKeys.AddOrUpdate(signingKey.Reference.KeyId, signingKey, (_, _) => signingKey);
    }

    public bool RemoveSigningKey(string keyId)
    {
        if (string.IsNullOrWhiteSpace(keyId))
        {
            return false;
        }

        return signingKeys.TryRemove(keyId, out _);
    }

    public IReadOnlyCollection<CryptoSigningKey> GetSigningKeys()
        => signingKeys.Values.ToArray();

    private static void EnsureAlgorithmSupported(string algorithmId)
    {
        if (!SupportedAlgorithms.Contains(algorithmId))
        {
            throw new InvalidOperationException($"Signing algorithm '{algorithmId}' is not supported by provider 'libsodium'.");
        }
    }

    private sealed class LibsodiumEcdsaSigner : ICryptoSigner
    {
        private readonly CryptoSigningKey signingKey;
        private readonly ICryptoSigner fallbackSigner;

        public LibsodiumEcdsaSigner(CryptoSigningKey signingKey)
        {
            this.signingKey = signingKey ?? throw new ArgumentNullException(nameof(signingKey));
            fallbackSigner = EcdsaSigner.Create(signingKey);
        }

        public string KeyId => signingKey.Reference.KeyId;

        public string AlgorithmId => signingKey.AlgorithmId;

        public ValueTask<byte[]> SignAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();
            // TODO(SEC5.B1): replace fallback with libsodium bindings once native interop lands.
            return fallbackSigner.SignAsync(data, cancellationToken);
        }

        public ValueTask<bool> VerifyAsync(ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> signature, CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();
            return fallbackSigner.VerifyAsync(data, signature, cancellationToken);
        }

        public JsonWebKey ExportPublicJsonWebKey()
            => fallbackSigner.ExportPublicJsonWebKey();
    }
}
#endif
