using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Authority.Plugins.Abstractions;
using StellaOps.Authority.Plugin.Standard.Storage;
using StellaOps.Authority.Storage.Mongo.Documents;
using StellaOps.Authority.Storage.Mongo.Stores;
using Xunit;

namespace StellaOps.Authority.Plugin.Standard.Tests;

public class StandardClientProvisioningStoreTests
{
    [Fact]
    public async Task CreateOrUpdateAsync_HashesSecretAndPersistsDocument()
    {
        var store = new TrackingClientStore();
        var revocations = new TrackingRevocationStore();
        var provisioning = new StandardClientProvisioningStore("standard", store, revocations, TimeProvider.System);

        var registration = new AuthorityClientRegistration(
            clientId: "bootstrap-client",
            confidential: true,
            displayName: "Bootstrap",
            clientSecret: "SuperSecret1!",
            allowedGrantTypes: new[] { "client_credentials" },
            allowedScopes: new[] { "scopeA" });

        var result = await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);

        Assert.True(result.Succeeded);
        Assert.True(store.Documents.TryGetValue("bootstrap-client", out var document));
        Assert.NotNull(document);
        Assert.Equal(AuthoritySecretHasher.ComputeHash("SuperSecret1!"), document!.SecretHash);
        Assert.Equal("standard", document.Plugin);

        var descriptor = await provisioning.FindByClientIdAsync("bootstrap-client", CancellationToken.None);
        Assert.NotNull(descriptor);
        Assert.Equal("bootstrap-client", descriptor!.ClientId);
        Assert.True(descriptor.Confidential);
        Assert.Contains("client_credentials", descriptor.AllowedGrantTypes);
        Assert.Contains("scopeA", descriptor.AllowedScopes);
    }

    private sealed class TrackingClientStore : IAuthorityClientStore
    {
        public Dictionary<string, AuthorityClientDocument> Documents { get; } = new(StringComparer.OrdinalIgnoreCase);

        public ValueTask<AuthorityClientDocument?> FindByClientIdAsync(string clientId, CancellationToken cancellationToken)
        {
            Documents.TryGetValue(clientId, out var document);
            return ValueTask.FromResult(document);
        }

        public ValueTask UpsertAsync(AuthorityClientDocument document, CancellationToken cancellationToken)
        {
            Documents[document.ClientId] = document;
            return ValueTask.CompletedTask;
        }

        public ValueTask<bool> DeleteByClientIdAsync(string clientId, CancellationToken cancellationToken)
        {
            var removed = Documents.Remove(clientId);
            return ValueTask.FromResult(removed);
        }
    }

    private sealed class TrackingRevocationStore : IAuthorityRevocationStore
    {
        public List<AuthorityRevocationDocument> Upserts { get; } = new();

        public ValueTask UpsertAsync(AuthorityRevocationDocument document, CancellationToken cancellationToken)
        {
            Upserts.Add(document);
            return ValueTask.CompletedTask;
        }

        public ValueTask<bool> RemoveAsync(string category, string revocationId, CancellationToken cancellationToken)
            => ValueTask.FromResult(true);

        public ValueTask<IReadOnlyList<AuthorityRevocationDocument>> GetActiveAsync(DateTimeOffset asOf, CancellationToken cancellationToken)
            => ValueTask.FromResult<IReadOnlyList<AuthorityRevocationDocument>>(Array.Empty<AuthorityRevocationDocument>());
    }
}
