using ZB.MOM.WW.GalaxyRepository;
using ZB.MOM.WW.GalaxyRepository.Grpc;
using ZB.MOM.WW.MxGateway.Server.Sessions;

namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Sessions;

public sealed class ArrayAddressNormalizerTests
{
    /// <summary>Verifies a bare array attribute name gains the trailing array suffix.</summary>
    [Fact]
    public void Normalize_BareArrayName_AppendsArraySuffix()
    {
        ArrayAddressNormalizer normalizer = CreateNormalizer();

        Assert.Equal("Obj.Arr[]", normalizer.Normalize("Obj.Arr"));
    }

    /// <summary>Verifies an already-suffixed address is returned unchanged.</summary>
    [Fact]
    public void Normalize_AlreadySuffixed_ReturnsUnchanged()
    {
        ArrayAddressNormalizer normalizer = CreateNormalizer();

        Assert.Equal("Obj.Arr[]", normalizer.Normalize("Obj.Arr[]"));
    }

    /// <summary>Verifies a scalar attribute is returned unchanged.</summary>
    [Fact]
    public void Normalize_ScalarAttribute_ReturnsUnchanged()
    {
        ArrayAddressNormalizer normalizer = CreateNormalizer();

        Assert.Equal("Obj.Scalar", normalizer.Normalize("Obj.Scalar"));
    }

    /// <summary>Verifies an address absent from the cache is returned unchanged.</summary>
    [Fact]
    public void Normalize_UnknownAddress_ReturnsUnchanged()
    {
        ArrayAddressNormalizer normalizer = CreateNormalizer();

        Assert.Equal("Obj.Unknown", normalizer.Normalize("Obj.Unknown"));
    }

    /// <summary>Verifies null, empty, and whitespace addresses are returned unchanged.</summary>
    /// <param name="address">The blank address under test.</param>
    [Theory]
    [InlineData("")]
    [InlineData("   ")]
    public void Normalize_BlankAddress_ReturnsUnchanged(string address)
    {
        ArrayAddressNormalizer normalizer = CreateNormalizer();

        Assert.Equal(address, normalizer.Normalize(address));
    }

    private static ArrayAddressNormalizer CreateNormalizer()
    {
        IReadOnlyList<GalaxyObject> objects =
        [
            new GalaxyObject
            {
                GobjectId = 1,
                TagName = "Obj",
                ContainedName = "Obj",
                Attributes =
                {
                    new GalaxyAttribute
                    {
                        AttributeName = "Arr",
                        // Galaxy SQL already appends "[]" to array attribute references.
                        FullTagReference = "Obj.Arr[]",
                        IsArray = true,
                    },
                    new GalaxyAttribute
                    {
                        AttributeName = "Scalar",
                        FullTagReference = "Obj.Scalar",
                        IsArray = false,
                    },
                },
            },
        ];

        GalaxyHierarchyCacheEntry entry = GalaxyHierarchyCacheEntry.Empty with
        {
            Status = GalaxyCacheStatus.Healthy,
            Objects = objects,
            Index = GalaxyHierarchyIndex.Build(objects),
        };

        return new ArrayAddressNormalizer(new StubGalaxyHierarchyCache(entry));
    }

    private sealed class StubGalaxyHierarchyCache(GalaxyHierarchyCacheEntry current) : IGalaxyHierarchyCache
    {
        /// <summary>Gets the current cache entry.</summary>
        public GalaxyHierarchyCacheEntry Current { get; } = current;

        /// <summary>No-op stub refresh; the fixed <see cref="Current"/> entry never changes.</summary>
        /// <param name="cancellationToken">Token to cancel the asynchronous operation.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public Task RefreshAsync(CancellationToken cancellationToken) => Task.CompletedTask;

        /// <summary>No-op stub wait; the fixed <see cref="Current"/> entry is already loaded.</summary>
        /// <param name="cancellationToken">Token to cancel the asynchronous operation.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public Task WaitForFirstLoadAsync(CancellationToken cancellationToken) => Task.CompletedTask;
    }
}
