using ZB.MOM.WW.Secrets.Abstractions;
using ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.DependencyInjection;

namespace ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.Tests.DependencyInjection;

/// <summary>
/// The deferred invalidator exists to break the scadaproj#1 dependency cycle, which makes it a
/// pass-through that could silently pass nothing through — the exact failure mode (inert seam,
/// green suite) this library has shipped before. These tests pin the two halves of its contract:
/// nothing resolves before the first eviction, and evictions actually reach the real invalidator.
/// </summary>
public sealed class DeferredSecretCacheInvalidatorTests
{
    private sealed class RecordingInvalidator : ISecretCacheInvalidator
    {
        public List<SecretName> Evicted { get; } = [];

        public void Invalidate(SecretName name) => Evicted.Add(name);
    }

    [Fact]
    public void Construction_does_not_resolve_the_inner_invalidator()
    {
        // THE point of the type: resolving eagerly is what closed the singleton cycle and hung
        // every hosted process at startup.
        bool resolved = false;

        _ = new DeferredSecretCacheInvalidator(() =>
        {
            resolved = true;
            return null;
        });

        Assert.False(resolved);
    }

    [Fact]
    public void Invalidate_forwards_to_the_resolved_invalidator()
    {
        var inner = new RecordingInvalidator();
        var deferred = new DeferredSecretCacheInvalidator(() => inner);
        var name = new SecretName("gate/alpha");

        deferred.Invalidate(name);

        Assert.Equal([name], inner.Evicted);
    }

    [Fact]
    public void The_lookup_runs_once_and_the_instance_is_reused()
    {
        int lookups = 0;
        var inner = new RecordingInvalidator();
        var deferred = new DeferredSecretCacheInvalidator(() =>
        {
            lookups++;
            return inner;
        });

        deferred.Invalidate(new SecretName("gate/alpha"));
        deferred.Invalidate(new SecretName("gate/beta"));

        Assert.Equal(1, lookups);
        Assert.Equal(2, inner.Evicted.Count);
    }

    [Fact]
    public void A_null_resolution_means_evictions_are_no_ops()
    {
        // Mirrors the reconciler's own contract: no invalidator registered, nothing to evict.
        var deferred = new DeferredSecretCacheInvalidator(() => null);

        deferred.Invalidate(new SecretName("gate/alpha"));
    }
}
