using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
using StellaOps.Cryptography;
using Xunit;

namespace StellaOps.Cryptography.Tests;

public class DefaultCryptoProviderSigningTests
{
    [Fact]
    public async Task UpsertSigningKey_AllowsSignAndVerifyEs256()
    {
        var provider = new DefaultCryptoProvider();
        using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
        var parameters = ecdsa.ExportParameters(includePrivateParameters: true);

        var signingKey = new CryptoSigningKey(
            new CryptoKeyReference("revocation-key"),
            SignatureAlgorithms.Es256,
            privateParameters: in parameters,
            createdAt: DateTimeOffset.UtcNow);

        provider.UpsertSigningKey(signingKey);

        var signer = provider.GetSigner(SignatureAlgorithms.Es256, signingKey.Reference);

        var payload = Encoding.UTF8.GetBytes("hello-world");
        var signature = await signer.SignAsync(payload);

        Assert.NotNull(signature);
        Assert.True(signature.Length > 0);

        var verified = await signer.VerifyAsync(payload, signature);
        Assert.True(verified);

        var jwk = signer.ExportPublicJsonWebKey();
        Assert.Equal(signingKey.Reference.KeyId, jwk.Kid);
        Assert.Equal(SignatureAlgorithms.Es256, jwk.Alg);
        Assert.Equal(JsonWebAlgorithmsKeyTypes.EllipticCurve, jwk.Kty);
        Assert.Equal(JsonWebKeyUseNames.Sig, jwk.Use);
        Assert.Equal(JsonWebKeyECTypes.P256, jwk.Crv);
        Assert.False(string.IsNullOrWhiteSpace(jwk.X));
        Assert.False(string.IsNullOrWhiteSpace(jwk.Y));

        var tampered = (byte[])signature.Clone();
        tampered[^1] ^= 0xFF;
        var tamperedResult = await signer.VerifyAsync(payload, tampered);
        Assert.False(tamperedResult);
    }

    [Fact]
    public void RemoveSigningKey_PreventsRetrieval()
    {
        var provider = new DefaultCryptoProvider();
        using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
        var parameters = ecdsa.ExportParameters(true);
        var signingKey = new CryptoSigningKey(new CryptoKeyReference("key-to-remove"), SignatureAlgorithms.Es256, in parameters, DateTimeOffset.UtcNow);

        provider.UpsertSigningKey(signingKey);
        Assert.True(provider.RemoveSigningKey(signingKey.Reference.KeyId));

        Assert.Throws<KeyNotFoundException>(() => provider.GetSigner(SignatureAlgorithms.Es256, signingKey.Reference));
    }
}
